maesn
For developers

How to integrate with SnelStart: Two authentication flows, and one account key

Nearly every write-up of this system describes two keys and stops there. That is the development flow. Production works differently: your customer signs in through a redirect SnelStart calls OAuth, the key is delivered to a callback rather than copied by hand, and you only get there through a partner review with a seven day observation period behind it.

Lennart Svensson, CTO and Co-Founder at Maesn
Lennart Svensson
CTO and Co-Founder · · Updated
Illustration for How to Integrate with SnelStart: Two Authentication Flows
The context

The two keys are the development flow, not the whole story

Almost every description of this system, ours included until now, says SnelStart authenticates with two keys and stops there. That is true of the flow you build against and false of the one you ship on. SnelStart runs two separate authentication processes: development and test, which is the two keys below, and production, which is a redirect and has its own section further down.

The reason the second one is missing from the public record is worth knowing before you trust anything else you read about this API: SnelStart’s reference sits behind a login, so write-ups are reconstructed from the half that is visible.

For the development flow, two credentials is unremarkable. What matters is that no single person can produce both of them, and that changes what your onboarding screen can and cannot do.

The connection key belongs to your customer. They sign in to their own account, open Koppelingen, pick Maatwerk, agree to the scopes and save the value. It is per company, it is theirs to revoke, and it is the half you can collect inside your own product.

The subscription key belongs to you. It comes from a developer account you register once, and it identifies your application rather than any customer. No onboarding form can ask for it, because your customer does not have one and never will.

One connectionTwo accounts
Your customer
Connection Key
Koppelingen · Maatwerk

Created in their own SnelStart account after agreeing to the scopes. Per company.

You
Subscription Key
Developer portal

One per application, from your own developer account. Not something a customer can hand you.

Only one of the two is a product step. The connection key can be collected in your own interface. The subscription key is yours, it is registered once, and no onboarding screen can ask a customer for it.

The handshake crosses two accounts. Sources for both halves: Maesn's connect guide, checked 13 August 2026.

There is a third condition in front of both, and it is the one most likely to surface during a sales call rather than a build. Access is tied to the plan: Maesn’s connect guide states that your customer must hold the inZicht or inControle package to set up access at all. Those are the upper two packages on SnelStart’s own price list.

None of the development flow is a redirect, which is why this is one of the seven systems that can be connected without a hosted page while you build. That changes in production, and the section below is what it changes into.

Connection steps, the package requirement and both key sources from Maesn’s SnelStart connect guide, checked 13 August 2026. Package names cross-checked against SnelStart’s price list. Maesn’s documentation and SnelStart’s own pages write the brand “Snelstart”; quotations keep their source’s spelling.

One value does not travel through your product
The connection key goes into the connect flow. The subscription key is handed over once to your Maesn contact when the integration is set up. Worth knowing before you design a screen that tries to collect both.
The context

Production is a redirect, and the key never touches a screen

Once you are an approved partner the handshake changes shape. SnelStart calls this flow OAuth, and it is worth taking that word loosely: it does not follow the standard pattern, and the clearest way to read it is as a separate process rather than as a variation on the two keys.

What you get from the partner application is an application short name, which behaves like a client id. Nothing in this flow can start without it. You send your customer to an activation URL built from it, carrying a reference key that behaves like a state parameter and a success URL for the end of the journey.

Your customer signs in to SnelStart and accepts the scopes your application asked for. Then the part that surprises people: SnelStart does not hand the connection key back through the browser. It posts it to a callback address you registered when you applied, together with the reference key you sent, so that the callback can be matched to the browser session. Your side acknowledges with a 201, and only then is your customer redirected onward.

Both flows end at the same token endpointHTTP
POST https://auth.snelstart.nl/b2b/token
Content-Type: application/x-www-form-urlencoded
 
grant_type=clientkey&clientkey=<connection key>
 
-> { "access_token": "...", "token_type": "bearer",
"expires_in": 3599 }

This is the join between the two flows. In development the connection key is copied out of the interface, in production it arrives at the callback, and from that point the exchange is identical. Every API call afterwards carries the bearer token plus the subscription key in Ocp-Apim-Subscription-Key, and missing either one returns a 401.

The production flow, the callback behaviour, the token endpoint and the header pair from Maesn’s internal SnelStart authentication reference, checked 13 August 2026. SnelStart’s own documentation is behind a login, which is why this flow is absent from public write-ups of this API.

Refreshing is one call, not the whole flow again

The access token lasts an hour. Because the connection key is stored per customer, a refresh is the same token request again rather than another trip through the redirect. Your customer signs in once, not hourly.

Through Maesn none of the above reaches your code. The activation link, the callback, the state matching and the storage of the connection key sit behind the connection, and what you hold is one account key. Unified authentication is what makes a system with two different handshakes look the same as a system with one.

The problem

The production key arrives after the build, not before

This is the part that belongs in a project plan, and it is missing from every description of this system we have published so far. On most platforms you register, you get keys, you build, and a marketplace listing is optional distribution you can add later. Here the order is inverted.

SnelStart’s developer portal states it plainly, in Dutch: “Ben je als ontwikkelaar klaar met het bouwen van de koppeling en gereed om een permanente maatwerk- of productiesleutel te ontvangen? Dan kun je de koppeling aanmelden om door ons te worden gecertificeerd.” Finish the integration, submit it, and a permanent key follows approval.

Maesn’s own documentation says what that gates. To use the interface in production, “you must become an official Snelstart Partner. Once approved, your application gains access to live customer environments (beyond the default test company).” Until that approval, a fully working integration reaches exactly one company, and it is not a customer’s.

The clock matters too. A development subscription is free and valid for 90 days with limited calls. If your build slips past that window, the key you have been testing with stops being the key you have.

And there is a step between submitting and going live that is easy to miss when you plan backwards from a launch date. When you tell SnelStart’s partner manager that the integration is ready, a seven day observation period begins. During that week the integration has to make at least 150 calls without producing too many errors.

That is a requirement you have to arrange rather than wait out. A hundred and fifty calls in a week is not much traffic, but it is more than an idle integration produces, so somebody has to drive it deliberately. This is the point in the process where Maesn steps in with you rather than watching.

Certification wording from SnelStart’s developer portal, the partner requirement, the 90-day validity and the one-time production fee from Maesn’s connect guide, both checked 13 August 2026. Certification here is SnelStart’s word for SnelStart’s process.

Two accounts to open before day one
The documentation itself sits behind a free temporary account on the developer portal, and it is published in Dutch. Budget the reading, not just the writing.
The problem

Three of the four writable objects have no read switched on yet

Counted across the 30 systems in this catalogue, SnelStart is the only one where Maesn has more object types switched on for writing than for reading: four create against three read. That sounds like an advantage and is mostly a warning.

What it describes is our own enablement rather than a shape SnelStart imposes. Booking proposals, credit notes and files are switched on for writing, and their reads are marked available on request rather than unavailable. Contacts is the single object enabled on both sides today. So there is no round trip for three of your four writes right now, and nothing you post can be fetched back and compared with what you meant to send until those reads are switched on.

4 writable, 3 readableThe only system with more of the first
What you can write
  • Booking proposalsNo read
  • Credit notesNo read
  • FilesNo read
  • ContactsReadable
What you can read

Accounts, Contacts, Tax rates

Accounts and tax rates are what a booking proposal has to name correctly, so the readable side is mostly the coding context for the writable side.

Three writes have no round trip. The response to the request is the only confirmation you will get, which puts the weight on validating the payload before it leaves rather than reconciling afterwards.

Writable against readable, counted from the coverage data across all 30+ systems. Checked 13 August 2026.
What is switched on for each object today, and how rare the write is across the catalogue
ObjectReadWriteSystems with the write
Booking proposalson requestyes10 of 30
Credit noteson requestyes2 of 30
Fileson requestyes4 of 30
Contactsyesyescommon
Accountsyeson requestcommon
Tax ratesyesnocommon

Counted from the generated coverage data, checked 13 August 2026. On request means not switched on by default rather than unavailable, which is the distinction that matters in this table: only Tax rates create is a plain no. Credit notes create is supported on two of the 30 systems and Files create on four, so two of the three objects here are among the rarer capabilities in the catalogue rather than gaps.

The practical consequence is where your effort goes. On a system with a full round trip you can write optimistically and reconcile later. Here the response to the request is the confirmation, so validation belongs in front of the call. Unified error handling is what makes that failure legible in the same shape as every other system, instead of a vendor-specific rejection you have to learn to read.

How it works

A booking proposal states its tax twice

The object you most often write is also the one with the strictest payload rule. A booking proposal carries tax information at the booking level and again on every individual line, both are mandatory, and the two representations have to agree. A mismatch is rejected rather than reconciled.

That is internal accounting logic surfacing in an interface rather than an oversight. It also explains what the readable side of this system is for: accounts and tax rates are precisely the reference data you need in order to state the pair correctly. The three readable objects are the coding context for the four writable ones.

The same tax, stated at both levelsJSON
{
"tax": { "rate": 21.0, "amount": 21.00 },
"lines": [
{ "amount": 100.00,
"tax": { "rate": 21.0, "amount": 21.00 } }
]
}

The shape is the point rather than the field names, which differ per payload: state the tax once at booking level and once per line, and keep them consistent. Through Maesn the duplication is produced from a single normalised structure.

Lines without tax are where the rule gets slippery

The two-level rule reads cleanly while every line carries tax. Introduce lines that carry none, and stating the same thing at both levels stops being a copy of one value and turns into a question about what the booking level should now say. This is the part of the payload teams most often get wrong on the first attempt, so it is worth building a case for it before you meet it in production.

The double representation and its validation behaviour are documented in Maesn’s material for this system, checked 13 August 2026. SnelStart’s own reference is behind a registered account and published in Dutch, so the field-level specimen above is illustrative of the rule rather than copied from it.

Which objects and operations are actually enabled, and which of the 37 are marked on demand, is listed object by object on the SnelStart API page rather than repeated here.

How it works

A country is an identifier, and the list can change

Objects here are fragmented in a way that costs calls. A contact does not carry a country code, it carries a country identifier that points at a separate record. Getting from that identifier to something you can display or store means another request.

The part that turns a lookup into a design decision is that the list is not fixed. Countries can be created, changed and deleted, so treating the mapping as a constant you import once will drift. A cache is the right answer and it needs a refresh policy, not a seed script.

This is the ordinary case for reference data across accounting systems, and it is the reason a shared shape exists at all. Through the common data model the identifier is resolved before the object reaches you, so a contact arrives with the same country representation it would have on any other connected system.

The lookup you do not want in a loopHTTP
GET /contacts -> { "countryId": "..." }
GET /countries/{id} -> { "code": "NL", ... }
 
# one contact, two calls, and the second
# result is not safe to cache forever

Fetching a page of contacts this way is the classic N+1: one call for the list and one per distinct country. Resolving and caching centrally is what removes it.

How it works

Nothing calls you, and the test company is the default

No object in this system carries a native event. Eight of the 30+ connected systems send something, and this is not one of them, so there is no subscription to register and no delivery to verify. Staying current is a scheduled read on the same filters and paging you use everywhere else.

In the event model that is the pull half doing all of the work. A pull catches up after an outage, which a push does not, and the interval is your decision rather than a constraint the vendor sets.

One caveat specific to this system is worth stating before you tune anything. While you are on a development key you are reading the default test company, so whatever you measure about volume, timing and page sizes is measured against data that is not your customer’s. Treat those numbers as a smoke test rather than a baseline.

On demand is not a delivery date
Eighteen objects here are marked on demand rather than unavailable. That marking means technically feasible and not yet built, it carries no availability commitment, and it is least reliable in exactly this column. Plan against what is enabled today and tell us which objects you need.
Where our part ends

What Maesn covers, and what stays with you

What we take off the list:

  • The token lifecycle. The exchange, the hourly refresh and the storage of both keys per tenant happen behind the connection, so your code holds one account key and no timers.
  • The reference resolution. Country identifiers and the other fragmented lookups are resolved before objects reach you, which is where the N+1 would otherwise live.
  • The duplicated tax. One normalised booking structure goes in and the two representations this system requires are produced from it, consistently.

What stays with you, and the first two are not engineering problems:

  • Partner approval and the production key. The application and the submission are filed in your name, and no interface in front of them shortens the review, so the calendar time belongs in your plan. You do not walk it alone though: we supply the callback address the application asks for, help you cut the scope list down to what your endpoints actually need, and drive the traffic the observation week requires.
  • Your customer’s package. Access needs inZicht or inControle. That is a question for your qualification call, not your backlog.
  • What a write means. We can make a rejected booking proposal legible, and we cannot make an unreadable object readable. Where there is no round trip, the decision about what to record on your side is yours.
FAQ

Frequently asked questions

How do I enable access to SnelStart for an integration?

While you are building, two things have to exist. Your customer needs the inZicht or inControle package and creates a connection key under Koppelingen, Maatwerk. You need your own developer account for a subscription key. In production the second half changes: your customer signs in through a redirect instead of copying a key, and SnelStart delivers it to a callback.

Does SnelStart support OAuth?

For production, yes, and SnelStart calls it that itself, though it does not follow a standard OAuth pattern. You send your customer to an activation page built from an application identifier, they sign in and accept the scopes, and SnelStart posts the connection key to a callback you registered. The development flow is the one without a redirect, which is why most descriptions of this system say there is none.

What is the SnelStart observation period?

A week of supervised traffic before you go live. Once your integration is ready you tell SnelStart's partner manager, and for seven days they watch it: you need at least 150 calls in that window and not too many errors. It is a scheduled exercise rather than something that passes on its own, and it sits between the partner application and production access.

What does a SnelStart integration cost to put into production?

A development subscription is free and runs for 90 days with limited calls. A production subscription key is a one-time fee of 250 euro, and it is issued after your integration has been submitted and approved rather than on request. Your customer pays for their own package separately.

Why does my integration only see one company?

Because you are still on a development key. Maesn's documentation states that production use requires official partner status, and that approval is what grants access to live customer environments beyond the default test company. Until then a working integration is limited to that one company.

Can I read back a booking proposal I created in SnelStart?

Not through Maesn today. Booking proposals, credit notes and files are switched on for writing and their reads are marked available on request, so there is no round trip in place at the moment. That is our enablement status rather than a limit of the SnelStart API, so if your product needs those reads, ask for them and plan the write path to validate before it sends in the meantime.

Why does my booking proposal fail validation?

Most often because the tax does not agree with itself. A booking proposal carries tax at the booking level and on every line, both are required, and the two representations have to match. A mismatch is rejected rather than reconciled, so the duplication is a consistency rule and not a formality.

Why does a SnelStart contact return a country identifier instead of a country code?

Because the country is a separate record rather than a static code. Resolving it takes another call, and the list is not fixed: countries can be created, changed and deleted, so a cached mapping needs a refresh strategy rather than a one-off import.

Build once on the Unified API.

SnelStart approves before it issues a production key, BuchhaltungsButler meters ten uploads a minute and Xero meters what you read. Build against one interface and each of those becomes a row in a table rather than a project.