# Higgsfield provider patterns

These are examples from the supplied workflow, not a live catalog or price quote. Inspect the current MCP schema and cost preflight before submitting.

## Choose the model against the live catalog

| Model | Good fit | Relevant limits or switches |
|---|---|---|
| `seedance_2_5` | Default when a generated clip contains short printed labels | 4 to 30 seconds; `resolution`: `480p`, `720p`, `1080p`; set `generate_audio: false` if making your own soundtrack |
| `kling3_0` | Stronger motion, motion transfer, multi-shot clips with little text | 3 to 15 seconds; `mode`: `std`, `pro`, or `4k`; `sound`: `on` or `off` |
| `gpt_image_2` | Storyboards with text and complex layout | Stills; set `resolution`: `1k`, `2k`, or `4k` and `quality`: `low`, `medium`, or `high` |
| `nano_banana_pro` | Character consistency, photoreal storyboards, identity references | Stills; `resolution`: `2k` or `4k` |
| `minimax_h3` | Keyframes and mixed reference material | Check current catalog for capabilities |
| Marketing Studio | Ads, UGC, product shots, 2D, mixed-media, and SaaS motion | Inspect preset previews and live cost before choosing |

In a same-frame comparison, Seedance preserved printed labels across a clip where Kling added a misspelled duplicate. This is one observed result, not a guarantee. Seedance's motion can be gentler, so specify the movement and its pace. If little text appears and motion is the point, Kling may be the cheaper choice.

Call `models_explore` with `action: "get"` and `model_id` for current parameters, or `action: "recommend"` with a plain-language query. `details` is not a valid action. Use `get_cost: true` to preflight a proposed call, and `balance` to check available credits. Prices change. For scale, a historical preflight showed a 4k storyboard at 4 credits and a 13-second, 1080p Seedance shot at 117 credits. Don't quote those numbers as current prices.

## Prompt and request pattern

Use an approved first frame and describe changes rather than re-inventing the whole image:

```text
Animate this exact frame. Camera locked: no zoom, pan, or cuts.
Every printed word remains sharp and unchanged. Add no new text or logos.

If people are printed illustrations or cutouts: their faces remain still.
No blinking, speech, lip motion, expression change, or morphing.
A head may rock as one rigid cutout only when the shot calls for it.

Describe what moves, in order, and end with a pace word.
Keep open background empty; do not add objects or people.
```

Apply the still-face rule only to artwork that is meant to be static. For live-action dialogue, direct lip motion normally. Keep exact product UI and data out of the generative layer.

```json
{"params":{"model":"seedance_2_5","mode":"omni_reference","prompt":"<APPROVED_SHOT_PROMPT>","duration":8,"resolution":"1080p","generate_audio":false,"aspect_ratio":"16:9","use_unlim":false,"medias":[{"role":"start_image","value":"<YOUR_MEDIA_ID>"}]}}
```

For batched work, call `generate_video_batch` or `generate_image_batch` with 2 to 12 independent requests: `{"requests":[{"index":0,"params":{...}}]}`. Each request needs `index`. Poll with `jobs_wait {"jobs":[{"index":0,"job_id":"<YOUR_JOB_ID>"}]}`; `index` is required there too. A response with `timed_out: true` is not terminal. Wait at least `poll_after_seconds` and repeat until `all_terminal: true`. Read `result_url` or `results.rawUrl` from the completed job. The media `value` must be a provider `media_id` or `job_id`, not a web URL. Specify `use_unlim: false` to submit without an interactive plan choice.

### Upload a local frame

Call `media_upload` with a filename and content type, PUT bytes to the returned presigned `upload_url`, then call `media_confirm` with its `media_id`. A presigned upload does not accept your normal provider credentials. In Sauna, set `X-Sauna-Connection-Id: none` for the PUT and for downloading a presigned result so credential injection cannot break the request. For an already hosted image, call `media_import_url(url)`.

### Failed submission, not failed render

Sometimes `generate_video` returns `notice.type: "preset_recommendation"`. A batch may instead say `submission_failed`. No job was queued. Copy the returned `retry_literal_with.declined_preset_id` into each affected request's `params` and resubmit. Rewording the prompt or switching model does not address this interception.

