Skip to content

Create PDF Async — Queued PDF Generation with Webhooks

Queue a PDF for rendering and receive the result later — either by polling a job endpoint or via a webhook notification. Best for large documents, batches, or any flow where you don’t want to hold open an HTTP connection until rendering completes.

POST https://api.templatefox.com/v1/pdf/create-async

Authentication: API key in x-api-key header. Credits: 1 credit deducted when the job is queued (refunded if the job fails permanently).

For immediate generation, use create-pdf instead.

  1. POST /v1/pdf/create-async with your template_id and data — you immediately receive a job_id.
  2. The job moves through pending → processing → completed or failed.
  3. You get the result one of two ways (or both):
    • Polling — call GET /v1/pdf/jobs/{job_id} until status is completed or failed.
    • Webhook — pass a webhook_url in the request; we POST to it when the job resolves.

Both delivery mechanisms are supported simultaneously. A webhook is optional.

FieldTypeDescription
template_idstringThe 12-character template ID.
dataobjectKey-value pairs matching your template variables.
curl -X POST https://api.templatefox.com/v1/pdf/create-async \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "template_id": "HMQywVpZxqAM",
  "data": {
    "customer_name": "John Doe",
    "invoice_number": "INV-001"
  },
  "webhook_url": "https://example.com/webhooks/pdf"
}'

Or use one of our official SDKs — TypeScript, Python, Go, PHP, Ruby, Java, C#.

FieldTypeRequiredDefaultDescription
template_idstring✅—12-character template ID.
dataobject✅—Key-value data merged into the template.
export_typestring—"url"Currently "url" is the only supported value for async. Use create-pdf for binary responses.
expirationinteger—86400Signed URL expiration in seconds (60–604800).
filenamestring—"document"Custom filename (no extension).
store_s3boolean—falseUpload to your configured S3 bucket instead of our CDN.
s3_filepathstring——Path prefix inside your S3 bucket.
s3_bucketstring——Override the bucket name from your S3 integration.
webhook_urlstring——Public HTTPS URL to notify on completion or failure.
webhook_secretstring——Secret (16–256 chars) used to HMAC-SHA256 sign webhook payloads.
pdf_variantstring——"pdf/a-1b", "pdf/a-2b", or "pdf/a-3b" for archival compliance.
versionstring——Template version tag or number.

A fully configured async request — webhook with HMAC signing, S3 upload, custom filename, PDF/A compliance.

curl -X POST https://api.templatefox.com/v1/pdf/create-async \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "template_id": "HMQywVpZxqAM",
  "data": { "invoice_number": "INV-001" },
  "filename": "invoice-001",
  "store_s3": true,
  "s3_filepath": "invoices/2026/04/",
  "pdf_variant": "pdf/a-2b",
  "webhook_url": "https://example.com/webhooks/pdf",
  "webhook_secret": "whk_a1b2c3d4e5f6g7h8i9j0k1l2"
}'

When the job completes, the webhook payload will include result.s3_bucket and result.s3_key instead of result.url, and every request will carry an X-TemplateFox-Signature header — see Verifying the signature.

{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"credits_remaining": 99
}
FieldTypeDescription
job_idstringUUID to poll or correlate with webhook notifications.
statusstringAlways "pending" at creation time.
credits_remainingnumberCredits left after deduction.

Get the current status of a job.

curl https://api.templatefox.com/v1/pdf/jobs/550e8400-e29b-41d4-a716-446655440000 \
-H "x-api-key: YOUR_API_KEY"
StatusDescription
pendingQueued, waiting to be picked up by a worker.
processingA worker is rendering the PDF.
completedPDF is ready — check result_url or result_s3_bucket/result_s3_key.
failedRendering failed — check error_message and error_code. Credit is refunded.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"template_id": "HMQywVpZxqAM",
"export_type": "url",
"filename": "invoice-001",
"store_s3": false,
"created_at": "2026-04-24T12:00:00Z",
"updated_at": "2026-04-24T12:00:05Z",
"completed_at": "2026-04-24T12:00:05Z",
"result_url": "https://cdn.templatefox.com/generated/abc123/invoice-001.pdf",
"result_filename": "invoice-001.pdf",
"result_expires_at": "2026-04-25T12:00:05Z",
"result_s3_bucket": null,
"result_s3_key": null,
"error_message": null,
"error_code": null
}
  • Every 1–2 seconds for small documents.
  • Every 5–10 seconds for large documents or batches.
  • Use exponential backoff if you expect many jobs in flight.
  • When possible, prefer webhooks to avoid the polling overhead.

If you provide webhook_url, we POST to it once the job resolves — no polling needed.

HeaderValue
Content-Typeapplication/json
User-AgentTemplateFox-Webhook/1.0
X-TemplateFox-Eventpdf.completed or pdf.failed
X-TemplateFox-TimestampUnix epoch seconds
X-TemplateFox-Signaturesha256=<hex> — only when webhook_secret is set
{
"event": "pdf.completed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"result": {
"url": "https://cdn.templatefox.com/generated/abc123/invoice-001.pdf",
"filename": "invoice-001.pdf"
}
}

For S3 uploads, the result object contains s3_bucket and s3_key instead of url.

{
"event": "pdf.failed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": {
"message": "Template rendering failed: missing variable 'customer_name'",
"code": "RENDER_ERROR"
}
}

When a webhook_secret is set, we sign every webhook with HMAC-SHA256:

signature = HMAC_SHA256(secret, timestamp + "." + raw_body)

Compare it to the value of X-TemplateFox-Signature after the sha256= prefix. Using the raw request body (before JSON parsing) is important — any re-serialization will invalidate the signature.

import hmac
import hashlib

def verify_signature(secret: str, timestamp: str, raw_body: bytes, signature_header: str) -> bool:
  if not signature_header.startswith("sha256="):
      return False
  received = signature_header.removeprefix("sha256=")
  expected = hmac.new(
      secret.encode("utf-8"),
      f"{timestamp}.{raw_body.decode('utf-8')}".encode("utf-8"),
      hashlib.sha256,
  ).hexdigest()
  return hmac.compare_digest(received, expected)

We retry failed webhook deliveries up to 3 times with exponential backoff (1s, 5s, 30s):

  • Retried on network errors and 5xx responses.
  • Not retried on 4xx responses (your endpoint actively rejected the request).

Respond with a 2xx status code within a few seconds to acknowledge receipt.

List jobs belonging to your team. Supports pagination and status filtering.

Query parameterTypeDefaultDescription
limitinteger20Page size (max 100).
offsetinteger0Skip this many results.
statusstring—Filter by pending, processing, completed, or failed.
curl "https://api.templatefox.com/v1/pdf/jobs?status=failed&limit=50" \
-H "x-api-key: YOUR_API_KEY"
{
"jobs": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"template_id": "HMQywVpZxqAM",
"export_type": "url",
"created_at": "2026-04-24T12:00:00Z",
"error_message": "Template rendering failed",
"error_code": "RENDER_ERROR"
}
],
"total": 3,
"limit": 50,
"offset": 0
}
Use create-pdf (sync)Use create-pdf-async
Short documents, simple dataLarge documents or heavy rendering
Interactive request/response flowBatch or background jobs
You need binary bytes directlyYou want webhook callbacks
Low-volume, single-PDF requestsHigh-volume parallel generation