Make invoice PDFs from template data
You keep one invoice design and send new names, line items, and totals each time you bill a client. thirds.ai fills the template, renders the PDF, and returns a signed download link. This walkthrough uses the Client invoice design from the gallery, a real request, the real output, and the errors you are most likely to meet.
Start from the Client invoice template
Open Client invoice in the gallery and choose Copy to my account. Your copy starts at version 1, and your edits never change the gallery original. Open Templates in your account to find the template ID, which starts with tpl_.
The template reads these fields. Its schema requires every one of them, and it rejects extra fields.
| Field | Type | Example |
|---|---|---|
business_name | string, 1 to 80 characters | Northline Studio |
business_email | hello@northline.example | |
client_name | string, 1 to 80 characters | Fieldnote Labs |
client_email | accounts@fieldnote.example | |
invoice_number | string, 1 to 30 characters | NS-1042 |
issued_on | date as YYYY-MM-DD | 2026-09-03 |
items | 1 to 12 objects with description and amount | Product launch design, 2400 |
subtotal | number | 3250 |
tax | number | 650 |
total | number | 3900 |
payment_terms | string, 1 to 120 characters | Due within 14 days |
payment_note | string, 1 to 180 characters | Please use NS-1042 as the payment reference. |
The template shows issued_on with the date filter and each amount with the currency filter, so 2400 prints as $2,400.00. Compute the subtotal, tax, and total in your app and send the results. The template does not do arithmetic. For another currency, format the text in your app and change the template to print a plain string. Read Use templates with your data for the full field syntax.
Send the data with one request
Replace the template ID with your own and replace YOUR_API_KEY with your key in a private terminal. Give each invoice its own Idempotency-Key, such as the invoice number, so a retry returns the same job instead of a second PDF.
curl --fail-with-body --silent --show-error https://thirds.ai/v1/pdf \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: invoice-NS-1042' \
--data '{
"template_id": "tpl_00000000000000000000000000000000",
"version": 1,
"data": {
"business_name": "Northline Studio",
"business_email": "hello@northline.example",
"client_name": "Fieldnote Labs",
"client_email": "accounts@fieldnote.example",
"invoice_number": "NS-1042",
"issued_on": "2026-09-03",
"items": [
{ "description": "Product launch design", "amount": 2400 },
{ "description": "Campaign image set", "amount": 850 }
],
"subtotal": 3250,
"tax": 650,
"total": 3900,
"payment_terms": "Due within 14 days",
"payment_note": "Please use NS-1042 as the payment reference."
}
}'
Send version: 1 in a request that you may retry. If you leave the version out, thirds.ai selects the newest version when it prepares the job, so a later publish can change which design a retry uses. The page size comes from the template, which is A4.
Wait for the file and download it
The request waits briefly for the result. A 200 response means the job has finished, and a 202 response means it is still queued or running. Keep the returned id and poll GET /v1/pdf/{id} with a delay between checks until status is succeeded.
Then read download.url. It is a relative path, such as /v1/downloads/..., so add https://thirds.ai in front of it before you give it to a download client. The signed link lasts 15 minutes, and anyone with the link can download the file, so keep it private. The file itself stays available for 30 days. Save it to your own storage, and compare its size and SHA-256 with artifact.byte_size and artifact.sha256 before you mark the invoice as sent. Create your first PDF shows each command.
This is the output for the data above.
Fix the common failures
A failed render costs nothing, and a request that fails validation never creates a job. Read error.code on a request error, or error.category and error.code on a failed job, and act on the code. The message text can change.
| What you see | What it means and what to do |
|---|---|
400 template_data_invalid | The data fails the schema. The error lists up to 16 field paths, such as data/items/0/amount. Fix those fields. |
template_missing_data | The template reads a field that the data does not include. Send every field, including an empty list. |
402 insufficient_credits | Add credits or wait for the next monthly grant, then retry with the same key. |
409 idempotency_conflict | The same key was used with different input. Find the original job, or use a new key for a new invoice. |
429 rate_limited | Wait for Retry-After, then retry with the same key. Send fewer requests at once. |
Job category invalid_input | The template or an asset is broken. Fix it in the editor before you create another render. |
Job category renderer_failure | Internal recovery has ended. Start a new job with a retry limit, or contact support with the job and request IDs. |
Test the template with no items, one item, twelve items, and one long description before you connect it to live billing. Rows grow with their text, and the totals sit in a fixed place on the page. Retry requests and download files and Understand credits and billing cover every code.
Keep every invoice safe to retry
Save the job ID beside the invoice in your own records. When a connection drops, check that job before you send another request. The same account, operation, key, and input return the same job for 24 hours, so your app never pays for a duplicate. You can also register a webhook and let thirds.ai tell your server when the PDF is ready.
Read next
- The invoices page shows the full workflow from design to delivery.
- Use templates with your data explains schemas, versions, and filters.
- Create branded PDFs and images from words, HTML, or a picture introduces every way to start.
- Make static ad variants from one template uses the same data idea for images.
Sign in to copy the Client invoice template and send your first request with the free monthly credits.

