---
url: /orchestration/recipes/convert-image.md
---

> Documentation index: https://developer.civitai.com/llms.txt — every page on this site has a markdown version at its URL + `.md`.

# Image conversion

`convertImage` is a utility step for format conversion, resizing, and blurring. It applies zero or more transforms in order, then encodes the result to the requested format. Cost is a flat **1 Buzz** ([Cost](#cost)).

## The request shape

```json
{
  "$type": "convertImage",
  "input": {
    "image":      "https://...",        // source — URL, data URL, or Base64
    "transforms": [ /* optional */ ],   // resize / blur — applied in order
    "output":     { "format": "jpeg" }, // required — format + per-format settings
    "public":     false                 // optional — restricted, see "Public output"
  }
}
```

`transforms` is optional (omit to change format or settings only). `output` is required. `public` is optional and restricted — see [Public output](#public-output).

## Examples

### Format conversion

Convert any image to JPEG:

```http
POST https://orchestration.civitai.com/v2/consumer/workflows?wait=60
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "steps": [{
    "$type": "convertImage",
    "input": {
      "image": "https://image.civitai.com/.../source.png",
      "output": { "format": "jpeg", "quality": 85 }
    }
  }]
}
```

### Resize then convert

Resize to a target width (aspect ratio preserved) and encode to WebP:

```json
{
  "$type": "convertImage",
  "input": {
    "image": "https://...",
    "transforms": [{ "type": "resize", "targetWidth": 512 }],
    "output": { "format": "webp", "quality": 85 }
  }
}
```

### Region blur

Blur one or more rectangular areas — useful for privacy masking:

```json
{
  "$type": "convertImage",
  "input": {
    "image": "https://...",
    "transforms": [{
      "type": "blur",
      "blur": 60,
      "mode": "include",
      "regions": [
        { "x1": 50, "y1": 50, "x2": 400, "y2": 400 }
      ]
    }],
    "output": { "format": "jpeg", "quality": 85 }
  }
}
```

`mode: "include"` blurs only inside the regions; the rest stays sharp. `mode: "exclude"` blurs everything *except* the regions — use it to protect a subject while blurring the background.

### Full-image blur

`mode: "exclude"` with an empty `regions` array blurs the entire image (nothing is excluded):

```json
{
  "type": "blur",
  "blur": 40,
  "mode": "exclude",
  "regions": []
}
```

### PNG with metadata stripped

```json
{
  "$type": "convertImage",
  "input": {
    "image": "https://...",
    "output": { "format": "png", "hideMetadata": true }
  }
}
```

## Transforms reference

Transforms run in array order. You can chain multiple transforms — for example, resize first and then blur.

### `resize`

| Field | Default | Notes |
|-------|---------|-------|
| `type` | — ✅ | Must be `"resize"`. |
| `targetWidth` | *(none)* | Target width in pixels, 1–4096. Height is calculated to preserve aspect ratio. |

### `blur`

| Field | Default | Notes |
|-------|---------|-------|
| `type` | — ✅ | Must be `"blur"`. |
| `blur` | — ✅ | Gaussian blur intensity, 1–100. |
| `mode` | — ✅ | `"include"` — blur only inside regions. `"exclude"` — blur everywhere except regions. |
| `regions` | `[]` | Pixel-coordinate rectangles `{ x1, y1, x2, y2 }`. With `mode: "exclude"` and no regions, the entire image is blurred. With `mode: "include"` and no regions, nothing is blurred. |

## Output formats reference

### `jpeg`

| Field | Default | Notes |
|-------|---------|-------|
| `format` | — ✅ | `"jpeg"` |
| `quality` | `85` | 1–100. Higher = better quality, larger file. |
| `hideMetadata` | `false` | Strip EXIF and other metadata. |

### `png`

| Field | Default | Notes |
|-------|---------|-------|
| `format` | — ✅ | `"png"` |
| `hideMetadata` | `false` | Strip metadata. |

PNG is lossless — no quality setting.

### `webp`

| Field | Default | Notes |
|-------|---------|-------|
| `format` | — ✅ | `"webp"` |
| `quality` | `85` | 1–100. Applies only when `lossless: false`. |
| `lossless` | `false` | Enable lossless WebP compression. |
| `maxFrames` | `null` | Cap frame count for animated sources. Set to `1` to extract only the first frame. |
| `hideMetadata` | `false` | Strip metadata. |

### `gif`

| Field | Default | Notes |
|-------|---------|-------|
| `format` | — ✅ | `"gif"` |
| `maxFrames` | `null` | Cap frame count. Set to `1` to extract the first frame. |
| `hideMetadata` | `false` | Strip metadata. |

::: tip JPEG and PNG with animated sources
JPEG and PNG are inherently single-frame. Animated source images (GIF, animated WebP) are automatically reduced to the first frame when encoding to these formats — there is no `maxFrames` field to set. Use WebP or GIF output to preserve animation.
:::

## Reading the result

```json
{
  "status": "succeeded",
  "steps": [{
    "name": "0",
    "$type": "convertImage",
    "status": "succeeded",
    "output": {
      "blob": {
        "id": "blob_...",
        "url": "https://.../signed.jpg",
        "width": 512,
        "height": 342
      }
    }
  }]
}
```

Blob URLs expire — see [Reading results](https://developer.civitai.com/orchestration/recipes.md#reading-results).

## Public output

`"public": true` stores the result in a bucket that serves it directly. The returned `url` is then unsigned and permanent for the blob's lifetime, and `urlExpiresAt`, if set, is when the object is scheduled for deletion:

```json
{
  "blob": {
    "id": "blob_...",
    "url": "https://.../blob_....jpg",
    "urlExpiresAt": null
  }
}
```

The point of this is caching. A signed URL carries a fresh signature every time it is issued, so the same image is a different URL — and a different CDN cache key — on every request. An unsigned URL is stable, so a CDN can serve it, and reads never reach the orchestrator at all.

::: warning Restricted, and genuinely public
This field requires an elevated token; ordinary consumer tokens get `403`. Anyone holding the URL can read the object, with no NSFW level, blocked reason, or mature-content restriction applied — and because the URL is served straight from storage, a block decided *after* the fact does not take it back. Only deleting the blob does.
:::

A public result does not reuse a cached private one, or vice versa. The two are separate objects, so asking for `"public": true` re-runs the conversion even when the same conversion already exists privately.

::: tip Result caching
`convertImage` is deterministic: the same source image, transforms, and output settings always produce the same blob. The orchestrator caches the result, so repeated identical calls skip re-processing and return the cached blob immediately.
:::

## Cost

Flat **1 Buzz** per step — regardless of source image size, number of transforms, or output format.

## Chaining with other steps

`convertImage` is most useful as a post-processing step. Chain it after `imageGen` using `$ref` to reference the previous step's output:

```json
{
  "steps": [
    {
      "name": "gen",
      "$type": "imageGen",
      "input": {
        "engine": "flux2",
        "model": "klein",
        "operation": "createImage",
        "modelVersion": "4b",
        "prompt": "A photorealistic cat sitting in a sunny garden"
      }
    },
    {
      "name": "convert",
      "$type": "convertImage",
      "input": {
        "image": { "$ref": "gen", "path": "output.images[0].url" },
        "transforms": [{ "type": "resize", "targetWidth": 1024 }],
        "output": { "format": "webp", "quality": 90 }
      }
    }
  ]
}
```

## Troubleshooting

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `400` with "output is required" | Missing `output` field | `output` is always required — include at minimum `{ "format": "jpeg" }`. |
| `400` with "targetWidth out of range" | Value outside 1–4096 | Clamp to 1–4096. |
| `400` with "blur out of range" | Value outside 1–100 | Clamp to 1–100. |
| `400` with "mode is required" | Blur transform sent without `mode` | `mode` is required on `blur` — set `"include"` or `"exclude"`. |
| `403` with "Publicly readable blob output is restricted to privileged callers" | `"public": true` on an ordinary token | Drop `public` (or omit it) — the default signed URL works for every caller. |
| Output height different from expected | `resize` maintains aspect ratio | Only `targetWidth` is specified; height is derived from the original aspect ratio. |
| Animated source collapsed to one frame | JPEG or PNG output requested | These formats are single-frame; use WebP or GIF output to preserve animation. |

## Related

* [`SubmitWorkflow`](https://developer.civitai.com/orchestration/reference/operations/SubmitWorkflow.md) · [`GetWorkflow`](https://developer.civitai.com/orchestration/reference/operations/GetWorkflow.md)
* [Image upscaling](https://developer.civitai.com/orchestration/recipes/image-upscaler.md) — chain upscaling before `convertImage` for high-res output in a target format
* [Image background removal](https://developer.civitai.com/orchestration/recipes/image-background-removal.md) — produce a transparent cutout, then chain `convertImage` to resize or re-encode it
* [Prompt enhancement](https://developer.civitai.com/orchestration/recipes/prompt-enhancement.md) — another 1-Buzz utility step
