Admin API
Administer an organisation without a browser session: scoped, expiring tokens for invoicing exports, provisioning clients and members, and audit feeds.
Open Settings › Admin API in the console
Create a token
Open Settings › Admin API in the console (owner or admin, with a verified email address). Give the token a name, tick the scopes it needs and choose how long it lives: up to 365 days, 90 by default (the console offers 30/90/180/365). The secret is shown exactly once; only its first characters are kept for recognition. Store it in your integration's secret store, never in source.
Write scopes can only be granted by an organisation owner, because a write token acts as owner on the API. Tokens are minted by a signed-in session only: a token can never create, list or revoke tokens.
# the token is shown once, at creation: keep it in your integration's secret store export SLUIS_ADMIN_TOKEN="sluis_admin_…" curl https://api.sluis.ai/admin/orgs \ -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN"
Authentication and scopes
Send the secret as a bearer token on every request. A token is bound to the organisation it was created in and reaches only the endpoints its scopes allow; everything else answers 403. Scopes are grouped by family: read opens GET on the family, write opens every method on it and implies read. Read-only families have no write scope.
Session-only whatever the token carries: token management, two-factor settings, provider and gateway credentials, legal agreements, organisation switching, policy version history, and every billing payment action.
| Family | Read (GET) | Write |
|---|---|---|
usageUsage per organisation (invoicing export) | GET /admin/v1/usage · GET /admin/usage | — |
orgsOrganisations and their clients, budgets and margins | GET /admin/orgs | POST /admin/orgs · PUT /admin/orgs/{id}/budget · PUT /admin/orgs/{id}/margin |
membersMembers, roles and invitations (invite by email only) | GET /admin/members | POST /admin/members · PUT/DELETE /admin/members/{id} · POST /admin/members/{id}/invite |
keysAPI keys and workloads | GET /admin/keys · GET /admin/workloads* | POST/PUT/DELETE /admin/keys* · /admin/workloads* |
policyThe live default policy (read only) | GET /admin/policy | — |
auditAudit log and the operator audit (metadata, no payloads) | GET /admin/audit · /admin/operator-audit · /admin/requests/live | — |
billingBalance, invoices and statements (read only, never a payment) | GET /admin/billing · /admin/billing/invoice/{id} · /admin/billing/statement/{y}/{m} · /admin/usage/org-spend | — |
# a token without the scope for this endpoint { "error": { "message": "token scope does not allow this endpoint", "type": "sluis_error" } }
Usage export for invoicing
GET /admin/v1/usage returns the usage of your organisation tree for a window, one entry per organisation — including zero-usage clients, so an invoice run sees every customer. Amounts are integer cents excluding VAT, computed with the same query as the console billing page, so the export reconciles with your monthly statement.
from and to are YYYY-MM-DD dates in UTC, half-open [from, to), at most 92 days; omitted they cover the previous calendar month. Each organisation carries per-provider-and-model lines. Store the period on your invoice so a re-run of the same month is idempotent on your side. Root organisations only: a client-organisation token gets 403.
# usage:read — every organisation under the root, per provider/model, for one month curl "https://api.sluis.ai/admin/v1/usage?from=2026-08-01&to=2026-09-01" \ -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN"
{
"period": { "from": "2026-08-01", "to": "2026-09-01" },
"currency": "EUR",
"root": { "id": "…", "name": "Agency BV" },
"organisations": [{
"id": "…", "name": "Client BV", "kind": "client", "parent_id": "…",
"requests": 123,
"tokens": { "input": 1, "output": 2, "total": 3 },
"list_cents": 1000, "billable_cents": 1200,
"lines": [{ "provider": "openai", "model": "gpt-5", "requests": 10,
"input_tokens": 1, "output_tokens": 2, "list_cents": 500, "billable_cents": 600 }]
}],
"total_billable_cents": 1200
}| from | YYYY-MM-DD (UTC), inclusive. Default: first day of the previous month. |
| to | YYYY-MM-DD (UTC), exclusive. Default: first day of the current month. Window ≤ 92 days. |
| billable_cents | Integer cents excl. VAT: what the root is charged for that organisation. list_cents is provider list price (0 on BYOK). |
Provisioning organisations, members and keys
With orgs:write an agency creates client organisations and sets their budget and margin from its own CRM; the owner who minted the token becomes the first owner of each new client, so the minter must still be an owner. members:write invites by email only, changes roles and removes people — a token cannot grant the owner role, set a password or remove an owner. keys:write provisions API keys and workloads.
Every mutation runs the same validation, role and billing rules as the console: creating client organisations still requires active billing. A token cannot grant the owner role, set passwords, or read request and response payloads.
# orgs:write — a new client organisation under your agency (201 → { id, name, kind, parent_id, … }) curl https://api.sluis.ai/admin/orgs \ -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "Client BV", "allocated_budget_microeur": 250000000 }' # its monthly budget (micro-euros; null clears) and reseller margin (basis points) curl -X PUT https://api.sluis.ai/admin/orgs/$ORG_ID/budget -d '{ "allocated_budget_microeur": 500000000 }' … curl -X PUT https://api.sluis.ai/admin/orgs/$ORG_ID/margin -d '{ "margin_bps": 2000 }' …
# members:write — invite (201; an activation mail goes out), change a role, remove # a token invites by email only: it cannot set a password or grant the owner role curl https://api.sluis.ai/admin/members \ -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '{ "email": "j.devries@client.nl", "role": "member" }' curl -X PUT https://api.sluis.ai/admin/members/$USER_ID -d '{ "role": "admin" }' … curl -X DELETE https://api.sluis.ai/admin/members/$USER_ID …
# keys:write — a workload, then a key inside it (the secret is returned once) curl https://api.sluis.ai/admin/workloads -d '{ "name": "crm-bot" }' … curl https://api.sluis.ai/admin/keys \ -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "prod", "workload_id": "…", "environment": "production" }'
Expiry, rate limit, revocation and audit
Every console-minted token expires; an expired token answers 401 exactly like a revoked one, and the console lists it as expired. Revoke from the same table — the token stops authenticating within 30 seconds. Each token is limited to 600 requests per minute; over the limit the gateway answers 429 with a Retry-After header, so back off and retry.
Every mutation made with a token is written to the operator audit (GET /admin/operator-audit, also in the console under Audit log) with the token's id and name as the actor, so a trail always names the integration that acted. audit:read returns metadata only — never request or response payloads. Tokens are revoked automatically when the user who minted them is removed, loses the owner role or changes their password.
# audit:read — a mutation made with a token names the token as its actor curl https://api.sluis.ai/admin/operator-audit -H "Authorization: Bearer $SLUIS_ADMIN_TOKEN" { "rows": [{ "action": "org.create", "target_type": "organisation", "target_id": "…", "actor_email": "admin-token", "metadata": { "name": "Client BV", "actor_token": { "id": "…", "name": "crm-sync" } } }] }
Errors
Errors use the same envelope as the rest of the API: an error object with a message and a type. The status code tells you what to do.
A 403 with "token scope does not allow this endpoint" means the token exists and is valid but lacks the scope: mint a new token with the right scope rather than widening an existing one — scopes are fixed at creation.
| Status | Meaning |
|---|---|
401 | Missing, invalid, revoked or expired token. |
403 | The token's scopes do not cover this endpoint, or the role rules refuse the action. |
429 | Over the per-token rate limit; wait Retry-After seconds. |
400 | Invalid body or query (the message names the field). |