Articles on: API Reference

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


{
  "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":


{
  "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:


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


Failure:


{
  "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.

Updated on: 01/10/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!