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

# Presets and signed URLs

> Named transformation presets and HMAC-signed URLs for gated content.

## Named presets

Define a transform bundle once, reference it by name everywhere. Convertly ships **built-in presets** (no setup required) and supports **workspace presets** you save via the API.

### Built-in presets

Available on both the CDN and Media API without creating a database record:

| Name      | Aliases          | Use case                      |
| --------- | ---------------- | ----------------------------- |
| `thumb`   | `avatar`         | Square avatars and thumbnails |
| `hero`    | `blog-hero`      | 16:9 editorial heroes         |
| `og-card` | `social-preview` | Open Graph / social cards     |
| `product` | `ecommerce`      | Product catalog tiles         |
| `email`   | —                | Email-safe width              |
| `gallery` | —                | Large square gallery tiles    |

```html theme={"system"}
<img src="https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}?preset=hero" />
<img src="https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}/p/og-card" />
```

Workspace presets with the same name override built-ins.

### Workspace presets

A workspace preset is a saved set of params (`w`, `h`, `q`, `fit`, `gravity`, `format`, plus the text-overlay options) stored only in the active workspace.

There are two equivalent ways to reference a preset:

```html theme={"system"}
<!-- Query form -->
<img src="https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}?preset=hero" />

<!-- Path form -->
<img src="https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}/p/hero" />
```

Use whichever reads better in your codebase. They resolve identically and share the same edge cache.

Individual URL params override the preset's values for one-off tweaks:

```html theme={"system"}
<!-- The hero preset is 1920x720, but for this one card we want 800 wide -->
<img src="https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}/p/hero?w=800" />
```

### Manage presets

| Method   | Endpoint                | Use                                                                                                               |
| -------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GET`    | `/api/cdn-presets`      | List every preset on the workspace.                                                                               |
| `POST`   | `/api/cdn-presets`      | Create a preset. Body: `{ "name": "hero", "params": { "w": 1920, "h": 720, "fit": "cover", "format": "auto" } }`. |
| `PATCH`  | `/api/cdn-presets/{id}` | Replace a preset's params (provide the full new shape).                                                           |
| `DELETE` | `/api/cdn-presets/{id}` | Delete a preset. URLs using `?preset=` for it return `404`.                                                       |

Names must be lowercase letters, digits, dashes, or underscores (32 chars max). You can also manage presets visually from **Settings → Image CDN → Transform presets**.

```bash theme={"system"}
curl -X POST "https://convertly.sh/api/cdn-presets" \
  -H "Authorization: Bearer $CONVERTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "hero",
    "params": {
      "w": 1920,
      "h": 720,
      "fit": "cover",
      "gravity": "auto",
      "format": "auto",
      "q": "auto"
    }
  }'
```

### When to use a preset vs inline params

Use a preset when:

* The same transform shows up in more than 2-3 places in your codebase.
* A design-system component (Hero, Card, Thumb) has a standard variant.
* You want to be able to retune quality / size without touching markup.

Inline params are fine for one-off transforms. Both work the same way at the cache level.

## Signed URLs

By default, CDN URLs are public — anyone with the URL can fetch the image at any size. That's fine for `<img src>` on a marketing page. For gated content (paid downloads, subscriber-only assets, signed-link emails), use signed URLs: a server-generated HMAC signature is appended as `?s=…`, and any tampering with the URL (changing width, format, swapping the file id or slug) invalidates it.

<Note>
  Signed URLs are tamper-proof by default and do **not** expire unless you add `exp` (Unix seconds). Pass `expiresAt` to `POST /api/delivery-keys/{id}/sign` to mint time-limited links. After expiry the CDN returns `403 Signed URL has expired.`
</Note>

### Generate a signed URL

```bash theme={"system"}
curl -X POST "https://convertly.sh/api/delivery-keys/{signingKeyId}/sign" \
  -H "Authorization: Bearer $CONVERTLY_DELIVERY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fileId": "7c3f2a91-8b64-4d5e-a2f7-91c06b48de35",
    "params": { "w": 1200, "q": 85, "format": "webp" },
    "expiresAt": 1735689600
  }'

# {
#   "url": "https://cdn.convertly.sh/assets-acme/{fileIdOrSlug}?w=1200&q=85&format=webp&s=...",
#   "signature": "..."
# }
```

* `{signingKeyId}` is the row id you got when you created the signing key (visible in **Image CDN → Delivery**).
* The `Authorization` header carries the CDN signing key token. Keep it server-side.

The returned URL is good only for the exact parameters that were signed. Tampering returns `403`. Browsers, CDNs, and `<img src>` treat it like any other URL — there's nothing special to do client-side.

### When to use signed URLs

* Paid downloads where you want to bind a URL to a specific transform you priced.
* Subscriber-only galleries where you don't want anyone to scrape and serve the same images elsewhere.
* Email campaigns with one-shot artwork that shouldn't be reused.

For general marketing pages and product catalogs, signed URLs add server-side work (you need to sign at render time) for no real protection — `<img src>` is public by nature. Use the unsigned form there.

### Programmatic signing

Most apps should use the `POST /api/delivery-keys/{id}/sign` endpoint above — it returns a ready-to-embed URL.

If you need to sign in application code, the algorithm matches what that endpoint uses: canonical query string (sorted keys, excluding `s`), HMAC over `{endpointNamespace}:{fileId}:{canonicalQuery}` with a per-signing-key secret. Include optional `exp` (Unix seconds) in the canonical string when minting time-limited URLs. Retrieve the secret from the dashboard when you create or rotate a signing key.
