diff --git a/concepts/generation.mdx b/concepts/generation.mdx
new file mode 100644
index 0000000..3d3ed1d
--- /dev/null
+++ b/concepts/generation.mdx
@@ -0,0 +1,111 @@
+---
+title: "The Generation API"
+sidebarTitle: "Generation"
+description: "Generate images from a text prompt or edit them from an instruction on hosted diffusion models. One async API, one job shape, many models."
+icon: "sparkles"
+keywords: ["generation api", "ai media generation api", "text to image api", "image editing api", "diffusion model api", "hosted diffusion models", "ai image api", "generative media api"]
+canonical: "https://rendobar.com/docs/concepts/generation"
+---
+
+
+
+Rendobar's Generation API creates new media from a prompt on hosted diffusion models. It is one of the platform's products, and it submits, runs, and returns like every other job. You describe what you want, the job runs on a GPU, and the result comes back as a signed file URL.
+
+## Modalities
+
+Images are the first modality. You can generate an image from a text prompt, or edit existing images from a written instruction.
+
+
+
+ Turn a text prompt into a webp image. Pick a tier or pin an exact model.
+
+
+ Edit one to four reference images from a plain-language instruction.
+
+
+
+More modalities will join the same surface over time. Only image generation and editing are available today.
+
+## Tiers or exact models
+
+Every generation job takes a `model`. You can name a tier and let the platform choose, or pin an exact model id.
+
+- **Tiers** (`economy`, `standard`, `premium`) express a price and quality posture without naming a model. Omit `model` and you get `economy`.
+- **Exact ids** (for example `qwen-image-2512`) pin the model and unlock its own controls, such as denoise steps, guidance, and a negative prompt.
+
+Tier aliases can be re-pointed to newer models as the catalog grows. Pin an exact id when you need a result to stay stable across that change.
+
+## Discover models
+
+`GET /models` returns the live catalog: every model with its supported jobs, tier, relative price, capabilities, and step range. Filter to one job type with `?job=`. Build a model picker against this endpoint instead of hardcoding the list.
+
+```bash
+curl "https://api.rendobar.com/models" \
+ -H "Authorization: Bearer rb_YOUR_KEY"
+```
+
+```json
+{
+ "data": [
+ {
+ "id": "qwen-image-2512",
+ "jobs": ["image.generate"],
+ "tier": "premium",
+ "underReview": false,
+ "priceTier": "$$$$",
+ "maxRefImages": 0,
+ "steps": { "default": 50, "min": 20, "max": 50 },
+ "supports": { "negativePrompt": true, "guidance": true },
+ "enhanceDefault": false,
+ "status": "active"
+ }
+ ]
+}
+```
+
+`priceTier` compares models against each other, from `$` to `$$$$`. It is not a per-image charge, because jobs bill on the compute they actually use, cost-plus. See [credits and billing](/concepts/credits).
+
+A model with `underReview: true` is pulled from tier resolution while we re-evaluate its cost and quality. It stays listed so a caller pinning it can see why the submit was rejected, but no tier resolves to it and pinning it returns `VALIDATION_ERROR`.
+
+## Same job, same shape
+
+A generation job is a [job](/concepts/job) like any other. You submit it to `POST /jobs`, it runs async, and you poll, [wait](/sdk#wait), or receive a [webhook](/guides/webhooks). The image jobs return a single webp file in `output.file`, with `output.data` set to `null`.
+
+One thing is specific to generation. While the model denoises, the job emits `job.preview` events carrying a small frame of the image as it resolves, so a client can show the picture arriving instead of a spinner. Previews are decoration: they are never replayed, and a model can emit none. See [watch it render](/jobs/image-generate#watch-it-render).
+
+```ts
+import { createClient, outputUrl } from "@rendobar/sdk";
+
+const client = createClient({ apiKey: "rb_YOUR_KEY" });
+
+const job = await client.jobs.run({
+ type: "image.generate",
+ params: { prompt: "A paper boat on a still pond at dawn", model: "standard" },
+});
+
+console.log(outputUrl(job)); // signed URL to the webp
+```
+
+## See also
+
+- [Image generate](/jobs/image-generate): text to image, with the full model catalog
+- [Image edit](/jobs/image-edit): instruction editing with one to four reference images
+- [How a job works](/concepts/job): statuses and the output shape
+- [SDK](/sdk): `jobs.run`, `jobs.wait`, and reading the output
+- [Credits and billing](/concepts/credits): how compute-based billing works
diff --git a/docs.json b/docs.json
index ede5e85..e839d3a 100644
--- a/docs.json
+++ b/docs.json
@@ -34,6 +34,7 @@
"group": "Concepts",
"pages": [
"concepts/job",
+ "concepts/generation",
"guides/webhooks",
"guides/callbacks"
]
@@ -52,6 +53,14 @@
"jobs/captions/animate",
"jobs/captions/burn"
]
+ },
+ {
+ "group": "Generate",
+ "icon": "sparkles",
+ "pages": [
+ "jobs/image-generate",
+ "jobs/image-edit"
+ ]
}
]
},
diff --git a/jobs/compress.mdx b/jobs/compress.mdx
index 26dd0ed..282f52f 100644
--- a/jobs/compress.mdx
+++ b/jobs/compress.mdx
@@ -1,7 +1,7 @@
---
title: "Compress media to a target quality or size"
sidebarTitle: "Compress"
-description: "Compress an image, video, or audio file to the smallest size that clears a perceptual quality bar, or hit a byte budget and get the quality scored. AVIF, JPEG XL, AV1, Opus, and more."
+description: "Get an image, video, or audio file down to a target quality or byte budget. The encoder searches candidate encodes and returns the smallest that clears it."
icon: "file-zipper"
keywords: ["image compression api", "video compression api", "compress to target size api", "avif api", "jpeg xl api", "smallest file at quality", "ssimulacra2 api", "vmaf api", "compress video to 1mb", "audio compression api"]
canonical: "https://rendobar.com/docs/jobs/compress"
diff --git a/jobs/image-edit.mdx b/jobs/image-edit.mdx
new file mode 100644
index 0000000..10e23a5
--- /dev/null
+++ b/jobs/image-edit.mdx
@@ -0,0 +1,272 @@
+---
+title: "Edit an image with an instruction"
+sidebarTitle: "Image edit"
+description: "Use a written instruction to edit one to four reference images on hosted diffusion models. Swap a background, restyle, or compose."
+icon: "wand-magic-sparkles"
+keywords: ["image editing api", "instruction image edit api", "ai image edit api", "qwen image edit api", "flux kontext api", "background swap api", "restyle image api", "reference image edit", "compose images api"]
+canonical: "https://rendobar.com/docs/jobs/image-edit"
+---
+
+
+
+`image.edit` takes one to four reference images and a written instruction, then produces a new image on a hosted diffusion model. No masks and no coordinates. You describe the change in plain language and the model applies it. The result is a webp file returned as a signed URL.
+
+This is one product in [Rendobar's Generation API](/concepts/generation).
+
+## Edit an image
+
+Reference images go in `inputs.images` as an array of URLs. The instruction goes in `params.prompt`.
+
+
+
+```ts SDK
+import { createClient, outputUrl } from "@rendobar/sdk";
+
+const client = createClient({ apiKey: "rb_YOUR_KEY" });
+
+const job = await client.jobs.run({
+ type: "image.edit",
+ inputs: { images: ["https://cdn.rendobar.com/assets/examples/photo.jpg"] },
+ params: {
+ prompt: "Place the product on a pale grey studio backdrop with a soft shadow underneath",
+ },
+});
+
+console.log(outputUrl(job)); // signed URL to the edited webp
+```
+
+```bash cURL
+curl -X POST "https://api.rendobar.com/jobs" \
+ -H "Authorization: Bearer rb_YOUR_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "type": "image.edit",
+ "inputs": { "images": ["https://cdn.rendobar.com/assets/examples/photo.jpg"] },
+ "params": { "prompt": "Place the product on a pale grey studio backdrop with a soft shadow underneath" }
+ }'
+# Returns { "data": { "id": "job_...", "status": "waiting" } }.
+# Poll GET /jobs/{id} until "status": "complete".
+```
+
+```python Python
+import requests, time
+
+base = "https://api.rendobar.com"
+headers = {"Authorization": "Bearer rb_YOUR_KEY"}
+
+job = requests.post(
+ f"{base}/jobs",
+ headers=headers,
+ json={
+ "type": "image.edit",
+ "inputs": {"images": ["https://cdn.rendobar.com/assets/examples/photo.jpg"]},
+ "params": {"prompt": "Place the product on a pale grey studio backdrop with a soft shadow underneath"},
+ },
+).json()["data"]
+
+while job["status"] not in ("complete", "failed", "cancelled"):
+ time.sleep(1)
+ job = requests.get(f"{base}/jobs/{job['id']}", headers=headers).json()["data"]
+
+print(job["output"]["file"]["url"]) # the edited webp
+```
+
+
+
+Each URL can be a public link or an [uploaded asset's](/sdk#uploads) content URL. The output size follows the first reference image unless you set `width` and `height`.
+
+## Reference images
+
+`inputs.images` accepts one to four URLs. The per-model cap is tighter than the schema ceiling, so a request that clears the schema can still be rejected against the resolved model.
+
+| Model | Reference images |
+|---|---|
+| `flux-2-klein-4b` (economy) | Up to 4 |
+| `qwen-image-edit-2511-lightning` (standard) | Up to 3 |
+| `qwen-image-edit-2511` (premium) | Up to 3 |
+
+Passing more images than the resolved model accepts returns `VALIDATION_ERROR` before anything is billed. Multiple references let you compose a scene, for example a product from one image on a background from another.
+
+## Tiers and models
+
+Set `model` to a tier alias or pin an exact model id. Omit it and you get the `economy` tier. Aliases can be re-pointed as the catalog grows, so pin an exact id when you need a result to stay stable.
+
+| Tier | Model | Price | Reference images | Controls when pinned |
+|---|---|---|---|---|
+| `economy` (default) | `flux-2-klein-4b` | `$` | Up to 4 | prompt only |
+| `standard` | `qwen-image-edit-2511-lightning` | `$$$` | Up to 3 | steps (4 to 8) |
+| `premium` | `qwen-image-edit-2511` | `$$$$` | Up to 3 | steps (20 to 40), guidance, negative prompt |
+
+Every `image.edit` model is reachable through a tier. There is no pin-only model on this job type today.
+
+
+`Price` is the model's `priceTier`, a relative comparison from `$` to `$$$$`. It is not a per-image charge. Jobs bill on the compute they actually use, cost-plus. See [credits and billing](/concepts/credits).
+
+
+## Model-specific controls
+
+Tier aliases accept the base fields only. To use `steps`, `guidance`, or `negativePrompt`, pin an exact model id. Only `qwen-image-edit-2511` accepts `guidance` and `negativePrompt`. `flux-2-klein-4b` fixes its own step count and exposes the base fields only.
+
+
+
+```ts SDK
+await client.jobs.run({
+ type: "image.edit",
+ inputs: { images: ["https://cdn.rendobar.com/assets/examples/photo.jpg"] },
+ params: {
+ prompt: "Turn the daytime sky into a clear starry night",
+ model: "qwen-image-edit-2511",
+ steps: 30,
+ guidance: 4,
+ negativePrompt: "artifacts, halos",
+ },
+});
+```
+
+```bash cURL
+curl -X POST "https://api.rendobar.com/jobs" \
+ -H "Authorization: Bearer rb_YOUR_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "type": "image.edit",
+ "inputs": { "images": ["https://cdn.rendobar.com/assets/examples/photo.jpg"] },
+ "params": {
+ "prompt": "Turn the daytime sky into a clear starry night",
+ "model": "qwen-image-edit-2511",
+ "steps": 30,
+ "guidance": 4,
+ "negativePrompt": "artifacts, halos"
+ }
+ }'
+```
+
+```python Python
+requests.post(
+ f"{base}/jobs",
+ headers=headers,
+ json={
+ "type": "image.edit",
+ "inputs": {"images": ["https://cdn.rendobar.com/assets/examples/photo.jpg"]},
+ "params": {
+ "prompt": "Turn the daytime sky into a clear starry night",
+ "model": "qwen-image-edit-2511",
+ "steps": 30,
+ "guidance": 4,
+ "negativePrompt": "artifacts, halos",
+ },
+ },
+)
+```
+
+
+
+## The output
+
+A completed job carries a single webp file in `output.file`, typed `image`. `output.data` is `null`.
+
+```json
+{
+ "data": null,
+ "file": {
+ "url": "https://api.rendobar.com/dl/job_abc123?token=",
+ "path": "output.webp",
+ "type": "image",
+ "size": 498000,
+ "meta": { "format": "webp", "width": 1024, "height": 1024 }
+ },
+ "files": [
+ { "url": "https://api.rendobar.com/dl/job_abc123?token=", "path": "output.webp", "type": "image", "size": 498000, "meta": { "format": "webp", "width": 1024, "height": 1024 } }
+ ],
+ "expiresAt": 1735689600000
+}
+```
+
+The full [output shape](/concepts/job#the-output) is the same for every job type.
+
+## Watch it render
+
+An edit job emits `job.preview` events while the model denoises, each carrying a small webp frame. Subscribe with the SDK to show the edit resolving instead of a spinner.
+
+```ts
+const sub = client.realtime.subscribeJob(created.id, {
+ onPreview: (e) => setPreview(`data:image/webp;base64,${e.data}`),
+ onComplete: (job) => sub.unsubscribe(),
+});
+```
+
+Previews are decoration and never hold up a job. The `standard` tier finishes in four steps, so it usually completes before a frame is worth showing. See [the field reference](/jobs/image-generate#watch-it-render) on the generate page.
+
+## Parameters
+
+
+ One to four reference image URLs. A public link or an uploaded asset's content URL. The per-model cap applies.
+
+
+
+ The change to make, in plain language. No masks, no coordinates. Up to 4000 characters.
+
+
+
+ A tier alias (`economy`, `standard`, `premium`) or an exact model id. A tier lets the platform pick the model. A pinned id unlocks that model's own controls.
+
+
+
+ Requested output width in pixels, up to 4096. Defaults to the first reference image. Snapped to what the model can render.
+
+
+
+ Requested output height in pixels, up to 4096. Defaults to the first reference image.
+
+
+
+ A fixed seed makes the result reproducible. Omit it for a fresh result each time.
+
+
+
+ Rewrite the instruction for the model before editing. Off by default on every model in the catalog today. The default is per model (`enhanceDefault` in [`GET /models`](#discover-models)), so omit it to keep whatever the model ships with.
+
+
+
+ Denoise steps. More steps means more detail and more time. Requires a pinned model that exposes steps. The accepted range depends on the model.
+
+
+
+ How strictly to follow the instruction. Higher is stricter. Range 1 to 10. Requires `qwen-image-edit-2511`.
+
+
+
+ What to keep out of the edited image. Up to 1000 characters. Requires `qwen-image-edit-2511`.
+
+
+## Discover models
+
+`GET /models?job=image.edit` lists the edit models with their reference-image caps, controls, and relative price. The [response shape](/jobs/image-generate#discover-models) is the same for both generation job types.
+
+```bash
+curl "https://api.rendobar.com/models?job=image.edit" \
+ -H "Authorization: Bearer rb_YOUR_KEY"
+```
+
+## See also
+
+- [Generation API](/concepts/generation): the modalities and the shared model catalog
+- [Image generate](/jobs/image-generate): make an image from a text prompt
+- [Job output](/concepts/job#the-output): the output shape every job returns
+- [SDK](/sdk): `jobs.run`, uploads, and reading the output
+- [Webhooks](/guides/webhooks): receive `job.completed` instead of polling
diff --git a/jobs/image-generate.mdx b/jobs/image-generate.mdx
new file mode 100644
index 0000000..e6a4ab8
--- /dev/null
+++ b/jobs/image-generate.mdx
@@ -0,0 +1,311 @@
+---
+title: "Generate an image from a prompt"
+sidebarTitle: "Image generate"
+description: "Generate a webp image from a text prompt on hosted diffusion models. Pick an economy, standard, or premium tier, or pin an exact model id."
+icon: "image"
+keywords: ["image generation api", "text to image api", "flux api", "qwen image api", "ai image api", "diffusion api", "generate image from prompt", "webp image api", "text to image rest api"]
+canonical: "https://rendobar.com/docs/jobs/image-generate"
+---
+
+
+
+`image.generate` turns a text prompt into an image on a hosted diffusion model. Ask for a tier and the platform picks the model, or pin an exact model id to reach its own controls. The result is a webp file returned as a signed URL, the same async job shape as everything else on Rendobar.
+
+This is one product in [Rendobar's Generation API](/concepts/generation).
+
+## Generate an image
+
+
+
+```ts SDK
+import { createClient, outputUrl } from "@rendobar/sdk";
+
+const client = createClient({ apiKey: "rb_YOUR_KEY" });
+
+// jobs.run submits and waits for the finished job in one call.
+const job = await client.jobs.run({
+ type: "image.generate",
+ params: {
+ prompt:
+ "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
+ width: 1024,
+ height: 1024,
+ },
+});
+
+console.log(outputUrl(job)); // signed URL to the webp
+```
+
+```bash cURL
+curl -X POST "https://api.rendobar.com/jobs" \
+ -H "Authorization: Bearer rb_YOUR_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "type": "image.generate",
+ "params": {
+ "prompt": "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
+ "width": 1024,
+ "height": 1024
+ }
+ }'
+# Returns { "data": { "id": "job_...", "status": "waiting" } }.
+# Poll GET /jobs/{id} until "status": "complete".
+```
+
+```python Python
+import requests, time
+
+base = "https://api.rendobar.com"
+headers = {"Authorization": "Bearer rb_YOUR_KEY"}
+
+job = requests.post(
+ f"{base}/jobs",
+ headers=headers,
+ json={
+ "type": "image.generate",
+ "params": {
+ "prompt": "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
+ "width": 1024,
+ "height": 1024,
+ },
+ },
+).json()["data"]
+
+while job["status"] not in ("complete", "failed", "cancelled"):
+ time.sleep(1)
+ job = requests.get(f"{base}/jobs/{job['id']}", headers=headers).json()["data"]
+
+print(job["output"]["file"]["url"]) # the webp
+```
+
+
+
+No `inputs` are needed. The prompt is the whole request. Every generation job is async, so you submit, then poll, [wait](/sdk#wait), or receive a [webhook](/guides/webhooks).
+
+## The output
+
+A completed job carries a single webp file in `output.file`. `output.data` is `null`, because generation writes a file rather than computing an answer.
+
+```json
+{
+ "data": null,
+ "file": {
+ "url": "https://api.rendobar.com/dl/job_abc123?token=",
+ "path": "output.webp",
+ "type": "image",
+ "size": 512000,
+ "meta": { "format": "webp", "width": 1024, "height": 1024 }
+ },
+ "files": [
+ { "url": "https://api.rendobar.com/dl/job_abc123?token=", "path": "output.webp", "type": "image", "size": 512000, "meta": { "format": "webp", "width": 1024, "height": 1024 } }
+ ],
+ "expiresAt": 1735689600000
+}
+```
+
+The real dimensions are in `output.file.meta`. Each model snaps your requested `width` and `height` to a size it can render, so the returned image can differ from what you asked for. The full [output shape](/concepts/job#the-output) is the same for every job type.
+
+## Watch it render
+
+A generation job emits `job.preview` events while the model denoises, each carrying a small webp frame. Subscribe with the SDK to show the picture resolving instead of a spinner.
+
+```ts
+const sub = client.realtime.subscribeJob(created.id, {
+ onPreview: (e) => {
+ // e.data is base64 webp, roughly 256px on its long edge.
+ setPreview(`data:image/webp;base64,${e.data}`);
+ setBlur((1 - (e.progress ?? 0)) * 20); // ease the blur out as it sharpens
+ },
+ onComplete: (job) => sub.unsubscribe(),
+});
+```
+
+| Field | Meaning |
+|---|---|
+| `seq` | Monotonic per job. Drop any frame whose `seq` is at or below the last one you rendered. |
+| `progress` | Denoise progress from 0 to 1 at capture, or `null` when the model does not report it. |
+| `width` / `height` | Dimensions of the preview frame, not of the final image. |
+| `data` | Base64 webp, roughly 256px on its long edge. |
+
+Previews are decoration and never hold up a job. They are ephemeral: nothing is replayed on reconnect, and a late subscriber sees only the newest frame. A model with no fast decoder emits none, and a four-step model resolves too late to be worth watching. Build for zero frames and treat anything you get as a bonus.
+
+## Tiers and models
+
+Set `model` to a tier alias for a price and quality posture without naming a model, or pin an exact model id to reach that model's own controls. Omit `model` and you get the `economy` tier.
+
+
+
+```ts SDK
+// Tier alias: the platform picks the model.
+await client.jobs.run({
+ type: "image.generate",
+ params: { prompt: "A red bicycle leaning on a brick wall", model: "premium" },
+});
+
+// Pinned model id: unlocks that model's own controls.
+await client.jobs.run({
+ type: "image.generate",
+ params: {
+ prompt: "A red bicycle leaning on a brick wall",
+ model: "qwen-image-2512",
+ steps: 40,
+ guidance: 4.5,
+ negativePrompt: "blurry, low quality",
+ },
+});
+```
+
+```bash cURL
+curl -X POST "https://api.rendobar.com/jobs" \
+ -H "Authorization: Bearer rb_YOUR_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "type": "image.generate",
+ "params": { "prompt": "A red bicycle leaning on a brick wall", "model": "premium" }
+ }'
+```
+
+```python Python
+requests.post(
+ f"{base}/jobs",
+ headers=headers,
+ json={
+ "type": "image.generate",
+ "params": {"prompt": "A red bicycle leaning on a brick wall", "model": "premium"},
+ },
+)
+```
+
+
+
+The three tiers map to these models today. Aliases can be re-pointed as the catalog grows, so pin an exact id when you need a result to stay stable.
+
+| Tier | Model | Price | Controls when pinned | Best for |
+|---|---|---|---|---|
+| `economy` (default) | `flux-2-klein-4b` | `$` | prompt only | Drafts and fast iteration at the lowest cost |
+| `standard` | `z-image-turbo` | `$$` | steps (8 to 12) | Everyday images at turbo speed |
+| `premium` | `qwen-image-2512` | `$$$$` | steps, guidance, negative prompt | Highest fidelity with full control |
+
+You can also pin a model that no tier points at:
+
+| Model | Price | Controls | Best for |
+|---|---|---|---|
+| `flux-2-dev` | `$$$$` | steps (10 to 50), guidance | FLUX.2 quality with guidance control |
+
+
+`Price` is the model's `priceTier`, a relative comparison from `$` to `$$$$`. It is not a per-image charge. Jobs bill on the compute they actually use, cost-plus, so a slower model on a bigger GPU costs more per image. See [credits and billing](/concepts/credits).
+
+
+### Models under review
+
+A model can be pulled from tier resolution while we re-evaluate its cost and quality. It stays in [`GET /models`](#discover-models) with `underReview: true`, and pinning it returns `VALIDATION_ERROR` at submit before anything is billed.
+
+`ernie-image-turbo` and `qwen-image-2512-lightning` are under review today. Use a tier, or pin one of the models above.
+
+## Model-specific controls
+
+Tier aliases accept the base fields only. To use `steps`, `guidance`, or `negativePrompt`, pin an exact model id. Sending a control the resolved model does not support returns `VALIDATION_ERROR` before anything is billed.
+
+| Control | Models that accept it | Range |
+|---|---|---|
+| `steps` | `z-image-turbo`, `qwen-image-2512`, `flux-2-dev` | Per model, see table above |
+| `guidance` | `qwen-image-2512`, `flux-2-dev` | 1 to 10 |
+| `negativePrompt` | `qwen-image-2512` | Up to 1000 characters |
+
+`flux-2-klein-4b` fixes its own step count and exposes the base fields only.
+
+## Parameters
+
+
+ What you want, in plain language. No special syntax. Up to 4000 characters.
+
+
+
+ A tier alias (`economy`, `standard`, `premium`) or an exact model id. A tier lets the platform pick the model. A pinned id unlocks that model's own controls.
+
+
+
+ Requested output width in pixels, up to 4096. Snapped to what the model can render. The real size comes back in `output.file.meta`.
+
+
+
+ Requested output height in pixels, up to 4096. Snapped to what the model can render.
+
+
+
+ A fixed seed makes the result reproducible. Omit it for a fresh random image each time.
+
+
+
+ Rewrite the prompt for the model before generating. Off by default on every model in the catalog today. The default is per model (`enhanceDefault` in [`GET /models`](#discover-models)), so omit it to keep whatever the model ships with.
+
+
+
+ Denoise steps. More steps means more detail and more time. Requires a pinned model that exposes steps. The accepted range depends on the model.
+
+
+
+ How strictly to follow the prompt. Higher is stricter. Range 1 to 10. Requires `qwen-image-2512` or `flux-2-dev`.
+
+
+
+ What to keep out of the image. Up to 1000 characters. Requires `qwen-image-2512`.
+
+
+## Discover models
+
+`GET /models` lists every generation model with its tier, relative price, capabilities, and step range, so a model picker never has to hardcode the catalog. Filter to one job type with `?job=`.
+
+```bash
+curl "https://api.rendobar.com/models?job=image.generate" \
+ -H "Authorization: Bearer rb_YOUR_KEY"
+```
+
+```json
+{
+ "data": [
+ {
+ "id": "flux-2-klein-4b",
+ "jobs": ["image.generate", "image.edit"],
+ "tier": "economy",
+ "underReview": false,
+ "priceTier": "$",
+ "maxRefImages": 4,
+ "steps": null,
+ "supports": { "negativePrompt": false, "guidance": false },
+ "enhanceDefault": false,
+ "status": "active"
+ }
+ ]
+}
+```
+
+Read `tier` to group models, `priceTier` to sort them by cost, and `underReview` to grey out the ones that will not run. `steps` is `null` on a model that fixes its own step count.
+
+## Errors
+
+A bad request fails on `POST /jobs` before anything is billed. An unknown model id, or a control the resolved model does not support, returns `VALIDATION_ERROR`. After the job starts, failures carry the standard [error](/support/errors) shape.
+
+## See also
+
+- [Generation API](/concepts/generation): the modalities and the shared model catalog
+- [Image edit](/jobs/image-edit): change an existing image from an instruction
+- [Job output](/concepts/job#the-output): the output shape every job returns
+- [SDK](/sdk): `jobs.run`, `jobs.wait`, and reading the output
+- [Webhooks](/guides/webhooks): receive `job.completed` instead of polling