# Jobs and resumable polling

> Persist asynchronous job IDs, poll with bounded backoff, and stop on every terminal status.

Canonical guide: <https://api.blackpearl.com/docs/jobs>

Raw Markdown: <https://api.blackpearl.com/docs/raw/jobs.md>

OpenAPI: <https://api.blackpearl.com/v1/openapi.json>

Playbooks, Audiences, Brand research, Prospecting, and Documents return asynchronous jobs. The shared `type` field selects the typed `input` and `result`: `playbooks` uses `PublicPlaybookJobResponse`, `audiences` uses `PublicAudienceJobResponse`, `brands` uses `PublicBrandJobResponse`, `prospecting` uses `ProspectingJobResponse`, and `documents` uses `DocumentJobResponse`.

## Lifecycle

| Status | Terminal | Client behavior |
|---|---:|---|
| `queued` | No | Wait before polling again. |
| `running` | No | Report `progress` if useful and keep polling. |
| `succeeded` | Yes | Validate and render the typed `result`. |
| `failed` | Yes | Stop and handle `error_code` when present, then surface the job's `error` message. |
| `canceled` | Yes | Stop; there is no public cancel operation in the initial contract. |

Do not wait only for `succeeded`, because that creates infinite loops after failures or cancellation.

## Resumable polling algorithm

1. After a successful create response, persist `id` before the first poll.
2. Poll `GET /v1/jobs/{job_id}` after 2 seconds.
3. Back off exponentially with jitter, capped at 10 seconds.
4. Set a local deadline appropriate to the caller. A deadline stops the client, not server work.
5. Keep the ID when the deadline expires and resume it later rather than creating duplicate work.
6. Stop on `succeeded`, `failed`, or `canceled`.

Before the create request, persist an `Idempotency-Key`. If the connection fails before the response arrives, repeat the exact job-creation operation and input with that key during its 24-hour replay window. This recovers the original response instead of creating duplicate work.

The [Quickstart](https://api.blackpearl.com/docs/getting-started) includes a complete cURL implementation. Tested Python and TypeScript implementations under `docs/examples` add jitter and use the same terminal-state rules.

## Ownership and billing

A job is visible only to a key for its owning project. A missing or cross-project ID returns `not_found`. Creating a job records accepted work and its eventual capability usage. Poll requests are subject to request rate limits but are not billed as new generation requests.

Job creation requires a positive spendable credit balance, but does not reserve or estimate the run's price. Settlement uses the actual provider cost after execution. If that amount exceeds the remaining balance, the platform consumes the balance without taking it negative, triggers configured auto recharge, and terminates the job as `failed` with `error_code` `insufficient_credits`; `result` remains null.

`progress` is coarse and is not a completion-time promise. Do not build an SLA from it. Every public job includes `expires_at`, exactly 30 days after `created_at`. Job metadata, input, and results remain readable through that timestamp. Store any result your application needs longer.

After `expires_at`, direct job and Audience candidate reads return `410` with `error.code` `job_expired`. Cleanup removes retained input, result, failure detail, stages, and private diagnostics, but preserves the minimal ownership tombstone plus usage, cost, credit, and audit ledgers. Expiry does not cancel work, reverse billed usage, or erase accounting history.

## Poll errors

Safe GET requests may be retried for `rate_limit_exceeded` after `Retry-After` and for documented transient `5xx` failures with bounded exponential backoff. Fix authentication, entitlement, quota, and budget errors before retrying. See [Errors](https://api.blackpearl.com/docs/errors).

Job retrieval is OpenAPI operation `get_job_v1_jobs__job_id__get`. Its response is the same `type`-discriminated union of Playbooks, Audiences, Brands, Prospecting, and Documents Job schemas. Documents results contain file references; download their bytes through the authenticated endpoint documented in [Documents](https://api.blackpearl.com/docs/documents) before the Job expires.
