# Origami API ## Docs - [Introduction](https://docs.origami.chat/index.md): Build, enrich, and read lead data programmatically with the Origami API. - [Quickstart](https://docs.origami.chat/quickstart.md): Enrich your first batch of companies with the v2 API in under 5 minutes. - [Authentication](https://docs.origami.chat/authentication.md): Create and manage API keys, scope requests to projects, and stay within rate limits. - [Reading data](https://docs.origami.chat/reading-data.md): Filter, sort, paginate, and export enriched table rows with the v2 API. - [v2 API beta](https://docs.origami.chat/agents/index.md): Run Origami's agent from your own code: send a brief, get back a table. - [Objects and relationships](https://docs.origami.chat/agents/objects.md): The first-class objects in the Origami v2 API, how they fit together, and where to start. - [Quickstart](https://docs.origami.chat/agents/quickstart.md): Start an agent, poll for the result, follow up, and fetch the data. - [Run object](https://docs.origami.chat/agents/run-object.md): How to read a run's status, the actions it took, and the tables it built. - [Install the skill](https://docs.origami.chat/agents/skill.md): Teach your AI coding assistant to drive the v2 API for you. - [List projects (cursor-paginated; ?search=)](https://docs.origami.chat/agents/reference/list-projects.md): Projects are managed **from the parent**: every `/projects/*` endpoint acts on the API key's own (parent) org and ignores the `x-origami-project` header. Newest first; a parent with no projects gets an empty page — "you have no projects" is not an error. - [Create a project (child org)](https://docs.origami.chat/agents/reference/create-project.md): Creates a project under the API key's parent org. `monthlyCredits` is the optional per-project budget cap in credits — `null` or omitted means uncapped. A child org's key cannot create projects (nesting is one level deep) → `400 PARENT_REQUIRED`. - [Fetch a project](https://docs.origami.chat/agents/reference/get-project.md) - [Update a project's name / budget cap](https://docs.origami.chat/agents/reference/update-project.md): Partial update. For `monthlyCredits`, a number **sets** the cap, an explicit `null` **clears** it, and omitting the field leaves it alone. - [Delete a project (two-step; ?confirm=true, ?dryRun=true)](https://docs.origami.chat/agents/reference/delete-project.md): Deletes the project (child org) and cascades across its whole entity tree. Follows the v2 deletes convention — deliberately two-step: - [Create an agent and admit its first run](https://docs.origami.chat/agents/reference/create-agent.md): 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… - [List agents (cursor-paginated; ?search=)](https://docs.origami.chat/agents/reference/list-agents.md) - [Get an agent](https://docs.origami.chat/agents/reference/get-agent.md) - [Archive an agent (soft-delete)](https://docs.origami.chat/agents/reference/archive-agent.md): Soft-deletes the agent. **If the agent's auto-created workspace has no other active agents**, the workspace is also soft-deleted in the same transaction. Hand-supplied workspaces (those passed in `workspaceId` on create) are never auto-deleted. - [Send a follow-up run](https://docs.origami.chat/agents/reference/send-run.md): Admits a new run on an existing agent. Same agent, same workspace, same conversation context. Responds `202 Accepted` with the initial run object; agent work runs in the background. Use this to answer a `needs_input` question — the agent picks up from the prior conversation. - [List runs for an agent (cursor-paginated)](https://docs.origami.chat/agents/reference/list-runs.md) - [Get a run (poll for status / final result)](https://docs.origami.chat/agents/reference/get-run.md): Returns the run object. Works while the run is still running (`status: "running"` with partial `actions[]`) and after it terminates. This is the primary read path — every call after `POST /agents` polls this endpoint until `status !== "running"`. - [Cancel the agent's currently-active run (idempotent, cooperative)](https://docs.origami.chat/agents/reference/cancel-run.md): The per-agent mutex guarantees at most one in-flight run per agent, so cancel takes only the agentId. Wraps the same machinery the UI's stop route uses (`markCancelling` + Redis `cancelStream` publish). - [List tables (cursor-paginated; ?workspaceId=)](https://docs.origami.chat/agents/reference/list-tables.md) - [Get a table — name, leadCount, columns, credits, optional economics](https://docs.origami.chat/agents/reference/get-table.md): The canonical "what's in this table and what did it cost?" surface in v2. Same shape as the entries in `Run.response.tables[]` and `GET /agents/{id}/tables`. - [List tables in the agent's workspace](https://docs.origami.chat/agents/reference/list-agent-tables.md): The fallback discovery surface in v2. The run object only carries `response.tables[]` for tables a specific run *touched* (empty when the run made no mutations or stopped on a question), so callers that need "every table this agent has access to" use this endpoint instead. - [List a table's columns](https://docs.origami.chat/agents/reference/list-columns.md) - [List rows (typed cells; ?cells=flat / ?format=csv)](https://docs.origami.chat/agents/reference/list-rows.md): Returns Row objects (`{ object: "row", id, cells }`) with polymorphic typed cells by default (`scalar` / `value` (+run) / `sequence`), in the canonical list envelope **plus `total`** (the filtered row count for the query's scope). `?cells=flat` returns the v1-style `{ slug: value }` rows (deliberate… - [Fetch one row](https://docs.origami.chat/agents/reference/get-row.md) - [Fetch one cell](https://docs.origami.chat/agents/reference/get-cell.md) - [Upsert rows on matchColumns (the single v2 batch write primitive)](https://docs.origami.chat/agents/reference/upsert-rows.md): v2's one row-write endpoint — there is no bare per-row insert (an insert-only call is just an upsert whose rows match nothing). All writes go through `api_batches` and are idempotent per `batchId`. - [Upsert rows from a CSV file (same operation, CSV transport)](https://docs.origami.chat/agents/reference/upsert-rows-file.md): The CSV transport for `POST /tables/{tableId}/rows/upsert`: `content` is a base64-encoded CSV whose headers are input-column slugs (the same keys as JSON upsert rows) with one record per data row. Identical match/enrich semantics, identical per-`batchId` idempotency, identical row cap — a CSV with m… - [Stop active cell work for a table (the API "stop" button)](https://docs.origami.chat/agents/reference/cancel-table-cells.md): Cancel every active (waiting / queued / running) `cell_run` for the table — the API equivalent of the in-app "stop" button. Running cells finish their in-flight execution but skip further work once they observe the cancelled status; sent history is never touched. - [List enrichment runs (cursor-paginated; ?tableId=)](https://docs.origami.chat/agents/reference/list-enrichment-runs.md): Org-wide list of enrichment runs — the tracked batches of column-over-row work (upserts, file ingests) — newest first. Optional `?tableId=` filter. `GET /batches` is the deprecated alias. - [Track an enrichment run (incl. per-row upsert outcomes)](https://docs.origami.chat/agents/reference/get-enrichment-run.md): Run status, row count, enrichment counts, credits used, and — for `upsert`-type runs — the per-row `outcomes[]` ledger plus `outcomeCounts`. `GET /batches/{batchId}` is the deprecated alias. - [List a table's enrichment runs (cursor-paginated)](https://docs.origami.chat/agents/reference/list-table-enrichment-runs.md): Same list as `GET /enrichment-runs?tableId=`, but the table is resolved org-scoped first — a missing or cross-org table returns `404 TABLE_NOT_FOUND` instead of an empty list. - [List workspaces (cursor-paginated; ?search=)](https://docs.origami.chat/agents/reference/list-workspaces.md) - [Bootstrap a workspace (upload-first flows)](https://docs.origami.chat/agents/reference/create-workspace.md) - [Fetch a workspace](https://docs.origami.chat/agents/reference/get-workspace.md) - [Delete a workspace (two-step; ?confirm=true)](https://docs.origami.chat/agents/reference/delete-workspace.md): Permanently deletes a workspace and its entire entity tree (tables, rows, cells, sequences, chat history, documents). Follows the v2 deletes convention — deliberately **two-step** so it can't fire on a single ambiguous instruction: - [List workspace-scoped documents (cursor-paginated)](https://docs.origami.chat/agents/reference/list-documents.md) - [Upload files into a workspace (the single ingest verb)](https://docs.origami.chat/agents/reference/upload-documents.md): The one ingest path for every file type — base64 file bytes inside JSON (no multipart). CSVs become new tables (`mode: "table"`, the default for `.csv`), append into an existing table (`mode: "append"` + `tableId`), or store as documents (`mode: "document"`); any other extension is stored as a docum… - [Read a document (metadata + content)](https://docs.origami.chat/agents/reference/get-document.md) - [Rename a document](https://docs.origami.chat/agents/reference/rename-document.md): DB-only rename — re-slugs the basename from `name` while preserving the directory prefix and extension; the document id is stable. A collision with another live document at the derived path → `409 DOCUMENT_PATH_TAKEN`. - [Delete a workspace document](https://docs.origami.chat/agents/reference/delete-document.md) - [List a workspace's campaigns](https://docs.origami.chat/agents/reference/list-workspace-campaigns.md): A campaign is a first-class `campaigns` row (org-global, workspace-homed) whose queue is the set of sequences stamped with its id — one sequence per person. Campaigns are homed on a workspace, so the workspace is the primary list scope. Returns the canonical list envelope with `nextCursor: null` (on… - [List campaigns that send from a table](https://docs.origami.chat/agents/reference/list-table-campaigns.md): The campaigns whose enrolled sequences send from this table. A campaign owns no table — its `tableId` is derived from the dominant table of its enrolled sequences — so this resolves the table's active campaign(s). Returns the canonical list envelope with `nextCursor: null`. - [Create a campaign on a table (agentic)](https://docs.origami.chat/agents/reference/create-campaign.md): Agentic campaign creation. Minimal body `{ instructions }` — no column or channel; campaigns can be multi-channel and the agent infers the rest from the instructions and the table. Delegates to the chat agent via the v2 run path and responds `202 Accepted` with `{ agent, run, table }` (poll `GET /ag… - [Fetch a campaign](https://docs.origami.chat/agents/reference/get-campaign.md) - [Delete a campaign (?confirm=true, ?dryRun=true)](https://docs.origami.chat/agents/reference/delete-campaign.md): Soft-deletes the campaign (instantly halting its picker) and cancels its orphaned sequences. Follows the v2 deletes convention: - [List the people in a campaign (keyset-paginated, filtered)](https://docs.origami.chat/agents/reference/list-campaign-people.md): The people enrolled in the campaign — one row per enrolled sequence (recipient), with send status, fit score / explanation, and identity profile. Keyset-paginated via an opaque `cursor`; the list envelope additionally carries a top-level `total`. `GET /campaigns/{campaignId}/sequences` is a synonym… - [Synonym of GET /campaigns/{campaignId}/people](https://docs.origami.chat/agents/reference/list-campaign-sequences.md): Identical to `GET /campaigns/{campaignId}/people` — same `campaign_person` item shape, same `search` / `status` (CSV) / `cursor` / `limit` params, same top-level `total`. In the campaign model a person IS a sequence. - [Campaign performance stats](https://docs.origami.chat/agents/reference/get-campaign-stats.md) - [Request a content change to a campaign (agentic)](https://docs.origami.chat/agents/reference/edit-campaign.md): The ONLY way to change campaign content (message templates, per-lead copy, the brief). Minimal body `{ instructions }` — a natural- language change request the chat agent fulfils. Delegates to the v2 run path and responds `202 Accepted` with `{ agent, run, campaign }` (poll `GET /agents/{agentId}/ru… - [Launch a campaign — activate + run the launch pipeline (alias /send)](https://docs.origami.chat/agents/reference/launch-campaign.md): Marks the campaign ready: sets `status: active` and runs the full launch pipeline campaign-keyed (sender gate, duplicate auto-cancel, per-account scheduling). Idempotent on an already-active campaign. - [Alias of POST /campaigns/{campaignId}/launch](https://docs.origami.chat/agents/reference/send-campaign.md): Alias of `POST /campaigns/{campaignId}/launch` — activates the campaign and runs the launch pipeline. (Before the campaigns refactor this was a batch "mark sequences ready" op; it is now the campaign-activate launch.) Same body, params, and result shape. - [Pause a campaign (idempotent; ?dryRun=true)](https://docs.origami.chat/agents/reference/pause-campaign.md): Pauses the campaign. Idempotent — pausing an already-paused campaign is a no-op. `?dryRun=true` returns `{ dryRun: true, campaignId, wouldPause }` with no writes; a real pause returns the transition result carrying the `pause` facts. - [Resume a campaign (idempotent; ?dryRun=true)](https://docs.origami.chat/agents/reference/resume-campaign.md): Resumes the campaign from where its sequences left off (the same `active` transition as launch; resume vs fresh-launch facts are derived from prior state). Idempotent. `?dryRun=true` returns `{ dryRun: true, campaignId, wouldResume }` with no writes; a real resume returns the transition result carry… - [List sequences (bounded scope, cursor-paginated, filtered)](https://docs.origami.chat/agents/reference/list-sequences.md): Requires one of `workspaceId` / `tableId` / `columnId` (else `400 MISSING_SCOPE`). Optional `status` / `channel` / `recipient` filters compose with `cursor` / `limit`. - [List a table's sequences (cursor-paginated, filtered)](https://docs.origami.chat/agents/reference/list-table-sequences.md): Sequences scoped to one table — the quick "does this table have any sequences?" read. Same filters (status / channel / recipient) and pagination as `GET /sequences`, but the table is resolved org-scoped first, so a missing or cross-org table returns 404 TABLE_NOT_FOUND instead of an empty list. - [Fetch a sequence with steps inline (provider internals redacted)](https://docs.origami.chat/agents/reference/get-sequence.md) - [Stop a sequence (header stop; sent history preserved)](https://docs.origami.chat/agents/reference/stop-sequence.md): Branches: nothing-to-stop (fresh draft) / stopped / noop (already stopped). `?dryRun=true` reports the would-be effect with no writes. - [Delete a sequence (?force=true, ?dryRun=true)](https://docs.origami.chat/agents/reference/delete-sequence.md): Soft-deletes one sequence. Without `?force=true`, a sequence with a sent footprint is guarded — `409 SEQUENCE_HAS_SENT_MESSAGES`; `?force=true` (`forceDeletionOfSentMessages`) extends the delete to already-sent messages. `?dryRun=true` reports the would-be effect (`{ dryRun, sequenceId, status, forc… - [List scheduled agents (cursor-paginated; filters workspaceId/enabled)](https://docs.origami.chat/agents/reference/list-scheduled-agents.md) - [Create a scheduled agent (disabled by default)](https://docs.origami.chat/agents/reference/create-scheduled-agent.md): Requires workspaceId/name/prompt/cron. Invalid cron → INVALID_CRON / CRON_TOO_FREQUENT. - [Fetch a scheduled agent (+ planBlocked, last run)](https://docs.origami.chat/agents/reference/get-scheduled-agent.md) - [Edit a scheduled agent](https://docs.origami.chat/agents/reference/update-scheduled-agent.md) - [Delete a scheduled agent (soft-delete)](https://docs.origami.chat/agents/reference/delete-scheduled-agent.md) - [Enable a scheduled agent](https://docs.origami.chat/agents/reference/enable-scheduled-agent.md) - [Disable a scheduled agent](https://docs.origami.chat/agents/reference/disable-scheduled-agent.md) - [Manually trigger a run](https://docs.origami.chat/agents/reference/trigger-scheduled-agent.md) - [Run history (failed runs carry a workspace-chat handoff)](https://docs.origami.chat/agents/reference/list-scheduled-agent-runs.md): Run history newest-first, in the canonical list envelope. Not paginated — `nextCursor` is always `null`. - [Org account overview (plan, capabilities, workspace usage)](https://docs.origami.chat/agents/reference/get-account.md) - [Credit balance (supersedes v1 GET /credits)](https://docs.origami.chat/agents/reference/get-credits.md) - [Legacy alias: delete a workspace (body-confirm protocol)](https://docs.origami.chat/agents/reference/delete-workspace-legacy.md): Deprecated alias for `DELETE /workspaces/{workspaceId}`. Keeps the original body-confirm protocol: with `confirm` omitted/false it returns `409 CONFIRMATION_REQUIRED` whose `details` carry the workspace name and the `willDelete: { tables, rows }` impact preview; the `{ "confirm": true }` retry perfo… - [Legacy alias: upload files (use POST /workspaces/{workspaceId}/documents)](https://docs.origami.chat/agents/reference/upload-documents-legacy.md): Deprecated alias for `POST /workspaces/{workspaceId}/documents` — identical body and responses. - [Legacy alias: rename a document (use PATCH .../documents/{documentId})](https://docs.origami.chat/agents/reference/rename-document-legacy.md): Deprecated alias for `PATCH /workspaces/{workspaceId}/documents/{documentId}` — identical body and responses. - [Legacy alias: create a campaign (use POST /tables/{tableId}/campaigns)](https://docs.origami.chat/agents/reference/create-sequence-legacy.md): Deprecated alias for `POST /tables/{tableId}/campaigns` — identical body and responses (agentic campaign creation from `{ instructions }`, `202` with `{ agent, run, table }`). - [Legacy alias: delete a sequence (use DELETE /sequences/{sequenceId})](https://docs.origami.chat/agents/reference/delete-sequence-legacy.md): Deprecated alias for `DELETE /sequences/{sequenceId}`. Body-flag protocol: soft-delete; `409 SEQUENCE_HAS_SENT_MESSAGES` unless the body sets `forceDeletionOfSentMessages: true`. Supports body `dryRun: true`. - [Legacy alias: list enrichment runs (use GET /enrichment-runs)](https://docs.origami.chat/agents/reference/list-batches.md): Deprecated alias for `GET /enrichment-runs` — identical parameters and response. - [Legacy alias: track an enrichment run (use GET /enrichment-runs/{runId})](https://docs.origami.chat/agents/reference/get-batch.md): Deprecated alias for `GET /enrichment-runs/{runId}` — identical response. - [Webhooks overview](https://docs.origami.chat/webhooks/overview.md): Receive signed event POSTs from Origami when sequencer activity happens. - [Using webhooks with the API](https://docs.origami.chat/webhooks/using-with-the-api.md): Combine the v2 API and webhooks into one submit, notify, resolve loop. - [Set up an endpoint](https://docs.origami.chat/webhooks/setup.md): Configure a webhook endpoint in Settings → Developers and verify it with a test event. - [Verify webhook signatures](https://docs.origami.chat/webhooks/signatures.md): Standard-Webhooks-compatible HMAC-SHA256 verifier snippets in Node, Python, Go, Ruby, Rust, and curl + openssl. - [Retries & idempotency](https://docs.origami.chat/webhooks/retries.md): The retry schedule, 410 Gone behavior, and the webhook-id idempotency key. - [Message sent](https://docs.origami.chat/webhooks/events/message-sent.md): Origami POSTs this event to your configured URL when a sequencer email or LinkedIn DM finishes sending. - [Reply received](https://docs.origami.chat/webhooks/events/reply-received.md): Origami POSTs this event when an inbound email or LinkedIn DM matches one of your active sequences. We also stop any matched sequences that should pause on reply; `data.newly_stopped` tells you whether THIS event's sequence transitioned to stopped. - [Connection requested](https://docs.origami.chat/webhooks/events/connection-requested.md): Origami POSTs this event when a sequencer LinkedIn invite is sent. Shape mirrors `Message sent`: `channel` is always `"linkedin"` and `data.message.body` carries the optional connection note (a LinkedIn premium feature — empty string when no note was attached). - [Connection accepted](https://docs.origami.chat/webhooks/events/connection-accepted.md): Origami POSTs this event when a LinkedIn invite from a sequence is accepted by the recipient. - [Test](https://docs.origami.chat/webhooks/events/test.md): Origami POSTs this event when you click **Test endpoint** in the dashboard. Always delivered regardless of subscription — it's a wire-connectivity check, not a business signal. - [Migrating from v1 to v2](https://docs.origami.chat/api-v1-to-v2-migration.md): Map every v1 Data API route to its canonical v2 equivalent. - [List tables](https://docs.origami.chat/api-reference/tables/list-tables.md): Returns all tables in the organization with column metadata. The API key scopes to the org — no workspace ID needed. - [Read table rows](https://docs.origami.chat/api-reference/tables/read-table-rows.md): Read all rows in a table with pagination, filtering, and sorting. - [Insert rows](https://docs.origami.chat/api-reference/rows/insert-rows.md): Insert rows into an existing table. Returns a batch ID immediately — poll `GET /batches/{batchId}` for enrichment progress and results. - [Get batch](https://docs.origami.chat/api-reference/batches/get-batch.md): Check the status of an async batch. When all enrichments are complete, the response includes the full enriched row data and total credits used. - [Get credit balance](https://docs.origami.chat/api-reference/credits/get-credit-balance.md): Returns the current credit balance for the organization tied to this API key. ## OpenAPI Specs - [openapi-v2](https://docs.origami.chat/openapi-v2.yaml) - [openapi-webhooks](https://docs.origami.chat/openapi-webhooks.yaml) - [openapi-v1](https://docs.origami.chat/openapi-v1.yaml) - [openapi](https://docs.origami.chat/api-reference/openapi.json)