Skip to main content

Authentication

Every API request is authenticated. The platform accepts two kinds of credentials:

CredentialHeaderTypical caller
API keyX-API-Key: <key>Servers, scripts, integrations, AI agents — anything that runs without a person signing in.
Access token (JWT)Authorization: Bearer <token>Front-ends where a person signs in, such as the generated app.

Send one or the other, not both. Whichever you use, the request is resolved to a tenant and an account (a user profile in that tenant, or the system account), and that account decides what the request may read and write.

A few routes need no credential at all: OPTIONS preflight requests, and the signed /binaries/... URLs described in Files, where the signed token in the URL is the credential.

API keys​

An API key is created in the app and belongs to exactly one tenant. Present it in the X-API-Key header:

curl https://api.mosterd.com/data/2f9c6e1a-7b34-4d58-9c21-0a5e8f1b2c3d/companies \
-H "X-API-Key: $MOSTERD_API_KEY"

Tenant-scoped routes only​

An API key only works on routes that carry the tenant id in the path — /data/{tenant}/..., /metadata/{tenant}/..., /system/{tenant}/..., /tenants/{tenant}, and so on. The tenant in the path must be the tenant the key was created in; a key used against any other tenant is rejected with 401 Unauthorized.

Routes without a tenant segment, such as GET /tenants (list the tenants you have access to) or POST /tenants, do not accept API keys. They need a signed-in user's access token.

Which account the key acts as​

A key is either bound to a user profile or not:

  • User-bound key — the request runs as that user profile. It gets the same role and the same access control as the person would when signed in. If the profile is suspended or its activeUntil has passed, the key stops working too. When the user profile is linked to an object in the model — typically a person — the key reaches that object through the profile, for example with GET /data/{tenant}/persons/own.
  • System key (no user profile) — the request runs as the system account. The system account has no role restrictions and no record-level access control: it can read and write everything in the tenant. Use it for trusted back-end integrations only, and prefer a user-bound key whenever the caller should see what a particular person sees.

Expiry and revocation​

A key may have an expiry date. Once it has passed, or once the key is revoked, every request with that key is rejected with 401 Unauthorized. There is no refresh mechanism — create a new key and replace the old one in the caller's configuration.

The platform stores only a hash of the key, so a lost key cannot be retrieved — only revoked and replaced. Keep it in a secret store, never in a git repository or in front-end code.

Access tokens (JWT)​

Interactive users sign in through the platform's identity provider, Auth0. The front-end obtains an access token and sends it as a bearer token:

curl https://api.mosterd.com/data/2f9c6e1a-7b34-4d58-9c21-0a5e8f1b2c3d/companies \
-H "Authorization: Bearer $ACCESS_TOKEN"

The API accepts a token only when:

  • it is issued by the sign-in server of the same environment (https://login.rulebooks.ai/ for production, https://login.develop.rulebooks.nl/ for develop),
  • its audience (aud) contains https://api.mosterd, and
  • it is signed correctly and not expired.

When requesting the token, pass https://api.mosterd as the audience — for example through authorizationParams.audience in the Auth0 SPA SDK. Without it, Auth0 returns a token the API does not accept, and every request fails with 401 Unauthorized.

From signed-in user to tenant account​

A token identifies a person (its sub claim), not a tenant. On a tenant-scoped route the platform looks up which user profile that person has in the tenant named in the path:

  • If there is one and it is active, the request runs as that user profile, with its role and access control.
  • If the person has no user profile in that tenant, or the profile is inactive, the request is rejected with 403 Forbidden.

One person can therefore have accounts in several tenants with a single sign-in. GET /tenants returns the tenants the signed-in person has a user profile in, which is how a front-end lets the user pick one.

Roles​

Every user profile has a coarse tenant role that is checked before the request reaches any data:

RoleMay do
adminEverything, including /system/..., /audit/... and /admin/....
memberRead and write data; no access to /system/..., /audit/... or /admin/....
viewerRead only — POST, PATCH and DELETE are refused; no access to /system/..., /audit/... or /admin/....

A request the role does not allow is rejected with 403 Forbidden. On top of the role, the model's access control decides which individual records the account can see and change.

The role applies the same way to a signed-in user and to a user-bound API key. System keys have no role and are not restricted.

Errors​

StatusCause
401 UnauthorizedNo credential; an API key that is unknown, expired, revoked, or belongs to another tenant; an API key on a route without a tenant segment; an access token that is invalid, expired, or has the wrong issuer or audience.
403 ForbiddenThe signed-in person has no active user profile in the tenant, or the account's role does not allow the method or path.