GET, a POST, and a delete for
each, all shaped the same way.
If you’ve used the Stripe API, this will feel familiar: resource-oriented URLs,
self-describing JSON, and one list envelope everywhere.
How every object is shaped
Three conventions hold across the entire API. Internalize these and you can read any response without checking the reference. Objects name their own type. Every object carries anobject field naming
what it is — "agent", "run", "table", "campaign", and so on. You never
have to infer a type from context.
items and an opaque nextCursor. Some lists add a top-level
total.
nextCursor back as the cursor query parameter to get the next page;
nextCursor: null marks the last one. There is no page/pageSize. See
reading data for the full pagination walkthrough.
Objects reference each other by id. A run carries an agentId and a
workspaceId; a table carries a workspaceId; a sequence carries a
campaignId, tableId, and rowId. Follow the ids to move between objects.
The object graph
Read it top-down: your account contains projects; a project (or the parent org itself) contains workspaces; a workspace holds tables, documents, agents, and campaigns; and the rest hang off those.Tenancy: parent org and projects
Every API key is parent-wide — it belongs to your parent (agency) organization and can act on the parent or any of its projects.A child org under your parent — a customer’s isolated set of workspaces
and tables. Credits and the concurrency pool stay shared at the parent; a
project can carry an optional
monthlyCredits budget cap. Manage projects
from the parent with GET /projects,
POST /projects, and
GET /
PATCH /
DELETE /projects/{projectId}.Your org’s plan, capabilities, and workspace usage, from
GET /account. Always parent-scoped.Your credit balance, from
GET /account/credits.
Credits are the billing unit for agent runs and enrichment.x-origami-project: <projectId> header on any
request. Omit it to act on the parent. The /projects/* and /account
endpoints ignore the header. See authentication
for details.
Agents and runs
An AI worker in a workspace. You create one, then drive it with runs. An agent
does one run at a time. Endpoints:
POST /agents,
GET /agents,
GET /agents/{id},
DELETE /agents/{id}.One prompt and the work that follows it. Runs are asynchronous — you get a
running run back immediately and poll until status is terminal. Endpoints:
POST /agents/{id}/runs,
GET /agents/{id}/runs,
GET /agents/{id}/runs/{runId},
POST /agents/{id}/cancel. See the
run object for every field.A recurring agent that runs on a cron schedule. Full CRUD plus
enable/disable, manual trigger, and run history under
/scheduled-agents.Data: workspaces, tables, rows
A container for tables, documents, agents, and campaigns. Agents auto-create
one when you don’t supply a
workspaceId. Endpoints:
GET /workspaces,
POST /workspaces,
GET /workspaces/{workspaceId},
DELETE /workspaces/{workspaceId}.A set of rows and the columns that enrich them, plus lifetime credit cost.
Read one with
GET /tables/{tableId}; list
them with GET /tables.A field on a table, classified by
kind: input (user-entered, the only
writable kind), enrichment (runs per row to fetch a value), score
(relevance), or sequence (drafts outreach). List with
GET /tables/{tableId}/columns.One record in a table (the wire vocabulary calls these “leads” —
leadCount). Cells are keyed by column slug and typed. Read with
GET /tables/{tableId}/rows or
GET /tables/{tableId}/rows/{rowId}. Write with
POST /tables/{tableId}/rows/upsert or the
CSV variant .../rows/upsert-file.A single column’s value on a single row, with run metadata where present.
Read with
GET /tables/{tableId}/rows/{rowId}/cells/{columnId}.A tracked batch of column-over-row work — every upsert and file ingest creates
one. Poll it for status, counts, credits used, and per-row upsert outcomes.
Endpoints:
GET /enrichment-runs,
GET /enrichment-runs/{runId},
GET /tables/{tableId}/enrichment-runs.An enrichment run is the object formerly called a “batch”.
GET /batches
and GET /batches/{batchId} remain as deprecated aliases; its id and
batchId fields hold the same value.A file uploaded into a workspace. Upload, list, read, rename, and delete under
/workspaces/{workspaceId}/documents.Outreach: campaigns, sequences, steps
A first-class outreach campaign, homed in a workspace. Its queue is the set of
sequences stamped with its id — one per person. Create and edit campaigns
agentically (
POST /tables/{tableId}/campaigns,
POST /campaigns/{campaignId}/edits); read
people and stats; and control its lifecycle with
launch,
pause, and
resume.One recipient’s thread within a campaign — in the campaign model, a person
is a sequence. Read one with its steps inline
(
GET /sequences/{sequenceId}), list them
in scope, stop, or
delete. Content edits go through the
campaign edit, not the sequence.A single message or connection request within a sequence — channel, subject,
body, and send status. Returned inline on the sequence detail.
Working conventions
Async runs
Agent work returns a
running run; poll until status is terminal, honoring
Retry-After.Cursor pagination
Every list returns
{ items, nextCursor }. Pass nextCursor back as
cursor.Authentication
Parent-wide API keys and the
x-origami-project header for project scoping.Migrating from v1
Map every v1 route to its v2 equivalent.
POST can send an Idempotency-Key header; the first
response is replayed for retries with the same key for 24 hours. Row upserts also
dedup on the body batchId.
Errors. Every error uses { error, code, details?, handoff? }. Codes are
UPPERCASE_SNAKE_CASE. A 4xx the user can fix in-app carries a forwardable
handoff link.
Where to start
Authenticate
Create an API key and confirm access with
GET /account. See
authentication.Run an agent, or bring your own data
Hand a brief to
POST /agents and let it
build a table — follow the quickstart. Already have
rows? Upsert them with
POST /tables/{tableId}/rows/upsert.Read the results
Pull rows with
GET /tables/{tableId}/rows — see
reading data.Reach out (optional)
Draft a campaign with
POST /tables/{tableId}/campaigns, then
launch it.