Authentication
One key per integration, sent as a bearer token. There is no login, no session and no refresh.
Every request carries the key in the Authorization header. A missing, malformed, expired or revoked key is answered with 401 UNAUTHENTICATED, and the answer never says which of those it was.
curl 'https://api.notulai.nl/v1/usage' \
-H 'Authorization: Bearer nla_live_...'Two kinds of keys
An integration key belongs to an integration: a non-human member of the organisation, created by an org admin under Koppelingen. The integration is the actor: it owns the calls, it appears as the author of everything it creates, and it costs one licence while it is active. It sees what a regular member sees, organisation-wide meetings plus its own, and its scopes are the only limit on what it may do.
A personal key belongs to a single user, created on their own profile page. It acts as that user: it sees exactly what they see, including their own private meetings, and it can never do more than the user themselves. Its effective rights are the intersection of the key's scopes and the user's own permission level. A scope on a personal key opens nothing the user could not already do in the app, and when the user is deactivated or leaves the organisation, their personal keys are revoked with them.
That last property is the rule of thumb: build an organisation's integration on an integration key, never on the personal key of one employee. Personal keys are for your own automation. Each kind is enabled separately per organisation; a personal key used while the feature is off is answered with 403 PERSONAL_API_KEYS_DISABLED.
Both kinds hold at most two live keys per owner, which is what makes rotation possible without a gap: issue the second key, move your systems over, then revoke the first. A key can also be given an expiry date, and an expired key stops counting against the limit of two.
Restricting a key to your own addresses
A key can be tied to a list of IP addresses or ranges. A call from anywhere else is refused with 403 IP_NOT_ALLOWED, and the address we saw is in details.address so you can tell a wrong entry from a wrong network. The address is read at our own edge and cannot be spoofed by a header.
A key is not a user
If a key leaks