Authentication
A caller outside the web app, such as the CLI or another app you sign in to with your LangAlpha account, authenticates by sending a token in the Authorization header:
Authorization: Bearer <token>The CLI’s browser sign-in is how you get one.
Kinds of credential
Section titled “Kinds of credential”| Credential | Looks like | Who uses it |
|---|---|---|
| Access token from a browser sign-in | An OAuth access token | The CLI after langalpha login, and other apps you sign in to with your LangAlpha account. |
| A computer’s token | lac1.... |
The CLI inside a computer, for the agent’s commands. |
| The web app’s sign-in | A session token | The web app itself. |
The server tells them apart by their form, and a token it refuses is refused outright, never retried as another kind.
Scopes
Section titled “Scopes”A token reaches only the routes that declare a scope it holds:
| Scope | What it covers |
|---|---|
agent:read |
Reading threads, their turns and streams, workspaces, computers, automations, and your model list. |
agent:run |
Starting, steering, answering, retrying and cancelling turns, and running an automation now. |
workspace:write |
Creating, changing, archiving and deleting workspaces and automations, and archiving and deleting threads. |
computer:write |
Adding, starting, stopping, changing and removing computers. |
data:read |
Market data, news, calendars, your portfolio and watchlists. |
Routes that declare no scope answer only to the web app’s sign-in, whatever token you send. That keeps brokerage orders, secrets, publishing and your account identity out of any token’s reach. An order waiting for approval is answered only in the app, and a token that tries gets a 403 with type order_decision_forbidden.
When a token is refused
Section titled “When a token is refused”| Status | Body or header | What to do |
|---|---|---|
| 401 | WWW-Authenticate: Bearer error="invalid_token" |
The token is unknown, expired or revoked. Sign in again. |
| 403 | detail.type is insufficient_scope, with the scopes the route needs |
Use a token that holds them. |
| 403 | detail.type is forbidden_for_token |
The action is closed to this kind of token, such as a delete with a computer’s token. |
| 503 | Retry-After |
The server cannot check the token right now. Retry after the time given. |
Who am I
Section titled “Who am I”GET /api/v1/auth/principal answers who the server takes the caller for. Every token reaches it, whatever its scopes.
| Field | Meaning |
|---|---|
user_id |
Your user id. |
auth |
How you authenticated: session, oauth, computer or service. |
client_id |
The app a browser sign-in token was issued to. |
scopes |
What the token may do. Empty for a session, which needs none. |
computer_id |
The computer, for a computer’s token. |
How a client learns to sign in
Section titled “How a client learns to sign in”GET /api/v1/auth/cli is public: a client calls it before it holds any credential, and the CLI’s login does exactly that.
| Field | Meaning |
|---|---|
auth_required |
false on a server that asks for no credential. |
issuer |
The sign-in provider to use for a browser sign-in, whose discovery document sits beside it. null when the server takes no browser sign-in. |
client_id |
The public client a CLI signs in as. null with issuer. |
scopes |
The scopes to ask the provider for, besides who the user is. Empty where the server grants scopes from your account rather than reading them from the token. |
A default self-hosted install answers with these fields:
{ "auth_required": false, "issuer": null, "client_id": null, "scopes": ["agent:read", "agent:run", "workspace:write", "computer:write", "data:read"]}