{ "exact" :

Syncing Dutch company data into Exact Online

Short answer

Exact Online stores the KVK number in the ChamberOfCommerce field on crm/Accounts, as free text with no validation. You fill it through the Exact REST API from your own middleware script that performs our lookup first. We do not ship an Exact connector: the integration is your script sitting between two APIs.

sync
# does the account exist?
GET ".../crm/Accounts?$filter="
    "ChamberOfCommerce eq '68750110'"

# if not: fetch the profile
curl ".../v1/lookup/68750110" \
  -H "Authorization: Bearer KEY"

{
  "statutoryName": "Acme B.V.",
  "isActive": true
}

Where the data lands

An Exact Online relation is an Account under crm/Accounts, scoped per administration:

POST https://start.exactonline.nl/api/v1/{division}/crm/Accounts

The fields a lookup can fill:

Exact fieldFrom the APIWatch out
NamestatutoryName, else nameInvoice name
ChamberOfCommercekvkNumberFree text, no validation
VATNumbervat.numberOnly when vat.valid is true
AddressLine1address.street plus house numberExact has no separate house number field
Postcodeaddress.postalCodeWith a space, keep it consistent
Cityaddress.city
Countryaddress.country
Websitewebsites[0]Only with enrich=true

That house number is the first thing that goes wrong. We return street, houseNumber and houseNumberAddition separately, Exact wants a single line. Concatenate in that order and keep the parts in your own database too, otherwise you can never match reliably on house number later.

The integration point is your script

There is no connector between us and Exact. What you build is a small piece of middleware, in any language, with four steps:

  1. A KVK number arrives, from a form, your CRM or an import file.
  2. GET /v1/lookup/{kvkNumber}?enrich=true on our side, checking isActive.
  3. A duplicate check in Exact with an OData filter on the KVK number.
  4. POST when absent, PUT on the existing GUID when present.

Step 3 is the one people skip, and it is exactly why Exact administrations fill up with duplicate relations:

GET /api/v1/{division}/crm/Accounts?$filter=ChamberOfCommerce eq '68750110'&$select=ID,Name
Authorization: Bearer {exact_access_token}
Accept: application/json

Zero results, then create:

{
  "Name": "Acme B.V.",
  "ChamberOfCommerce": "68750110",
  "VATNumber": "NL123456789B01",
  "AddressLine1": "Keizersgracht 123",
  "Postcode": "1015 CJ",
  "City": "Amsterdam",
  "Country": "NL"
}

For the batch side of this, when you are loading thousands of accounts at once, there is a longer piece in batch enrichment of Dutch company data.

Where the KVK number comes from

Exact is the destination, not the input. Somewhere earlier, a human picks a company. Three places that happens in practice, each with different consequences for your sync:

  • A signup form on your own site. Attach the widget to the KVK field: the visitor searches by name and you receive a number that was chosen, not retyped.
  • Your CRM. The number was validated at lead capture, so the Exact sync is only a copy step. This is the calmest variant, because the duplicate check already happened earlier in the chain.
  • An import file from an accountant or an old system. Here normalisation is step zero, not a nicety: that column definitely contains text, spaces and empty cells.

If all you have is a company name, add a search step with GET /v1/search?... first. It returns candidates with their KVK numbers and a human picks one. Auto-selecting the first hit for a name like “Van der Berg Holding” reliably produces the wrong account, and a wrong account in your bookkeeping only surfaces at the first invoice.

What actually breaks in production

Rotating refresh tokens. Exact uses short-lived access tokens and a refresh token that is replaced on every use. If two processes refresh at the same time, one wins and the other is logged out permanently. Store the tokens centrally, refresh under a lock, and persist the new refresh token before you use it. This is the single most common reason an Exact integration is dead on Monday morning.

KVK numbers without their leading zero. Older KVK numbers start with a zero. Run the data through Excel or a numeric column and 01234567 becomes 1234567, and your OData filter matches nothing. Always left-pad to eight digits before searching or writing.

Historically polluted ChamberOfCommerce values. Because Exact does not validate the field, it contains everything: numbers with dots, with spaces, prefixed with “KVK”, and sometimes a 12-digit branch number. Your duplicate check with an exact filter misses those records and creates a second relation. Normalise first: strip to digits, left-pad to eight, and clean up the existing mess in a one-off run.

Rate limits on both sides during an import. Exact meters per minute and per day and reports it in the X-RateLimit headers on every response. Our side has its own per-minute limit. Importing thousands of relations, you hit whichever is slower. Read the Exact headers instead of retrying blindly, and use POST /v1/lookup/batch on our side so the lookups are not the bottleneck.

A company that no longer exists. On NOT_FOUND the registration has been struck off. Do not create a relation, and do not auto-delete an existing one either: invoices and journal entries hang off it. Flag it in your own system and let a human decide.

Frequently asked

Do you have an Exact Online connector?
No. We ship a REST API. You build the Exact side yourself: a script or small service that performs our lookup and writes the result through the Exact REST API. Nothing Exact-specific is needed on our side.
Which field holds the KVK number?
ChamberOfCommerce on crm/Accounts. The VAT number goes in VATNumber. Both are free text: Exact does not check whether the number is well formed or exists, so validation has to happen in your script before you write.
Do I need a separate integration per administration?
Yes. Every Exact URL contains a division, which is the administration id. With multiple administrations you run the same sync per division and track per division which accounts you already processed.
Should the statutory name be the account name?
For bookkeeping usually yes, because invoices need the statutory name. For recognition by your own staff the trade name is often clearer. The practical answer is statutoryName in Name and the trade name in a search or notes field.

Updated:

One call gives you the whole company

KVK data, the derived VAT number and a live VIES check, in a single request. The free plan covers 50 lookups a month and needs no card.

50
free lookups a month
1
request instead of three
0
cards, contracts or sales calls