Skip to content

Jobs

Submit, poll, and list uniquify jobs. All routes require authentication and are owner-scoped.


Create a job

POST /jobs

Submit one source with your settings; get back a jobId to poll. Accepts multipart/form-data (with a file upload) or application/json (with a pre-uploaded inputKey).

Body

You must provide a source (one of image or inputKey) and settings.

Settings resolve through a precedence chain — the first of these that is present wins:

  1. recipeConfig or recipeId on the request (sending both is a 400)
  2. presetId on the request
  3. the default preset bound to your API key (set when the key was created)

If none of the three resolves, you get a 400. So a key with a default preset can post nothing but the image; a key without one must name settings on every call.

copies, strength and options are not part of that chain — they are per-call knobs folded on top of whichever base was resolved, and they override the recipe's own values. That is deliberate: it lets one saved preset serve callers who each want a different fan-out or intensity. See Configuring your own settings.

FieldTypeRequiredNotes
imagefileone ofMultipart file upload of the source (≤ 100 MB).
inputKeystringone ofKey of a pre-uploaded source under the temp/ prefix.
recipeIdstringsee aboveA bundled starter preset (see Options).
recipeConfigobjectsee aboveA full recipe graph. See Configuring your own settings.
presetIdstringsee aboveA saved preset — system or your own. See Presets.
copiesinteger ≥ 1noNumber of variants. Asking for more than the limit (100000) is a 400, not a clamp.
strengthinteger 0–100noDefault transform intensity. Clamped into range.
deliverydrive | ephemeralnoWhere results go. Defaults to drive. See Delivery modes.
optionsobjectnoMerged onto the recipe's global_settings (e.g. ultraSafe, noise, carousel). ultraSafe needs an Agency plan or higher — see Ultra Safe.

Response — 202 Accepted

json
{ "jobId": "8f3c1a90-…", "statusUrl": "/jobs/8f3c1a90-…" }

statusUrl is relative to the API base — poll it (see Get a job).

Errors

StatuserrorWhen
400bad_requestNo source; both recipeId and recipeConfig; no settings at all (no recipeId/recipeConfig/presetId and no default preset on the key); unknown recipeId or presetId; copies above the limit; unsupported file type; recipe failed validation (findings in details).
402insufficient_balanceYour shared balance can't cover the job (details.balance, details.required). Nothing runs.
403forbiddenThe resolved recipe asks for a feature your plan doesn't include — currently Ultra Safe, which is Agency and higher. Refused before anything is uploaded or charged, rather than run without the feature.
413payload_too_largeUpload exceeds 100 MB.
429rate_limitedToo many job creations in the window (see Rate limits).

Example

bash
curl -s -X POST "$API/jobs" \
  -H "Authorization: Bearer $FLUIDGHOST_KEY" \
  -F "[email protected]" \
  -F "recipeId=full-refresh" \
  -F "copies=3" \
  -F "delivery=ephemeral" \
  -F 'options={"ultraSafe": true, "noise": 35}'

Get a job

GET /jobs/{id}

Poll a job's status. Returns the Job status shape. Poll until status is terminal (completed, partial, failed).

Response — 200 OK

json
{
  "jobId": "8f3c…",
  "status": "processing",
  "progress": { "done": 1, "total": 3 },
  "variants": [
    { "index": 0, "status": "completed", "fileName": "IMG_4827.JPG", "downloadUrl": ".../jobs/8f3c…/download/0", "detectionRisk": 12, "riskBand": "low" },
    { "index": 1, "status": "processing" }
  ]
}

Errors

StatuserrorWhen
404not_foundNo such job, or the job isn't yours (owner-scoped; the two are indistinguishable by design).

List jobs

GET /jobs

Your recent jobs, newest first (capped at 100). Each row is a Job status plus createdAt and copies.

Response — 200 OK

json
{
  "jobs": [
    {
      "jobId": "8f3c…",
      "status": "completed",
      "progress": { "done": 3, "total": 3 },
      "variants": [  ],
      "createdAt": "2026-07-03T14:00:00Z",
      "copies": 3
    }
  ]
}

For ephemeral jobs, note that the bytes are gone once downloaded or after the 1h purge — the job row remains, but its variants' downloadUrls may already be spent.

FluidGhost API — part of the Fluidvip ecosystem.