Skip to content

Configuring your own settings

Everything the FluidGhost Studio can do is expressible in a job request. This guide shows where a job's settings come from, the knobs that adjust them, and how to discover the exact vocabulary at runtime.

Two things, not one

It helps to separate them, because the API treats them differently:

  • The recipewhat work to do. A node graph. Exactly one source supplies it.
  • The knobscopies, strength, options. Per-call dials folded on top of whatever recipe was resolved. Always optional.

The knobs are not an alternative to a recipe; they modify one.

Where the recipe comes from

POST /jobs takes the first of these that is present:

#SourceUse it when
1recipeConfig or recipeId on the requestYou want to name the settings on every call. Sending both is a 400.
2presetId on the requestYou saved settings server-side and want to pick one per call.
3Your API key's default presetYou want the settings baked into the key, so calls carry only the image.

If none resolves, the request is a 400. Note the consequence: a key with a default preset needs no settings fields at all.

bash
# A key created with a defaultPresetId — this is the whole request.
curl -s -X POST "$API/jobs" \
  -H "Authorization: Bearer $FLUIDGHOST_KEY" \
  -F "[email protected]"

Saved presets

A preset is a named recipeConfig stored server-side. Two kinds: the system presets everyone can see, and your own. Full CRUD is at /presets.

Bind one to a key at creation time with defaultPresetId and every call on that key inherits it — that is source 3 above. A per-call presetId overrides the key's default, and an inline recipeId/recipeConfig overrides both.

The fast path: preset + knobs

Pick a starter preset and nudge it with knobs. This covers most jobs.

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

Bundled starter presets

recipeIdWhat it does
full-refreshA broad refresh — metadata, pixels, filename — for maximally distinct variants. A good default.
iphone-noise-mult10Reauthors as an iPhone capture with signal-dependent noise, fanned out to multiple copies.
strip-warp-outputStrips metadata, applies a warp, and re-encodes the output.

The live list (with copy counts) is always available from GET /options under starterRecipes.

The knobs

KnobTypeEffect
copiesinteger ≥ 1Number of variants. Over the limit (100000) is a 400, not a clamp — silently handing back fewer files than you asked for (and billing for them) is not something a client could detect.
strengthinteger 0–100Default intensity for transforms that don't set their own. Clamped into range.
deliverydrive | ephemeralWhere results go. See Delivery modes.
optionsJSON objectShallow-merged onto the recipe's global_settings.

copies and strength are authoritative: they override the recipe's own node parameters, not just its global_settings. That is what lets a preset stay silent on fan-out and intensity and leave both to the caller — so one saved preset can serve a job that wants 3 gentle copies and a job that wants 200 hard ones.

Common options fields:

FieldTypeMeaning
ultraSafebooleanReseed each copy until it's perceptually distinct from its siblings (with an early-stop circuit breaker). Billed at 2× and included on Agency and higher — asking for it on another plan is a 403. See Ultra Safe.
noiseinteger 0–100Signal-dependent noise applied last in the chain.
carouselobjectTreat the batch as one carousel shoot (shared device/location, stepped times, sequential filenames).

Multipart sends everything as strings, so options is a JSON-encoded string in the form field (as above). With a JSON body (see below) it's a real object.

The full path: recipeConfig

For total control, send a complete recipe graph. A recipe is a small dataflow DAG — nodes connected by edges — plus global_settings. This is exactly what the Studio builder produces and what the worker executes.

Because recipeConfig is JSON, it's easiest to send the whole job as a JSON body with a pre-uploaded inputKey rather than multipart:

bash
curl -s -X POST "$API/jobs" \
  -H "Authorization: Bearer $FLUIDGHOST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputKey": "temp/8f3c…/photo.jpg",
    "delivery": "ephemeral",
    "recipeConfig": {
      "global_settings": { "copies": 4, "defaultStrength": 55, "ultraSafe": true },
      "nodes": [ ... ],
      "edges": [ ... ]
    }
  }'

The knobs (copies, strength, options) still apply and fold into global_settings on top of what the recipe declares.

Building a recipeConfig by hand is advanced. The most reliable way to get one is to design it in the Studio and export it, then send that JSON verbatim. The node vocabulary (ops), filter names, and noise types are enumerated by GET /options.

Discover the vocabulary at runtime

Don't hardcode device names, city presets, or op names — read them from the API so your integration never drifts from the engine:

bash
curl -s "$API/options" -H "Authorization: Bearer $FLUIDGHOST_KEY"
json
{
  "devices": [  ], "deviceModels": [  ],
  "cities": [  ], "countryCodes": [  ],
  "ops": [  ], "filters": [  ], "noiseTypes": [  ],
  "strengthLevels": ["light", "medium", "strong"],
  "outputFormats": [  ],
  "starterRecipes": [ { "id": "full-refresh", "copies": 5, "nodeCount": 7 },  ],
  "limits": { "maxCopies": 50, "maxUploadBytes": 104857600 }
}

Full field reference: GET /options.

Validation

Whatever you send is validated server-side before the job is enqueued. A recipe that violates a rule returns 400 bad_request with rule-cited findings:

json
{
  "error": "bad_request",
  "message": "Recipe failed validation.",
  "details": {
    "errors": [ { "rule": "LIMIT_COPIES_RANGE", "message": "copies must be between 1 and 50" } ],
    "warnings": []
  }
}

Fix what details.errors cites and resubmit. Warnings don't block the job.

FluidGhost API — part of the Fluidvip ecosystem.