Some help with resources


I’m working on an integration for a client. Using some pointers I managed to use the system user flow to obtain an access token and I am able to call the REST API’s.

I however am having quite a hard time understanding a few things and hope someone can point me to the correct resources.

I have found the API reference documentation at:

I noticed OData (new for me) is used to perform selections and queries. The documentation also mentions carrier objects, archive and foreign keys. I know the concept of foreign keys from relational databases. I am struggling to create a mental picture to work with.

On top of the SuperOffice specific terms and techniques I am somewhat confused that the documentation mentions Persons and Companies while the API seems to have Persons and Contacts. This is probably a legacy situation (or I am seeing things wrong and Contacts does not contain Companies).

There are a few things I want to achieve:

  • Create, Read, Update and Delete Companies and Contacts
  • Export all (more than 11000) Companies and Contacts
  • Create, Update and Delete Sales


I am not necessarily  looking for direct answers. In order for me to grasp the section of SuperOffice that I need to work with I probably need to have the following information:

  • An overview of the relationship between entities
  • A guide into OData and how use that to select entities with their relations
  • An explanation of carrier objects and how they interact with the API (read and write)
  • An explanation of the archive concept


Any extra resource to help me get a better understanding would be extremely helpful.


Thank you.



RE: Some help with resources

A concrete example:


Results in an object with the following information:

  "Address": {
    "Postal": {
      "Address1": "test 1",

To select that field I am doing:${contextId}/api/v1/Contact/1?$select=Address/Postal/Address1

This however results in something where Address is empty.

Another concrete example.

I am posting the following object:

        Name: 'test api contact 4',
        Number1: 'debtor code',
        Category: {
          Id: 9,
        Business: {
          Id: 13,
        Phones: [
            Value: '0123456789'
        Emails: [
            Value: ''
        Urls: [
            Value: 'test-url-api'
        OrgNr: '12345678',
        Country: {
          CountryId: 528
        Street: {
          Address1: 'bezoekadres 1a',
          City: 'stad',
          County: 'land',
          Zipcode: '1234AB',
        Postal: {
          Address1: 'postadres 2b',
          City: 'stad2',
          Zipcode: '2345BC',

The Street and Postal value however remain empty. No indication of any error.

I am probably doing something wrong and am missing some crucial piece of information.
Av: Erik Westra 2. apr 2020

RE: Some help with resources

A response from the support desk:

yes, company which is stored in contact table and contact which is stored in person table is indeed legacy and confusing, we are fighting legacy here:

At the bottom of each table you will see the relationships between tables.


Also - take a look at this forum thread:

Av: Erik Westra 2. apr 2020

RE: Some help with resources

Using the provided resources I was able to solve one of the concrete cases: selecting a specific field.

From what I understand the OData query string parameters trigger the call to be handled differently. So I looked at the FindContact archive provider (

Here I found the field names were quite different, so I tried the following:

This did not help, but then I noticed the _Links object in the response. This stated that the Archive was at another location. This got me to the following:
GET${contextId}/api/v1/Contact?$filter=contactId eq 7&$select=streetAddress/line1
Which returns the information I requested.
I am not sure what is recommended. I could just use GET /api/v1/Contact/7 to obtain everything.
Av: Erik Westra 2. apr 2020

RE: Some help with resources

The OData is a wrapper around what we call archive providers, which provide search and multiple column results.

The list of columns for /api/v1/contact is here:


Archive column names are not 1-1 with entity field names because they developed independently of each other.

So to get the postal address1 field, you would get this URL:



Av: Christian Mogensen 2. apr 2020

RE: Some help with resources

Hi Erik,

All good questions.

1. An overview of the relationship between entities

Yes, Company == Contacts and Contact == Person. This is indeed a legacy issue

2. An explanation of the archive concept

I want to address this one before I do OData because they are somewhere related.

I have an extensive article about archive providers on the community site, so I will just link to it. Basically, these are the mechanism used to perform search queries. If you do not specify any return fields/columns, there are a couple defaults that are added. You obtain all available columns by first determining what entity you primarily want to search for. Then, locate the appropriate archive provider in the documentation to help determine what fields/columns you would like returned. I.e. FindContactPersonFindSaleFindDocument, etc.

Here is the FindContact archive provider page.

A couple things to note:

  1. The name is how archive providers are referenced
  2. Some archive providers expose several entities, and by specifying them, or excluding one, can be used for pre-filtering results.
  3. The column names represent the selectable fields in the results. 
  4. Each column has a datatype that will determine which restriction operator you can use towards it.
  5. Not all columns can be used to sort/orderby on.


3. A guide into OData and how use that to select entities with their relations

When viewing the REST WebAPI documentation, there is an overview of everything you can do with specific entities. These include:

  • GET
  • POST
  • PUT

Only when viewing an entity GET method, without parameters, is there the option to submit an OData query. As the image below shows, the GET Contact endpoint describes everything you need to know to make that query. SCROLL down the page for more details and examples.


4. An explanation of carrier objects and how they interact with the API (read and write)

If you already know the id value of the entity you want to work with, using the appropriate REST endpoint to get that entity, i.e. GET api/v1/Contact/321

If you do not know the id value of the entity to work with, perform a search with the archive providers.

When you create new entities, ALWAYS call the default endpoint first to get a repopulate entity to work with. 

Use the POST endpoint to create the new create the new entity.

Use the PATCH endpoint when you only want to update one or a few properties of an entity.

If you use PUT to update an entity, and leave out some crucial fields in the PUT body, those left-out fields will be deleted from the entity. Therefore, use PATCH instead. Only use PUT to update complete entities.

When you want to export a large number of entities, use the archive providers to obtain the desired entity information. Archive results are flat, so you can not expect to the "many"-side of a one-to-many relationship if you are using the Archive endpoint.

All online development should start with reading the Create Apps section on the community site. There we present everything you could want to know, from authentication to publishing in production, about development towards SuperOffice CRM online. 

Working with Address information used to be very complicated. With searching, you must specify all the fields you want returned, not just Address... With regards to adding or modifying address information, you must use the entity, and stick to only populating the Street or Postal properties and you should be OK. Example can be found here in the forum.

I hope this helps clarify a few things. 


Av: Tony Yates 2. apr 2020

RE: Some help with resources

Hi Tony,

do you happen to have some resources about the concept of carrier objects within SuperOffice?




Av: Erik Westra 3. apr 2020

RE: Some help with resources

HI Erik,

Sure. Not alot, but there is about carriers here in the docs.  The docs [examples] focus on the SOAP api, but the concepts are the same throughout the API. 

Hops this helps!

Av: Tony Yates 3. apr 2020

RE: Some help with resources

I'm currently figuring out how to create an export of all contacts and persons. It seems there are a few routes to achieve this, I can not test this with production amounts of data so I need your advise on this.


I could call the GET /api/v1/Contact (FindContact archive) and the GET /api/v1/Person (Person archive) endpoints. Then I have 2 options (as far as I understand):

  1. Set getAllRows to return everything - not sure how I can do that from this endpoint
  2. Use $top and $skip to do some form of paging - it is unclear how to determine the total number of rows (I could just repeat until I get no results)

The production database contains (at the moment) about 11.000 persons. My guess is that option 2. would be the best as it gives me the flexibility to trottle and will be more future proof.


Another option would be to use something like POST /api/v1/Agents/Find/FindFromRestrictionsColumns to query a different archive provider. Here I could use PageSize and PageNumber to do a form of paging.


The two methods are wildly different, so I need to pick an approach and be sure it's the correct one to prevent unnescessary rework. My questions:

  • Which approach would you advise?
  • Is there any documentation about (the different types) of pagination?


Thank you.

Av: Erik Westra 8. apr 2020

RE: Some help with resources

Hi Erik,

Ever heard the expression "six of one, half a dozen of the other"? It's really no difference between these approaches. It all depends on which on your prefer.

That said, the first approach, using the OData format, can be seen as a wrapper over the second approach. 

A couple points:

  • Not sure why you would need to use both Contact and Person endpoints, as the FindContact provider includes all 
  • An exception will be thrown if there is no restriction. Therefore, "getAllRows" can be used as a Restriction operand. Yes, you have to explicitly include one Restriction in order to exclude any restrictions, i.e. no WHERE clause.
    OData queries include getAllRows by default.

Yes, you can use top/skip or pagesize/pagenumber for paging. Loop over the results until the result are less then the top/pagesize value, or total count. You can get the total count by including an aggregate field in the select fields, i.e. $select=Count(contactId). There are some examples of aggregates in the archive providers article

Hope this helps!

Av: Tony Yates 8. apr 2020

RE: Some help with resources

Thank you for the pointers Tony.

About the FindContact and Person endpoint, the documentation of FindContact lists only `contact` as entity, that might have influenced my thinking.

Once scrolling down I noticed indeed some person fields. I'll have to do some tests to see if enough of the information is present.

Av: Erik Westra 8. apr 2020

RE: Some help with resources

Not sure why you would need to use both Contact and Person endpoints, as the FindContact provider includes all 

I might be missing something, I don't see how I can retrieve the contacts (persons) using the FindContact archive provider. I don't see the name field for example.

Av: Erik Westra 8. apr 2020

RE: Some help with resources

Hi Erik,

Ah, I didn't complete my thought on that first bullet point. The Person archive contains the both Person and Contact data. If you want all known data about each, yes calling both would be best. 

Best regards.

Av: Tony Yates 8. apr 2020

RE: Some help with resources

I am reading through the Create Apps section and it is unclear what steps need to followed for my specific application.

I will create a set of services that respond to webhooks and call API methods (authenticated using the system user token). There seem to be lots of different integration points for SuperOffice, some of which are run inside the SuperOffice environment. For these applications I can see that things like validation endpoints are very relevant, this is not the case for this integration.

It seems I am creating what you call in the documentation a 'Custom App'.

A few questions:

  • What steps are required for my application to eventually move to Production?
  • How do I get from Development to Stage and from Stage to Production?
  • I eventually need to perform tests with production data, is it possible to move data from the eventual production environment to a development or stage environment?
  • My service will consist of 2 stages: initial import (2 times) and a realtime sync (after the initial import). How is this going to be validated?
  • A lot of the requirements do not apply to the set of services we will create, how do we determine what set of requirements are applicable?


Thank you!



Av: Erik Westra 15. apr 2020

RE: Some help with resources

Hi Erik,

I see the requirements for custom app points to the standard app requirements, we'll fix this on the page here.

If you fill in the form for validation, there's a checkbox so you verify that you're actually protecting the database since you are using a system user. The system user have unlimited access to the database, meaning if you are not careful you could remove persons linked to associates or even worse update the owner company name which will cause license validation to fail and users will be locked out of the system. 

Here you will find an image that describes the road, as you see we take custom apps from SOD directly to Production after we run a quick validation:


Av: Margrethe Romnes 15. apr 2020