Layers
Partner APIConcepts

Jobs

The shared envelope for long-running operations.

View as Markdown

Long-running operations return a job: GitHub ingest, content generation, influencer creation, and top-performer cloning all use the same 202 Accepted response shape and the same polling endpoint.

The 202 + poll pattern

Any endpoint that kicks off work returns 202 Accepted immediately with a job envelope - jobId, kind, status, stage, plus a kind-specific set of pointers to the resource being produced (e.g. projectId, containerId) and a locationUrl you should poll.

POST /v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/content
→ 202 Accepted
{
  "jobId": "job_01HX9Y6K7EJ4T2ABCDEF01234",
  "kind": "content_generate",
  "status": "running",
  "stage": "queued",
  "projectId": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  "containerId": "cnt_7d18b9a1-8b2c-4f3e-a4d5-6e7f8a9b0c1d",
  "locationUrl": "/v1/jobs/job_01HX9Y6K7EJ4T2ABCDEF01234",
  "startedAt": "2026-04-18T12:04:11.000Z"
}

Then you poll:

GET /v1/jobs/job_01HX9Y6K7EJ4T2ABCDEF01234
→ 200 OK
{
  "jobId": "job_01HX9Y6K7EJ4T2ABCDEF01234",
  "kind": "content_generate",
  "status": "running",
  "progress": 0.42,
  "stage": "generating_visuals",
  "startedAt": "2026-04-18T12:04:11.000Z"
}

When it finishes, the same endpoint returns a terminal shape:

{
  "jobId": "job_01HX9Y6K7EJ4T2ABCDEF01234",
  "kind": "content_generate",
  "status": "completed",
  "finishedAt": "2026-04-18T12:07:33.000Z",
  "result": {
    "containerId": "cnt_7d18b9a1-8b2c-4f3e-a4d5-6e7f8a9b0c1d",
    "assets": [/* ... */]
  }
}

State machine

running ──┬── completed
          ├── failed
          └── canceled
  • running is the only non-terminal state.
  • completed, failed, and canceled are sticky. Once a job lands in one of them it stays there forever. You can safely cache terminal responses.
  • progress is a float in [0, 1]. It moves forward and never snaps back.
  • stage is a job-kind-specific string (for example cloning, analyzing, generating_visuals). The stages for each kind are documented on the endpoint that starts them.

How to poll

Poll until you see a terminal status. If you hit 429 during polling, honor the Retry-After header - don't treat rate limits as a job failure.

Job kinds

Five kinds ship today. Each has its own stage vocabulary.

kindTriggered byNotable stages
project_ingest_githubPOST /v1/projects/:id/ingest/githubcloning, analyzing, generating_sdk_patch, opening_pr, finalizing
project_ingest_websitePOST /v1/projects/:id/ingest/websitefetching, parsing, merging_context
appstore_ingestPOST /v1/projects/:id/ingest/appstorefetching, parsing, merging_context
content_generatePOST /v1/projects/:id/contentplanning, generating_visuals, assembling, rendering
influencer_createPOST /v1/projects/:id/influencersgenerating, rendering_references

There is no tiktok_lease job kind. Lease requests have their own status endpoint. Submit a request, poll the lease-request status endpoint, and assigned accounts appear in your social-accounts list once fulfilled.

Canceling a job

POST /v1/jobs/job_01HX9Y6K7EJ4T2ABCDEF01234.../cancel

Cancel is best-effort. The status code tells you what happened:

  • 202 - { "jobId": "...", "accepted": true } - cancellation accepted. The job exits at the next cancellable checkpoint.
  • 200 - { "jobId": "...", "accepted": false, "reason": "ALREADY_COMPLETED" | "ALREADY_FAILED" | "ALREADY_CANCELED", "stage"?: "..." } - job is already in a terminal state, so there's nothing to cancel.
  • 409 - canonical error envelope with code: "CONFLICT" and details.subcode: "JOB_CANCEL_UNAVAILABLE" - the current stage refuses cancellation (rolling it back would leave orphaned state). Wait for the next stage and retry, or let the job finish.

We don't roll back side effects of a completed stage. If you need to undo something a completed job did, look up the resource it produced and act on it directly.

See the endpoint reference for the full response contract: POST /v1/jobs/:jobId/cancel.

Error shape

A failed job carries a structured error:

{
  "jobId": "job_01HX9Y6K7EJ4T2ABCDEF01234...",
  "status": "failed",
  "finishedAt": "2026-04-18T12:09:02Z",
  "error": {
    "code": "PLATFORM_ERROR",
    "message": "Meta rejected the ad creative: aspect ratio not supported",
    "details": {
      "platform": "meta",
      "platformCode": "1487194",
      "platformMessage": "Video must be at least 4:5",
      "retryAfterMs": null
    }
  }
}

code is drawn from the stable error set. data is structured - always check details.platform and details.platformCode before parsing message, which is human-friendly and subject to change.

Idempotency

Any POST that starts a job accepts Idempotency-Key: <uuid>. If the same key arrives twice inside the replay window:

  • Same body: the original 202 { jobId, ... } is replayed. Safe to retry on network errors.
  • Different body: 409 IDEMPOTENCY_CONFLICT.

Use an idempotency key on every job-starting POST you make. Networks are messy; duplicate content_generate jobs cost credits.

Webhooks

If you prefer push notifications, register a webhook endpoint and subscribe to job.completed, job.failed, and job.canceled. Polling and webhooks coexist; webhooks let you avoid polling while preserving the same job contract.

On this page