Docs

Agents

Consumer-facing guide to the agent surface: the library of named, reusable agents your workspace can dispatch, and the run-control verbs (resume / cancel) that drive a live run from the outside. This is one of the three doors of the async surface — the other two are Chat (start an investigation, poll for the answer) and Routines (fire a Chat on a cron), and both can run as an agent from the library documented here.

The model

An agent is a named, reusable prompt — a specialization the platform's Master runtime executes with its own full toolkit. Two kinds live side by side in one roster:

  • Amdahl library agents ship in code — curated specializations like researcher. They are locked: you can dispatch them, pin them to Chats, and schedule them, but not edit or delete them.
  • Workspace agents are yours. Create one with a prompt (and optionally a tool_blocklist), get back a stable kebab-case slug, and dispatch it by that slug anywhere an agent parameter is accepted.

An agent is not a separate execution engine. When a Chat or Routine runs "as" an agent, it is one Master turn whose specialization is the agent's prompt — same toolkit, same data access, same answer envelope. The agent is the reusable instruction set, not a sandbox.

Where an agent plugs in:

SurfaceHow
A Chat turnPOST /chat with "agent": "<slug or id>" — the turn runs as that agent.
A Routineconfig.agents on the routine — one ref pins the fire to that agent; several restrict delegation to that roster.
DelegationA Master turn may delegate sub-work to agents from the roster mid-run.

The library

VerbRESTOperationScope
List the rosterGET /agentsagents.list_agentsagents:read
Read one agent (full prompt)GET /agents/:idagents.get_agentagents:read
CreatePOST /agentsagents.create_agentagents:write
UpdatePATCH /agents/:idagents.update_agentagents:write
DeleteDELETE /agents/:idagents.delete_agentagents:write

List the roster

bash
curl "https://app.amdahl.co/api/platform/v1/agents" \
  -H "X-API-Key: $API_KEY"

The listing is a lean projection — everything except the prompt body — with the Amdahl library merged ahead of your workspace's own rows:

json
{
  "agents": [
    {
      "id": "3c1e…",
      "slug": "researcher",
      "name": "Researcher",
      "description": "Rigorous multi-step research with citations.",
      "authored_by": "amdahl",
      "locked": true,
      "created_at": null
    },
    {
      "id": "9f2a…",
      "slug": "pipeline-analyst",
      "name": "Pipeline Analyst",
      "description": "Weekly pipeline health readout in our house style.",
      "authored_by": "tenant",
      "locked": false,
      "created_at": "2026-07-02T18:11:40.000Z"
    }
  ]
}

authored_by is the provenance (amdahl = code-defined library, tenant = yours); locked tells you up front whether an edit will be refused. Read one agent by UUID or slug to get the full prompt:

bash
curl "https://app.amdahl.co/api/platform/v1/agents/pipeline-analyst" \
  -H "X-API-Key: $API_KEY"

Create an agent

bash
curl -X POST "https://app.amdahl.co/api/platform/v1/agents" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pipeline Analyst",
    "description": "Weekly pipeline health readout in our house style.",
    "prompt": "You are our pipeline analyst. Always segment by deal size band, lead with the risks, and end with the three deals most worth attention this week."
  }'
FieldRequiredNotes
nameyesDisplay name, ≤120 chars.
promptyesThe instruction set the agent runs with.
slugnoStable kebab-case dispatch key. Omitted: derived from the name and uniquified automatically (-2 / -3 on collision). Supplied: must be well-formed and free — a collision returns a typed slug_conflict instead of guessing.
descriptionnoOne-line blurb for the roster, ≤2000 chars.
tool_blocklistnoOperation ids this agent may NOT call. Each id is validated against the live registry — an unknown id is refused up front (invalid_argument) rather than persisting as a silent no-op.
json
{
  "success": true,
  "agent": {
    "id": "9f2a…",
    "slug": "pipeline-analyst",
    "name": "Pipeline Analyst",
    "description": "Weekly pipeline health readout in our house style.",
    "authored_by": "tenant",
    "locked": false,
    "created_at": "2026-07-02T18:11:40.000Z"
  }
}

A slug that would shadow an Amdahl library agent is rejected before any row is written — library agents ship in code and cannot be replaced.

Update and delete

PATCH /agents/:id revises name / description / prompt / tool_blocklist in place, keeping the slug stable — refine the prompt after seeing output, and every Chat pin and Routine that references the slug picks up the new behavior on its next run. DELETE /agents/:id removes a workspace agent; past runs are untouched. Both refuse a locked library agent with a typed error rather than silently ignoring the edit. A Routine whose roster references a deleted agent skips its next fire loudly instead of running with a partial roster (see Routines: reliability semantics).

Dispatch it

bash
curl -X POST "https://app.amdahl.co/api/platform/v1/chat" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "How healthy is the pipeline this week?",
    "agent": "pipeline-analyst"
  }'

The response is the standard Chat START envelope — handles, never a blocking answer. From here the lifecycle is entirely Chat's: poll read_url, stream stream_url, answer pauses on resume_url.

Run control: resume and cancel

Two verbs drive a live run from the outside. They are the primitives behind the Chat lifecycle — resume_url posts to resume; the MCP respond / cancel_chat actions dispatch the same operations — exposed directly so any client can drive the loop.

VerbRESTOperationScope
Answer a pausePOST /agents/:session_id/resumeagents.resumeworkflows:write
Cancel a runPOST /agents/:session_id/cancelagents.cancelworkflows:write

:session_id is the run_id from the Chat START envelope.

Resume

A run paused in awaiting_input carries a pending_input object on its READ body: pending_input_type, pending_input_schema (the JSON schema your reply must match), and pending_input_context (the question / reason). POST the reply as input:

bash
curl -X POST "https://app.amdahl.co/api/platform/v1/agents/$RUN_ID/resume" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "input": { "response": "Focus on enterprise; ignore self-serve." } }'

The three pause shapes you will meet:

pending_input_typeThe run is askingReply shape
questionA free-form question (ask_a_human).{ "response": "…" } — or the pause's structured options when it offered multiple choice.
approvalSign-off on a draft or a destructive step.{ "approved": true } or { "approved": false, "feedback": "…" }.
continue_or_finishThe turn budget ran out.{ "action": "continue", "additional_turns": 10 } or { "action": "finish" }.

On success:

json
{
  "session_id": "9a8b7c6d-…",
  "conversation_id": "3f0e2a1b-…",
  "status": "queued",
  "message": "Resume job enqueued",
  "job_id": "agent-session-9a8b7c6d-…-resume"
}

Two failure shapes worth handling: a reply that does not match pending_input_schema returns status: "invalid_input" with a validation_error naming the mismatch — fix the payload and retry; a resume against a run that is not awaiting_input returns status: "invalid_state" with the current status — refetch the run instead of retrying.

Note that pauses are opt-in: a Chat started with the default on_question: "auto" never parks (the agent states its assumption and proceeds), and Routine fires are always headless. You will only ever resume runs you started with on_question: "pause" — see Chat: answering a human question.

Cancel

bash
curl -X POST "https://app.amdahl.co/api/platform/v1/agents/$RUN_ID/cancel" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Scope changed; will re-ask with a new brief" }'

Cancellation is safe at any non-terminal state: the run flips to canceled, the worker stops at its next safe checkpoint, and any sub-agents the run dispatched are canceled with it. An already-terminal run returns cannot_cancel with the current status echoed back. reason is optional and recorded in operator logs. A canceled turn stays in the Chat's history — a follow-up turn on the same Chat continues with full context rather than cold-starting.

Over MCP

The whole surface rides the Amdahl MCP server's single agents tool — the library CRUD, the Chat lifecycle, and Routines as actions on one tool:

FamilyActions
Librarylist_agents, get_agent, create_agent, update_agent, delete_agent
Chatstart_chat, chat_status, respond, cancel_chat
Routineslist_routines, get_routine, create_routine, update_routine, delete_routine, run_routine_now

respond and cancel_chat dispatch the same agents.resume / agents.cancel operations as the REST verbs above. The library reads are also MCP resources: agent://list (lean roster) and agent://<id> (one agent with its full prompt).

json
{ "action": "create_agent", "name": "Pipeline Analyst", "prompt": "You are our pipeline analyst. …" }
json
{ "action": "start_chat", "input": "How healthy is the pipeline this week?", "agent": "pipeline-analyst" }

Scopes

ScopeGrantsDefault role
agents:readList the roster, read one agent.viewer
agents:writeCreate / update / delete workspace agents.editor
workflows:writeagents.resume + agents.cancel (drive a live run).editor
conversations:write / conversations:readStart / read the Chats agents run in.editor / viewer

All are on the customer-agent key bundle. A read-only key can browse the roster and read runs, but cannot author agents or drive a run.

Design notes for agent authors

  • Write the prompt as a role, not a script. The agent already has the full toolkit and the platform's grounding rules; your prompt adds the specialization — voice, segmentation habits, what to lead with, what to never do. Short and opinionated beats long and procedural.
  • Use tool_blocklist to subtract, not to sandbox. Blocklisting keeps an agent focused (a pure-analysis agent that must never send email blocklists the action ops). It is not a security boundary — the caller's own scopes are; an agent can never do what the caller could not.
  • Iterate against real output. PATCH the prompt, re-dispatch the same ask, diff the answers. The slug stays stable, so nothing referencing the agent changes while you tune it.
  • Slugs are the API surface. Everything dispatches by slug; pick names you are happy to see in config files (pipeline-analyst, not test-agent-v2-final).

See also

  • Chat — the lifecycle every agent run rides on: START, poll, stream, the answer envelope.
  • Routines — schedule an agent to run on a cadence.
  • Endpoints — the synchronous verbs an agent composes over.
  • Tool catalog — the generated per-operation reference.