Skip to main content
Albus runs agents behind one HTTP API. A session is a named conversation: you pick the id, and running the same id again continues it. One POST runs a turn and returns the reply. Everything the agent did is recorded as a trace and in the session’s audit log. Read as much of https://docs.albus.sh as you need. Every page is Markdown with .md appended to its URL; https://docs.albus.sh/llms.txt is the map. This page is enough to write working code.

Limits

  • One run per session at a time. A second one gets 423. Run concurrently across session ids, not inside one.
  • Runs cost credits. 402 means the organization’s balance is empty. Stop and tell the user; retrying does not help.
  • Bounded retries. Albus retries a crashed run up to three attempts, then fails it with 502.
  • Observability is traces plus the per-session audit log. Spans are kept 90 days.

Authentication

Base URL https://albus.sh/api. Every request carries Authorization: Bearer <credential>. There are two credentials: /tokens and /invites identify you and need the browser session. Everything else accepts either.

Set up the CLI

status prints JSON: credential is api_key, session, or none; authenticated says whether Albus accepted it. It exits 0 either way; read the JSON. If authenticated is true: with credential: "api_key" skip to the SDK install; with credential: "session" skip the sign-in and mint an API key below. If it is false, sign in. You cannot complete a browser sign-in yourself. Print the URL, let the user open it:
The command prints the URL on its own line and blocks up to 180 seconds. The redirect goes to 127.0.0.1:8484-8487 on the machine running the CLI; if the user’s browser is elsewhere, they forward those ports (ssh -L 8484:localhost:8484 -L 8485:localhost:8485 -L 8486:localhost:8486 -L 8487:localhost:8487 user@host) or set ALBUS_API_KEY here instead. Verify with albus whoami, then mint a key for code:
Never write the key into source, committed config, or a shell profile you did not create for it.

Install an SDK

The Python client is also a context manager (with Albus(...) as albus:) and has an AsyncAlbus twin with the same methods. Every example below assumes albus from this block.

The API

The CLI mirrors it: albus sessions run|get|list|cancel|delete|audit, albus traces list|get, albus agents list|get|revision, albus secrets create|get|list|update|delete.

Run or resume a session

POST /sessions/{id} with user_prompt, agent_name, and agent. If the session does not exist it is created; if it does, the prompt is appended to the conversation and the model sees every earlier turn.
The response is the session plus the one message this run produced. The whole conversation is GET /sessions/{id}. wait_timeout_seconds (query; waitTimeoutSeconds in TypeScript). Block up to this many seconds for the reply. Omit to wait 30 minutes, the maximum. 0 returns as soon as the run is accepted, with session.state == "RUNNING" and no message; poll GET /sessions/{id} until state changes. A timeout is 504 and the run keeps going. Both SDKs default to 1800 and set a 31-minute transport timeout, so a waiting call needs no client configuration. agent is the configuration. Every distinct one under an agent_name is recorded as a revision (session.agent_revision), so you never create an agent, you name one. Fields: Long-lived credentials are never inlined. Store them (albus secrets create NAME) and reference them as albus.sh/secrets/<name> in headers or model.provider.credential. Albus resolves the reference when the run starts; the value never appears in a response, a revision, or the audit log. A literal MCP header value, such as a short-lived access token minted before the run, is sent as is and is not stored either: the revision records the header name with the value "<literal>", so a token minted per run does not create a new revision.

Idempotency and retries

Every run has an invocation key. Send it as invocation_key (invocationKey), which is the Idempotency-Key header over HTTP. The response returns the effective key in the same header, generated if you sent none. The key is unique in your organization and is how you read the run back: GET /traces/{invocation_key}. Rules:
  • Always send a key for unattended work. Derive it from the thing that must happen exactly once: the event id, or the session id plus a turn counter (support-1234-triage-1). Never a timestamp or a fresh random value per attempt; each one starts another run.
  • Retry 504 with the same key. The run is still going; the same key waits on it. Retry connection failures the same way.
  • Do not retry 400, 402, 409, 423, or 502 as-is. They will fail the same way. To rerun a failed run, use a new key.
  • Pick session ids you already have. The ticket, the pull request, the job. ^[0-9a-zA-Z._:-]+$, 2–100 characters. Reuse continues; a new id starts over.

Status codes

Python raises errors.AlbusError subclasses (ErrTimeout, ErrLocked, ErrConflict, ErrInvocationFailed, ErrInvocationCanceled, ErrInsufficientCredit, …) with .status_code. TypeScript throws the same names from @albus-ts/sdk/models/errors with .statusCode.

Read a session and its messages

GET /sessions/{id} returns session and messages in chronological order. Each message has role (user or assistant), content, the invocation_key that produced it, and a cursor.
session.state is RUNNING while a turn is in flight, otherwise DONE, FAILED, or CANCELED for the last one. session.current_invocation_key is the running turn’s key while RUNNING.

List sessions

GET /sessions returns sessions ordered by their most recent matching run. Filters: agent_name, agent_revision (needs agent_name), status (RUNNING, DONE, FAILED, CANCELED), invocation_key, since, until (default: the last 31 days). Page size limit is 25 by default, 100 at most.

Cancel

POST /sessions/{id}/cancel asks the running turn to stop and returns 202 once the request is accepted; the run ends shortly after with state CANCELED. 409 means nothing was running. A caller still waiting on the run gets 410.
DELETE /sessions/{id} removes the session and its messages. Traces and audit events are not messages; they stay.

Traces and spans

Every run is a trace: its status, timing, failure, and its spans: the run (invocation), each step, the model_call in each step, and every tool_call, each with input, output, timings, and token usage. This is how you find out what the agent did. GET /traces lists runs across sessions, newest first, without spans. Filters: agent_name, agent_revision, status, session_id, since, until. limit defaults to 10, max 100. GET /traces/{invocation_key} returns one run with its spans. attempts=final (default) returns the last attempt’s spans; attempts=all includes superseded attempts, whose tool calls still had their effects. payloads=false drops inputs and outputs and raises the page size from 25 to 500.
Span ids read as a path: inv3 is the third run of the session, inv3.attempt1.step2.tool1 the first tool call of its second step. Payloads over the limit are cut with an [omitted: N of M bytes; sha256=…] marker. A FAILED span carries error; a failed run carries failure with kind (crash, no_progress, interrupted, internal) and message. After 90 days spans_expired is true and the run itself still reads. Debugging loop: run, read the trace, change the prompt or a tool, run again with a new key, compare. From a shell, albus traces get KEY and albus traces list --agent-name NAME --status FAILED print the same.

Audit events

GET /sessions/{id}/audit is the security record of a session: an immutable, chronological list of everything that happened, arguments and results included. Each event has id, session_id, invocation_key, agent_revision, type, event_time, and a payload shaped by its type. Events written before the rename keep their old names (run_succeeded, run_failed, llm_call, tool_result); filter on both when you care about history. Model output, tool arguments, and tool output are stored truncated, with contentBytes, contentSha256, and contentTruncated, so a stored value can be checked against the original. Secrets never appear.
The audit log is per session. For the same events across sessions, filtered by agent, revision, status, or time, use traces. From a shell: albus sessions audit ID --limit 50.

Pagination

Every list is cursor-paged with after and limit.
  • List responses (/sessions, /traces, /sessions/{id}/audit, spans in /traces/{key}) return next_cursor when there is more. Pass it as after. No next_cursor, no more pages.
  • Session messages (/sessions/{id}) have no next_cursor. Pass the last message’s cursor as after; a page shorter than limit is the end.
  • limit maxima: 100 for sessions and traces, 1000 for messages and audit events, 500 for spans without payloads and 25 with.

Fan out

Concurrency is across sessions. One key per unit of work, one session per work item.
Python

Where the rest is