> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Images

> Generate images over REST — one call, and it waits; every rendered image comes back as a file — and search a licensed stock photo library, free.

`POST /v1/media/images` renders synchronously: the reply carries the finished [files](https://vetta.sh/docs/api/files.md) and what the render cost. There is no job to poll — an image is done before the request returns, so it is never a [`media_job`](https://vetta.sh/docs/api/video#the-media-job-object). The same render is what an agent's [`generate_image`](#in-a-session) tool does; the [catalogue](https://vetta.sh/docs/api/media.md) is shared. Images are not [model calls](https://vetta.sh/docs/concepts/model-router.md): the image catalogue is its own, and an image model is never an agent's `model`.

## The image generation object

**object** `string`

Always `image_generation`.

**model** `string`

The model that rendered, which is the one you named or the default it picked.

**files** `File[]`

One [file](https://vetta.sh/docs/api/files.md#the-file-object) per rendered image, `n` of them when the model produced that many. Each is `published` and carries `source.type = image_generation`.

**cost\_micro\_usd** `integer`

What was debited for the render, in integer micro-USD, on the media tier. See [Pricing](https://vetta.sh/docs/platform/pricing.md#media-generation--per-finished-job-media-component).

## Generate images

`POST /v1/media/images` → `200 OK` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) **required**.

**prompt** `string` *required*

What to render.

**model** `string`

A model id from [`GET /v1/media/models?kind=image`](https://vetta.sh/docs/api/media#search-models). Omitted, the platform's default image model is used — the first the catalogue lists that takes what the request carries: with `image_urls`, one whose catalogue entry takes an image input; with `character`, one that pins a character. If that default is unavailable (`403`/`404`, `429` or a `5xx`), the next one is tried, and the reply's `model` says which rendered; a model you name never falls back. A named model whose entry says it takes none of what you sent is `validation_failed` before anything is billed; an id the catalogue does not know is `validation_failed`.

**image\_urls** `string[]`

Up to 4 reference images for an edit (the platform's own edit model takes 3) or an image-to-image render, each a stored image's `fil_` id — an upload, or an earlier render's file — or a public `http(s)` URL. A `fil_` this organization does not hold is `not_found`; one that is not an image, a private or loopback host, and a `data:` or `file:` URL are `validation_failed`.

**character** `string`

Render as a trained [character](https://vetta.sh/docs/api/characters.md) — its `chr_` id or name — so the same face carries across every image. It must be `ready`; one still `training` or `failed` is `validation_failed`. With `image_urls`, every one must be a still of that character (a `fil_` rendered with it): the image is then an edit of those stills that keeps their room, props and outfit and changes only the pose and framing, and it counts as a still of the character, so it can open a [character video](https://vetta.sh/docs/api/characters.md). Such an edit runs on the platform's set-edit models; naming any other `model` is `validation_failed`. Each edit is checked against the still it came from: if it shows someone else, it is rendered once more, and if the second one is someone else too it is filed (and billed) as a plain image, not as a still of the character.

**style** `string`

Draw the image in a named [look](#looks), by name or slug, such as `Phone snap` or `quiet-luxury`. An unknown look is `validation_failed`, and the message lists them.

**character\_strength** `number`

With `character` only: 0.5 to 1, how strictly the face is held. Leave it out unless a face drifts in a close-up; `0.85` is a good start. Without `character`, or outside 0.5–1, it is `validation_failed`.

**n** `integer`

How many images, 1–4. Default 1. Every one that renders is filed; when some of them fail, the rest are still filed and billed, and only all of them failing is `provider_error`.

**aspect\_ratio** `string`

Such as `16:9` or `1:1`. On a model that publishes the ratios it takes, one it does not is clamped to the nearest it does; a model that takes none ignores it.

**seed** `integer`

Repeat an earlier result, on models that honour a seed.

That is the whole request. Each output is filed in the format the model returned it — PNG unless the model says otherwise — and `content_type` on the file says which. Not on the request today: an explicit `size` or `resolution`, `quality`, `output_format`, a transparent `background`, more than 4 references or outputs, and any per-model or per-provider passthrough. A body that carries one of them is `validation_failed`, not quietly dropped; ask for the field if you need it.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/images \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{ "prompt": "a paper kite over a grey harbour", "n": 2, "aspect_ratio": "16:9" }'
```

```ts SDK theme={"system"}
const out = await client.media.images.generate({
  prompt: "a paper kite over a grey harbour",
  n: 2,
  aspect_ratio: "16:9",
});
for (const file of out.files) console.log(file.id, file.content_type);
```

```bash CLI theme={"system"}
vetta media generate --kind image --prompt "a paper kite over a grey harbour" --n 2 --aspect-ratio 16:9
```

As a trained character — no `image_urls`, because the character is the reference:

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/images \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{ "prompt": "Ava waves at the camera on a rooftop at golden hour", "character": "Ava", "aspect_ratio": "9:16" }'
```

Each rendered file records `source.character_id` — the `chr_` it rendered as.

```json Response theme={"system"}
{
  "object": "image_generation",
  "model": "example/thrifty-image",
  "files": [
    {
      "id": "fil_01J...",
      "object": "file",
      "name": "image-fil_01J....png",
      "content_type": "image/png",
      "size_bytes": 184201,
      "scope": "published",
      "session_id": null,
      "sha256": "…",
      "created_at": "2026-09-06T12:00:00.000Z"
    }
  ],
  "cost_micro_usd": 38723
}
```

Read the bytes with [`GET /v1/files/{id}?download=true`](https://vetta.sh/docs/api/files#retrieve-a-file).

## Looks

A look sets the scene, light and wardrobe of a render; with `character`, the face stays theirs. The looks are: Candid selfie, Phone snap, Realistic, Everyday, Library, Quiet luxury, Through the glass, Flight mode, Selfcare, Japandi, Foggy morning, Rainy day, 90s editorial, Wide-angle outfit, Elevator mirror, Escalator and Tokyo street style. A character's usual looks are its [`style_pack`](https://vetta.sh/docs/api/characters#the-character-object).

## Billing

A render is billed **what it actually cost**, once it has happened, on the media tier; nothing is quoted or held beforehand, because a picture costs cents. A request on an empty balance is refused with `insufficient_credits` (402) before it reaches a model; a low balance is not turned away. The debit settles under the request's idempotency key, so a replayed request returns the same files and charges once — and the same key reaches the model's provider, so a retried render is not rendered twice. A render the model refuses or fails is `provider_error` and is not billed.

## In a session

An agent renders with the [`generate_image`](https://vetta.sh/docs/capabilities/tools#generation-tools) tool — the same route, the same catalogue, the same fields (`prompt`, `model?`, `image_urls?`, `character?`, `style?`, `character_strength?`, `n?`, `aspect_ratio?`, `seed?`) — and gets the `file_id`s back in the same turn, because the render is synchronous. Two differences from calling the route yourself:

* **The model default is yours to set.** `tools.configs.generate_image.config.models` on the agent names the model that runs when the agent does not choose one; with none pinned, the platform's default is used, as here. It is a default, not an allow-list — the agent may still name any id the catalogue publishes.
* **The bill lands on the agent.** The debit carries the `session_id` and settles against the agent's [budget](https://vetta.sh/docs/concepts/budgets.md) as the `media` component; a render through this route carries no session and settles against the organization.

## Errors

| Code | When |
| - | - |
| `validation_failed` | Missing `Idempotency-Key`, an unknown `model`, an `image_urls` entry that is a private host or a stored file that is not an image, a `character` that is not `ready`, a `character` with `image_urls` that are not all stills of it, a set edit that names a `model` outside the set-edit models, an unknown `style`, a `character_strength` without a `character` or outside 0.5–1, `n` outside 1–4, a field the request does not carry. |
| `not_found` | A `character`, or an `image_urls` `fil_`, this organization does not have. |
| `insufficient_credits` | The prepaid balance is empty. Nothing is rendered or billed. A retry of a request already billed under its `Idempotency-Key` is not refused. |
| `provider_error` | The model failed or refused; nothing is filed and nothing is billed. A refusal carries the model's reason after the status when it can be repeated — e.g. `the media provider answered 400: …` — so the request can be corrected. |
| `feature_not_configured` | This deploy has no generation provider configured, or `character` was sent to one that trains no characters. |

## Stock photos

`GET /v1/media/stock` — scope `agents:read`

Searches a stock photo library and returns up to `count` photos, each with a direct image URL at the size you asked for, a small preview, and the photographer credit the licence asks you to show. Free: a search is booked to the [ledger](https://vetta.sh/docs/platform/billing.md) as a `search` line at `$0`, so it shows up in a spend breakdown as a call that happened, not as one that was never priced.

**query** `string` *required*

What the photo should show. 1–200 characters.

**count** `number` (default: 10)

How many photos to return, 1–30. There is no cursor: `count` is the whole page.

**orientation** `string`

`landscape`, `portrait` or `square`.

**color** `string`

A dominant colour — a name such as `red`, `blue`, `brown`, `black`, `white`, or a hex value such as `#978E82`.

**size** `string`

The image size `url` points at: `small`, `medium` or `large`. Default `large`. Picks the rendition only — it does not narrow the results.

```bash cURL theme={"system"}
curl -fsSL "https://api.vetta.sh/v1/media/stock?query=office+workspace&count=3&orientation=landscape" \
  -H "authorization: Bearer sk_live_..."
```

```ts SDK theme={"system"}
const { data } = await client.media.stock.search({ query: "office workspace", count: 3, orientation: "landscape" });
```

```bash CLI theme={"system"}
vetta media stock "office workspace" --count 3 --orientation landscape
```

```json Response theme={"system"}
{
  "data": [
    {
      "object": "stock_photo",
      "id": "2014422",
      "width": 3024,
      "height": 2016,
      "url": "https://images.example/2014422.jpeg?w=940",
      "preview_url": "https://images.example/2014422.jpeg?w=280",
      "alt": "Brown rocks during golden hour",
      "photographer": "Joey Farina",
      "attribution_url": "https://photos.example/photo/2014422/",
      "dominant_color": "#978E82"
    }
  ]
}
```

### The stock photo object

**object** `string`

Always `stock_photo`.

**id** `string`

The library's own id for the photo. Opaque and unprefixed — it is not a Naive Managed Agents resource id and cannot be fetched on its own; keep the photo by importing its `url` as a file.

**width** `number`

Pixel width of the original.

**height** `number`

Pixel height of the original. `width > height` is a landscape photo.

**url** `string`

A direct image URL at the requested `size`. Fetchable without a key.

**preview\_url** `string`

A small thumbnail, for picking before downloading.

**alt** `string | null`

A one-line description of the photo, when the library has one.

**photographer** `string | null`

Who took it. The licence asks that this credit be shown wherever the photo is used.

**attribution\_url** `string | null`

The page to link the credit to. This is the one field that may point at the library's own domain.

**dominant\_color** `string | null`

The photo's average colour as a hex value.

### Keeping a photo

A result is a URL, not a file. To hold the bytes as a `fil_` — so a session can attach it, or a page can serve it from your own storage — import `url` through [Files](https://vetta.sh/docs/api/files.md); the imported file records where it came from as a URL import.

### In a session

Agents reach the same search as the [`find_stock_photo`](https://vetta.sh/docs/capabilities/tools) tool, which takes `query`, `count` and `orientation`. It is on by default with permission `allow`.

### Errors

| Status | Code | When |
| - | - | - |
| `400` | `validation_failed` | `query` missing, `count` outside 1–30, or a filter value outside its set. |
| `429` | `rate_limited` | The library's request allowance for this deploy is spent; retry shortly. |
| `501` | `feature_not_configured` | This deploy has no stock photo library configured. |
| `502` | `provider_error` | The library answered with an error. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.