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

# Use Destinations

> Deliver generated images to your own storage (S3, Cloudflare R2, or Bunny Storage).

Use the `destination` option on `POST /image/generate` when you want each successful image uploaded to storage you control. By default, Mynth only stores images on the Mynth CDN — no destination is required.

Skip destinations when the Mynth CDN URL (`mynth_url`) is enough, or when you re-upload from that URL yourself.

Destinations are for **output** delivery. To send **input** images into generation, see [Upload Input Images](/guides/upload-input-images).

## Pricing

Destination delivery is included with image generation. Mynth does not charge an extra fee for uploading to your destination.

You still pay generation cost per successful image. Your storage provider may bill for storage and egress separately.

## Create a destination

Create destinations in the [destinations dashboard](https://mynth.io/dashboard/destinations). That is the primary path for most teams.

1. Open the destinations dashboard.
2. Create a destination with a slug (`name`), provider credentials, and path config.
3. Optionally run a test upload from the dashboard to verify credentials.

The slug is immutable after creation. It must match `/^[a-z0-9-]+$/` (1–64 characters): lowercase letters, digits, and dashes only.

Supported providers:

| Provider `id` | Storage                      | Secret fields                        | Notable options                                                                           |
| ------------- | ---------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- |
| `s3`          | Amazon S3 (or S3-compatible) | `access_key_id`, `secret_access_key` | `bucket`, `region`, optional `endpoint`, `force_path_style`                               |
| `r2`          | Cloudflare R2                | `access_key_id`, `secret_access_key` | `account_id`, `bucket`, optional `jurisdiction` (`default`, `eu`, `fedramp`)              |
| `bunny`       | Bunny Storage                | `password`                           | `storage_zone`, optional `region` (`de`, `uk`, `ny`, `la`, `sg`, `se`, `br`, `jh`, `syd`) |

Management API routes (`/destinations`) require OAuth login, not API keys. Use the dashboard, or create via REST after OAuth (see below).

## Pass a destination on generate

Reference the destination by its slug:

```ts theme={"theme":"kanagawa-dragon"}
const task = await mynth.image.generate({
  prompt: "Product photo of a ceramic mug on marble",
  model: "google/gemini-3.1-flash-image",
  destination: "my-bucket",
});

const image = task.getImages()[0];
console.log(image?.url); // destination public URL when url_template is set
console.log(image?.mynth_url); // always the Mynth CDN URL
console.log(image?.destination); // { status: "success", name: "my-bucket" }
```

Set a client-wide default with the SDK constructor or `MYNTH_DESTINATION`:

```ts theme={"theme":"kanagawa-dragon"}
const mynth = new Mynth({
  destination: "my-bucket", // or omit and set MYNTH_DESTINATION
});

// Per-request destination overrides the client default
await mynth.image.generate({
  prompt: "Product photo of a ceramic mug on marble",
  model: "google/gemini-3.1-flash-image",
  destination: "staging-bucket",
});
```

If the slug does not exist for your account, generate validation fails before the task is created.

## Read delivery fields on the result

When a destination is requested, each successful image includes delivery fields.

Success with `url_template` configured:

```json theme={"theme":"kanagawa-dragon"}
{
  "status": "success",
  "id": "img_...",
  "url": "https://cdn.my-domain.com/images/img_....webp",
  "mynth_url": "https://...",
  "cost": "0.03",
  "size": "1024x1024",
  "destination": {
    "status": "success",
    "name": "my-bucket"
  }
}
```

| Field         | Meaning                                                                                                    |
| ------------- | ---------------------------------------------------------------------------------------------------------- |
| `url`         | Public URL built from `url_template`, or `null` when no `url_template` is set or destination upload failed |
| `mynth_url`   | Always the Mynth CDN URL for a successful image                                                            |
| `destination` | Per-image delivery status for the requested destination                                                    |

Without `url_template`, a successful destination upload still stores the object, but `url` is `null`. Read `mynth_url` (or build the public URL yourself from the resolved path).

`task.urls` in the SDK only includes non-null `url` values. Prefer `task.getImages()` and read `url` / `mynth_url` / `destination` when you use destinations.

```ts theme={"theme":"kanagawa-dragon"}
for (const image of task.getImages()) {
  if (image.destination?.status === "success") {
    console.log(image.id, image.url ?? image.mynth_url);
  } else if (image.destination?.status === "failed") {
    console.error(image.id, image.destination.error.code, image.mynth_url);
  }
}
```

## Handle delivery failure

Destination upload runs after the image is generated. **Delivery failure does not fail generation.**

If destination upload fails:

* The image result still has `status: "success"`
* `mynth_url` still points at the Mynth CDN
* `url` is `null`
* `destination` is:

```json theme={"theme":"kanagawa-dragon"}
{
  "status": "failed",
  "name": "my-bucket",
  "error": {
    "code": "DESTINATION_UPLOAD_FAILED",
    "message": "Failed to upload to destination"
  }
}
```

Error codes:

| Code                        | When                                                                       |
| --------------------------- | -------------------------------------------------------------------------- |
| `DESTINATION_INIT_FAILED`   | Destination could not be loaded (missing config, secret, or provider init) |
| `DESTINATION_UPLOAD_FAILED` | Provider upload failed                                                     |
| `DESTINATION_FAILED`        | Other delivery error                                                       |

Optional error fields: `message`, `provider_response`.

Always check `destination.status` per image when you depend on your own storage.

## Configure path and URL templates

When you create or update a destination, set:

| Config field    | Required | Purpose                                                              |
| --------------- | -------- | -------------------------------------------------------------------- |
| `path_template` | Yes      | Object key/path in your bucket or storage zone (max 2048 characters) |
| `url_template`  | No       | Public URL for the result `url` field; must include `{path}`         |

Mynth resolves `path_template`, uploads the file to that path, then builds `url` from `url_template` by substituting `{path}`. Double slashes in the resulting URL (except after `https:`) are collapsed to a single slash.

### Path template tokens

| Token                 | Resolves to                                              |
| --------------------- | -------------------------------------------------------- |
| `{id}`                | Mynth image id (for example `img_...`)                   |
| `{YYYY}`              | Upload year (from the worker clock)                      |
| `{MM}`                | Month, zero-padded (`01`–`12`)                           |
| `{DD}`                | Day of month, zero-padded                                |
| `{uuid}` / `{uuidv4}` | Random UUID v4                                           |
| `{uuidv7}`            | UUID v7                                                  |
| `{ulid}`              | ULID                                                     |
| `{meta.key}`          | String value of `metadata.key` from the generate request |

Rules:

* The output format extension is always appended (`.webp`, `.png`, or `.jpg`). Do not put the extension in `path_template`.
* `{meta.key}` only substitutes **string** metadata values. Missing or non-string values become the literal text `undefined`.
* Metadata keys in templates may use letters, digits, and underscores only: `{meta.userId}`, not `{meta.user-id}`.

Example:

```text theme={"theme":"kanagawa-dragon"}
path_template: /images/{YYYY}/{MM}/{meta.campaign}/{id}
url_template:  https://cdn.my-domain.com/{path}
```

With `metadata: { "campaign": "spring" }`, `output.format: "webp"`, and image id `img_abc`, the object path becomes:

```text theme={"theme":"kanagawa-dragon"}
/images/2026/07/spring/img_abc.webp
```

and `url` becomes:

```text theme={"theme":"kanagawa-dragon"}
https://cdn.my-domain.com/images/2026/07/spring/img_abc.webp
```

Use [Use Metadata](/guides/use-metadata) when path templates need per-request string values via `{meta.key}`.

## REST example

Generate with a destination (API key auth):

```bash theme={"theme":"kanagawa-dragon"}
curl https://api.mynth.io/image/generate \
  -X POST \
  -H "Authorization: Bearer $MYNTH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Product photo of a ceramic mug on marble",
    "model": "google/gemini-3.1-flash-image",
    "destination": "my-bucket"
  }'
```

Creating a task does not return images immediately. Wait in the SDK, poll task endpoints, or use webhooks — see [Async and polling](/guides/async-and-polling). When the generate task completes, each successful image may include `destination` as shown above.

### Create a destination via API (OAuth)

Management routes require OAuth, not an API key:

```bash theme={"theme":"kanagawa-dragon"}
curl https://api.mynth.io/destinations \
  -X POST \
  -H "Authorization: Bearer <oauth-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-bucket",
    "provider": {
      "id": "s3",
      "bucket": "my-bucket",
      "region": "us-east-1"
    },
    "secret": {
      "access_key_id": "...",
      "secret_access_key": "..."
    },
    "config": {
      "path_template": "/images/{id}",
      "url_template": "https://cdn.my-domain.com/{path}"
    }
  }'
```

Other management endpoints (OAuth):

| Method   | Path                     | Purpose                                                                   |
| -------- | ------------------------ | ------------------------------------------------------------------------- |
| `GET`    | `/destinations`          | List destinations                                                         |
| `GET`    | `/destinations/:id`      | Get one by id (`dst_...`)                                                 |
| `PUT`    | `/destinations/:id`      | Update `provider`, `config`, and optionally `secret` (slug cannot change) |
| `DELETE` | `/destinations/:id`      | Delete                                                                    |
| `POST`   | `/destinations/:id/test` | Upload a test file; body `{ "path": "test/upload.txt" }`                  |

## How destinations fit generation

1. Mynth generates the image and post-processes it (`output.format` / `quality`).
2. Mynth uploads the image to the Mynth CDN and, if `destination` was set, to your storage in parallel.
3. The result sets `mynth_url` to the CDN URL.
4. If destination upload succeeded, `destination.status` is `"success"` and `url` is the `url_template` result (or `null` without `url_template`).
5. If destination upload failed, the image remains successful: `url` is `null`, `mynth_url` is still set, and `destination` reports the error.

Delivery runs once per successfully generated image in the same `image.generate` task. Completed generate webhooks include per-image `destination` when a destination was requested.

## Related guides

* [Generate Images](/guides/generate-images) — full generate request options
* [Use Metadata](/guides/use-metadata) — request metadata and `{meta.key}` path tokens
* [Upload Input Images](/guides/upload-input-images) — inputs for generation, not output storage
* [Use Webhooks](/guides/use-webhooks) — receive completed results with destination fields
