Skip to main content
Anything that can’t finish inside a request hands you a Job and keeps working in the background. Searches, fetches, enrichment, campaign drafts, domain purchases, mailbox provisioning, and chat messages all return this same object, so you write the waiting logic once.

Status

succeeded is the important difference from v2. A v2 run could report completed while cells were still filling in. A v3 Job stays running through enrichment, so when it succeeds the numbers are final and you can act on them immediately.

Polling

Poll GET /jobs/{job_id} while status is queued or running. Every running response carries next_poll_at and a Retry-After header. Honor them: polling faster does not finish the Job sooner, because those reads are served from a short-lived cache. See the quickstart for a poll loop you can copy. Lost a Job id? List them:
Pass metadata when you admit the Job so you can correlate the list (and the webhook) back to your own run id.

Webhooks

Subscribe to the job.* group instead of polling: Payloads carry a compact summary (counts and resource ids), your metadata, and credits. They never include row-level data or row_ids — fetch those with GET /jobs/{job_id}. See the Job event reference. sequence on the payload is a monotonic generation. At-least-once retries can arrive out of order — ignore any event whose sequence is not greater than the last one you processed for that job_id.

Credits

credits.spent is this Job’s spend alone. settled: false on a quality: "accurate" run means the number can still adjust down after delivered-lead settlement. Reconcile billing after settled: true. credits is null on operations that spend nothing.

needs_input

When status is needs_input, needs_input is one of:
  • questions — answer with POST /jobs/{job_id}/input { "answers": ["..."] }. The same Job id resumes.
  • handoff — a human step (for example Stripe SCA). Send the user the URL. Origami resumes the Job itself after the in-app step completes.

Cancel

POST /jobs/{job_id}/cancel is cooperative. A successful call sets cancel_requested: true and returns the current snapshot. Repeating cancel on an already-flagged Job is an idempotent 200. A Job that cannot be cancelled returns 409 JOB_NOT_CANCELLABLE. Partial work is kept. Credits already spent are not refunded.

Retryable agent failures

Failed AGENT Jobs keep a retry signal on error.code: Retryable means re-running the same call on the same resources is reasonable. Don’t create a fresh list and pay for the lookups twice.

Where Jobs come from

Everything else in v3 is synchronous — it either succeeds or errors on the spot.