> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.templated.io/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Async Rendering and Webhooks

By default, `POST https://api.templated.io/v1/render` is synchronous: the API waits until the file is ready and returns its URL. Most image and PDF renders take about 2 seconds.

With `"async": true`, the API answers immediately and the render continues in the background. Use async for videos, long multi-page renders, or when your platform has short request timeouts.

## Start an async render

```json
{
  "template": "TEMPLATE_ID",
  "format": "mp4",
  "duration": 10000,
  "async": true,
  "webhook_url": "https://your-server.com/templated-webhook"
}
```

`webhook_url` is optional for normal async renders. It is **required** when you combine `async` with `merge` or `zip` (the API returns 400 without it).

## The immediate response

The API returns the normal render object with `status: "PENDING"`:

```json
{
  "id": "ce424057-6b54-41bb-afec-adc35a2b9175",
  "url": "https://...",
  "width": 1920,
  "height": 1080,
  "format": "mp4",
  "status": "PENDING",
  "templateId": "TEMPLATE_ID",
  "templateName": "My Template",
  "createdAt": "..."
}
```

For multi-page templates you get a list of these objects, one per page.

**Do not use this `url` yet.** The file does not exist until the render finishes. Opening it too early can show an AccessDenied (403) page. The final location can also be different. Save the `id` and get the final URL from the webhook or from a status check.

## Option 1: Webhook

When the render finishes, Templated sends a POST with a JSON body to your `webhook_url`.

Success:

```json
{
  "success": true,
  "render_id": "ce424057-6b54-41bb-afec-adc35a2b9175",
  "status": "COMPLETED",
  "url": "https://...",
  "storage_url": null
}
```

Failure:

```json
{
  "success": false,
  "render_id": "ce424057-6b54-41bb-afec-adc35a2b9175",
  "status": "FAILED"
}
```

- `url` is the final file. Use this one.
- `storage_url` is the copy in your own bucket when Custom Storage is connected; otherwise `null`.
- With `merge` or `zip`, the webhook is sent once for the whole group and includes `render_ids`, the merged PDF or ZIP `url`, and for ZIP a `download_page_url`. If a page fails, `status` is `FAILED`. If the group takes more than 5 minutes, `status` is `TIMEOUT`.

### Webhook delivery rules

- Templated waits **3 seconds** for your endpoint to answer.
- There are **no retries**. If your server is down or slow, the notification is lost.
- Answer with 200 right away and do the heavy work afterwards.
- If you missed a webhook, check the render status with the GET request below.

Webhooks are also sent for synchronous renders when you include `webhook_url`.

## Option 2: Check the status (polling)

```
GET https://api.templated.io/v1/render/{renderId}
Authorization: Bearer YOUR_API_KEY
```

The `status` field is `PENDING`, `COMPLETED` or `FAILED`. When it is `COMPLETED`, use the `url` from this response. Wait a few seconds between checks.

Docs: https://templated.io/docs/renders/retrieve/

## Best practices

- Use async with a webhook for videos.
- Store the render `id` from the immediate response.
- Use the URL from the webhook or the GET response, not the one from the first response.
- Keep a polling fallback in case a webhook is missed.
- If a render fails, send it again. Failed renders are not retried automatically.

## FAQ

Q: Why does my render URL show AccessDenied?
A: You used `async: true` and opened the URL before the render finished. Wait for the webhook or poll `GET /v1/render/{id}` until `status` is `COMPLETED`.

Q: What does the async response contain?
A: The normal render object with `status: "PENDING"`. Save the `id`.

Q: What does the webhook send?
A: A POST with `success`, `render_id`, `status`, `url` and `storage_url`.

Q: Does Templated retry failed webhooks?
A: No. It waits 3 seconds and does not retry. Use polling as a backup.

Q: How do I check if a render is ready?
A: Call `GET https://api.templated.io/v1/render/{id}` and check `status`.

Q: Do I need a webhook for async renders?
A: Not for single renders; you can poll instead. It is required when you use async with `merge` or `zip`.