Docs

Agents

The library of named, reusable agents your workspace can dispatch, and the run-control verbs that drive a live run from the outside.

Write a prompt once, name it, and dispatch it anywhere. That is 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

Two kinds, one roster

Amdahl library agents

Ship in code - curated specializations like gtm-strategist. Locked: dispatch, pin and schedule them, but you cannot edit or delete them.

Workspace agents

Yours. Create one with a prompt and an optional tool blocklist, get back a stable kebab-case slug, and dispatch it wherever an agent parameter is accepted.

An agent is a named, reusable prompt — a specialization the platform's Master runtime (the general-purpose agent behind every Chat turn) executes with its own full toolkit. Two kinds live side by side in one roster:

  • Amdahl library agents ship in code — curated specializations like gtm-strategist. They are locked: you can dispatch them, pin them to Chats, and schedule them, but not edit or delete them. When one is retired, its slug stays reserved: a call that names it returns not_found with a message that says what replaced it, and no workspace agent can take the slug.
  • 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.ai/api/platform/v1/agents" \
  -H "X-API-Key: $AMDAHL_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
{
  "data": {
    "agents": [
      {
        "id": "4705…",
        "slug": "gtm-strategist",
        "name": "GTM Strategist",
        "description": "Turns this workspace's own customer conversations into a go-to-market recommendation: who to sell to, how to position, where pipeline is leaking and, when the run has external search, what the market says that your buyers do not. Every call names its evidence and what would disprove it.",
        "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.ai/api/platform/v1/agents/pipeline-analyst" \
  -H "X-API-Key: $AMDAHL_KEY"

Create an agent

bash
curl -X POST "https://app.amdahl.ai/api/platform/v1/agents" \
  -H "X-API-Key: $AMDAHL_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
{
  "data": {
    "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.ai/api/platform/v1/chat" \
  -H "X-API-Key: $AMDAHL_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 answer_chat_question and cancel_chat tools 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.ai/api/platform/v1/agents/$RUN_ID/resume" \
  -H "X-API-Key: $AMDAHL_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
{
  "data": {
    "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.ai/api/platform/v1/agents/$RUN_ID/cancel" \
  -H "X-API-Key: $AMDAHL_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

Each operation is its own tool on the Amdahl MCP server. The read tools only read and change nothing. The write tools start runs and make changes, so a client may ask the user to approve those calls.

FamilyRead toolsWrite tools
Librarylist_agents, get_agentcreate_agent, update_agent, delete_agent
Chatget_chat_runstart_chat, answer_chat_question, cancel_chat
Routineslist_routines, get_routinecreate_routine, update_routine, delete_routine, run_routine_now

answer_chat_question 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).

The first example below is a create_agent call and the second a start_chat call:

json
{
  "slug": "pipeline-analyst",
  "name": "Pipeline Analyst",
  "prompt": "You are our pipeline analyst. …"
}
json
{
  "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.