xSD 4.5
Image generation with reference images and connected series.
xSD 4.5 generates one or more images from a text prompt, optionally guided by reference images so that a face, a character or a style carries across them. It is the model for a connected series: one subject held consistent across several frames from a single request.
- Connected series (`sequential`) — the same subject across several frames in one call
- Reference images for face anchoring, style transfer and multi-subject composition
- Flat per-image billing: every size costs the same
Generation is asynchronous. POST answers `202` with a `requestId` and a `pollUrl`; the result arrives by polling that URL or through an HMAC-signed webhook when you pass `webhookUrl`. Signed output URLs stay valid for 23 hours.
Authentication. Add Authorization: Bearer xm_live_… to every request.
Account responsibility. Every request must include the real email of YOUR end-user via endUserEmail. You are accountable for what they generate — monitor activity and act on abuse, or your account may be suspended.
Quick example
Send a generation in 5 lines. Pick your language below.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Editorial photo of a young woman in a cobalt-blue silk gown on a rainy Tokyo street at night, neon reflections, 85mm portrait lens"
}'Request parameters
The fields you can include in the request body. Anything not listed is ignored.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
endUserEmail | string (email) | yes | — | REQUIRED. Email of YOUR real end-user inside your product — the person who actually triggered this generation. We use it to attribute every request and give you a chain of responsibility: you can list, audit, and delete that user's generations. Do NOT pass a fake address, a shared placeholder, or someone else's email — that's a policy violation. If an end-user generates disallowed content and you do not act, your account can be suspended (see Acceptable use). Example: |
prompt | string | yes | — | English text prompt describing the image. Best results follow the structure: subject + action + environment, then style + lighting + composition. For text inside the image, use double quotes (Poster with the title "Hello"). Example: |
model | string | yes | — | Public model alias. Required — pass the alias of the model you want to use (e.g. xSD4.5). See the list of currently available aliases below. Example: |
references | string | string[] | no | — | Reference image(s) used for face/style/character consistency. Single HTTPS URL or array of 1–10 URLs. When using multiple references, name them in the prompt (e.g. "use image_1 for the face, image_2 for the outfit") so the model knows what to copy from each. Example: |
size | string | no | "2K" | Output dimensions. Either a preset (`2K`, `4K` — model picks aspect ratio when used alone) or explicit pixels (e.g. `2048x2048`, `1664x2496`, `2560x1440`). Explicit-pixel values are validated up front: for this model total pixels must be in 3,686,400..16,777,216 (i.e. between 2560×1440 and 4096×4096 worth of pixels, in any shape) and aspect ratio (W/H) in 1/16..16; out-of-bounds values are rejected with `400 validation_error` before any debit. `1K` is not accepted — use `2K` or larger. This model bills a flat rate per image, so a `4K` output costs the same as a `2K` one. Combine with `aspectRatio` to pick a specific aspect under a preset. When `size` is explicit `WxH`, any `aspectRatio` is ignored (explicit `size` wins). Example: |
aspectRatio | "auto" | "square" | "landscape" | "portrait" | no | "auto" | Convenience selector for aspect ratio: `square` (1:1), `landscape` (16:9), `portrait` (3:4). Combine with a preset `size` (`2K` / `4K`) and the server resolves to the dimensions this model is tuned for (e.g. `size: "2K"` + `aspectRatio: "landscape"` → `2848x1600`, `+ "portrait"` → `1728x2304`). The exact pixels are model-specific and are not a fixed contract — they are the bucket this model is tuned for at that tier, and can change; read the delivered size back from `images[].size`, or pass an explicit `WxH` size if you need to pin it. Billing follows the resolution tier, so the exact dimensions never change the price. `auto` (default) lets the model pick. For other ratios (e.g. tall 9:16) pass an explicit `WxH` size instead (e.g. `1440x2560`). Silently ignored when `size` is already explicit `WxH` — explicit `size` always wins, no error is raised. Example: |
n | integer | no | 1 | How many images to generate in this single request. 1–15, independent of how many references you pass — each image is produced separately, so references are not drawn from the same budget here. (The combined `references + images ≤ 15` ceiling applies only to `sequential` mode, where one call carries both.) Each image is billed. Example: |
seed | integer | no | random (server-generated) | Determinism seed. Same seed + same prompt + same model + same other params = same output. If omitted, the server picks a random seed for you and returns it in `GET /v1/generations/:requestId` (`input.seed`), so any generation can be reproduced after the fact — even when you didn't pass one in. Example: |
watermark | boolean | no | false | Whether to add a small platform watermark in the corner. |
guidanceScale | number | no | ~7.5 | How strictly the model follows the prompt. Lower = more creative interpretation, higher = stricter rendering. Typical range 5–10. Example: |
responseFormat | "url" | "b64_json" | no | "url" | How images are returned. Only "url" is currently supported — signed CDN URLs valid for 23 hours. "b64_json" is reserved and rejected with `400 validation_error` before any debit; download from the returned URL instead. |
sequential | "auto" | "disabled" | no | "disabled" | Set to "auto" to generate a connected series of images where the same subject is preserved across frames (storyboards, life stages, brand-visual sets). Requires `sequentialOptions.maxImages`. Do NOT combine with `n > 1`. Model-specific: only some models support series — asking an unsupported model for one is rejected with `400 validation_error` before you are charged. In "auto" mode the MODEL decides how many frames to return, based on your prompt: if the prompt only describes variety ("different angles"), it will satisfy that inside a single image as a collage. Ask for separate images explicitly — e.g. "a series of 4 SEPARATE images — do not combine them into a grid, collage or sheet". |
sequentialOptions | { maxImages: integer } | no | — | Required whenever `sequential` is "auto" — the request is rejected without it. `maxImages` is an upper bound on how many images the series may contain, 1–15. This is a ceiling, NOT a guaranteed count — the model returns anywhere from 1 to this many, depending on how explicitly the prompt asks for separate images. Keep it at or above the number of frames your prompt asks for; a lower ceiling than the prompt requests is contradictory and typically comes back as one collage. Constraint: references.length + maxImages ≤ 15. xTokens are reserved for the full ceiling up front and the unused part is refunded automatically once the generation settles. Example: |
webhookUrl | string (https URL) | no | — | Optional public HTTPS URL we will POST the final record to once the generation is succeeded or failed. The body is identical to what GET /v1/generations/:requestId returns. Each delivery is HMAC-signed (`X-XMode-Signature: v1=<hex>`) and timestamped (`X-XMode-Timestamp`); reject anything older than 5 minutes. Private/loopback IPs are refused (SSRF guard). See the Webhook delivery section in the Introduction for verification code. Example: |
Response
The POST answers 202 Accepted with this shape — the job is queued, not finished. Read the result from pollUrl, which returns the same record with the fields below filled in. Output URLs are signed and valid for 23 hours.
{
"requestId": "req_3xN7kQ1aJpL9wDvE",
"model": "xSD4.5",
"status": "queued",
"prompt": "Editorial photo of a cat in space, cinematic, 85mm portrait lens",
"referencesCount": 0,
"endUserEmail": "alice@your-product.com",
"createdAt": "2026-05-02T19:45:15.204Z",
"pollUrl": "https://api.xmode.ai/v1/generations/req_3xN7kQ1aJpL9wDvE"
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
requestId | string | yes | — | Unique id of the generation. Use it with GET /v1/generations/:requestId to poll. |
model | string | yes | — | Public alias of the model that will produce the images. |
status | "queued" | "processing" | "succeeded" | "failed" | "expired" | yes | — | Lifecycle marker. POST always returns `queued`. GET returns the current state. `expired` means the request succeeded more than 23 hours ago and signed URLs no longer work — re-run if you need the images again. |
pollUrl | string | no | — | Present on `queued` responses only. Absolute URL to GET for status. Recommended polling cadence: every 5 seconds until terminal. |
images | array | no | — | Present only when status=`succeeded`. Each item has `id`, `expiresAt`, and EITHER `url` (signed, valid 23 h) OR `error: { code: "storage_failed" }` if our storage layer dropped that single image. If every image of the request is dropped there is nothing to deliver, so the request comes back `failed` and refunded instead. The URL is re-signed on every read, so re-reading this record replaces a link that went stale on your side — but that never moves `expiresAt`, which is fixed when the generation completed. Once it passes, the record returns status `expired` with no URL and the bytes are gone. Long-term storage will be available on a paid plan in the future; until then, download inside the window or re-generate. |
error | object | no | — | Present only when status=`failed`. `{ code, message }`. The same code values as the top-level error format (validation_error, content_policy, provider_error, provider_timeout, internal_error). |
cost | object | no | — | Present only when status=`succeeded`. `{ xTokens: number }`. Debited at request time; refunded automatically on `failed`. |
finishedAt | string (ISO 8601) | no | — | Present on terminal statuses (`succeeded`, `failed`, `expired`). When the generation reached its final state. |
endUserEmail | string | yes | — | Echoed back so you can confirm the grouping. Always lowercase. |
createdAt | string (ISO 8601) | yes | — | When the request was accepted (POST time). |
Use cases
Common patterns. Copy any block, replace the API key, and you have a runnable request.
Text to image
Simplest case. Just a prompt and the end-user email. Returns 1 image at default size.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Editorial photo of a young woman in a cobalt-blue silk gown on a rainy Tokyo street at night, neon reflections, 85mm portrait lens"
}'Single reference (face anchor)
Pass one HTTPS URL as `references`. The model preserves face/style/lighting from the reference unless overridden by the prompt.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Make her wear a leather jacket on a snowy New York street at dusk. Cinematic still.",
"references": "https://images.pexels.com/photos/1239291/pexels-photo-1239291.jpeg",
"size": "1664x2496"
}'Multiple references (face + outfit)
Pass an array and name each image in the prompt. Up to 10 references. The model needs explicit roles to know what to copy from each.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Use image_1 for the face and image_2 for the outfit. Editorial fashion photo on a Tokyo street, 85mm lens.",
"references": [
"https://example.com/face.jpg",
"https://example.com/outfit.jpg"
],
"size": "1664x2496"
}'Multiple images at once
Set `n` to generate variations in a single call. Each image is billed separately.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Cute kitten astronaut floating in a starfield, illustration",
"size": "2048x2048",
"n": 4
}'Storyboard / life stages (sequential)
Use `sequential: "auto"` with `sequentialOptions.maxImages` to get a series where the same subject is preserved across frames. Do not also pass `n`. `maxImages` is a ceiling, not a count — spell out in the prompt that you want SEPARATE images and how many, or the model will deliver the variety inside one image as a collage. Keep `maxImages` at or above the number the prompt asks for.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Generate a series of 4 SEPARATE images of the same person — do not combine them into a grid, collage or contact sheet. Keep the face, styling and lighting consistent across all four. Image 1: as a child. Image 2: as an adolescent. Image 3: as an adult. Image 4: as an elderly person.",
"size": "2K",
"sequential": "auto",
"sequentialOptions": {
"maxImages": 4
}
}'Deterministic output (with seed)
Same seed + same prompt = same image. Useful for A/B comparisons and reproducibility.
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Minimalist product photo of a black ceramic coffee mug on a white surface, soft shadow",
"size": "2048x2048",
"seed": 42
}'Skip polling — receive a webhook
Pass `webhookUrl` and we POST the final record there once the generation is done. No need to poll. The body is the same shape as GET /v1/generations/:requestId. Verify `X-XMode-Signature: v1=<hex>` against your account webhook secret (HMAC-SHA256 of `"<timestamp>.<rawBody>"`).
curl -X POST https://api.xmode.ai/v1/generations \
-H "Authorization: Bearer xm_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"endUserEmail": "alice@your-product.com",
"model": "xSD4.5",
"prompt": "Polaroid-style portrait of a woman with freckles, soft daylight",
"size": "2048x2048",
"webhookUrl": "https://your-app.example.com/hooks/xmode"
}'Limits
- Max images per request (`n`): 15
- Max references: 10
- Max reference size: 10 MB per reference
- Total pixels range: 3.69M – 16.78M (2560×1440 … 4096×4096 worth of pixels)
- Aspect ratio range: 1/16 – 16
- Prompt max length: 8000 characters