Sessions
A session is a per-user, per-business, substrate-accumulating container. It lets multiple API calls share a growing pool of evidence (queries, metrics, clusters, knowledge base hits, external observations) so later calls can reason over everything gathered earlier without re-fetching or re-passing it.
Sessions are optional. Most tools are stateless and work without one. You reach for a session when a flow performs a sequence of related lookups and a downstream tool needs to see the union of those results.
What is a session
Concretely, a session is a row in the substrates table plus an in-memory substrate view kept warm in Redis. Every call that writes to the substrate (for example data.query, data.search, data.cluster_search, context.remember) appends to that session's substrate. Every call that reads (context.query_substrate, context.ask, context.substrate) does so over the same pool.
Pass the session by adding one header to any tool call:
X-Session-Id: <session_uuid>When the header is absent, substrate-writing tools still work but their output is transient: it is returned in the response and not persisted anywhere.
When to use a session
Use a session when any of the following is true:
- You plan to call
context.askorcontext.query_substrate. These tools reason over accumulated substrate; without a session they have nothing to reason over. - You are running an agent-style flow where a loop of data lookups feeds a final synthesis step.
- You want the same evidence used by your frontend to carry over into a downstream webhook, a scheduled follow-up, or a different client.
- You want later calls to see
context.remembernotes you pushed earlier.
When not to use a session
Skip the session header for stateless operations:
- Fetching or listing artifacts (
artifacts.get,artifacts.list). - Creating or patching an artifact when you already have the content.
- Listing webhooks, API keys, or audit rows.
- One-shot knowledge base queries where you want the raw hits and nothing else.
Calling these tools with a session ID is harmless but pollutes the substrate with irrelevant writes.
Create a session
curl -s -X POST "$AMDAHL_BASE/sessions" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "Content-Type: application/json" \
-d '{ "label": "Q2 churn analysis" }'Response:
{
"id": "5f3c...",
"status": "active",
"label": "Q2 churn analysis",
"created_at": "2026-04-14T12:00:00.000Z"
}Export the session id for subsequent calls:
export AMDAHL_SESSION="5f3c..."Use a session
Pass X-Session-Id on every tool call that should participate:
curl -s -X POST "$AMDAHL_BASE/data/query" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $AMDAHL_SESSION" \
-H "Content-Type: application/json" \
-d '{
"sql": "SELECT company_name, sentiment_score FROM interactions ORDER BY sentiment_score ASC LIMIT 5"
}'To push external context into the session (for example a web finding your frontend already has):
curl -s -X POST "$AMDAHL_BASE/context/remember" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $AMDAHL_SESSION" \
-H "Content-Type: application/json" \
-d '{
"text": "Gartner reports the customer support automation market grew 18% YoY in Q1 2026.",
"source": "web",
"company_name": "Gartner"
}'Once enough evidence has accumulated, synthesize:
curl -s -X POST "$AMDAHL_BASE/context/ask" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $AMDAHL_SESSION" \
-H "Content-Type: application/json" \
-d '{ "query": "Summarize the top churn drivers and propose three retention plays." }'To inspect what the substrate currently holds without spending tokens on synthesis:
curl -s "$AMDAHL_BASE/context/substrate" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $AMDAHL_SESSION"Session lifecycle
Sessions have three statuses:
active. The default. Substrate writes are accepted, reads are fast.complete. You calledDELETE /sessions/:idor the session was marked complete by an agent. The substrate is frozen; reads still work, writes are rejected.archived. Older than the retention window. The Redis copy is dropped; the database row remains for audit. Reads still work but cold-start latency is higher.
List your sessions:
curl -s "$AMDAHL_BASE/sessions?limit=20" \
-H "Authorization: Bearer $AMDAHL_KEY"Delete (complete) a session when you are done with it:
curl -s -X DELETE "$AMDAHL_BASE/sessions/$AMDAHL_SESSION" \
-H "Authorization: Bearer $AMDAHL_KEY"Substrate sessions vs agent sessions
Two things on the platform are called "sessions". They are different tables with different purposes:
- Substrate sessions (the ones on this page, under
/sessions). Plain data containers. No worker, no status machine beyond active / complete / archived. Cheap to create, cheap to keep around. Use for any human-driven or client-driven flow that accumulates evidence. - Agent sessions (under
/agents/runand/agents/:id/status). A managed agent execution. Each agent session runs a specific profile (content writer, researcher, copilot, etc.) with a structured status machine, resume points, pending inputs, and a final result artifact. Agent sessions are expensive: they spin up a worker, consume LLM tokens, and emit webhook events.
The two interact: an agent session internally creates and writes to a substrate session for its own bookkeeping, and your own substrate session can feed an agent if you pass the session id when starting the agent. But you create them via different endpoints and you poll them differently.
For agent sessions see agents.md.
Example: multi-turn research workflow
# 1. Create the session
SESSION=$(curl -s -X POST "$AMDAHL_BASE/sessions" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-d '{"label":"Acme Q2 renewal prep"}' | jq -r '.id')
# 2. Pull schema + clusters so you know what to query
curl -s -X POST "$AMDAHL_BASE/data/explore" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $SESSION" \
-d '{"scope":"all"}'
# 3. Run a targeted SQL query
curl -s -X POST "$AMDAHL_BASE/data/query" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $SESSION" \
-d '{"sql":"SELECT * FROM interactions WHERE company_name = '\''Acme Corp'\'' ORDER BY created_at DESC LIMIT 50"}'
# 4. Pull qualitative hits from the knowledge base
curl -s -X POST "$AMDAHL_BASE/data/search" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $SESSION" \
-d '{"query":"Acme renewal concerns","mode":"hybrid"}'
# 5. Add a web note
curl -s -X POST "$AMDAHL_BASE/context/remember" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $SESSION" \
-d '{"text":"Acme announced a layoff of 8% on 2026-03-30.","source":"web"}'
# 6. Synthesize
curl -s -X POST "$AMDAHL_BASE/context/ask" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-H "X-Session-Id: $SESSION" \
-d '{"query":"What are the top three risks to the Acme renewal and what should we do next?"}'
# 7. Save the answer as an artifact
curl -s -X POST "$AMDAHL_BASE/artifacts" \
-H "Authorization: Bearer $AMDAHL_KEY" \
-d '{
"artifact_type":"analysis",
"title":"Acme Q2 renewal risks",
"content_markdown":"...paste the answer..."
}'Each substrate-writing call in steps 2 through 5 appends to the same pool. Step 6 reasons over the union. Step 7 persists the output as a durable artifact that survives the session.
For a more complete walkthrough see recipes/run-research.md.