Appearance
Core concepts
This page is the deeper tour: jobs, sources, settings, variants, delivery modes, and the detection-risk audit. Read it once and the reference pages become a lookup rather than a puzzle.
Jobs
A job is one uniquify run: one source in, copies variants out. Jobs are asynchronous — uniquifying a batch (especially video) is real compute — so submitting a job returns a jobId immediately, and you poll for the result.
A job moves through a lifecycle:
| Status | Meaning |
|---|---|
queued | Accepted and waiting for a worker. |
processing | A worker picked it up; variants are being produced. |
completed | Every variant succeeded. |
partial | The job finished but only some variants succeeded. |
failed | The job failed wholesale, or every variant failed. |
completed, partial, and failed are terminal — poll until you see one of them. A job also carries a progress counter ({ done, total }) so you can show a progress bar.
Ultra Safe early stop. With Ultra Safe on, a batch reseeds each copy until it's perceptually distinct from its siblings. If too many copies in a row can't be separated, a circuit breaker stops the batch early:
progress.stoppedEarlyistrue,done(<total) valid variants were produced, andstoppedReasonexplains it. This is a feature, not an error — the copies you got are all distinct. See Ultra Safe.
Sources
The source is the media you uniquify. Supply it one of two ways:
- Multipart upload — send the bytes as an
imagefield onPOST /jobs. - Pre-uploaded key — upload once via a presigned URL, then reference it as
inputKeyon the job (useful for large videos or reusing one source across jobs).
Supported formats, detected by file signature (not the filename or client MIME, both spoofable):
| Kind | Formats |
|---|---|
| Image | JPEG, PNG, WebP, TIFF, HEIC, AVIF |
| Video | MP4, MOV |
Anything else is rejected at the boundary with 400 bad_request before a byte is stored. The upload cap is 100 MB; oversized uploads return 413 payload_too_large.
Settings
Settings describe how to uniquify and how many variants to make. A job's recipe comes from exactly one place, and the knobs adjust it:
- Starter preset — name a bundled recipe with
recipeId. The fast path. - Saved preset — name one of your own with
presetId, or bind it to an API key so calls need no settings at all. - Full recipe — send a complete
recipeConfig: the entire node graph the Studio builds (device, place, time, exposure, noise, warp, crop, audio, output format, and more). Total control.
The API takes the first that is present: inline recipeConfig/recipeId, then presetId, then your key's default preset. Sending both recipeId and recipeConfig is a 400; so is resolving none of the three. The knobs fold on top of whichever won. Every resolved recipe is validated server-side; an invalid one returns 400 bad_request with rule-cited findings.
The full settings surface, the starter recipes, and worked examples are in Configuring your own settings. The live option vocabulary (device presets, city presets, filter/noise names, limits) is available at runtime from GET /options.
Convenience knobs
| Knob | Type | Effect |
|---|---|---|
copies | integer ≥ 1 | How many variants to produce. Over the limit (100000) is a 400, not a clamp. |
strength | integer 0–100 | Default intensity for transforms that don't set their own. |
delivery | drive | ephemeral | Where results go (see below). |
options | object | Shallow-merged onto the recipe's global_settings — e.g. ultraSafe, noise, carousel settings. |
Ultra Safe
Every variant is distinct from the source by default. Ultra Safe makes each copy distinct from its siblings as well: the worker scores each new copy against every copy already accepted in the batch and re-rolls the ones that land too close. Turn it on with ultraSafe: true in options or in a recipe's global_settings.
Three things follow from re-rolling, and all three are the point rather than side effects:
- It is billed at 2× the per-photo rate on your plan — a delivered file may have been rendered several times. Videos are never surcharged; the video path has its own runner and ignores the flag.
- A run is no longer byte-reproducible. A locked seed no longer reproduces the same files, which is why the exact-count path (no re-rolls) exists alongside it.
- The batch may stop early. If too many copies in a row can't be separated, a circuit breaker halts the run:
progress.stoppedEarlyistrue,stoppedReasonsays why, and you are billed only for what was delivered.
It works best up to about 15 copies per photo. Beyond that the breaker is increasingly likely to stop the run before the count you asked for.
Ultra Safe is included on the Agency plan and higher. On any other plan:
POST /jobs/quoteprices the run without it (the plain rate,ultraSafe: false) and answersultraSafeAllowed: false;POST /jobsrefuses a recipe that asks for it with403 forbidden, and refuses it before anything is uploaded or charged.
The job is refused rather than quietly run without the feature: Ultra Safe is a promise about the output, and silently handing back near-twins is the outcome it exists to prevent. Check GET /me → features.ultraSafe if you want to branch before submitting.
Variants
A variant is one produced result. Each variant is generated from its own seed, so no two variants in a batch are alike — different pixels, different metadata, a different device-authentic filename.
A completed variant carries:
index— its position in the batch (0-based),fileName— a device-authentic download name matching the spoofed make/model (e.g.IMG_4827.HEIC,PXL_20251222_225202463.jpg),- a result pointer — either a persistent
url(drive delivery) or a one-timedownloadUrl(ephemeral delivery), - an optional detection-risk audit (see below),
- for a video, an optional browser-playable
previewUrl; for a Live Photo, the paired.MOV/.mp4companions and their sharedcontentIdentifier.
If a single variant fails, its status is failed and it carries an error — the rest of the batch can still succeed (partial).
Delivery modes
Every job chooses where its results go, via the delivery knob:
drive (default for the web app)
Each variant is written into your FluidGhost Drive, in a per-session folder, exactly as the Studio does. Variants carry a persistent url. Results stay until you delete them. Use this when you want your results archived and browsable.
ephemeral (one-time, no Drive)
Results are not saved to your Drive. Instead each variant gets a one-time download link (downloadUrl) served by the API:
- The link deletes its object on the first successful download. A completed download is the trigger; an interrupted/dropped download does not delete, so you can retry.
- A second download of an already-consumed variant returns
410 Gone. - Multi-copy jobs return one link per variant — each deletes on its own download.
- Anything never downloaded is purged after one hour (a hard fallback so nothing lingers).
Use this for "uniquify it, grab it, and leave nothing behind." Full details and the interrupted-download semantics are in One-time results.
Detection-risk audit
Optionally, each variant carries a detection-risk measurement: how similar the result still is to the original, on a 0–100 scale where higher = more detectable.
detectionRisk— the headline score (0–100).riskBand— a coarselow/medium/high.riskAudit— the per-hash distances behind the score (PDQ, dHash, pHash, pixel MAD, and for video the frames sampled + mean/worst-frame risk).metadataRisk— lingering metadata tells (e.g. EXIF/GPS/MakerNotes that survived), with aseverityand a list oftells.
Pre-calibration, treat the score as a relative distinctness measure, not a guarantee. It's there so you can tell, per variant, how far each result moved from the source.
Account & balance
Your account has one shared ecosystem balance — the same one FluidTalk conversations spend. Jobs debit it per variant before running and refund on failure. Read it any time with GET /me, and top up in the dashboard. See Billing.