VobeSoft

VobeSoft API

Guide for connecting your own systems, version 1

API version 1 30 October 2026

1Introduction

The VobeSoft API lets your own systems read and change the data of your VobeSoft environment: collections, items, the rows of their lists and their files.

Addresshttps://www.vobesoft.nl/vobesoft/api/external/v1, or the address of your organization's own VobeSoft environment (6.1)
AuthenticationAn API key in the X-Api-Key header
FormatJSON; files as multipart/form-data
Dates and timesISO 8601 in UTC, for example 2026-10-01T09:30:00Z
PagingpageIndex from 0, pageSize 25 by default, at most 50
Supportsupport@vobesoft.com, with the correlation id of the call (3.3)

With the API your systems can:

  • read the structure of your collections: their categories and their fields;
  • read items with their values, and the rows of their lists;
  • search items and rows by their values and moments, and ask what changed since your last search;
  • create, update and delete items and rows;
  • send files with an item or a row, and download them.

1.1 Version 1 and how it changes

This guide describes version 1; every path contains /v1/. Within version 1:

  • a response may gain properties, and an error code or a field type may gain values. Ignore properties you do not know, treat an unknown error by its HTTP status, and leave fields of an unknown type alone;
  • a property is never removed or renamed, and neither its meaning nor the status codes of an endpoint change.

A change that would break this becomes version 2, with its own paths, offered next to version 1 until clients have moved.

2Getting started

From a new key to a first item in five steps: one in VobeSoft, four calls.

  1. Create a key in Settings, API (section 3.1). Its form shows the endpoints that are open for your organization: choose those your system needs, and give the key permissions on the collections it works with (section 4).
  2. List the collections the key may read with GET /collections (8.1), and note the id of the one you need.
  3. Read its structure with GET /collections/{collectionId} (8.2). It gives the ids of its categories and fields.
  4. Create an item with POST /collections/{collectionId}/items and the values of its fields (10.1). The response gives the id of the new item.
  5. Read the item back with GET /collections/{collectionId}/items/{itemId} (9.1).
Work with ids

Collections, categories, fields, items and rows are identified by number. Ids never change; names can be changed by your organization at any time. Your system learns an item's id when it creates the item, from a search (chapter 15), from a linked item field, or from the rows of a list. Keep the ids your system needs.

3Authentication

Every call identifies itself with an API key: a secret token that an administrator of your organization creates in VobeSoft. The key decides which endpoints the call may use and which data it may see and change.

A key belongs to your organization, not to the person who created it. Changes made with a key show in the history of an item as changes made by the API.

3.1 Creating a key

Keys are created in VobeSoft by a user whose role may manage API keys. A key is never sent to you by VobeSoft: your organization creates and controls its own keys.

  1. In VobeSoft, open Settings and choose API. The page lists the keys of your organization and what they used of your budget.
  2. Choose New API key and give the key a name that tells which system uses it, for example CRM synchronization.
  3. Choose an expiry date, at most two years from today. The key works up to and including that day.
  4. Choose the endpoints the key may call, and give it permissions on the collections and categories it needs. Section 4 explains both.
  5. If you want, give the key limits of its own below your organization's budget, so one system can never use up the budget of the others. Section 5 explains limits.
  6. Choose Create key. VobeSoft shows the key once. Copy it and store it where your system reads its secrets.
The key is shown only once

VobeSoft keeps only a fingerprint of the secret part of a key, never the key itself, so nobody can show it to you again. If a key is lost, create a new one and disable the old one.

3.2 What a key looks like

A key is a single line of 55 characters: a prefix of 11 characters, a dot and a secret of 43 characters.

vs_Q3mT9xLc.Q3mT9xLcK2pV7wNf0sHqR4aYbE8uZ1jD6gW5oPiMnAt
Prefix: vs_ and the first 8 characters of the secret. VobeSoft shows it in the list of keys, so you can tell keys apart. Secret: only your system knows it in full.

Send the whole line, prefix and dot included, exactly as VobeSoft showed it.

3.3 Sending the key

Send the key in the X-Api-Key header of every request, over HTTPS. There is no login call and no session: each request stands on its own.

curl
curl "https://www.vobesoft.nl/vobesoft/api/external/v1/collections" \
  -H "X-Api-Key: $VOBESOFT_API_KEY"

When the key is accepted, the call responds as usual. This one lists the collections the key may read:

Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Correlation-Id: 7c4f2a9e-1b3d-4e8a-9c21-7d5e0f6a3b12

{
  "totalCount": 3,
  "results": [
    { "id": 3100, "name": "Advisors" },
    { "id": 3200, "name": "Customers" },
    { "id": 3300, "name": "Policies" }
  ],
  "pageIndex": 0,
  "pageSize": 25
}
Every response has a correlation id

Each response, an error or not, carries an X-Correlation-Id header; an error body repeats it. Log it with every call. When you contact VobeSoft support at support@vobesoft.com about a call, the correlation id lets support find exactly that call.

The key is only read from the X-Api-Key header. A key in the address, in a query parameter or in an Authorization header is not read, and the call is treated as one without a key. Send the header once: a request with two X-Api-Key headers is refused.

3.4 When a key is refused

A call whose key cannot be used returns 401 with the code UNAUTHENTICATED. The response is the same whatever the reason:

Response
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
X-Correlation-Id: 0b17ad0c-eccc-43d7-b7bb-771436fb94e5

{
  "code": "UNAUTHENTICATED",
  "message": "The API key is missing, invalid, disabled, or expired.",
  "correlationId": "0b17ad0c-eccc-43d7-b7bb-771436fb94e5"
}
Situation Response What to do
No X-Api-Key header, an empty one, or two of them 401 UNAUTHENTICATED Send the key once, in the header.
The value is not a key, or not a key VobeSoft knows 401 UNAUTHENTICATED Check that the whole key is sent, without spaces or line breaks. If it was lost, create a new key.
The key is disabled 401 UNAUTHENTICATED An administrator can enable it again in Settings, API.
The key's expiry date has passed 401 UNAUTHENTICATED Create a new key; the list of keys shows the old one as expired.

While your environment is in maintenance, a call with a valid key returns 503 MAINTENANCE. Wait the number of seconds in the Retry-After header (300) and try again.

An accepted key can still be refused a call, when the endpoint is not granted to it or a limit is reached. Sections 4 and 5 explain those responses.

3.5 The life of a key

  • Expiry. Every key has an expiry date at most two years ahead. After that day every call with the key is refused. Before then, an administrator can move the date by editing the key, to at most two years from the day of the edit.
  • Disabling and enabling. An administrator can disable a key in Settings, API, and enable it again later. Both take effect from the next call.
  • Changing a key. Changes to a key's name, expiry date, endpoints, permissions and limits also apply from the next call. The key itself, the line your system sends, never changes.
  • Seeing use. The list of keys shows when each key was last used and what it used of your budget. A key's Access log lists its calls, newest first, each with its status, error code and correlation id.

Replacing a key without downtime

Your organization can have several keys at the same time. To replace a key, for example before it expires:

  1. Create a new key with the same endpoints, permissions and limits.
  2. Give the new key to your system and check that its calls succeed.
  3. Disable the old key.

All keys of your organization share one budget, so the old and the new key together never get more calls than one key would (section 5).

4Endpoints and permissions

A call succeeds only when it passes three checks: the endpoint is open for your organization, the key has the endpoint, and the key has permission on the data.

Check Set by Response when it fails
The endpoint is open for your organization VobeSoft; the key form in Settings, API shows only open endpoints 403 ENDPOINT_NOT_AVAILABLE
The key has the endpoint Your administrator, in the key's form 403 ENDPOINT_NOT_ALLOWED
The key has permission on the data Your administrator, in the key's permissions 404 NOT_FOUND for data it may not read, or may not write at all; 403 PERMISSION_DENIED for a part of a write it may not make (4.2)

4.1 Endpoints

An endpoint is a group of calls that a key is given as a whole. The names are those of the key form.

Endpoint Allows Calls
Collection structure Reading the list of collections, and one collection with its categories and fields 8.1, 8.2
Categories Reading one category of a collection with its fields 8.3
Read items Reading one item with its values, and the rows of its lists 9.1, 9.2, 9.3
Search items Finding the items of a collection by their values 15.1
Search rows Finding the rows of a list by their values, over a collection or within one item 15.2
Create items Creating an item with its values 10.1
Update items Changing the values you name of an item 10.2
Delete items Deleting an item 10.3
Add rows Adding a row to a list of an item 11.2, 11.3
Update rows Changing the values you name of a row 11.4
Delete rows Deleting a row 11.5
Download files Getting a download link to one file, valid for 5 minutes 12.4

When VobeSoft closes an endpoint for your organization, every key is refused that endpoint from then on. The keys keep it, and it works again when the endpoint opens.

4.2 Permissions

A key's permissions are set per collection, per category and per list category, as for a role in VobeSoft.

Permission on Read Create Update Delete
Collection The collection, its structure and its items Create items Change items, and send values or files with a new item Delete items
Category The values of its fields Not offered Write the values of its fields Not offered
List category Its rows Add rows Change rows, and send values or files with a new row Delete rows

The key form saves only a consistent set: create, update and delete each come with read on the same collection, category or list category; a permission on a category needs the same permission on its collection; a permission on a list category needs read on its collection.

What each call needs

Call Needs
Read the structure (8.1, 8.2) Read on the collection; a collection shows only its categories with read
Read a category (8.3) Read on the collection and the category
Read an item (9.1) Read on the collection; the item shows the values of its categories with read
Read rows (9.2, 9.3) Read on the collection and the list category
Search items (15.1) Read on the collection; an item shows the values of its categories with read. Read on the category of every field the search names
Search rows (15.2) Read on the collection and the list category. Read on the category of every field the search names
Create an item (10.1) Read and create on the collection. With values or files, also update on the collection, and read and update on the category of each field written
Update an item (10.2) Read and update on the collection, and on the category of each field written
Delete an item (10.3) Read and delete on the collection
Add a row (11.2, 11.3) Read on the collection; read and create on the list category, and update on it for values or files. A linking list also needs the other side (11.1)
Update a row (11.4) Read on the collection; read and update on the list category. A many to many list also needs the other side (11.1)
Delete a row (11.5) Read on the collection; read and delete on the list category. A linking list also needs the other side (11.1)
Download a file (12.4) Read on the item or row that holds it: its collection, and the category or list category of its field
Not found or not allowed

Data the key may not read returns 404 NOT_FOUND, as data that does not exist does. A write the key may not make at all returns 404 as well, for example an update of an item whose collection the key may read but not update. A write the key may make, but not in every part, returns 403 PERMISSION_DENIED: a field it may not write, or a permission it misses on the other side of a linking list.

The API checks the collection, the item or row and the key's permission for the write on them before it reads the body of a write. A write that fails there gets its 404 even when its body is also wrong.

5Limits

VobeSoft limits how many calls your organization makes per endpoint, how many calls one key runs at the same time, and how long one call may take.

5.1 Three windows

Every endpoint has its own limits over three windows:

Window What counts Frees up
Minute Every call that ended in the last 60 seconds, except calls refused at the endpoint check or because a limit was reached Continuously: a call stops counting 60 seconds after it ended
Day Successful calls (status 2xx) since 00:00 UTC At 00:00 UTC
Month Successful calls since the first day of the month, 00:00 UTC At 00:00 UTC on the first day of the next month

A call takes its place in the minute when it ends, so a long call counts in the minute in which it finishes. A day may be unlimited; a minute and a month always have a limit. Settings, API shows the limits of every endpoint and what your organization used of them.

5.2 One budget for all keys

The limits belong to your organization and are shared by all its keys: two keys reading items share the same minute, day and month limits. Your administrator can give a key a lower limit of its own for an endpoint, so one system can never use the whole budget. Such a key is refused when either its own limit or the organization's is reached.

VobeSoft can also set a total for your organization over all endpoints together, per minute, day or month. It counts the calls of all your keys on every endpoint in the same way as the windows of 5.1. When a window of the total is used up, every endpoint is refused until that window frees up. Settings, API shows the total and what your organization used of it when your organization has one.

5.3 Calls at the same time

One key runs only a limited number of calls at the same time, over all endpoints together; Settings, API shows the number. A call over it is refused with 429 CONCURRENCY_LIMIT_REACHED and Retry-After: 1, without the limit headers of 5.4, and counts toward no window. Send the calls of one key one after another, or wait until one of its running calls has finished. Each key counts only its own calls, so another key of your organization is not held back.

5.4 Limit headers

The response to a call that passes the endpoint checks of section 4 carries the state of the tightest minute limit for that endpoint: the organization's, the key's own, or your organization's total over all endpoints:

Response headers
RateLimit-Limit: 60;w=60
RateLimit-Remaining: 59
RateLimit-Reset: 60
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 60
Header Meaning
RateLimit-Limit The limit, and after w= the window in seconds
RateLimit-Remaining Calls left in the window
RateLimit-Reset Seconds until a call frees up
X-RateLimit-… The same values for clients that read the older header names

A 429 response (5.5) carries these headers for the limit that was reached instead, except CONCURRENCY_LIMIT_REACHED, which has none (5.3). After the daily limit, for example, RateLimit-Limit shows the day's limit with w=86400, RateLimit-Remaining is 0 and RateLimit-Reset gives the seconds until 00:00 UTC. The headers do not show what is left of the day and the month before a limit is reached; Settings, API does.

5.5 When a limit is reached

Response When Retry-After
429 RATE_LIMITED The minute is used up Seconds until the call of the minute that ended first stops counting
429 DAILY_QUOTA_EXCEEDED The day is used up Seconds until 00:00 UTC
429 MONTHLY_QUOTA_EXCEEDED The month is used up Seconds until the next month starts
429 CONCURRENCY_LIMIT_REACHED The key already runs as many calls at the same time as it may (5.3) 1

Wait at least the seconds of Retry-After before calling again. A call refused for a limit does not count toward your limits, but it is refused again until the window frees up, or for calls at the same time until one of the key's running calls has finished. A used up minute, day or month of one endpoint leaves the other endpoints free. Your organization's total (5.2) refuses every endpoint with the same code, and calls at the same time (5.3) count over all endpoints.

5.6 How long a call may take

A call has a time limit of one minute from the moment VobeSoft takes it up, which for a small body is once the body has arrived. When it is reached:

  • a body that has not fully arrived returns 408 REQUEST_TIMEOUT and the connection closes; nothing is written;
  • a search that is not answered yet returns 504 PROCESSING_TIMEOUT; narrow it with more conditions.

Every other call, and a create or update whose body has arrived, is carried out in full, also when it takes longer.

6Requests and responses

6.1 Paths

Every path starts with https://www.vobesoft.nl/vobesoft/api/external/v1 and names the data in the words of this guide: collections, their categories, their items, the rows in a list category of an item, and files. Inside a body, a field is named by its fieldId. When your organization works in a VobeSoft environment of its own, use that environment's address instead; VobeSoft tells you which.

6.2 JSON

  • Bodies are JSON in UTF-8 with names in camelCase. Send Content-Type: application/json, or multipart/form-data when you send files (section 12). A body, or a part of one, in another character set than UTF-8, or with bytes that are no UTF-8, returns 400.
  • A body takes only the members this guide names, each once, and every member it needs, such as a value's fieldId. Anything else, such as value instead of values, returns 400 VALIDATION_FAILED, and details tells where in the body it is. Names match whatever their case.
  • Ids are whole numbers; a user is identified by a GUID. An id in a body that does not fit in 64 bits returns 400.
  • An empty value is null. In a write, null clears a value.
  • A JSON body is at most 1 MB, the body of a search at most 64 KB (15.3), and a multipart/form-data body at most 50 MB (12.1); a larger one returns 413 REQUEST_TOO_LARGE, at once when its Content-Length says so, else as soon as more has arrived. A part of a multipart/form-data body that holds JSON is at most 1 MB as well, and such a body has at most 25 parts.

6.3 Dates and times

Every date and time the API returns is an ISO 8601 moment in UTC, ending in Z, for example 2026-10-01T12:15:17.655Z. In a request, send ISO 8601 in one of these forms:

  • a date, 2026-10-01, which is read as 00:00 UTC on that day;
  • a date and time, 2026-10-01T09:30 or 2026-10-01T09:30:00, optionally with a fraction of a second, 2026-10-01T09:30:00.655.

A time may end in Z or an offset such as +02:00; without either it is read as UTC. Any other form, such as 01-10-2026, 1/10/2026 or 09:30, returns 400, so a day and a month are never swapped.

A date field holds a day. VobeSoft's screens show it without a time and store the chosen day as 00:00 UTC, so 1 October reads as 2026-10-01T00:00:00Z. Send a date field's value as a date, such as 2026-10-01.

A dateRange chosen in VobeSoft's screens holds 00:00 in the time zone of the person who chose it instead. In the Netherlands a range from 1 October therefore reads as "from": "2026-09-30T22:00:00Z": two hours before UTC midnight in summer, one hour in winter. Take the day of such a moment in the Netherlands' time zone. A range your system sends as dates, such as 2026-10-01, shows as 1 October in the Netherlands.

Other moments, such as a creation date, show in the viewer's time zone. In the Netherlands that is one hour later than UTC in winter and two hours later in summer.

6.4 Paging

A call that returns a list returns one page of it:

Query parameter Default Meaning
pageIndex 0 The page, counted from 0
pageSize 25 Results per page, at most 50; a larger value returns pages of 50, and the response's pageSize then shows 50

The response gives the results of the page and totalCount, the number of results over all pages, so you know how many pages there are. This key may read three collections; asking for pages of two, the second page holds the third:

GET /collections?pageIndex=1&pageSize=2
{
  "totalCount": 3,
  "results": [
    { "id": 3300, "name": "Policies" }
  ],
  "pageIndex": 1,
  "pageSize": 2
}

The number of pages is totalCount divided by pageSize, rounded up. A page past the last one is empty. A negative pageIndex or a pageSize below 1 returns 400 VALIDATION_FAILED.

6.5 What a create returns

A create returns 201 with the id of the new item or row in the body, and its path in the Location header. The path is relative to the server: resolve it against the address you called.

Response
HTTP/1.1 201 Created
Location: /vobesoft/api/external/v1/collections/3300/items/8001
Content-Type: application/json; charset=utf-8

{ "id": 8001 }

An update and a delete return 204 without a body.

7Errors

Every error has the same body, with a fixed code to act on and the correlation id of the call.

Response
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Correlation-Id: b500875a-6d03-49f8-a543-ee4cfbf560e9

{
  "code": "VALIDATION_FAILED",
  "message": "The request is invalid.",
  "correlationId": "b500875a-6d03-49f8-a543-ee4cfbf560e9",
  "details": "field 5302: expected a text"
}
Property Meaning
code What went wrong, one of the codes below. Act on this, not on the message.
message A fixed English text for the code.
correlationId The id of the call, as in the X-Correlation-Id header.
details Only when the request itself is wrong, writes a read only list category, or adds a row to a full list (section 11): what is wrong, naming fields, categories, positions and ids. A position counts from 1: user 2 is the second entry of a value's list, upload 1 the first file sent for a field.

7.1 Codes

Status Code Meaning What to do
400 VALIDATION_FAILED The request is wrong; details tells what Correct the request
401 UNAUTHENTICATED The key is missing, invalid, disabled or expired Section 3.4
403 ENDPOINT_NOT_AVAILABLE The endpoint is not open for your organization Section 4
403 ENDPOINT_NOT_ALLOWED The key does not have the endpoint Give the key the endpoint
403 PERMISSION_DENIED The key may read the data but not make this write, or the list category is read only Give the key the permission (4.2)
403 WRITE_REFUSED Your organization's rules refused the write. The response does not say which rule. Ask your administrator, or support@vobesoft.com with the correlation id, which rule applies
404 NOT_FOUND It does not exist, or the key may not read it Check the ids and the key's permissions
405 METHOD_NOT_ALLOWED The path does not take this method Appendix A
408 REQUEST_TIMEOUT The body did not fully arrive within the time limit of a call (5.6); nothing was written Send it again, with less per call or over a faster connection
409 ALREADY_LINKED The item is already linked in this list Nothing; it is there already
409 LIST_FULL The write would add a row to a list that holds its maximum of rows; details names the list and the item Delete a row of that list first, or ask your administrator about the maximum (11.1)
413 REQUEST_TOO_LARGE The body is above 1 MB for JSON, above 64 KB for a search, or above 50 MB for multipart/form-data, or a part of it that holds JSON is above 1 MB Send less per call: fewer values or conditions, or fewer or smaller files
429 RATE_LIMITED The minute limit is reached Wait Retry-After (5.5)
429 DAILY_QUOTA_EXCEEDED The day limit is reached Wait Retry-After (5.5)
429 MONTHLY_QUOTA_EXCEEDED The month limit is reached Wait Retry-After (5.5)
429 CONCURRENCY_LIMIT_REACHED The key already runs as many calls at the same time as it may Wait until one of its calls has finished (5.3)
500 INTERNAL_ERROR An unexpected error Try again later; contact support@vobesoft.com with the correlation id if it keeps happening
503 MAINTENANCE Your environment is in maintenance Wait Retry-After
504 PROCESSING_TIMEOUT The search was not answered within the time limit of a call (5.6) Narrow the search; contact support@vobesoft.com with the correlation id if it keeps happening

7.2 What to try again

  • 429 and 503: try again after the seconds of Retry-After.
  • 408: nothing was written, so the same call can be sent again.
  • 504: the same search sent again takes as long; narrow it first.
  • 500, or no response at all: try again after a pause, a few times at most, keeping in mind what a repeat does:
    • a create that is sent again can create a second item or row;
    • an update sent again is harmless for values, but adds its files and links a second time, as files and links are added to what a field holds (12.1);
    • a delete sent again returns 404 when the first one went through: the item or row is gone, as intended.
  • Every other error: do not send the same request again; it gets the same response.

8Collections and categories

The structure calls tell your system which collections, categories and fields exist, and their ids.

8.1 List the collections

GET/collections
Endpoint Collection structurePermission read on the collection

The collections the key may read, sorted by name, one page at a time.

Query parameters

Name Type Meaning
pageIndex integer Optional. The page, from 0 (6.4).
pageSize integer Optional. 25 by default, at most 50.
Request
GET /vobesoft/api/external/v1/collections HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "totalCount": 3,
  "results": [
    { "id": 3100, "name": "Advisors" },
    { "id": 3200, "name": "Customers" },
    { "id": 3300, "name": "Policies" }
  ],
  "pageIndex": 0,
  "pageSize": 25
}

Responses

Status When
200 Always, also when the key may read no collection (totalCount 0)
400 VALIDATION_FAILED A negative pageIndex, or a pageSize below 1

8.2 Read a collection

GET/collections/{collectionId}
Endpoint Collection structurePermission read on the collection

One collection with the categories the key may read, each with its fields, sorted by their order.

Path parameters

Name Type Meaning
collectionId integer The id of the collection.
Request
GET /vobesoft/api/external/v1/collections/3300 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "id": 3300,
  "name": "Policies",
  "categories": [
    {
      "id": 4300,
      "collectionId": 3300,
      "name": "General",
      "isList": false,
      "isReadOnly": false,
      "order": 0,
      "maxItems": null,
      "fields": [
        { "id": 5300, "name": "Policy number", "type": "autoNumber", "isRequired": false, "isReadOnly": false, "order": 1 },
        { "id": 5301, "name": "Product", "type": "choice", "isRequired": false, "isReadOnly": false, "order": 2, "isMultiSelect": false, "choiceListId": 210,
          "choices": [
            { "id": 210001, "text": "Fleet", "isActive": true },
            { "id": 210004, "text": "Liability", "isActive": true },
            { "id": 210002, "text": "Travel", "isActive": false }
          ] },
        { "id": 5302, "name": "Title", "type": "text", "isRequired": false, "isReadOnly": false, "order": 3 },
        { "id": 5303, "name": "Notes", "type": "longText", "isRequired": false, "isReadOnly": false, "order": 4 },
        { "id": 5304, "name": "Start date", "type": "date", "isRequired": false, "isReadOnly": false, "order": 5 },
        { "id": 5305, "name": "Cover period", "type": "dateRange", "isRequired": false, "isReadOnly": false, "order": 6 },
        { "id": 5306, "name": "Account manager", "type": "employee", "isRequired": false, "isReadOnly": false, "order": 7 },
        { "id": 5307, "name": "Premium", "type": "number", "isRequired": false, "isReadOnly": false, "order": 8 },
        { "id": 5313, "name": "Direct debit", "type": "checkbox", "isRequired": false, "isReadOnly": false, "order": 9 },
        { "id": 5308, "name": "Created", "type": "creationDate", "isRequired": false, "isReadOnly": false, "order": 10 },
        { "id": 5309, "name": "Signature", "type": "signature", "isRequired": false, "isReadOnly": false, "order": 11 },
        { "id": 5310, "name": "Documents", "type": "file", "isRequired": false, "isReadOnly": false, "order": 12 },
        { "id": 5311, "name": "Customer", "type": "object", "isRequired": false, "isReadOnly": false, "order": 13, "collectionId": 3200 },
        { "id": 5312, "name": "Annual premium", "type": "formula", "isRequired": false, "isReadOnly": false, "order": 14 }
      ],
      "relation": null
    },
    {
      "id": 4301,
      "collectionId": 3300,
      "name": "Claims",
      "isList": true,
      "isReadOnly": false,
      "order": 1,
      "maxItems": null,
      "fields": [
        { "id": 5320, "name": "Description", "type": "text", "isRequired": false, "isReadOnly": false, "order": 1 },
        { "id": 5321, "name": "Amount", "type": "number", "isRequired": false, "isReadOnly": false, "order": 2 },
        { "id": 5322, "name": "Attachments", "type": "file", "isRequired": false, "isReadOnly": false, "order": 3 }
      ],
      "relation": null
    }
  ]
}

Properties of a category

Property Type Meaning
id integer The id of the category, used in the paths of rows.
collectionId integer The collection the category belongs to.
name string The name of the category.
isList boolean true for a list category: an item has rows in it instead of values.
isReadOnly boolean The category is marked as read only in VobeSoft. A value for one of its fields is refused (section 13), and a read only list takes no new, changed or deleted rows (section 11).
order integer Its place among the categories.
maxItems integer or null For a list, the most rows an item's list may hold; null for no maximum. A write that would add a row beyond it is refused (11.1).
fields list The fields of the category.
relation object or null For a list that links items of another collection, how it links (11.1); null otherwise.

Properties of a field

Property Type Meaning
id integer The id of the field: the fieldId of its values.
name string The name of the field.
type string The kind of value it holds (section 13).
isRequired boolean The field is marked as required in VobeSoft. A new item or row needs a value in it, and a write cannot clear it (section 13).
isReadOnly boolean The field is marked as read only in VobeSoft. A value for it is refused (section 13).
order integer Its place in the category.
isMultiSelect boolean Choice fields only: more than one choice may be chosen.
choiceListId integer or null Choice fields only: the shared choice list it uses; null when the field has choices of its own. Both kinds are read and written the same way, by choice id.
choices array Choice fields only: the choices it offers, each { id, text, isActive }, in the order your organization set for them. A value names a choice by its id (section 13). isActive is false for a choice your organization no longer offers; the API still accepts it, so check it before you offer the choice to your users.
collectionId integer Linked item fields only: the collection it links to.

Responses

Status When
200 The collection
404 NOT_FOUND No such collection, or the key may not read it

8.3 Read a category

GET/collections/{collectionId}/categories/{categoryId}
Endpoint CategoriesPermission read on the collection and the category

One category with its fields, in the shape of a category in 8.2.

Path parameters

Name Type Meaning
collectionId integer The id of the collection.
categoryId integer The id of one of its categories.
Request
GET /vobesoft/api/external/v1/collections/3300/categories/4301 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "id": 4301,
  "collectionId": 3300,
  "name": "Claims",
  "isList": true,
  "isReadOnly": false,
  "order": 1,
  "maxItems": null,
  "fields": [
    { "id": 5320, "name": "Description", "type": "text", "isRequired": false, "isReadOnly": false, "order": 1 },
    { "id": 5321, "name": "Amount", "type": "number", "isRequired": false, "isReadOnly": false, "order": 2 },
    { "id": 5322, "name": "Attachments", "type": "file", "isRequired": false, "isReadOnly": false, "order": 3 }
  ],
  "relation": null
}

Responses

Status When
200 The category
404 NOT_FOUND No such collection or category, or the key may not read it

9Reading items and rows

9.1 Read an item

GET/collections/{collectionId}/items/{itemId}
Endpoint Read itemsPermission read on the collection; values per readable category

One item with the values of every category the key may read. A list category gives only its number of rows; read the rows with 9.2.

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
Request
GET /vobesoft/api/external/v1/collections/3300/items/8001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "id": 8001,
  "collectionId": 3300,
  "createdAt": "2026-10-01T12:15:17.655Z",
  "lastEditedAt": "2026-10-20T08:42:11.3172946Z",
  "lastEditedBy": { "id": "3f2b9c1e-7a4d-4e0b-9b6a-1c2d3e4f5a6b", "name": "Mila Jansen" },
  "categories": [
    {
      "id": 4300,
      "name": "General",
      "isList": false,
      "values": [
        { "fieldId": 5300, "type": "autoNumber", "value": "POL-137" },
        { "fieldId": 5301, "type": "choice", "value": [ { "id": 210004, "text": "Liability" } ] },
        { "fieldId": 5302, "type": "text", "value": "Fleet policy Van Dijk" },
        { "fieldId": 5303, "type": "longText", "value": "Renewal agreed by phone." },
        { "fieldId": 5304, "type": "date", "value": "2026-10-01T00:00:00Z" },
        { "fieldId": 5305, "type": "dateRange", "value": { "from": "2026-10-01T00:00:00Z", "to": "2026-12-31T00:00:00Z" } },
        { "fieldId": 5306, "type": "employee", "value": [ { "id": "3f2b9c1e-7a4d-4e0b-9b6a-1c2d3e4f5a6b", "name": "Mila Jansen" } ] },
        { "fieldId": 5307, "type": "number", "value": 1250.5 },
        { "fieldId": 5313, "type": "checkbox", "value": true },
        { "fieldId": 5308, "type": "creationDate", "value": "2026-10-01T12:15:17.655Z" },
        { "fieldId": 5309, "type": "signature", "value": null },
        { "fieldId": 5310, "type": "file", "value": null },
        { "fieldId": 5311, "type": "object", "value": { "itemId": 7001 } }
      ]
    },
    {
      "id": 4301,
      "name": "Claims",
      "isList": true,
      "rowCount": 1
    }
  ]
}
  • A category gives its fields in the order of the structure, also those without a value (null). Only mirror and formula fields are left out (section 13).
  • A linked item shows only its id. Reading that item applies the key's permissions to it again, and may return 404.
  • lastEditedAt and lastEditedBy tell when and by whom the item was last edited (9.4).

Responses

Status When
200 The item
404 NOT_FOUND No such item in this collection, a deleted item, an item a user has not saved yet, or the key may not read it

9.2 Read the rows of a list

GET/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows
Endpoint Read itemsPermission read on the collection and the list category

The rows of one list category of an item, one page at a time, newest first: the row with the highest id comes first. Each row gives the values of the list category's fields, as an item does (9.1).

Parameters

Name In Meaning
collectionId, itemId path The item.
listCategoryId path The id of a list category of the item's collection.
pageIndex, pageSize query Optional; paging as in 6.4.
Request
GET /vobesoft/api/external/v1/collections/3300/items/8001/categories/4301/rows?pageIndex=0&pageSize=25 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "totalCount": 1,
  "results": [
    {
      "id": 9001,
      "itemId": 8001,
      "lastEditedAt": "2026-10-14T13:05:40.0281533Z",
      "lastEditedBy": { "id": "7dc27c45-fc87-4d43-9799-32b2aa34d004", "name": "External API" },
      "values": [
        { "fieldId": 5320, "type": "text", "value": "Windscreen damage" },
        { "fieldId": 5321, "type": "number", "value": 350.0 },
        { "fieldId": 5322, "type": "file", "value": null }
      ]
    }
  ],
  "pageIndex": 0,
  "pageSize": 25
}

Each row carries itemId, the item whose list holds it, and when and by whom the row itself was last edited (9.4).

In a list that links items, each row holds the link to its item; 11.3 shows such rows.

The rows include a row that a user is adding in VobeSoft and has not saved yet. Like any other row, it can be updated (11.4) and deleted (11.5).

A row added while your system reads the pages moves the later rows down by one, so the next page repeats a row; a deleted row makes the next page skip one. When totalCount changes between pages, read again from page 0.

Responses

Status When
200 The page, empty when the list has no rows
400 VALIDATION_FAILED A negative pageIndex, or a pageSize below 1
404 NOT_FOUND No such item or list category, or the key may not read them

9.3 Read one row

GET/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId}
Endpoint Read itemsPermission read on the collection and the list category

One row by its id, for example the row a create just made (its Location).

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
listCategoryId integer The id of a list category of the item's collection.
rowId integer The id of the row.
Request
GET /vobesoft/api/external/v1/collections/3300/items/8001/categories/4301/rows/9001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "id": 9001,
  "itemId": 8001,
  "lastEditedAt": "2026-10-14T13:05:40.0281533Z",
  "lastEditedBy": { "id": "7dc27c45-fc87-4d43-9799-32b2aa34d004", "name": "External API" },
  "values": [
    { "fieldId": 5320, "type": "text", "value": "Windscreen damage" },
    { "fieldId": 5321, "type": "number", "value": 350.0 },
    { "fieldId": 5322, "type": "file", "value": null }
  ]
}

Responses

Status When
200 The row
404 NOT_FOUND No such row in this list of this item, a deleted row, or the key may not read it

9.4 When an item or row was last edited

Every item and row tells when it was last edited and by whom, in the reads above and in the searches of chapter 15.

Field Type Meaning
lastEditedAt moment or null The moment of the last edit, in UTC (6.3). A new item or row has the moment it was made. null when it was not edited since VobeSoft began to record edits.
lastEditedBy object or null Who made that edit, { "id", "name" }: a user of your organization by name, a write through this API as External API whichever key made it, VobeSoft's other processes by their own name, and anyone else as Deleted user. null when no user was recorded.

An item's lastEditedAt moves when:

  • one of its fields changes, saved in VobeSoft's screens, through this API, or by an import that keeps history;
  • a row is added to, changed in or removed from one of its lists;
  • a row of a linking list is added, changed or removed on the other side: the linked item of a one to many row, and the paired row of a many to many row with that row's item.

A row's lastEditedAt moves when the row is added or changed; its item's moves with it. It does not move for:

  • a file added or linked in VobeSoft's document management;
  • a change saved without history, such as by some imports and background processes;
  • a value VobeSoft computes, such as a formula or mirror field, that changes because something else changed;
  • the link of an existing many to many row, the item an item leaves in a one to many list, the row an object field shows in the list of the item it links, and the rows of other items that go when an item is deleted;
  • reading.

A deleted item or row is not found any more; nothing reports its deletion.

10Creating, updating and deleting items

Your organization's rules apply to every write; a write they refuse is not saved.

10.1 Create an item

POST/collections/{collectionId}/items
Endpoint Create itemsPermission read and create on the collection; with values also update on the collection and on each written field's category

Creates an item with the values you send. Fields you do not send stay empty, or get their default value.

Path parameters

Name Type Meaning
collectionId integer The id of the collection to create the item in.

Request body

Property Type Meaning
values list Optional. Each entry a fieldId and its value in the shape of section 13. Every entry needs both; null as value is allowed.
createdFrom object Optional. The item this one is created from: fieldId, the field of the new item that links to it, and itemId, that item. See below.
Request
POST /vobesoft/api/external/v1/collections/3300/items HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{
  "values": [
    { "fieldId": 5301, "value": [210004] },
    { "fieldId": 5302, "value": "Fleet policy Van Dijk" },
    { "fieldId": 5303, "value": "Renewal agreed by phone." },
    { "fieldId": 5304, "value": "2026-10-01" },
    { "fieldId": 5305, "value": { "from": "2026-10-01", "to": "2026-12-31" } },
    { "fieldId": 5306, "value": ["3f2b9c1e-7a4d-4e0b-9b6a-1c2d3e4f5a6b"] },
    { "fieldId": 5307, "value": 1250.5 },
    { "fieldId": 5313, "value": true },
    { "fieldId": 5311, "value": { "itemId": 7001 } }
  ]
}
Response 201
HTTP/1.1 201 Created
Location: /vobesoft/api/external/v1/collections/3300/items/8001
Content-Type: application/json; charset=utf-8

{ "id": 8001 }

VobeSoft first checks every value; when one is wrong, nothing is saved and the call returns 400. Then it creates the item: the rules of your organization for a new item run, then the values are saved with the rules for a change. When a rule refuses the values, the new item is removed again and the call returns 403 WRITE_REFUSED. An automatic number the new item got is not given out again.

Every required field the key may write (isRequired, 8.2) needs a value in the new item: one you send, the field's default value, or one your organization's rules set when the item is created. When one stays empty, the new item is removed again before its values are saved, and the call returns 400 with field {id}: required. Required fields the key may not write are not asked.

Creating an item from another item

createdFrom adds the new item from a list of another item, for example a new policy from a customer's list of policies: the rules for adding an item from a list run, and the new item is linked to the other item through the field you name.

Request body
{
  "createdFrom": { "fieldId": 5311, "itemId": 7001 },
  "values": [
    { "fieldId": 5302, "value": "Fleet policy Van Dijk" }
  ]
}

The link is set also when the linking field is read only. Writing the linking field as one of the values, as in the first request of 10.1, also links the item, but as a change of the new item instead of an addition from the list. Name the field in one of the two, not in both: field 5311: named in createdFrom and in values.

Responses

Status When
201 Created; the body has the id, Location its path
400 VALIDATION_FAILED The body or a value is wrong; details names it, see below
403 PERMISSION_DENIED The key may not send values with a create (update on the collection), or not write one of the fields
403 WRITE_REFUSED Your organization's rules refused the item
404 NOT_FOUND No such collection, or the key may not read it or create items in it
409 LIST_FULL A link field, among the values or as createdFrom, would add the item to a full list of the other item (11.1)
413 REQUEST_TOO_LARGE The body is above its limit (6.2)

Examples of details:

Sent details
A number for a text field field 5302: expected a text
A value for an automatic number field 5300: fields of this type cannot be written
A field of another collection field 999999: not a field of this collection
A first user id that is not a GUID field 5306: user 1 is not a user id
A text of 501 characters field 5302: longer than 500 characters
Nothing for Title, had your organization marked it required field 5302: required
A value for Notes, had your organization marked it read only field 5303: read only

10.2 Update an item

PATCH/collections/{collectionId}/items/{itemId}
Endpoint Update itemsPermission read and update on the collection and on each written field's category

Changes only the fields you name; every other field keeps its value. null clears a field, except a required one: field {id}: required. A required field you do not name is not asked, also when it is empty.

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.

Request body

Property Type Meaning
values list Each entry a fieldId and its value (section 13).
Request
PATCH /vobesoft/api/external/v1/collections/3300/items/8001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{
  "values": [
    { "fieldId": 5307, "value": 1500 },
    { "fieldId": 5303, "value": null }
  ]
}
Response 204
HTTP/1.1 204 No Content

A read afterwards shows the premium as 1500.0 and the notes as null; the other values are unchanged.

Responses

Status When
204 Saved
400 VALIDATION_FAILED The body or a value is wrong; details names it (10.1)
403 PERMISSION_DENIED The key may not write one of the fields
403 WRITE_REFUSED Your organization's rules refused the change
404 NOT_FOUND No such item, or the key may not read it or update items here
409 LIST_FULL A link field would add the item to a full list of the other item (11.1)
413 REQUEST_TOO_LARGE The body is above its limit (6.2)

10.3 Delete an item

DELETE/collections/{collectionId}/items/{itemId}
Endpoint Delete itemsPermission read and delete on the collection

Deletes the item. The rows of its own lists go with it, and the item also leaves the lists of other items that link it.

When your organization's rules keep the item, the API returns 403 WRITE_REFUSED and the item stays.

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
Request
DELETE /vobesoft/api/external/v1/collections/3300/items/8001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 204
HTTP/1.1 204 No Content

Responses

Status When
204 Deleted
403 WRITE_REFUSED Your organization's rules kept the item
404 NOT_FOUND No such item, already deleted, or the key may not read or delete items here

11List rows

A list category holds rows. Some lists hold values of their own; others link items of another collection. How you add a row depends on the kind of list.

A list category your organization marks as read only in VobeSoft (isReadOnly in the structure, 8.2) takes no new, changed or deleted rows, whatever the key's permissions. Such a write returns 403 PERMISSION_DENIED with category {id}: read only, before its body is read.

11.1 Three kinds of list

The relation of a list category in the structure (8.2) tells which kind it is.

Plain list relation: null

Each row holds its own values, like the claims of a policy.

Add a row with its values (11.2).

One to many kind: "oneToMany"

Each row links one item of another collection, and that item is in one such list at most: its own field points back. Like a customer's policies, where each policy has one customer.

Add a row with link (11.3).

Many to many kind: "manyToMany"

Each row links an item of another collection, which has a list of its own pointing back. Like customers and their advisors: each list shows the other side.

Add a row with link (11.3).

The customers collection of the example has one list of each linking kind:

Two of the categories of GET /collections/3200
[
  {
    "id": 4201,
    "collectionId": 3200,
    "name": "Policies",
    "isList": true,
    "isReadOnly": false,
    "order": 1,
    "maxItems": null,
    "fields": [
      { "id": 5201, "name": "Policy", "type": "object", "isRequired": false, "isReadOnly": false, "order": 1, "collectionId": 3300 }
    ],
    "relation": {
      "kind": "oneToMany",
      "linkFieldId": 5201,
      "relatedCollectionId": 3300,
      "relatedFieldId": 5311,
      "relatedCategoryId": null
    }
  },
  {
    "id": 4202,
    "collectionId": 3200,
    "name": "Advisors",
    "isList": true,
    "isReadOnly": false,
    "order": 2,
    "maxItems": null,
    "fields": [
      { "id": 5202, "name": "Advisor", "type": "object", "isRequired": false, "isReadOnly": false, "order": 1, "collectionId": 3100 }
    ],
    "relation": {
      "kind": "manyToMany",
      "linkFieldId": 5202,
      "relatedCollectionId": 3100,
      "relatedFieldId": 5110,
      "relatedCategoryId": 4101
    }
  }
]
Property of relation Meaning
kind oneToMany or manyToMany.
linkFieldId The field of the row that holds the linked item.
relatedCollectionId The collection of the linked items.
relatedFieldId The field on the other side that points back: in one to many the linked item's own field (a policy's Customer), in many to many the field of the list on the other side.
relatedCategoryId Many to many only: the list on the other side (an advisor's Customers). null for one to many.
Policy 8001field Customer (5311)
⇄one to many
Customer 7001list Policies (4201)
Customer 7001list Advisors (4202)
⇄many to many
Advisor 7101list Customers (4101)

How the two sides stay in step

  • One to many. A row in the customer's list and the policy's Customer field are the same link. Adding the row sets the field; setting the field (10.1, 10.2) adds the row. Adding a policy that is in another customer's list moves it to this customer. Deleting the row clears the policy's field; the policy stays.
  • Many to many. Adding a row on one side adds the matching row on the other side; deleting either row deletes both. The items themselves stay.

Permissions for a linking list

Besides its permission on the list category (4.2), the key needs on the other side:

  • One to many, to add or delete a row: read on the other collection, and update on the category of the field that points back (the policy's Customer field).
  • Many to many, to add, update or delete a row: read on the other collection, and the same permission on the list on the other side (the advisor's Customers).

A maximum of rows

A list category with a maxItems (8.2) holds at most that many rows per item, counting every row that is not deleted, also one a user is still adding. A write that would add a row to a full list returns 409 LIST_FULL, naming the list and the item, for example category 4201 of item 7001: holds at most 1 row. Every way of adding a row counts:

  • adding a row to a plain or a linking list (11.2, 11.3);
  • the matching row a many to many add creates in the other item's list;
  • setting the link field of a one to many relation, as a value or as createdFrom (10.1, 10.2), which adds the item to the other item's list.

An item the list links already returns ALREADY_LINKED instead.

11.2 Add a row to a plain list

POST/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows
Endpoint Add rowsPermission read and create on the list category; with values also update

Adds a row with the values you send; your organization's rules for a new row apply. Every required field of the list the key may write needs a value in the new row, as for a new item (10.1).

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
listCategoryId integer The id of a list category of the item's collection.

Request body

Property Type Meaning
values list Optional. Each entry a fieldId of the list category and its value (section 13).
Request
POST /vobesoft/api/external/v1/collections/3300/items/8001/categories/4301/rows HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{
  "values": [
    { "fieldId": 5320, "value": "Windscreen damage" },
    { "fieldId": 5321, "value": 350 }
  ]
}
Response 201
HTTP/1.1 201 Created
Location: /vobesoft/api/external/v1/collections/3300/items/8001/categories/4301/rows/9001
Content-Type: application/json; charset=utf-8

{ "id": 9001 }

A plain list takes no link:

Response 400 to { "link": { "itemId": 7001 } }
{
  "code": "VALIDATION_FAILED",
  "message": "The request is invalid.",
  "correlationId": "70058f7a-c7f2-4a40-9411-84ac7f888bea",
  "details": "link: this list links no item; send the row's values"
}

Responses

Status When
201 Added; the body has the id of the new row, Location its path
400 VALIDATION_FAILED The body or a value is wrong, a required field stays empty, or a link is sent; details names it (10.1)
403 PERMISSION_DENIED The list category is read only (category 4301: read only), or the key sends values or files without update on the list category
403 WRITE_REFUSED Your organization's rules refused the row
404 NOT_FOUND No such item or list category, or the key may not read them or add rows to the list
409 LIST_FULL The list holds its maximum of rows (11.1)
413 REQUEST_TOO_LARGE The body is above its limit (6.2)

11.3 Add a row to a linking list

POST/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows
Endpoint Add rowsPermission read and create on the list category, and the other side (11.1)

Adds an existing item to the list. The body holds only link. When the list has more fields than the link, set them afterwards with an update of the new row (11.4); its required fields are not asked here. The link is set also when the list's link field is read only; a list category that is read only takes no rows at all (section 11).

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
listCategoryId integer The id of a list category of the item's collection.

Request body

Property Type Meaning
link object Required. itemId: the item to add, an item of the related collection that is not deleted.

One to many: a policy into a customer's list

Request
POST /vobesoft/api/external/v1/collections/3200/items/7001/categories/4201/rows HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{ "link": { "itemId": 8002 } }
Response 201
HTTP/1.1 201 Created
Location: /vobesoft/api/external/v1/collections/3200/items/7001/categories/4201/rows/9101
Content-Type: application/json; charset=utf-8

{ "id": 9101 }

The customer's list now holds both policies: 8002 just added, and 8001, whose Customer field was set when it was created (10.1). Policy 8002's Customer field now reads { "itemId": 7001 }. The examples of this section shorten each row to its id and values; a read gives the rest of 9.2 as well.

GET …/items/7001/categories/4201/rows
{
  "totalCount": 2,
  "results": [
    { "id": 9101, "values": [ { "fieldId": 5201, "type": "object", "value": { "itemId": 8002 } } ] },
    { "id": 9100, "values": [ { "fieldId": 5201, "type": "object", "value": { "itemId": 8001 } } ] }
  ],
  "pageIndex": 0,
  "pageSize": 25
}

Many to many: an advisor into a customer's list

Request
POST /vobesoft/api/external/v1/collections/3200/items/7001/categories/4202/rows HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{ "link": { "itemId": 7101 } }
Response 201
HTTP/1.1 201 Created
Location: /vobesoft/api/external/v1/collections/3200/items/7001/categories/4202/rows/9201
Content-Type: application/json; charset=utf-8

{ "id": 9201 }

The advisor's own list of customers got the matching row, with an id of its own:

GET /collections/3100/items/7101/categories/4101/rows
{
  "totalCount": 1,
  "results": [
    { "id": 9202, "values": [ { "fieldId": 5110, "type": "object", "value": { "itemId": 7001 } } ] }
  ],
  "pageIndex": 0,
  "pageSize": 25
}

Responses

Status When
201 Added; the body has the id of the new row
400 VALIDATION_FAILED No link, values sent along, or a linked item that is deleted or of another collection
403 PERMISSION_DENIED The list category is read only, or the key misses a permission on this list or on the other side
403 WRITE_REFUSED Your organization's rules refused the row
404 NOT_FOUND No such item or list, or the key may not read it or add rows to it
409 ALREADY_LINKED The item is already in this list
409 LIST_FULL This list, or for many to many the other item's list, holds its maximum of rows (11.1)
Response 409 to adding policy 8002 again
{
  "code": "ALREADY_LINKED",
  "message": "The item is already linked in this list.",
  "correlationId": "6ce146c6-3cf1-423f-a305-fb8857db1502"
}
Response 400 to the link sent among the values
{
  "code": "VALIDATION_FAILED",
  "message": "The request is invalid.",
  "correlationId": "9ced30f6-f749-462e-9759-7588019ac4fd",
  "details": "field 5201: a new row of this relation list takes the item it links in link, not in values"
}

11.4 Update a row

PATCH/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId}
Endpoint Update rowsPermission read and update on the list category; for many to many also the other side

Changes only the fields of the row you name; null clears a field. The body is as in 10.2.

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
listCategoryId integer The id of a list category of the item's collection.
rowId integer The id of the row.
Request
PATCH /vobesoft/api/external/v1/collections/3300/items/8001/categories/4301/rows/9001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{ "values": [ { "fieldId": 5321, "value": 400 } ] }
Response 204
HTTP/1.1 204 No Content

In a linking list:

  • the link of a row cannot be changed; delete the row and add a new one. Sending a changed link is refused with field 5201: the link of a relation row cannot be changed; delete the row and add a new one;
  • file fields of a many to many list cannot be written: field {id}: a file field of a many to many list cannot be written.

Responses

Status When
204 Saved
400 VALIDATION_FAILED A value is wrong; details names it
403 PERMISSION_DENIED The list category is read only (category 4301: read only), or the key misses the other side of a many to many list
403 WRITE_REFUSED Your organization's rules refused the change
404 NOT_FOUND No such row, or the key may not read it or change rows of this list
413 REQUEST_TOO_LARGE The body is above its limit (6.2)

11.5 Delete a row

DELETE/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId}
Endpoint Delete rowsPermission read and delete on the list category; for a linking list also the other side

Deletes the row. What else happens depends on the kind of list:

  • plain list: only the row goes;
  • one to many: the linked item stays, and its field that pointed back becomes null;
  • many to many: the matching row on the other side goes too; both items stay.

Path parameters

Name Type Meaning
collectionId integer The id of the item's collection.
itemId integer The id of the item.
listCategoryId integer The id of a list category of the item's collection.
rowId integer The id of the row.
Request
DELETE /vobesoft/api/external/v1/collections/3200/items/7001/categories/4202/rows/9201 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 204
HTTP/1.1 204 No Content

Afterwards the advisor's list of customers is empty: { "totalCount": 0, "results": [], "pageIndex": 0, "pageSize": 25 }.

Responses

Status When
204 Deleted
403 PERMISSION_DENIED The list category is read only, or the key misses the other side of a linking list
403 WRITE_REFUSED Your organization's rules refused the delete
404 NOT_FOUND No such row, already deleted, or the key may not read it or delete rows of this list

12Files

Files travel with the create or update of an item or a row, in one request; a download is a short lived link.

12.1 Sending files

To send files, send the request as multipart/form-data instead of JSON. Each part has a name:

Part name Holds
values Optional. The list of values as JSON, as values in a JSON body.
createdFrom Optional, create of an item only. As in 10.1, as JSON.
link Add of a row to a linking list only. As in 11.3, as JSON. A linking add takes no files, so a JSON body (11.3) does the same.
The id of a file field, for example 5310 One file, with its file name. Send one part per file; several files for one field are several parts with the same name.

Files are added to what the field already holds. This request creates a policy with a title and one document:

Request
POST /vobesoft/api/external/v1/collections/3300/items HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: multipart/form-data; boundary=vobesoft-boundary

--vobesoft-boundary
Content-Disposition: form-data; name="values"
Content-Type: application/json

[ { "fieldId": 5302, "value": "Fleet policy Van Dijk" } ]
--vobesoft-boundary
Content-Disposition: form-data; name="5310"; filename="policy-schedule.pdf"
Content-Type: application/pdf

(the bytes of the file)
--vobesoft-boundary--
curl
curl "https://www.vobesoft.nl/vobesoft/api/external/v1/collections/3300/items" \
  -H "X-Api-Key: $VOBESOFT_API_KEY" \
  -F 'values=[{"fieldId":5302,"value":"Fleet policy Van Dijk"}];type=application/json' \
  -F "5310=@policy-schedule.pdf;type=application/pdf"

The response is the same as for a JSON create: 201 with the new id. The same parts work for an update (10.2, 11.4) and for adding a row (11.2).

Limits on files

  • The whole request is at most 50 MB, its parts and files together, in at most 25 parts. A part that holds JSON (values, createdFrom, link) is at most 1 MB, as a JSON body is (6.2); a file part is not.
  • Each file field keeps the limits set for it in VobeSoft, such as the largest file and the file types allowed. A file over them returns 400, naming the field and the file's position among the files of that field: field 5310: upload 2 is larger than the field allows (10 MB).
  • A file part needs a file name. A part whose name is not values, createdFrom, link or the id of a field returns 400.
  • Of a file name, VobeSoft keeps only what follows its last / or \. That name has at most 255 characters, is not only dots, and holds no control or format characters, nor any character Windows refuses in a file name (" * : < > ? |). Otherwise the call returns 400, naming the file, for example field 5310: upload 1 has a name that cannot be stored.
  • The body needs a boundary in its Content-Type, and part headers that can be read; otherwise the call returns 400.

In values, the value of a file field adds links and removes files:

Value Does
{ "links": [ { "name": "…", "url": "https://…" } ] } Adds links to web pages; http and https only.
{ "remove": [ 60011 ] } Removes these files and links of the field, by their id.
null Removes every file and link of the field.
  • links and remove each take at most 1000 entries.
  • A link's name has at most 255 characters and holds no control or format characters; otherwise the call returns 400, for example field 5310: link 1 has a name that cannot be stored.
  • A link's address is stored, and read back, in its ASCII form: an international domain name in its punycode form, other characters escaped. In that form it has at most 2048 characters.

One update can add a file, add a link and remove a file at once:

Request
PATCH /vobesoft/api/external/v1/collections/3300/items/8003 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: multipart/form-data; boundary=vobesoft-boundary

--vobesoft-boundary
Content-Disposition: form-data; name="values"
Content-Type: application/json

[ { "fieldId": 5310, "value": {
    "links": [ { "name": "Policy in the portal", "url": "https://portal.example.com/policies/4711" } ],
    "remove": [ 60011 ] } } ]
--vobesoft-boundary
Content-Disposition: form-data; name="5310"; filename="signed-offer.pdf"
Content-Type: application/pdf

(the bytes of the file)
--vobesoft-boundary--

The response is 204. Without files to upload, send the same values as a JSON body.

12.3 Files in an item

A file field reads as a list of its files and links:

From GET /collections/3300/items/8003
{ "fieldId": 5310, "type": "file", "value": [
  { "id": 60012, "fileName": "Policy in the portal", "contentType": null, "sizeInBytes": null, "url": "https://portal.example.com/policies/4711" },
  { "id": 60013, "fileName": "signed-offer.pdf", "contentType": "application/pdf", "sizeInBytes": 1024, "url": null }
] }
Property Meaning
id The id of the file: for its download (12.4) and to remove it (12.2).
fileName The file name, or the name of a link.
contentType, sizeInBytes For a file; null for a link.
url For a link, its address; null for a file.

12.4 Download a file

GET/files/{fileId}
Endpoint Download filesPermission read on what holds the file

A link to the bytes of a stored file, valid for 5 minutes. The file must be held by a file field of an item or a row the key may read.

Path parameters

Name Type Meaning
fileId integer The id of a file, from an item or a row (12.3).
Request
GET /vobesoft/api/external/v1/files/60013 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Response 200
{
  "url": "https://….blob.core.windows.net/…?sv=…&se=2026-10-01T12%3A21%3A14Z&sr=b&sp=r&rscd=…&rsct=application%2Fpdf&sig=…",
  "expiresAt": "2026-10-01T12:21:14.3580792Z"
}
  • Download the bytes with a plain GET of url before expiresAt. Do not send your key to it: the link needs none.
  • The download gives the file with its own name and content type.
  • Ask for a new link when one has expired; keep the file id, not the link.

Responses

Status When
200 The link
404 NOT_FOUND No such file, a link instead of a file, a file the key may not read, or a file without stored bytes

13Value reference

Every value reads as { "fieldId", "type", "value" } and is written as { "fieldId", "value" }. The type decides the shape of value.

Type Reads as Write
text, longText "Fleet policy Van Dijk" A text; a text at most 500 characters, or less when its field allows less, on one line without control characters; a longText as long as the body allows (6.2), line breaks included
number 1250.5 A finite number
date "2026-10-01T00:00:00Z" An ISO 8601 date, or date and time (6.3)
dateRange { "from": "…Z", "to": "…Z" } The same object, each end an ISO 8601 date or date and time (6.3). An end that is null or left out stays empty, and reads as null. VobeSoft does not check that from comes before to.
checkbox true true or false
choice [ { "id": 210004, "text": "Liability" } ] A list of choice ids, [210004], also when the field is not multi select: then the list holds one id. At most 1000 entries; [] clears the field, as null does
employee [ { "id": "3f2b…", "name": "Mila Jansen" } ] A list of user ids: ["3f2b…"]; at most 100 different users of your organization
object { "itemId": 7001 } The same object: the linked item
file A list of files and links (12.3) Section 12
signature See below Read only
autoNumber "POL-137" Read only
creationDate "2026-10-01T12:15:17.655Z" Read only
mirror, formula Not in an item's values Read only
  • An empty value reads as null, and null clears a value in a write.
  • Writing a read only type returns 400: field 5300: fields of this type cannot be written.
  • A field your organization marks as read only in VobeSoft, or a field of a read only category (isReadOnly in the structure, 8.2), cannot be written either, whatever the key's permissions: 400 VALIDATION_FAILED with field {id}: read only. Only the link of createdFrom (10.1) and of a row added to a linking list (11.3) is set through such a field.
  • A text longer than 500 characters is refused with field 5302: longer than 500 characters, and a text longer than its field allows with that length, for example field 5302: longer than 40 characters. A number must be finite: NaN and Infinity are refused with field 5307: expected a number, and a number above its field's maximum with field 5307: above its maximum of 100.
  • A choice is written by its id; the choices of the field in the structure (8.2) list the ids it takes. This is the same for a field with a choice list and a field with choices of its own. A text is refused with field 5301: expected a list of choice ids, and a choice the field does not have is refused with field 5301: 210099 is not a choice of this field.
  • A user shows its name only when the user belongs to your organization; otherwise name is null.
  • An employee value names users of your organization only: another id is refused with field 5306: user 2 is not a user of the organization, by its position in the list. A user named twice is stored once. The list takes at most 1000 entries and 100 different users.
  • An item linked by an object field must be an item of the collection the field links to, a collection the key may read: otherwise field {id}: links a collection the key cannot read, or field {id}: the linked item is not an active item of the collection the field links to.
  • A required field (isRequired, 8.2) cannot be cleared: null, an empty text, an empty list or a date range without either end is refused with field {id}: required. A cleared text with a default value gets that value instead. A new item or row needs a value in every required field the key may write (10.1, 11.2). Checkbox and file fields are never asked, nor the types the API does not write.

13.1 Signatures

A signature reads as its image, an SVG in base64, with when and by whom it was signed:

A signature in an item
{ "fieldId": 5309, "type": "signature", "value": {
  "contentType": "image/svg+xml",
  "base64": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIC4uLg==",
  "signedAt": "2026-10-01T14:02:11Z",
  "signedBy": { "id": "3f2b9c1e-7a4d-4e0b-9b6a-1c2d3e4f5a6b", "name": "Mila Jansen" }
} }

14Good practice

  • Log every call with its status, its code and its correlation id.
  • Wait when asked. On 429 and 503, wait the seconds of Retry-After. On 500, wait and try again a few times, each time waiting longer.
  • Keep ids, not names, and read the structure again when your organization changes it: a field that no longer exists is refused with not a field of this collection.
  • Send only what changes. An update changes only the fields it names, so leave out the others.
  • Mind time zones. Store and compare moments in UTC (6.3).
  • Never send your key to a download link. The link needs none (12.4).
  • Ask for what changed with a search, instead of reading every item again (15.7).

15Searching items and rows

A search finds the items of a collection, or the rows of a list, by their values and moments, one page at a time.

A search is a POST with a JSON body that holds the conditions and the order (15.3 to 15.5). It returns items as 9.1 shows them and rows as 9.2 shows them, with only what the key may read. The searches are endpoints of their own, Search items and Search rows, with limits of their own (chapter 5).

15.1 Search items

POST/collections/{collectionId}/items/search
Endpoint Search itemsPermission read on the collection; values per readable category

The items of one collection that the conditions find, in the order you ask.

Parameters

Name In Meaning
collectionId path The id of the collection.
pageIndex, pageSize query Optional; paging as in 6.4.
criteria, sort body Optional; the conditions (15.3, 15.4) and the order (15.5). The body {} finds every item the key may read, newest first.
Request
POST /vobesoft/api/external/v1/collections/3300/items/search?pageIndex=0&pageSize=25 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{
  "criteria": {
    "operator": "and",
    "conditions": [
      { "fieldId": 5302, "operator": "contains", "value": "Van Dijk" },
      { "fieldId": 5301, "operator": "equals", "value": [210004] }
    ]
  },
  "sort": [ { "property": "lastEditedAt", "direction": "descending" } ]
}
Response 200
{
  "totalCount": 1,
  "results": [
    {
      "id": 8001,
      "collectionId": 3300,
      "createdAt": "2026-10-01T12:15:17.655Z",
      "lastEditedAt": "2026-10-20T08:42:11.3172946Z",
      "lastEditedBy": { "id": "3f2b9c1e-7a4d-4e0b-9b6a-1c2d3e4f5a6b", "name": "Mila Jansen" },
      "categories": [
        {
          "id": 4300,
          "name": "General",
          "isList": false,
          "values": [
            { "fieldId": 5301, "type": "choice", "value": [ { "id": 210004, "text": "Liability" } ] },
            { "fieldId": 5302, "type": "text", "value": "Fleet policy Van Dijk" }
          ]
        },
        {
          "id": 4301,
          "name": "Claims",
          "isList": true,
          "rowCount": 1
        }
      ]
    }
  ],
  "pageIndex": 0,
  "pageSize": 25
}
  • Each item is the item as 9.1 returns it: every category the key may read, with all of its fields, not only the fields the conditions name. The example shortens it.
  • totalCount is the number of items found over all pages.
  • An item a user is adding in VobeSoft and has not saved yet is found as well, although reading it by its id returns 404 until it is saved (9.1). A deleted item is not found.

Responses

Status When
200 The page, empty when nothing matches
400 VALIDATION_FAILED A request or body the search cannot read, or a mistake in the conditions or the order; details names the first mistake (15.6)
404 NOT_FOUND No such collection, or the key may not read it; or a field the body names that does not exist or the key may not read (15.6)
413 REQUEST_TOO_LARGE A body above 64 KB
504 PROCESSING_TIMEOUT Not answered within the time limit of a call (5.6)

15.2 Search rows

POST/collections/{collectionId}/categories/{listCategoryId}/rows/search
POST/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/search
Endpoint Search rowsPermission read on the collection and the list category

The rows of one list category that the conditions find: over every item of the collection with the first path, within one item with the second. Each row is the row as 9.2 returns it, with its itemId.

Parameters

Name In Meaning
collectionId path The id of the collection.
itemId path Only in the second path: the item whose rows are searched.
listCategoryId path The id of a list category of the collection.
pageIndex, pageSize query Optional; paging as in 6.4.
criteria, sort body Optional; as for items (15.3 to 15.5), with the rules for rows below.
Request
POST /vobesoft/api/external/v1/collections/3300/categories/4301/rows/search HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
Content-Type: application/json

{
  "criteria": {
    "operator": "and",
    "conditions": [
      { "fieldId": 5321, "operator": "greaterThan", "value": 100 },
      { "fieldId": 5302, "operator": "contains", "value": "Van Dijk" }
    ]
  },
  "sort": [ { "fieldId": 5321, "direction": "descending" } ]
}
Response 200
{
  "totalCount": 1,
  "results": [
    {
      "id": 9001,
      "itemId": 8001,
      "lastEditedAt": "2026-10-14T13:05:40.0281533Z",
      "lastEditedBy": { "id": "7dc27c45-fc87-4d43-9799-32b2aa34d004", "name": "External API" },
      "values": [
        { "fieldId": 5320, "type": "text", "value": "Windscreen damage" },
        { "fieldId": 5321, "type": "number", "value": 350.0 },
        { "fieldId": 5322, "type": "file", "value": null }
      ]
    }
  ],
  "pageIndex": 0,
  "pageSize": 25
}
  • A condition on a field of the list tests the row; the example asks for claims above 100. A condition on a field of a normal category of the collection tests the row's item: the example asks for claims of policies whose title holds Van Dijk. Fields of other lists are refused with 400.
  • A row search takes no path (15.4).
  • The rows of a deleted item are not found.

Responses

Status When
200 The page, empty when nothing matches
400 VALIDATION_FAILED As for items (15.1)
404 NOT_FOUND No such collection, list category or item, or the key may not read them; or a field the body names that does not exist or the key may not read (15.6)
413 REQUEST_TOO_LARGE A body above 64 KB
504 PROCESSING_TIMEOUT Not answered within the time limit of a call (5.6)

15.3 The body

criteria is a group: an operator, and or or, and its conditions. A member of conditions is a condition or a group of its own. A condition names a field by fieldId, or a moment of the item or row by property (15.4), with an operator and, except for isEmpty and isNotEmpty, a value.

A body with a group, a path and a moment
{
  "criteria": {
    "operator": "and",
    "conditions": [
      { "fieldId": 5302, "operator": "contains", "value": "Van Dijk" },
      { "fieldId": 5304, "operator": "greaterThan", "value": { "daysFromToday": -30 } },
      {
        "operator": "or",
        "conditions": [
          { "fieldId": 5301, "operator": "equals", "value": [210004, 210001] },
          { "fieldId": 5306, "operator": "isEmpty" }
        ]
      },
      { "path": [5311], "fieldId": 5205, "operator": "equals", "value": "Utrecht" },
      { "property": "lastEditedAt", "operator": "greaterThan", "value": "2026-10-01T00:00:00Z" }
    ]
  },
  "sort": [
    { "property": "lastEditedAt", "direction": "descending" },
    { "fieldId": 5302, "direction": "ascending" }
  ]
}

This finds the policies whose title holds Van Dijk, whose start date lies after the day 30 days ago, whose product is one of two or that have no account manager, whose customer (5311) has Utrecht as its text field 5205, and that were edited after 1 October; the most recently edited first.

  • Both criteria and sort may be left out. Send JSON (application/json), at most 64 KB.
  • The body is read strictly, as every body is (6.2), so a misspelled name never widens a search. The values of operator, property and direction are written as this chapter writes them.
  • A group holds at least one condition. Groups nest at most two levels below criteria.
  • A search holds at most 50 conditions. A condition's list of values holds at most 100 entries, all lists together at most 1000.

15.4 Conditions

The operators are equals, notEquals, greaterThan, lessThan, between, contains, notContains, isEmpty and isNotEmpty. Which of them a field takes, and what its value looks like, depends on its type in the structure (8.2):

Type Operators Value Finds
text, longText equals, notEquals, contains, notContains, isEmpty, isNotEmpty a text of 1 to 500 characters equals the whole text, whatever its case and trailing spaces; contains the text anywhere, taken literally; notEquals and notContains also items without a value
number, autoNumber equals, notEquals, greaterThan, lessThan, isEmpty, isNotEmpty a number; for autoNumber the number without its prefix, 137 where a read shows "POL-137" the comparison
date, creationDate equals, notEquals, greaterThan, lessThan, isEmpty, isNotEmpty a day, "2026-10-16", or { "daysFromToday": -30 } from -36500 to 36500 by whole day in UTC: equals on that day, greaterThan after it, lessThan before it; daysFromToday counts from today in VobeSoft
dateRange between, isNotEmpty { "from": day or null, "to": day or null }, at least one day ranges that lie within the days, both days included
checkbox equals, notEquals true or false false also items without a value
choice that takes one choice equals, notEquals, isEmpty, isNotEmpty a list of 1 to 100 choice ids equals any of them; notEquals none of them, items without a value included
choice that takes several equals, notEquals, contains, notContains, isEmpty, isNotEmpty a list of 1 to 100 choice ids equals exactly these choices; notEquals not exactly these; contains any of them; notContains none of them
object equals, notEquals, isEmpty, isNotEmpty { "itemId": id } a link to that item
employee contains, notContains, isEmpty, isNotEmpty a list of 1 to 100 ids of users of your organization contains any of them; notContains none of them; isEmpty and isNotEmpty only on a field of the searched item, of a linked item or of a row's item
file contains, notContains, isEmpty, isNotEmpty a text of 1 to 500 characters a file whose name holds the text, or none; isEmpty no file at all
signature, mirror, formula none cannot be searched

A choice is named by its id, as a read returns it. A choice of a field's own choices whose text is empty or holds a comma cannot be searched.

Fields of a list. In an item search, a condition on a field of a list category finds the items of which some row matches; an item without rows matches none. In a row search the field of the list tests the row itself (15.2).

Paths. In an item search, path follows one or two object fields to the item whose field the condition tests: "path": [5311] tests the policy's customer. Each step is an object field of a normal category, and the field at the end is a field of a normal category of the collection the last step links. Under and, conditions whose paths start with the same field test the same linked item.

Moments. A condition with property tests a moment of the item or, in a row search, of the row itself; a row's createdAt is when it was added, which a read of the row does not show. A moment is read to the millisecond:

Property Operators Value
createdAt greaterThan, lessThan a moment (6.3), compared to the moment, not by day
lastEditedAt greaterThan, lessThan, isEmpty, isNotEmpty a moment; isEmpty finds what was not edited since VobeSoft began to record edits (9.4)

Every field a search names, in a condition, a path or the order, must be one the key may read. A field it may not read returns the same 404 as a field that does not exist.

15.5 Order

sort lists at most three keys, each a field, { "fieldId": 5302 }, or a property, { "property": "id" }, "createdAt" or "lastEditedAt", with a direction, ascending or descending.

  • VobeSoft adds id as a last key in the direction of your last key, so the pages of a search never overlap or skip a result. Without sort the newest come first, by id descending, as in 9.2.
  • An item or row without a lastEditedAt comes first when sorting by it ascending, and last when descending.
  • An item search sorts by fields of the collection's normal categories, a row search by fields of the searched list. The rows of a one to many list (11.1) sort by the properties only. Mirror and formula fields do not sort.

15.6 Errors

A search that cannot be answered returns the first mistake in details: where it is in the body, or which field it concerns, and what is wrong. It never repeats a value you sent.

Status Example of details
400 VALIDATION_FAILED body: expected only criteria and sort
400 VALIDATION_FAILED criteria.conditions[2].operator: expected equals, notEquals, greaterThan, lessThan, between, contains, notContains, isEmpty or isNotEmpty
400 VALIDATION_FAILED field 5302: operator greaterThan does not apply to a text field
400 VALIDATION_FAILED field 5309: a signature field cannot be searched
400 VALIDATION_FAILED criteria: a member is named twice
404 NOT_FOUND field 9999: not found

The texts of details are written for a person reading your log; let your system act on the code (chapter 7).

15.7 Find what changed

To keep your system in step with VobeSoft, ask for what changed since your last search instead of reading every item again:

  1. Read everything once: search with "sort": [ { "property": "id", "direction": "ascending" } ] and read every page, each a call within your limits (chapter 5). Keep the highest lastEditedAt you saw; that is your cursor.
  2. At every poll, search for lastEditedAt after your cursor minus five minutes, sorted by lastEditedAt ascending, and read every page.
  3. Skip a result you already handled with the same id and lastEditedAt.
  4. Keep the highest lastEditedAt as your new cursor.
A poll with the cursor 2026-10-20T08:42:11.3172946Z
{
  "criteria": {
    "operator": "and",
    "conditions": [
      { "property": "lastEditedAt", "operator": "greaterThan", "value": "2026-10-20T08:37:11.3172946Z" }
    ]
  },
  "sort": [ { "property": "lastEditedAt", "direction": "ascending" } ]
}
  • An edit gets its moment when VobeSoft saves it, and a long save, such as an import, shows only when it is done. The five minutes catch an edit whose moment lies just before your last poll but that it could not see yet; step 3 drops what you already have.
  • Pages are read live, so items can move between pages while you read them, and a full read is not a snapshot of one moment.
  • The same works for rows with a row search (15.2), on the row's own lastEditedAt.
  • Which changes move lastEditedAt, and which do not, is in 9.4. A deleted item or row is not reported: compare the ids of a full read now and then to find what is gone.

AEndpoint reference

Every path starts with https://www.vobesoft.nl/vobesoft/api/external/v1. The endpoint column names the endpoint as the key form in Settings, API shows it.

Method Path Endpoint Section
GET /collections Collection structure 8.1
GET /collections/{collectionId} Collection structure 8.2
GET /collections/{collectionId}/categories/{categoryId} Categories 8.3
GET /collections/{collectionId}/items/{itemId} Read items 9.1
GET …/items/{itemId}/categories/{listCategoryId}/rows Read items 9.2
GET …/rows/{rowId} Read items 9.3
POST /collections/{collectionId}/items/search Search items 15.1
POST /collections/{collectionId}/categories/{listCategoryId}/rows/search Search rows 15.2
POST /collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/search Search rows 15.2
POST /collections/{collectionId}/items Create items 10.1
PATCH /collections/{collectionId}/items/{itemId} Update items 10.2
DELETE /collections/{collectionId}/items/{itemId} Delete items 10.3
POST …/categories/{listCategoryId}/rows Add rows 11.2, 11.3
PATCH …/rows/{rowId} Update rows 11.4
DELETE …/rows/{rowId} Delete rows 11.5
GET /files/{fileId} Download files 12.4

A path that starts with … continues the path above it: rows hang under their item, /collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows.

BChanges to this guide

Date Change
6 October 2026 Version 1 of the API and of this guide.
26 October 2026 Searching items and rows (chapter 15). Items and rows show when and by whom they were last edited, and rows show their item (9.4).
29 October 2026 A limit on the calls one key runs at the same time, with the code CONCURRENCY_LIMIT_REACHED (5.3), a total for your organization over all endpoints (5.2), and a time limit of one minute per call, with the codes REQUEST_TIMEOUT and PROCESSING_TIMEOUT (5.6).
30 October 2026 A body is read as UTF-8 only, and a multipart/form-data body has at most 25 parts, each part that holds JSON at most 1 MB (6.2). A text value holds no control characters (section 13).