VobeSoft API
Guide for connecting your own systems, version 1
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.
https://www.vobesoft.nl/vobesoft/api/external/v1, or the address of your organization's own VobeSoft environment (6.1)
X-Api-Key headermultipart/form-data
2026-10-01T09:30:00Z
pageIndex from 0, pageSize 25 by default, at most 50
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
codeor a fieldtypemay 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.
- 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).
- List the collections the key may read with
GET /collections(8.1), and note the id of the one you need. - Read its structure with
GET /collections/{collectionId}(8.2). It gives the ids of its categories and fields. - Create an item with
POST /collections/{collectionId}/itemsand the values of its fields (10.1). The response gives the id of the new item. - Read the item back with
GET /collections/{collectionId}/items/{itemId}(9.1).
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.
- In VobeSoft, open Settings and choose API. The page lists the keys of your organization and what they used of your budget.
- Choose New API key and give the key a name that tells which system uses it, for example CRM synchronization.
- Choose an expiry date, at most two years from today. The key works up to and including that day.
- Choose the endpoints the key may call, and give it permissions on the collections and categories it needs. Section 4 explains both.
- 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.
- Choose Create key. VobeSoft shows the key once. Copy it and store it where your system reads its secrets.
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_ 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 "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:
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
}
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:
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:
- Create a new key with the same endpoints, permissions and limits.
- Give the new key to your system and check that its calls succeed.
- 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 |
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:
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_TIMEOUTand 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, ormultipart/form-datawhen 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 asvalueinstead ofvalues, returns 400VALIDATION_FAILED, anddetailstells 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,nullclears 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-databody at most 50 MB (12.1); a larger one returns 413REQUEST_TOO_LARGE, at once when itsContent-Lengthsays so, else as soon as more has arrived. A part of amultipart/form-databody 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:30or2026-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:
{
"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.
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.
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
/collections 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. |
GET /vobesoft/api/external/v1/collections HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
{
"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
/collections/{collectionId} 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. |
GET /vobesoft/api/external/v1/collections/3300 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
{
"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
/collections/{collectionId}/categories/{categoryId} 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. |
GET /vobesoft/api/external/v1/collections/3300/categories/4301 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
{
"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
/collections/{collectionId}/items/{itemId} 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. |
GET /vobesoft/api/external/v1/collections/3300/items/8001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
{
"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.
lastEditedAtandlastEditedBytell 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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows 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. |
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}
{
"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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId} 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. |
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}
{
"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
/collections/{collectionId}/items 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.
|
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 } }
]
}
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.
{
"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
/collections/{collectionId}/items/{itemId} 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). |
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 }
]
}
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
/collections/{collectionId}/items/{itemId} 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. |
DELETE /vobesoft/api/external/v1/collections/3300/items/8001 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
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.
Each row holds its own values, like the claims of a policy.
Add a row with its values (11.2).
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).
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:
[
{
"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.
|
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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows 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).
|
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 }
]
}
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:
{
"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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows 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
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 } }
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.
{
"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
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 } }
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:
{
"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) |
{
"code": "ALREADY_LINKED",
"message": "The item is already linked in this list.",
"correlationId": "6ce146c6-3cf1-423f-a305-fb8857db1502"
}
{
"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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId} 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. |
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 } ] }
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
/collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/{rowId} 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. |
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}
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:
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 "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,linkor 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 examplefield 5310: upload 1 has a name that cannot be stored. - The body needs a
boundaryin itsContent-Type, and part headers that can be read; otherwise the call returns 400.
12.2 Links and removing files
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. |
linksandremoveeach 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:
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:
{ "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
/files/{fileId} 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). |
GET /vobesoft/api/external/v1/files/60013 HTTP/1.1
Host: www.vobesoft.nl
X-Api-Key: {your key}
{
"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
GETofurlbeforeexpiresAt. 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, andnullclears 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
(
isReadOnlyin the structure, 8.2), cannot be written either, whatever the key's permissions: 400VALIDATION_FAILEDwithfield {id}: read only. Only the link ofcreatedFrom(10.1) and of a row added to a linking list (11.3) is set through such a field. - A
textlonger than 500 characters is refused withfield 5302: longer than 500 characters, and a text longer than its field allows with that length, for examplefield 5302: longer than 40 characters. Anumbermust be finite:NaNandInfinityare refused withfield 5307: expected a number, and a number above its field's maximum withfield 5307: above its maximum of 100. - A choice is written by its id; the
choicesof 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 withfield 5301: expected a list of choice ids, and a choice the field does not have is refused withfield 5301: 210099 is not a choice of this field. - A user shows its name only when the user belongs to your organization; otherwise
nameisnull. - An
employeevalue names users of your organization only: another id is refused withfield 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
objectfield must be an item of the collection the field links to, a collection the key may read: otherwisefield {id}: links a collection the key cannot read, orfield {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 withfield {id}: required. A clearedtextwith 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:
{ "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
codeand 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
/collections/{collectionId}/items/search 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.
|
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" } ]
}
{
"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.
totalCountis 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
/collections/{collectionId}/categories/{listCategoryId}/rows/search /collections/{collectionId}/items/{itemId}/categories/{listCategoryId}/rows/search 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. |
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" } ]
}
{
"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.
{
"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
criteriaandsortmay 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,propertyanddirectionare 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
idas a last key in the direction of your last key, so the pages of a search never overlap or skip a result. Withoutsortthe newest come first, byiddescending, as in 9.2. - An item or row without a
lastEditedAtcomes 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:
- Read everything once: search with
"sort": [ { "property": "id", "direction": "ascending" } ]and read every page, each a call within your limits (chapter 5). Keep the highestlastEditedAtyou saw; that is your cursor. - At every poll, search for
lastEditedAtafter your cursor minus five minutes, sorted bylastEditedAtascending, and read every page. - Skip a result you already handled with the same
idandlastEditedAt. - Keep the highest
lastEditedAtas your new cursor.
{
"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).
|