---
title: "Create an agent and admit its first run"
method: POST
path: "/agents"
tags: ["Agents"]
---

# Create an agent and admit its first run

`POST /agents`

Creates a new agent (and, if `workspaceId` is null and no
`focusTableIds` are passed, an auto-created workspace), inserts
a synthetic user message carrying the prompt, claims the
per-org concurrent slot, and spawns the agent work in the
background. Responds `202 Accepted` with the initial run
object (`status: "running"`). Poll
`GET /agents/{id}/runs/{runId}` until `status !== "running"`.

## Headers

- `Idempotency-Key` string

## Request body

- CreateAgentRequest
  - `name` string — Optional human-readable label. Auto-generated from prompt if absent.
  - `prompt` string, required
  - `workspaceId` string, uuid, nullable
  - `focusTableIds` string[] — Table ids to focus the agent on. When the caller also supplies `workspaceId`, every id must belong to that workspace. When `workspaceId` is omitted, the workspace is inferred from the first id.
  - `attachments` UploadedRunAttachment[] — Bind existing resources to the run. A `document` is injected as an attached file the agent reads with `ctx.loadFile(path)` (its current path is re-resolved at run start); a `table` is merged into `focusTableIds`. Every attachment must belong to the run's workspace + org, else `400 INVALID_ATTACHMENT` before admission.
    - union — A document (injected as an attached file) or a table (merged into focusTableIds).
      - object
        - `kind` 'document', required
        - `documentId` string, uuid, required
      - object
        - `kind` 'table', required
        - `tableId` string, uuid, required
  - `model` 'origami-lite' | 'origami-max' — Public model id. Plan-aware default — highest model unlocked on the caller's plan (`origami-lite` for `starter`, `origami-max` for `pro`+). Resolved server-side to an internal chat-agent model; the response's `request.model` echoes the public id (`origami-lite` or `origami-max`), never an internal name. Legacy aliases `origami-fast` and `origami-mid` are still accepted and normalized to `origami-lite`.

## Response `202`

Run admitted; agent work is in the background.

- object
  - `agent` AgentSummary, required
    - `object` 'agent', required
    - `id` string, uuid, required
    - `name` string, required
    - `workspaceId` string, uuid, required
    - `createdAt` string, date-time, required
  - `run` Run, required
    - `object` 'run', required
    - `id` string, required
    - `agentId` string, uuid, required
    - `status` 'running' | 'completed' | 'needs_input' | 'step_cap_hit' | 'incomplete' | 'cancelled' | 'errored' | 'timed_out', required — Single discriminator for the run's lifecycle. `needs_input` means the run completed cleanly but the agent's last assistant message included an `ask-questions` tool call; answer by sending a follow-up run on the same agent. `incomplete` means the model finished the step but the AI SDK could not parse a tool call it tried to emit (or it hit the output-token cap mid-tool-call), so the multi-step loop ended without the work being done — recoverable by sending a follow-up run on the same agent. Deploy restarts are auto-followed server-side, so callers never observe a `superseded` value.
    - `prompt` string, required — The user prompt that drove this run, truncated to 200 characters with an ellipsis when longer. Suitable for a run-history list item; for the full prompt use Get Run.
    - `model` 'origami-lite' | 'origami-max', required — Public model id the run actually executed on. When the run has no assistant message yet (still admitting), falls back to the plan default.
    - `steps` RunSteps, required — Agent step progress. `completed` is the number of multi-step loop iterations the agent has actually executed; `max` is the plan-determined hard cap. Once `completed === max`, the run's `status` becomes `step_cap_hit`.
      - `completed` integer, required
      - `max` integer, required
    - `startedAt` string, date-time, required
    - `completedAt` string, date-time, nullable
    - `workspaceId` string, uuid, required
    - `request` RunRequest, required
      - `prompt` string, required
      - `model` 'origami-lite' | 'origami-max', required — Public model id — `origami-lite` or `origami-max`. Echoes the caller's request (after legacy-alias normalization) or the plan default when the request omitted `model`. Internal chat-agent ids (e.g. `origami-lobotomized`) are never surfaced here.
      - `focusTableIds` string[], required — Echoed verbatim from the request.
    - `response` RunResponse, required — `null` while `status === "running"`. Once terminal, an object with the agent's cleaned prose, the structured workspace mutations it performed (`actions[]`), the full TableObjects for every touched table (`tables[]`), and (optionally) the full message transcript. `tables[]` is the resolved version of `actions[].tableId` — same shape as `GET /api/v2/tables/{id}`, fanned out server-side. Pass `?include=stats` on the Get Run call to attach economics on each embedded TableObject.
      - `text` string, nullable, required — Cleaned user-facing assistant text — internal markup (`<internal>`, `<plan>`, `<suggestions>`, history-tool markers) is stripped server-side, identical to the stripping in `transcript` content. `null` when `status` is `errored` or `timed_out` (we suppress partial prose on failed runs).
      - `actions` RunAction[], required — Structured workspace-mutation actions the agent performed, in the order they fired. Projected from the persisted `data-*` parts the chat-agent's execute-code event listener writes onto each assistant message — the same audit trail the desktop UI renders. Empty when the run did no workspace mutations.
        - `type` 'table_created' | 'column_added' | 'columns_updated' | 'columns_deleted' | 'leads_added' | 'leads_deleted' | 'leads_restored' | 'columns_restored' | 'table_restored', required
        - `tableId` string, uuid, required
        - `tableName` string, nullable — Present on `table_created` and `table_restored`.
        - `columnId` string, uuid, nullable — Present on `column_added`.
        - `columnName` string, nullable — Present on `column_added`.
        - `count` integer — Present on `columns_deleted`, `leads_deleted`, `leads_restored`, `columns_restored`. Number of items affected.
        - `leadCount` integer, nullable — Present on `leads_added`. Number of leads inserted.
        - `deletedAt` string, date-time, nullable — Present on `columns_deleted` and `leads_deleted`.
      - `tables` Table[], required — Full TableObjects for every table this run touched (derived from `actions[].tableId` plus any `focusTableIds` the caller passed). Same shape as a single `GET /api/v2/tables/{id}` response. Empty when the run touched no tables.
        - `object` 'table', required
        - `id` string, uuid, required
        - `workspaceId` string, uuid, required
        - `name` string, required
        - `leadCount` integer, required — Non-deleted row count. v2 wire vocabulary speaks "leads" instead of "rows"; the underlying DB column is still `rows`.
        - `columns` TableColumn[], required
          - `object` 'column', required
          - `id` string, uuid, required
          - `name` string, required
          - `type` string, required — Raw DB column type. Common values: `static` (user-entered), `code` (user-TS that runs per row, includes enrichments, scoring, and sequence columns), plus other internal-typed kinds for future extensibility. Treat as an open string. Prefer `kind` for classification — it splits `code` columns into `enrichment`, `score`, and `sequence`.
          - `kind` 'input' | 'enrichment' | 'score' | 'sequence', required — Sequence-aware API classification of a column: * `input` — user-entered (`static`) column; the only kind writable via `POST /tables/{tableId}/rows/upsert`. * `enrichment` — `code` column that runs per row to fetch/compute a value. * `score` — relevance / fit-score column. * `sequence` — `code` column that drafts an outbound sequence (`ctx.upsertSequence(...)` / `.draftMessage(...)`, or one that already has sequences). List its sequences via `GET /tables/{tableId}/sequences`. Created via the agent or column code only — never via the upsert API.
          - `slug` string, nullable, required — Stable url-safe slug, or null when the column has none.
          - `autoTrigger` boolean, required — True when the column runs automatically on new leads. False for columns that only run when explicitly invoked (e.g. exports, manual-trigger code columns).
          - `credits` CreditsLifetime, required
            - `lifetimeUsed` integer, required — Sum of all settled cell-run charges, in credits. Stable and additive — once a row enrichment finishes it never moves backwards. Reservations (in-flight cell_runs) are not included.
          - `cells` CellsLiveness, required — Snapshot of cell-pipeline activity. The v2 run-finished signal is independent of the cell-pipeline-finished signal — when a run completes, downstream cells often still enrich in the background. Surfacing these counts lets the agent distinguish "still working on it" from "tried and failed", which is the difference between waiting and reporting a negative result.
            - `running` integer, required — Number of cells currently being processed (a `cell_run` is in `waiting`, `queued`, or `running` for that cell). `running > 0` means the agent should NOT report "no data found" — wait and re-read.
            - `errored` integer, required — Number of cells that hit a settled failure. Includes `errored`, `out_of_credits`, `subscription_required`, and `connection_required`. The user-driven `stopped` state is intentionally excluded — it's a cancellation, not a failure to surface.
          - `stats` ColumnStats
            - `avgCreditsPerRun` number, required — Average credits charged when this column actually runs.
            - `callRate` number, required — Fraction of leads that triggered this column (0-1).
            - `totalRuns` integer, required — Lifetime count of cell runs feeding the average.
          - `qualification` ColumnQualification
            - `pass` integer, required
            - `fail` integer, required
            - `unsure` integer, required
            - `total` integer, required
        - `credits` CreditsLifetime, required
          - `lifetimeUsed` integer, required — Sum of all settled cell-run charges, in credits. Stable and additive — once a row enrichment finishes it never moves backwards. Reservations (in-flight cell_runs) are not included.
        - `cells` CellsLiveness, required — Snapshot of cell-pipeline activity. The v2 run-finished signal is independent of the cell-pipeline-finished signal — when a run completes, downstream cells often still enrich in the background. Surfacing these counts lets the agent distinguish "still working on it" from "tried and failed", which is the difference between waiting and reporting a negative result.
          - `running` integer, required — Number of cells currently being processed (a `cell_run` is in `waiting`, `queued`, or `running` for that cell). `running > 0` means the agent should NOT report "no data found" — wait and re-read.
          - `errored` integer, required — Number of cells that hit a settled failure. Includes `errored`, `out_of_credits`, `subscription_required`, and `connection_required`. The user-driven `stopped` state is intentionally excluded — it's a cancellation, not a failure to surface.
        - `url` string, uri, required — Deep link a human can open. For programmatic row reads, use `GET /api/v2/tables/{tableId}/rows`.
        - `createdAt` string, date-time, required
        - `updatedAt` string, date-time, required
        - `stats` TableStats — Forward-looking table economics — the same numbers the desktop UI shows at the top of the table. Only present on responses that opted in via `?include=stats`.
          - `creditsPerLead` number, required — Estimated credits per sourced lead (pre-qualification).
          - `creditsPerQualifiedLead` number, required — Estimated credits per qualified lead, including the loss from dedup, exclusion, and filter cascade.
          - `findMoreEstimatedCredits` number, required — Per-execution cost of the "Find More" code, in credits.
          - `qualification` TableQualificationStats, required
            - `rate` number, nullable, required
            - `fetchRate` number, nullable, required — Filter-only qualification rate (excludes dedup / exclusion).
            - `qualifiedLeads` integer, required
            - `effectiveLeadsPerQualified` number, required — Empirical leads-processed per qualified lead.
            - `estimatedQualifiedLeads` integer, nullable, required
          - `funnel` TableFunnelStats, required
            - `totalSourcedLeads` integer, required
            - `postStaticLeads` integer, required — Leads surviving dedup + exclusion + required static filters.
            - `dedupedLeads` integer, required
            - `dedupRate` number, nullable, required
            - `excludedLeads` integer, required
            - `excludedRate` number, nullable, required
          - `running` TableRunningStats, required
            - `runningLeads` integer, required — Leads with active (queued / running / waiting) cell runs.
            - `oldestActiveRunStartedAt` string, date-time, nullable, required
          - `leadSources` TableLeadSourceStats[], required
            - `id` string, uuid, required
            - `name` string, required
            - `avgCreditsPerLead` number, required
            - `totalCredits` number, required
            - `totalLeads` integer, required
            - `source` 'historical' | 'estimate', required — `historical` when the average is derived from real cost_events; `estimate` when it was computed by static regex analysis of the lead source's TypeScript code.
          - `hasUnknownTotalWithMore` boolean, required — True when ≥1 active lead source has unknown TAM AND `has_more=true` (e.g. Twitter / LinkedIn post search). Consumers should render "X found (more available)" instead of a numeric TAM when set.
      - `transcriptTruncated` boolean, required — True when `transcript` was truncated to its hard cap. Always present (defaults to `false`) so consumers don't have to do a presence-check on every poll.
      - `transcript` TranscriptMessage[], nullable, required — Full message history projected onto the surface a desktop user with **dev mode off** would see. Specifically: - Assistant text is stripped of internal markup (`<internal>`, `<plan>`, `<suggestions>`, history-tool markers). - Tool calls are limited to the interactive widgets a non-dev user actually sees: `ask-questions`, `present-plan`, `contact-support`, `suggest-document-write`. The code the agent ran (`execute-code`), the VFS reads (`list-dir`, `read-file`, `grep`), and operational tools (`flag-session`) are dropped along with their results. - `reasoning` parts (dev-only in the UI) are dropped. - Empty assistant bubbles (text that was entirely internal markup and no public tool calls) are dropped. Opt-in only via `?include=transcript` on `GET /agents/{id}/runs/{runId}`. Default is `null`.
        - `role` 'user' | 'assistant' | 'tool'
        - `content` string
        - `toolCalls` object[]
          - `id` string
          - `name` string
          - `input` unknown
        - `toolCallId` string
        - `name` string
        - `result` string
        - `truncated` boolean
    - `todo` RunTodo, required
      - `pendingQuestions` PendingQuestion[], required — Structured questions extracted from the agent's most recent `ask-questions` tool call, if any. When non-empty AND the chat_stream completed cleanly, `status` is promoted to `"needs_input"`. Answer by sending a follow-up run on the same agent (`POST /agents/{id}/runs`) with the user's answer as `prompt` — any free-text string is accepted; the agent parses it as natural language.
        - `type` 'single-choice' | 'confirm', required — Question type the agent emitted. `single-choice` lists options the user is expected to pick from; `confirm` is a single approval prompt.
        - `question` string, required — Natural-language question text to surface to the user.
        - `suggestedAnswers` string[], required — Answers the agent proposed, in the order it listed them. For `single-choice` these are the agent's options; for `confirm` it is the single-entry array `["Confirm"]`. This is NOT a hard menu — the caller may always reply with any free-text via the follow-up run (see `freeformOption`). The field is here so a host LLM can bias its follow-up `prompt` toward what the agent expected.
        - `freeformOption` string, required — A stable, agent-agnostic CTA string clients can render as a final "your own answer" button next to `suggestedAnswers`. Always the literal `"Or something else"` in v2.0.
      - `nextActions` NextAction[], required — Structured next-action suggestions extracted from the agent's `<suggestions><action ...>...</action></suggestions>` block, if any. Surface the `label` to the user; act on `type` (when present) as a typed CTA the host can fire directly.
        - `label` string, required — Natural-language label the agent suggested.
        - `type` 'upload_csv' | 'find_more_leads' | 'export_csv' | 'input_required' | 'send_messages' | 'schedule_play' — Optional typed CTA the host can act on directly. When absent, treat as a plain text suggestion the user can ask the agent to do next.
        - `tableSlug` string — Optional table reference for table-scoped types (e.g. `export_csv`).
  - `workspace` object, required
    - `object` 'workspace', required
    - `id` string, uuid, required
    - `name` string, required
    - `createdAt` string, date-time, required
    - `createdByApi` boolean, required — True when the server auto-created this workspace for the agent. False when the caller supplied `workspaceId`.

## Other responses

- `400` — Bad request body or semantic validation failure. Route-level codes include `VALIDATION_ERROR`, `MODEL_NOT_AVAILABLE`, `UNKNOWN_WORKSPACE`, `UNKNOWN_TABLE`, and `WORKSPACE_TABLE_MISMATCH`.
- `401` — Missing or invalid API key
- `402` — Org out of credits at admission time. `402` can also be `SUBSCRIPTION_REQUIRED` when the plan does not include API access; both shapes use the same `{ error, code }` envelope.
- `403` — Workspace limit reached while auto-creating an API workspace.
- `429` — Org-wide concurrent-agent cap reached (shared with the Origami UI).

---

[API](https://skmtc.net/origami/apis/origami-agent-api.md) · [All operations](https://skmtc.net/origami/apis/origami-agent-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/origami/origami-agent-api/revisions/8083ad7fe081/schema)
