Skip to main content

When to use async

Sync (POST .../generate) returns the PDF in one response but ties up your HTTP connection for the whole render. Async (POST .../generate-async) returns a jobId immediately. Your server can do other work while the PDF is built. Use async for batches, large documents, or when you want a webhook instead of polling.

Flow

  1. Create POST /api/v1/templates/{id}/generate-async with JSON data.
  2. Wait poll GET /api/v1/jobs/{jobId} or receive a webhook.
  3. Download use downloadUrl from the job, or GET /api/v1/jobs/{jobId}/download.

Step 1: Create the job

curl -X POST https://pdfgorilla.io/api/v1/templates/TEMPLATE_ID/generate-async \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "customer": { "name": "Acme Corp" },
      "invoice":  { "number": "INV-2026-001", "total": "$650.00" }
    },
    "idempotencyKey": "invoice-INV-2026-001"
  }'
Response 202:
{
  "jobId": "cma1b2c3d4e5f6g7h8i9j0k",
  "status": "queued",
  "deduplicated": false
}

Idempotency

Set idempotencyKey to something unique per business object (invoice ID, order ID). If your client retries after a network error, you get the same job back with deduplicated: true instead of duplicate PDFs. In that case, status can be any existing job state (not always queued).

Step 2: Check status

Poll until status is no longer queued or processing:
curl https://pdfgorilla.io/api/v1/jobs/JOB_ID \
  -H "x-api-key: YOUR_API_KEY"
While running:
{
  "jobId": "cma1b2c3...",
  "status": "processing",
  "expiresAt": "2026-04-13T10:00:00.000Z",
  "downloadUrl": null,
  "error": null
}
When done:
{
  "jobId": "cma1b2c3...",
  "status": "completed",
  "expiresAt": "2026-04-13T10:00:00.000Z",
  "downloadUrl": "https://...",
  "error": null
}
On failure:
{
  "status": "failed",
  "error": { "message": "PDF rendering timed out." }
}

Suggested poll spacing

Time since createInterval
First 10 s2 s
10 s to 60 s5 s
After 60 s15 s

Step 3: Download

curl -L "SIGNED_URL_FROM_downloadUrl" --output doc.pdf
Or follow the redirect endpoint:
curl -L https://pdfgorilla.io/api/v1/jobs/JOB_ID/download \
  -H "x-api-key: YOUR_API_KEY" \
  --output doc.pdf
downloadUrl expires after a few minutes. Call GET .../jobs/{jobId} again for a fresh link. Finished files are only kept for a limited time (see Limits).

Full Node example

async function generateAsync(templateId, data, apiKey) {
  const create = await fetch(
    `https://pdfgorilla.io/api/v1/templates/${templateId}/generate-async`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": apiKey,
      },
      body: JSON.stringify({ data }),
    }
  );
  if (!create.ok) throw new Error((await create.json()).error);
  const { jobId } = await create.json();

  const deadline = Date.now() + 120_000;
  while (Date.now() < deadline) {
    await new Promise((r) => setTimeout(r, 2000));
    const st = await fetch(`https://pdfgorilla.io/api/v1/jobs/${jobId}`, {
      headers: { "x-api-key": apiKey },
    });
    const job = await st.json();
    if (job.status === "completed") return job.downloadUrl;
    if (job.status === "failed")
      throw new Error(job.error?.message ?? "failed");
    if (job.status === "canceled") throw new Error("canceled");
  }
  throw new Error("timeout");
}

Webhooks

For production, add webhookUrl and webhookSecret so you are notified when the job finishes. See Webhooks.

Cancel

curl -X POST https://pdfgorilla.io/api/v1/jobs/JOB_ID/cancel \
  -H "x-api-key: YOUR_API_KEY"
API reference: Get job status, Download job PDF, Cancel job.