xSD 4.5
High-quality image generation with optional reference images for face/character/style consistency.
xSD 4.5 generates one or more images from a text prompt. You can pass up to 10 reference images for face anchoring, style transfer, or multi-subject composition. The endpoint is **asynchronous**: POST returns `202` with a `requestId` and `pollUrl` immediately, and the result is delivered via polling (`GET /v1/generations/:requestId`) or via an HMAC-signed webhook if you supply `webhookUrl`. Signed image URLs are valid for 23 hours. Each request must be tagged with `endUserEmail` so generations can be grouped and managed per end-user inside your account.
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",
"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: total pixels must be in 3,600,000..16,777,216 and aspect ratio (W/H) in 1/16..16; out-of-bounds values are rejected with `400 validation_error` before any debit. `1K` is no longer accepted — use `2K` or larger. 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. Combine with a preset `size` (`2K` / `4K`) and the server resolves to explicit pixels for you (e.g. `size: "2K"` + `aspectRatio: "landscape"` → `2560x1440`). `auto` (default) lets the model pick. 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. Constraint: references.length + n ≤ 15. 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. "url" gives signed CDN URLs valid for 23 hours. "b64_json" returns base64 inline (large payloads). |
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). Use together with `sequentialOptions.maxImages`. Do NOT combine with `n > 1`. |
sequentialOptions.maxImages | integer | no | — | Number of frames in the connected series. 1–15. 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
On a successful generation you receive a 200 with this shape. Image 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. After 23 h the image is no longer accessible — long-term storage will be available on a paid plan in the future. Save a copy on your side 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",
"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",
"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",
"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",
"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`.
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",
"prompt": "Series of stylistically consistent images of the same person at four life stages: child, adolescent, adult, elderly.",
"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",
"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",
"prompt": "Polaroid-style portrait of a woman with freckles, soft daylight",
"size": "2048x2048",
"webhookUrl": "https://your-app.example.com/hooks/xmode"
}'Limits & pricing
- Max images per request: 15
- Max reference images: 10
- Per-image size limit: 10 MB per reference
- Total pixels range: 3.6M – 16.7M
- Aspect ratio range: 1/16 – 16
- Prompt max tokens: ~2000
- Rate limit: 500 requests / minute / API key
- Pricing: 10 xTokens per image (Each image in `n` is billed separately. Failed generations are not charged.)