Providers — Job protocol
The protocol is pull-based: you call the platform to claim jobs, then report the result. The platform never initiates connections to you.
1. Pull jobs
Section titled “1. Pull jobs”POST /api/v1/provider/jobs/pull?max_jobs=3Authorization: Bearer pv_...max_jobs — how many jobs to claim at once (1–10000, default 1). Each claimed job is
atomically assigned to you: no other provider can claim it.
{ "success": true, "data": { "jobs": [ { "job_id": "9f1c8b1e-7c2a-4a86-9b6e-2c5fa0b7d8e1", "model": "gemini-2.5-flash-image", "prompt": "a red panda astronaut floating in a nebula", "resolution": "1K", "aspect_ratio": "1:1", "attachments": [], "price": 0.08, "deadline_at": "2026-08-24T12:34:56Z", "timeout_seconds": 300 } ] }}If there are no queued jobs for your models, the response is {"jobs": []} — poll again
later with a reasonable interval (a few seconds).
Attachments (image-to-image)
Section titled “Attachments (image-to-image)”For image-to-image jobs, attachments contains presigned GET URLs to the input files
in our S3. They are valid for ~1 hour — download them immediately after pulling.
You need no S3 credentials — the URL is self-contained.
GET <presigned-attachment-url>2. Generate
Section titled “2. Generate”Run the generation with your own engine, keys, and proxy fleet. Follow the prompt and model spec. If the client requested an aspect ratio/resolution that you cannot produce, report a failure (see below) rather than returning a malformed image.
3. Upload the result
Section titled “3. Upload the result”You have two options:
Option A — presigned PUT (preferred)
Section titled “Option A — presigned PUT (preferred)”Get a presigned PUT URL for your result object from the platform, upload the file, and pass the resulting S3 key back. (The exact endpoint for issuing the presigned PUT is part of your onboarding — the admin provides it together with your credentials.)
Option B — direct URL
Section titled “Option B — direct URL”If you can host the result yourself (e.g. a public object storage URL), send it directly in
the result payload via the url field.
Note Either way, the client downloads the result from the platform, never from you.
4. Submit the result
Section titled “4. Submit the result”POST /api/v1/provider/jobs/9f1c8b1e-7c2a-4a86-9b6e-2c5fa0b7d8e1/resultAuthorization: Bearer pv_...Content-Type: application/jsonSuccess
Section titled “Success”{ "status": "success", "result_s3_key": "nb-results/9f1c8b1e-7c2a-4a86-9b6e-2c5fa0b7d8e1.png", "mime_type": "image/png"}or with a direct URL:
{ "status": "success", "url": "https://cdn.yourco.dev/results/9f1c8b1e...png", "mime_type": "image/png"}Failure
Section titled “Failure”Report failures with a stable error_code and a human-readable error_message — they are
shown to the client:
{ "status": "failed", "error_code": "provider_overload", "error_message": "Gemini quota exhausted on all healthy keys"}Recommended error codes:
error_code |
Meaning |
|---|---|
provider_overload |
Google quota/429/503 — transient, will be retried by the client |
empty_response |
200 OK but no content (censored/safety block) |
provider_error |
Provider-side failure |
invalid_payload |
Malformed request (bad base64, missing inputs) |
timeout |
Generation exceeded the deadline |
Idempotency & deadlines
Section titled “Idempotency & deadlines”- Submitting a result for an already-terminated job is not an error — the platform
returns
200and ignores the duplicate. Retry safely on network failures. - If you do not submit a result before
deadline_at, the job is automatically requeued and offered to another provider. A late result after requeue is rejected (404), because the job no longer belongs to you. - Claiming jobs you cannot finish (e.g. unsupported model) hurts everyone: if you pull it, complete it or report a failure — don’t let it sit until the deadline.