POST /sessions/{id} — albus sessions run in the CLI,
sessions.run_session in Python, sessions.runSession in TypeScript — with a
little more in the agent object. Each step shows the previous code with the
new lines marked +, and says what the addition changes about how the agent
behaves.
The examples use the Python SDK and, alongside it, the JSON agent file the CLI
takes with --agent-file. The field names are the same in both.
1. Run a simple agent
The smallest agent is a model, a system prompt, and a user prompt. No tools: the model answers from what it knows, and the reply is the whole result.id is the session: a conversation you name, which
Albus creates on the first run and continues on every later one, so the next
prompt to support-1234 sees this one. agent_name groups runs into an
agent, and the agent object is its configuration. Albus supplies the
model — no provider key of your own is needed until you bring your
own. system_prompt is prefixed with the current
date and time, so the model can answer time-sensitive questions; omit it and a
server default applies.
2. Make the run retry-safe
Add aninvocation_key. The call becomes idempotent: a network failure, a
504 from a long wait, or a lost response costs nothing, because retrying the
same key re-attaches to the same run instead of starting another.
Idempotency-Key header; in TypeScript
it is invocationKey. It is unique within your organization, and it is also how
you read the run back later — GET /traces/{invocation_key}. The effective key
always comes back in the Idempotency-Key response header, even when you let
the server generate one.
Choosing a session id. Ids match
^[0-9a-zA-Z._:-]+$, 2–100 characters.
Use something meaningful from your own system — the ticket (support-1234),
the pull request (pr-987), the job (nightly:2026-08-11) — so you can find
the session again without storing a mapping, and so the conversation naturally
continues where the work does.
Choosing an invocation key. Derive it from the thing that must happen
exactly once: the event id that triggered the run, or the session id plus a
turn counter (support-1234-triage-1). Never a timestamp or a random value
generated on each retry — that is a new invocation every time, which is what
the key exists to prevent. Reusing a key re-attaches even to a failed run, so
use the same key to recover a lost response and a new key to genuinely run
again.
3. Add web search and a terminal
Add the two built-in tools. The agent stops answering from memory alone: it can look things up on the web and run commands in a persistent Linux sandbox, and the model decides when to call each. Runs get longer and better grounded.tools is an object of tool blocks: include a block to offer that tool, omit
it to withhold it. web_search shows up in traces as WEB_SEARCH and
terminal as TERMINAL. In the CLI, --tool web_search --tool terminal
builds the same object when you are not using --agent-file. What each tool
does and what it costs is in Built-in tools.
Tool calls take steps. max_steps caps how many model steps a run may take
before it stops; add it when the agent has tools that could keep it busy.
4. Limit the tools the agent can see
Take a tool away. The model never learns the tool exists, so it cannot be talked into using it — the agent becomes a web researcher that cannot execute anything.tools object is an allowlist: what is not in it does not exist for the
model. Omit tools entirely and the agent has no built-in tools at all, as in
step 1. The same idea applies to MCP servers in the next step, where
allowed_tools picks which of a server’s tools the model is offered — and a
prompt that says “do not run commands” is not a substitute for either: the
model can ignore instructions, but it cannot call a tool it was never given.
5. Add a secret and an MCP server
Give the agent your own tools through an MCP server, authenticated with a secret. The agent can now act on your systems — here, search GitHub issues — and the credential never appears in the configuration, the audit log, or a revision. First store the credential. The value is the full header value the server expects,Bearer included:
/-separated segments of
[a-zA-Z0-9_-]), so group them the way you would files.
Then reference it from the agent as albus.sh/secrets/<name>:
http(s) URL — stdio servers need
an HTTP front.
A reference is resolved when the run starts, not when the request is
validated, so a reference to a secret that does not exist fails the invocation
rather than the request — create the secret first. Rotating is
albus secrets update: references keep working, the next run picks up the new
value, and the configuration is unchanged.
6. Configure memory
Add a memory group. What the agent learns now outlives the session: every invocation configured with the samegroup reads the same memories, whichever
session it runs in, and writes new ones at the points you list.
generation is when memories get written: end_of_invocation after the
invocation answers, agent whenever the agent decides mid-run that something
is worth keeping. A group is yours to scope — one per customer, per team, per
agent. Without memory, a session still remembers its own last 1000 messages;
memory is what carries knowledge between sessions. Read and prune a group
with GET /memories?group=support and DELETE /memories?group=support, or in
the console under Memories. More in Built-in
tools.
7. Read the trace and iterate
Every invocation is recorded as a trace: the invocation, each step, the model call in each step, and the tool calls it requested, as spans with their inputs, outputs, timings, and token usage. It is how you find out what the agent actually did — and the fastest loop for improving a prompt or a tool.Python
inv5 is the fifth invocation of the session,
inv5.attempt1.step1.tool1 the first tool call the first step requested. A
model_call span’s input is exactly what the model was given and its
output what it said, including the tool calls it asked for; a tool_call
span’s input is the arguments and its output the tool’s result. Long
payloads are shortened with an [omitted: …] marker and carry their full byte
count and SHA-256.
Read a trace with questions like these:
- Did the agent call the tool you expected? If
WEB_SEARCHfires wheregithub__search_issuesshould have, the tool’s description (from the MCP server) or yoursystem_promptdid not make the choice obvious. Name the tool and when to use it in the prompt, or tightenallowed_tools. - Did it get what it needed from the tool? Look at the
tool_calloutput. A tool that returns 40 KB of JSON for a one-line answer wastes steps and tokens; a tool that returns an error the model cannot act on gets retried. Fix the tool’s shape, not the prompt. - Where did the steps go? Count
stepspans and readusageon eachmodel_call. Loops of the same tool call with slightly different arguments are a sign the task is underspecified; amax_stepsstop is a sign the budget or the prompt is wrong. - Why did it fail? A
FAILEDspan carrieserror; the invocation carries afailure. A retried invocation has more than one attempt — passattempts=allto see the superseded ones, whose tool calls still had their effects.
GET /traces lists invocations across sessions, newest first, filtered by
agent_name, agent_revision, status, session_id, since, and until —
status=FAILED on an agent name is the first thing to look at when a job goes
wrong. Spans are kept for 90 days from the invocation’s start. The console
shows the same thing under Traces, with each invocation at
/traces/<invocation_key>.
Change the prompt or a tool, run again with a new invocation key, compare
the traces. Which brings us to what “change” means.
8. Every change is a revision
You never create an agent: you name one on a run, and Albus records the configuration. Every distinctagent object under one agent_name becomes a
revision, identified by a hash of the configuration — so each step in this
guide created a new revision of support-triage, and running the same
configuration again reuses the existing one.
agents get returns the current revision’s full configuration plus every
revision, newest first; agents revision returns one revision’s exact model,
prompt, tools, MCP servers, and memory. In the console, Agents lists them,
and each revision lives at /agents/<name>/revisions/<revision>.
Every session response carries session.agent_revision, every trace and audit
event carries agent_revision, and GET /traces?agent_name=…&agent_revision=…
lists every invocation that ran one configuration. That is the link from an
answer you are unhappy with back to the configuration that produced it:
agent_name stable for one job (support-triage, pr-reviewer) rather
than per run or per customer, so the revision list reads as the agent’s
history; session ids are where per-run identity belongs. Record the revision
next to your own results and you will always know what produced them. A
revision is configuration only — credentials are references, so rotating a
secret does not create one.
9. The audit log
The audit log is the security record of a session: an immutable, time-ordered account of every invocation, model call, and tool call — arguments and results included — and how each run ended. Where a trace is for understanding and iterating, the audit log is for answering what did this agent do, on whose request, with which configuration.
Every event carries
session_id, the invocation_key it belongs to, the
agent_revision that ran, event_time, and a payload shaped by its type.
Model output, tool output, and tool arguments are stored up to 32 KiB each,
alongside the complete value’s byte count, SHA-256 digest, and a truncation
flag (contentBytes, contentSha256, contentTruncated), so a stored value
can be checked against the original. Secrets never appear: references are
resolved at run time and the values are not written anywhere the API returns.
--after/--limit (default 100), passing the response’s
next_cursor as the next after. The log is per-session; for the same events
across sessions, filtered by agent, revision, status, or time, use
traces.
Clean up
Where next
- Bring your own key — run on your own model provider account.
- Built-in tools — web search, the terminal, and memory in detail.
- Errors — every status a run can return, and the fix.