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.
402means 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 URLhttps://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:
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:
Install an SDK
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.
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 asinvocation_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
504with 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, or502as-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.
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.
albus sessions audit ID --limit 50.
Pagination
Every list is cursor-paged withafter and limit.
- List responses (
/sessions,/traces,/sessions/{id}/audit, spans in/traces/{key}) returnnext_cursorwhen there is more. Pass it asafter. Nonext_cursor, no more pages. - Session messages (
/sessions/{id}) have nonext_cursor. Pass the last message’scursorasafter; a page shorter thanlimitis the end. limitmaxima: 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