API keys
Minting, listing, and revoking the credentials your integration uses. How a key is presented on a request is on Authentication; this page is about managing them.
Routes
| Method | Path | Needs |
|---|---|---|
| GET | /v1/api-keys |
Admin |
| POST | /v1/api-keys |
Admin |
| DELETE | /v1/api-keys/{id} |
Admin |
An API key cannot manage API keys
These three routes need a signed-in person. A request carrying X-Api-Key is refused here even
if the key is wide, and so is anything arriving through MCP.
That is what stops a narrow key from minting a wider one. It also means these routes cannot be part
of an automated provisioning flow: somebody with an Admin role signs in and creates the key, then
gives it to the integration.
There is no GET /v1/api-keys/{id} and no update route. A key's secret is unreadable after creation,
so a detail route would return what the list already returns, and changing a key's scopes after the
fact would silently widen a credential somebody else is already holding. To change what a key may do,
revoke it and mint another.
GET /v1/api-keys
Every key this organization has, newest first, revoked ones included. Admin. A plain array.
| Field | Type | Means |
|---|---|---|
id |
uuid | The key. This is what DELETE takes |
name |
string | What the firm called it |
prefix |
string | syl_live_ or syl_test_ |
lastFour |
string or null | The last four characters of the secret |
expiresAt |
date-time, null | When it stops working. Null means never |
revokedAt |
date-time, null | When it was turned off. Null means it is live |
revokedById |
uuid or null | Who turned it off |
lastUsedAt |
date-time, null | When it was last presented. Null means never used |
createdById |
uuid | Who minted it |
createdAt |
date-time | When |
scopes |
array | What it may do |
No secret is in this response and there is no route that can produce one. Syllevo stores a
SHA-512 hash of the key and nothing else. prefix and lastFour are here so a person holding three
keys can tell which one is in which integration without any of them being readable.
lastUsedAt: null is the field to look at before revoking something nobody recognizes. A key that
has never been presented is safe to turn off.
POST /v1/api-keys
Mints a key. Admin. Answers 201.
| Field | Type | Required | Means |
|---|---|---|---|
name |
string | yes | What the key is for. Up to 128 characters |
expiresAt |
date-time, null | no | When it should stop working. Must be in the future |
scopes |
array | yes | What it may do. At least one, at most 200 |
Each scope:
| Field | Type | Means |
|---|---|---|
permission |
ProfilePermission |
0 View, 1 ManageDocuments, 2 ManageSources, 3 SendReminders, 4 ManageSettings |
profileId |
uuid or null | Null means every client. A value narrows the key to that one |
{
"name": "Saga import job",
"expiresAt": "2027-09-11T00:00:00Z",
"scopes": [{ "permission": 0, "profileId": null }]
}
The response:
{
"key": {
"id": "66666666-6666-4666-8666-666666666666",
"name": "Saga import job",
"prefix": "syl_live_",
"lastFour": "0000",
"expiresAt": "2027-09-11T00:00:00Z",
"revokedAt": null,
"revokedById": null,
"lastUsedAt": null,
"createdById": "44444444-4444-4444-8444-444444444444",
"createdAt": "2026-09-11T10:00:00Z",
"scopes": [{ "permission": 0, "profileId": null }]
},
"plaintextKey": "syl_live_examplekeynotreal000000000000"
}
plaintextKey appears in this one response and nowhere else, ever. Store it at this moment or
mint another key. There is no recovery route, no support process that can read it back, and no
second copy anywhere in Syllevo.
The split between key and plaintextKey is the shape saying which half is repeatable. key is
exactly what GET /v1/api-keys returns for the rest of the key's life.
Scopes cannot reach past the creator
The scopes asked for must be a subset of what the caller holds themselves, profile narrowing
included. An Admin with View on four clients cannot mint a key with View on every client, and
cannot mint one with ManageSources on a client where they only hold View. Asking for more answers
403 api_key_scope_escalation.
403 covers two different refusals here and the status does not separate them, the code on the body
does: forbidden means the caller is not an Admin, api_key_scope_escalation means the scopes
reached too far.
Choose the narrowest scopes that work
A key is a credential that lives in somebody else's configuration file for years. Two habits are worth building in:
- Ask for the permissions the job actually uses. An import job that reads invoices needs
Viewand nothing else. Do not addManageDocumentsbecause it might be handy later; mint a second key when later arrives. - Name clients where you can. A key scoped to
profileId: nullreads every client the organization has, including ones onboarded after the key was minted.
expiresAt is optional and worth setting. Leaving it absent means the key lives until somebody
revokes it, which should be a decision rather than a blank field.
Scopes may not be empty. A key with no scopes would authenticate and then be refused everywhere by default-deny, which reads to whoever built the integration as a broken product rather than as a mistake in the request.
DELETE /v1/api-keys/{id}
Revokes a key, immediately and permanently. Admin. Answers 204.
The next request carrying that key gets 401 api_key_invalid. There is no grace period and no
un-revoke. If you revoked the wrong one, mint a new key and update the integration.
The row stays. Revoked is a state the list shows, not a row that disappears, because every audit entry naming that key id has to keep resolving long after the key is gone.
| Status | Code | Means |
|---|---|---|
404 |
not_found |
No such key, or it belongs to another organization |
409 |
conflict |
Already revoked. Somebody else got there first, so reload |
A key belonging to another organization answers 404, exactly as one that never existed does.
Distinguishing them would let one firm enumerate another's key ids by reading status codes.
Test keys and live keys
The prefix is decided by the deployment, not by the request. Production mints syl_live_, every other
environment mints syl_test_. There is no sandbox mode and no test-key parameter: a syl_test_ key
means a key from a non-production Syllevo, and it will not authenticate against
https://api.syllevo.com.
The prefix is deliberately on the front of every key so leak scanners recognize one in a commit.