Credentials
Credentials are the keys your integrations and agents call the API with. There are two kinds: a client_secret is an OAuth client you register here and exchange for tokens with its secret; a public_key is an API key Aleta registers for you; you keep the private key and sign an assertion with it. A key belongs to its owner, a person in the tenant: on v1 it has the owner's role, groups and access points, cut to the key's narrowing and capped by its scopes, and it loses what the owner loses. A key whose owner leaves the tenant, is deleted or suspended stops working, on v2 and v3 too. On v2 and v3 a key owned by an admin sees the whole tenant; a key owned by a user, or narrowed to some modules, sees only what its own access grants give, which is nothing unless Aleta gave it clients. An API key without an owner acts as its own API user, with the access Aleta gave it. Only an admin registers, revokes and gives keys an owner; a key acting as an admin may too. Reading requires the access:read scope, writes require access:write.
Data model
idstringStable identifier for the credential. A UUID — treat it as opaque.
namestringHuman label shown in the admin list — name it after the integration.
kindstringclient_secretorpublic_key.owner_user_idstring or nullThe person whose rights the key has; the key is listed under this user.
nullfor an API key that acts as its own API user until an admin gives it an owner.created_by_user_idstringWho registered the key: the admin, or the Aleta staff member for an API key.
scopesarray of stringThe scopes the key's tokens carry: the ceiling of what it may call.
narrowingobject or nullWhat the key may do of what its owner may.
nullis everything the owner has.modulesmaps a module toreadorread_write;access_pointslimits the key to the clients Aleta chose for it.client_idstring or nullclient_secretonly: the OAuth client id tokens are issued under.nullwhile provisioning is pending.key_idstring or nullpublic_keyonly: thekidyou sign assertions with.issuer, subjectstring or nullpublic_keyonly: theissandsubyou put in the assertion you sign.nullfor aclient_secret.statusstringpending_provisioning(aclient_secretnot yet provisioned),active, orrevoked.created_attimestampWhen the credential was registered.
last_used_attimestamp or nullWhen the credential last authenticated.
nullif never used.revoked_attimestamp or nullWhen the credential was revoked, if it has been.
write_quotaobjectThe credential's daily quota of v1 writes, set by Aleta.
per_dayis the number of writes per UTC day,used_todaythe writes counted so far today (nullif the count cannot be read right now), andresets_atthe next 00:00 UTC, when the count starts again. Past the quota a write gets429 rate_limitedwithRetry-After.
List a user's credentials
Returns the keys {user_id} owns, of both kinds, revoked ones included, newest first. {user_id} is a person who has or had a grant on the tenant; anything else (a machine user's id, an unknown id) is a 404, for every caller. A non-admin caller may only list their own — passing another user's id is a 403. Admins list anyone's.
The set is small and returned in full: the response keeps the standard v1 list envelope, but next_cursor is always null and limit echoes the row count.
curl https://api.aleta.ai/v1/users/019eb68d-5b44-7491-911a-ac66602675a7/credentials \
-H "Authorization: Bearer {access_token}"
{
"data": [
{
"id": "019f95e2-4c11-7a02-93d5-6b0e8f1c2a44",
"name": "Reporting sync",
"kind": "client_secret",
"owner_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
"created_by_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
"scopes": ["reporting:read"],
"narrowing": null,
"client_id": "client_01JCT4S9ZD9M9V0Q2W6R8XH3KE",
"key_id": null,
"issuer": null,
"subject": null,
"status": "active",
"created_at": "2026-07-24T09:14:02.000000Z",
"last_used_at": "2026-07-25T06:01:44.000000Z",
"revoked_at": null,
"write_quota": {
"per_day": 10000,
"used_today": 132,
"resets_at": "2026-07-26T00:00:00Z"
}
}
],
"next_cursor": null,
"limit": 1
}
Register a credential
Registers a client_secret key owned by {user_id} and returns 201 with the credential plus its secret and token endpoint — the only time either is disclosed. Store the secret immediately; the platform cannot show it again. API keys (public_key) are registered by Aleta.
Registration is an admin action: the caller's role must be admin, on top of the access:write scope, or 403. The owner {user_id} must be a person with a live grant on the tenant, not Aleta staff, or the answer is 404. The caller is the key's created_by_user_id.
Takes dry_run and Idempotency-Key. A dry run registers no OAuth client, so it answers without a client_id and without a secret. A replay of the same key answers the first registration with client_secret: null: the secret is shown once and never stored. Unknown body fields are a 400.
Body parameters
namestringHuman label for the credential. Required.
scopesarray of stringScopes to grant. Must be a subset of the caller's own scopes — escalation is a
403. Optional; omitting it grants the caller's read scopes.narrowingobject or nullOptional.
{"modules": {"core": "read", "reports": "read_write"}}keeps the key to those modules at those levels of what its owner has; an unknown module or level is a400. Left out, the key has everything its owner has. A key that is itself narrowed may only register a narrower key: a narrowing is required, with no module above the calling key's level, or the answer is403.
Extra response fields
client_secretstring or nullThe OAuth client secret. Present only here;
nullon a dry run, on a replay, and when no OAuth client could be made at registration (the key is thenpending_provisioningand cannot get tokens).token_endpointstringThe endpoint to POST a
client_credentialsgrant to when exchanging the secret for access tokens: the/oauth2/tokenof the sign-in service that issued the secret. Use it as returned.
curl -X POST https://api.aleta.ai/v1/users/019eb68d-5b44-7491-911a-ac66602675a7/credentials \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: {idempotency_key}" \
-d '{
"name": "Reporting sync",
"scopes": ["reporting:read"],
"narrowing": { "modules": { "core": "read" } }
}'
{
"id": "019f95e2-4c11-7a02-93d5-6b0e8f1c2a44",
"name": "Reporting sync",
"kind": "client_secret",
"owner_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
"created_by_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
"scopes": ["reporting:read"],
"narrowing": { "modules": { "core": "read" } },
"client_id": "client_01JCT4S9ZD9M9V0Q2W6R8XH3KE",
"key_id": null,
"issuer": null,
"subject": null,
"status": "active",
"created_at": "2026-07-24T09:14:02.000000Z",
"last_used_at": null,
"revoked_at": null,
"write_quota": {
"per_day": 10000,
"used_today": 0,
"resets_at": "2026-07-25T00:00:00Z"
},
"client_secret": "sk_live_...",
"token_endpoint": "https://{auth_domain}/oauth2/token"
}
Retrieve a credential
Fetch a single key by id — without its secret, always. Non-admins can only retrieve keys they own; others come back 404. Every id a list returns can be read here.
curl https://api.aleta.ai/v1/credentials/019f95e2-4c11-7a02-93d5-6b0e8f1c2a44 \
-H "Authorization: Bearer {access_token}"
Give a key its owner
Sets the person whose rights the key has from now on, and answers 200 with the credential (its write_quota.used_today is null here). Use it to give an API key that acts as its own API user an owner, or to move a key to another person. The owner must be a person with a live grant on the tenant, not Aleta staff, or the answer is 400. Admin only (403), and never from a narrowed key (403); a revoked key is 409, an unknown id 404.
A key never gets more by having an owner. The first owner of an API key limited to chosen clients gets those clients as the key's narrowing.access_points, and such a key needs an admin owner (400 otherwise). An API key that is not limited to chosen clients and is not itself an admin key takes no owner (400): it would get its owner's rights, more than it has. Register a new key for the person instead. The key keeps its scopes. Each change is logged in the key's history.
Takes dry_run and Idempotency-Key. Unknown fields are a 400.
Body parameters
owner_user_idstringThe new owner's user id. Required.
curl -X PATCH https://api.aleta.ai/v1/credentials/019f95e2-4c11-7a02-93d5-6b0e8f1c2a44 \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "owner_user_id": "019eb68d-5b44-7491-911a-ac66602675a7" }'
Revoke a credential
Permanently disables a key of either kind: its grant ends, an API key's live tokens are deleted, and a client_secret's OAuth client is disabled. Revocation is one-way; there is no un-revoke. Admin only (403). Returns 204, also when the key was already revoked (no second history entry), and 404 for an unknown id.
The body is optional: {"reason": "..."}, at most 500 characters (400 beyond), is kept with the key. Unknown fields are a 400. Takes dry_run and Idempotency-Key. A dry run leaves the key and its OAuth client active.
curl -X POST https://api.aleta.ai/v1/credentials/019f95e2-4c11-7a02-93d5-6b0e8f1c2a44/revoke \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "reason": "Rotated after the integration moved." }'
Related
- Users — the people keys belong to.
- Overview → Authentication — how tokens are exchanged and sent.