Skip to main content
A run hands a prompt to the Retriever agent. It is asynchronous: you create it, poll its status, then read its tables or output.

Lifecycle

Create a run

POST /v1/runs accepts prompt (required), workspace_id, conversation_id, and output. Any other field is rejected. The agent does not ask clarifying questions: nobody is there to answer. Put every decision in the prompt. If a fact is missing that would change the target, the agent stops and says what is missing instead of guessing. To correct a choice, start another run on the same conversation_id.
  • Without workspace_id or conversation_id, Retriever creates a new workspace and a new conversation.
  • With workspace_id only, the run opens a new conversation in that workspace. The agent can read the workspace’s tables, but it has not seen the earlier conversations.
  • With conversation_id, the run continues that conversation. The agent keeps its history and its tables. Only one run can be active per conversation, otherwise 409 conversation_busy.
  • A conversation that a run opens is named a few seconds later from the prompt, followed by - API conversation; a workspace that a run creates is named the same way, followed by - API workspace. If no name can be generated, the start of the prompt is used, followed by the same suffix.
  • The 202 response includes Location and Retry-After: 5, and the run’s workspace_id and conversation_id: keep them for follow-ups.

New conversation or follow-up?

Continue the same conversation when you refine or extend a result: more rows, other roles, a wider scope, a new column. The agent sees what it was asked and what it found, and its tables. Ask it to add to the existing table, without repeating the context. Leads already in the table being filled are left out of the searches that add to it and not billed, across sources: a person by LinkedIn profile, a company by domain. Leads of your other tables can come back. Not covered: a search for the right person at a given company, and contacts found row by row in a table; posts and web results are matched only when the same source returns them again. For people or companies that must never come back, use the exclusion list instead of listing names in the prompt. It is shared by your whole organization and filters what sources bring into every later table, not only this one. It does not filter Go, direct service calls, contacts found row by row in a table, rows the agent writes itself from a service call, or lists you import into a table. See Continue a run with a new prompt. Open a new conversation, in the same workspace_id, for a request that does not depend on an earlier one: another company, another list. Also use separate conversations to run requests at the same time, because a conversation runs one request at a time. If a run is still active and you want to change its brief, cancel it (POST /v1/runs/{id}/cancel), wait until GET /v1/runs/{id} shows cancelled (usually 15 to 30 seconds), then continue its conversation with the new brief. Before that, a new run in the conversation gets 409 conversation_busy. If it still does not show cancelled after a few minutes, start the new brief in a new conversation of the same workspace. Until the old run stops, it can still write to that workspace’s tables and use credits, and two runs for the same need are both billed.

Runs per organization

An organization executes at most 5 runs at the same time by default (queue.limit gives the current value). A run counts while it is running or recovering. Runs started from MCP (retriever_build_list_start) share the same slots.
  • A new run is always accepted with 202. It is never refused because of this limit.
  • When all your organization’s slots are taken, by executing runs or by older queued runs, the new run stays queued.
  • It starts on its own when one of your runs ends. Do not create it again: a second run is billed as a second run.
  • Your queued runs start oldest first. Runs of other organizations do not wait for yours.
  • A queued run already holds its conversation: a follow-up run on the same conversation_id gets 409 conversation_busy, and the app shows the conversation as running.
  • Go jobs (/v1/go/runs) and conversations run from the app do not use these slots.
  • Time spent queued does not count toward the maximum run duration (3 hours by default). That duration starts at started_at, which is null while the run is queued.
Every queued run read with GET /v1/runs/{id} or GET /v1/runs has a queue field that says why it waits. It is null once the run has left the queue.
On the 202 of POST /v1/runs, queue can be null or already out of date: GET /v1/runs/{id} is the reference.

Idempotency

Send an Idempotency-Key header (non-empty) to replay creation without duplicates:
  • Same key and same body: 202 with the original run.
  • Same key and different body: 409 idempotency_conflict.
  • The key is tied to the user, not the API key: two API keys for the same user share the same namespace.

Poll a run

Call GET /v1/runs/{id} until completed, failed, or cancelled:
  • Respect Retry-After (15 seconds while the run is active).
  • Send If-None-Match with the received ETag: the API returns 304 with no body if nothing changed.

Credits

GET /v1/runs/{id} returns credits.charged: the credits the run was charged so far, read from your credit history. It counts:
  • the services and data sources the run called, including the cells of the columns it launched (AI columns included),
  • the research agent that fills a column row by row,
  • browser sessions,
  • AI-written sequence messages written during the run: the first message of each lead the run enrolls in an active sequence. Every other AI sequence message, including those of a draft sequence the agent activates, is written when its step is due and is not charged on the run.
The main agent’s own model calls are not billed.
  • While the run is active, the amount grows. A new charge also changes the ETag.
  • The amount can keep growing after the run ends, while cells of the columns the run launched are still running. A charge can also be recorded just after the end, most often for a call cut by a timeout, or aborted by a Stop or a cancel, whose answer landed just after the cut.
  • On a Stop or a cancel, the cells still running go back to their previous value at once. A call they had already sent can still be billed on the run when its answer lands. A failed or cancelled run shows what it used so far, and can still grow a little after it stopped.
  • No field marks the amount as final. Read the rows of the run’s tables (artifacts, GET /v1/tables/{id}/rows?include_hidden=true: hidden columns run and bill too) until no cell has status running, then read the run again later. A cell that reads failed may still be retried automatically, up to two times, depending on its service and on why it failed. While it waits for its retry it reads failed, and nothing in the API tells a failed cell that will be retried from a final one. A retried cell waits its turn in the queue, which can take more than a few minutes, and the amount grows again if the retry gets an answer. Treat the amount as provisional until it has stopped changing over a longer window. Waiting for no running cell also waits for cells that other activity on the same tables started.
  • If your credit history cannot be read at that moment, the run is returned without credits: read it again.
  • The list GET /v1/runs and the create and cancel responses do not include credits.

Reconciling with GET /credits

credits.charged on a run or on a service job will not always match the change of your balance.
  • The balance belongs to your whole organization.
  • The balance drops when a call reserves its worst-case price. charged counts only what was settled, and the unused part of a reservation comes back.
  • Other spending also moves the balance: direct POST /services calls, Go, MCP, the app, scheduled runs, and your teammates. Grants and top-ups move it too. Sequence messages written outside a run are not on it: every AI sequence message except the first message of a lead enrolled in an active sequence during the run is written when its step is due, outside any run, including the messages of a draft sequence the agent activates.
  • A run’s charged can keep growing after the run ends, while cells of the columns it launched are still running. A run’s or a job’s charged can also grow after it ends when a charge is recorded late, most often for a call cut by a timeout whose answer landed just after the cut (on a run, also a call aborted by a Stop or a cancel). A job’s charge can also land after its end when its write took longer than the job waits for it, or when the job was taken over by another worker. Late charges usually land within seconds, but this is not guaranteed: read it again a few minutes after the end.
  • Service jobs only (a run reads your credit history directly): each charge is added to the job’s total when it settles. A failed addition is not tried again, because a retry could count the charge twice. The job’s charged then reads lower than the balance change in three cases: the addition to the job’s total fails; the charge is written to your credit history but the reply of that write is lost, so the job never learns of the charge; or the server is stopped after the charge and before recording it.
  • charged: null on a service job means its first call ran on a server from before this field. A job created before it but first run after shows a number.
  • While this field was being released, a job first claimed by a new server and then retried by an older one can show less than it cost.

Choose output

  • Schemas are JSON Schema draft 7, self-contained, validated without coercion.
  • For tables, the list and each row_schema are optional. Keys must be unique.
  • Declare ["string", "null"] for any field that may be missing: an overly strict schema blocks publication and the run fails.
Once the run finishes, GET /v1/runs/{id}/output returns:
  • JSON: type is json and value holds the value.
  • Tables: type is tables and tables lists each table (key, source_table_id, columns, row_count, links).
Frozen rows are read with GET /v1/runs/{id}/output/tables/{key}/rows. They include id, source_row_id, values, cells, and fit, and do not change afterward.

Artifacts

artifacts lists tables created (created) or updated (updated) by the run:
  • Artifacts point to live tables: their content may change after the run.
  • The list may include intermediate tables, or be empty.
  • To identify the final table without guessing, use a tables-type output contract.

Export

POST /v1/runs/{id}/exports with table_key and format (csv or json) generates the export during the request. The 201 response means it is ready. links.self is an absolute URL. Opening it downloads the file. That URL does not need an API key: the export id is enough, and it stays valid while the export exists. GET /v1/runs/{id}/exports/{exportId} is the same file for a caller who already has an API key.
  • The export contains business values, without cell metadata.
  • In CSV, experience and education lists are flattened to text. JSON keeps structure.