Every request carries an API key as a bearer token. There is no OAuth flow, no session, no refresh token, and no per-user identity: a key belongs to a business, not to a person.
curl https://stippa.nl/api/v1/appointments \
-H "Authorization: Bearer stp_live_a3f9k2Qx7mLpZ4vN8bR1tY6wS0dH5jE2cU9iO3nA7gK"
Getting a key
Keys are created by the business owner or an admin, in the product, under Dev > Sleutels. You cannot create one through this API, and no scope will ever let you: see permissions.
Ask the business owner
They need the Groei plan for read access, or Pro for writes and webhooks. Tell them which scopes you need and why. The creation screen groups scopes by resource and shows what each one unlocks, so "I need to read appointments and create them" maps onto what they see.
They pick scopes and an optional expiry
An expiry date is optional and worth setting for a key you hand to a contractor. The owner is emailed before it lapses.
They copy the key once
The secret is shown exactly once, at creation. We store a SHA-256 hash of it and never the key itself, so nobody at Stippa can read it back to you, including us on a support call. If it is lost, the only remedy is a new key.
This is a server-side secret. A key carries the full rights of whatever scopes it holds over every customer record the business has. Put it in an environment variable on a server you control. Never in a mobile app, a browser bundle, a repository, or a URL.
Key format
stp_live_a3f9k2Qx7mLpZ4vN8bR1tY6wS0dH5jE2cU9iO3nA7gK
└──┬───┘ └──────────────────┬───────────────────────┘
│ │
│ 43 characters, 256 bits of randomness
environment prefix
If you pattern-match on the prefix, match against a set of prefixes rather than the exact
string stp_live_. Our parser does, precisely so that adding another prefix later cannot break
your client.
The owner's key list shows stp_live_ plus the first six characters and the last four. That is
enough to match a key to the one in your deployment when something is wrong, and far too little
to reconstruct it.
When authentication fails
All three are 401, told apart by the code field. None of them is retryable.
code | Means | Fix |
|---|---|---|
UNAUTHORIZED | Missing, malformed, or not a key we know | Check the header value is complete |
KEY_REVOKED | The key was revoked | Ask the business for a new key |
KEY_EXPIRED | The key passed its expiry date | Ask the business for a new key |
Revocation takes effect on the very next request. Key validity is never cached, so there is no window in which a revoked key still works.
Repeated failed authentication is rate limited by IP, since a request that never authenticated has no key to count against. See rate limits.
Rotating a key
Keys are independent, so rotation needs no downtime and no maintenance window. Never revoke first.
Create the replacement
Ask the owner for a second key with the same scopes. Both are now live.
Deploy the new value
Change the environment variable and roll your service. Confirm from your own logs that traffic is flowing under the new key.
Watch the old key go quiet
The owner's key list shows each key's last-used time and request count. Wait until the old key has stopped being used, which also catches the instance you forgot about.
Revoke the old key
Immediate and irreversible. If something was still using it, you will know within one request.
Rotate on a schedule you decide, and immediately if a key has been in a log file, a screenshot, a support ticket, a chat message, or a repository. Revoking is free.
Call it from your backend
There is no CORS: this API sends no Access-Control-Allow-Origin header, so a browser cannot
call it from another origin. Keep the key server-side and have your own frontend talk to your own
backend.
For a booking form on a public website, use Stippa's embeddable booking widget instead. It needs no key.
Checking what a key can do
curl https://stippa.nl/api/v1/me \
-H "Authorization: Bearer stp_live_..."
The response names the business, the scopes granted to this key, and the scopes the business's plan entitles. A scope in the second list but not the first is a key that needs editing; a scope in neither is a plan that needs upgrading.
GET /api/v1/me accepts any valid key regardless of scope, so it is also the cheapest way to
check that a credential still works.