Pular para o conteúdo principal

Authentication

Every route in this section (except GET /health) requires an API key sent as a bearer token:

Authorization: Bearer <API_KEY>

Getting a key​

API keys are issued from the dashboard, shown once at creation. Store the value as soon as it's shown — there's no way to view it again later, only to revoke it and issue a new one.

A key looks like ak_xxxxxxxxxxxx....

Key scopes​

Every key is minted with a scope, chosen when you create it in the dashboard:

ScopeBound toDrives
sdk (default)A single projectThe mobile agent surface (/agent/* — sessions, messages, actions). This is the key your app embeds.
publishA single projectThe appilots CLI (/cli/* — syncing the MCP document) and issuing end-user tokens from your backend. Private — never ship it inside an app.
operatorThe whole workspace (no project)The escalation operator surface (/escalations/*). Lets your OWN systems — a helpdesk bridge, a Slack bot, an internal support panel — act as a human operator instead of only the Appilots dashboard. See Operator API.

The scopes are mutually exclusive by design: each key is accepted only by its own surface, even though all of them are ak_... bearer tokens sent the same way. An sdk key cannot run the CLI, an operator key is rejected by every /agent/* route, and so on. Never share a key across use cases — mint a separate key per integration.

Example​

curl https://api.appilots.com/api/v1/agent/session \
-X POST \
-H "Authorization: Bearer ak_xxxxxxxxxxxx..." \
-H "Content-Type: application/json" \
-d '{}'
const res = await fetch('https://api.appilots.com/api/v1/agent/session', {
method: 'POST',
headers: {
Authorization: 'Bearer ak_xxxxxxxxxxxx...',
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});

Conversation capabilities (session token)​

The API key identifies your application, not a conversation. Knowing a session, action, or escalation id is not enough to read or write it: every request on an existing conversation must also carry that conversation's session token.

  1. Create the conversation with POST /agent/session. The response carries sessionId and sessionToken.
  2. Send the token on every later request about that conversation:
X-Appilots-Session-Token: <sessionToken>

This applies to everything under /agent/message, /agent/message/stream, /agent/continue, /agent/session/:sessionId/*, /agent/action/:actionId/*, and /agent/escalation*. Those message, continue, and escalation routes no longer create a session implicitly — a request without sessionId returns 422.

The session token is a signed bearer secret, valid for the lifetime of the session. It cannot be retrieved again or re-issued for an existing session, so store it alongside the sessionId. Treat it as a credential: don't log it, and clear it on logout. The official SDKs handle all of this for you.

Verified end users (optional)​

A shared SDK key proves which app is calling, not which person is logged in. To attribute conversations to a real user — for identify, cross-device history, or external integrations that act on a user's behalf — your backend issues a short-lived end-user token after checking its own login session:

// Server only. Validate your app's own login session first.
const user = await requireLoggedInUser(request);
const res = await fetch('https://api.appilots.com/api/v1/cli/end-user-token', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.APPILOTS_PUBLISH_KEY}`, // scope: publish
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId: user.id, integrationGrants: [] }),
});
const { data } = await res.json(); // { token, expiresIn: 300 }
// Return data.token only to this logged-in user, with Cache-Control: no-store.
FieldTypeRequiredDescription
userIdstring (1–128 chars)YesYour stable, unique id for the user. Take it from your own session — never from client input.
integrationGrantsarray (max 16)NoExact tool calls this user may run through an external integration: { toolId, arguments }. The arguments must match exactly; there are no wildcards. Defaults to none.

The client then sends the token on its requests:

X-Appilots-User-Token: <token>

Tokens last five minutes, so fetch a fresh one when it expires — the SDKs do this through their getUserToken option. Once a user token is present, any userId in a request body must match it, otherwise the request returns 403. An invalid or expired token returns 401; it never silently falls back to anonymous.

401 responses​

A 401 means one of the credentials on the request was rejected:

  • a missing or malformed Authorization header (anything that doesn't start with Bearer ak_), an invalid or expired key, or a key of the wrong scope for the route;
  • a missing or invalid X-Appilots-Session-Token on a request about an existing conversation;
  • an invalid or expired X-Appilots-User-Token.

Treat any 401 as a signal to fix the credential rather than retry the request as-is. The message field says which one was rejected. See Errors for the full error code reference.

Keeping keys safe​

  • sdk keys ship inside your app, so treat them as semi-public. They can start new conversations, but they can't open existing ones without that conversation's session token, and they can't read the user registry.
  • publish and operator keys are private server-side credentials. Never put them in an app bundle, SDK configuration, or client-side code.

See Security best practices for more on this.