PDF

Make event ticket PDFs from registration data

Map registration data to the Event ticket template and create a branded ticket PDF with a QR code through the thirds.ai API.

Theo Whitfield 6 min read

Map each sign-up from your event platform to a saved ticket design, then create a print-ready PDF with a tear-off stub and a QR code. This guide uses one sample registration record and the gallery's Event ticket design. Your event platform keeps control of sign-ups and payment. Each successful ticket PDF costs one credit, and a failed render costs nothing.

Start from the Event ticket template

The Event ticket template is a landscape DL ticket, 794 by 374 pixels, with a tear-off stub. The stub shows the ticket number, seat, price, and a QR code built from the ticket number.

A landscape DL ticket with a dark navy body, an orange diagonal banner Fernbank Print Rooms presents, the white title Makers Night: Print and Paper, and a cream tear-off stub with ticket number MN-016-0342, seat B14, and a QR code.
Event ticket templatePDF

The saved schema requires all 13 fields below and rejects any field it does not name:

FieldRuleSample value
organiser1 to 50 charactersFernbank Print Rooms
event_name1 to 60 charactersMakers Night: Print and Paper
tagline1 to 120 charactersAn evening of letterpress demos, live poster printing, and good coffee with the people who make things.
event_datedate, YYYY-MM-DD2026-10-16
doors_open1 to 20 characters6:30 pm
venue_name1 to 40 charactersThe Old Foundry
venue_address1 to 60 characters22 Anchor Street, Bristol
ticket_type1 to 18 charactersGeneral entry
holder_name1 to 60 charactersRosa Delgado
ticket_number1 to 14 charactersMN-016-0342
seat1 to 8 charactersB14
pricenumber, 0 to 10000018
stub_note1 to 120 charactersShow this QR code at the door. Keep this stub for the raffle at 9 pm.

ticket_number also feeds the QR code: the design places a QR code element bound to that field, so every ticket gets its own scannable code with no extra work from you. thirds.ai does not generate 1D barcodes, only QR codes.

Map a registration to the ticket fields

Most event platforms and ticketing tools send a webhook or an export row when someone registers or pays. This example uses Python 3 and curl on macOS or Linux. The script uses only the standard library. Set THIRDS_API_KEY through your secret store, and set THIRDS_TEMPLATE_ID and THIRDS_TEMPLATE_VERSION to your published copy of the Event ticket template.

Your platform's export or webhook must supply the shape below. Save it as registration.json in a private folder and set THIRDS_WORK to that folder.

JSON
{
  "organiser_name": "Fernbank Print Rooms",
  "title": "Makers Night: Print and Paper",
  "tagline": "An evening of letterpress demos, live poster printing, and good coffee with the people who make things.",
  "session_date": "2026-10-16",
  "doors_time": "6:30 pm",
  "location_name": "The Old Foundry",
  "location_address": "22 Anchor Street, Bristol",
  "tier": "General entry",
  "first_name": "Rosa",
  "last_name": "Delgado",
  "confirmation_code": "MN-016-0342",
  "seat_assignment": "B14",
  "amount_paid": 18,
  "check_in_note": "Show this QR code at the door. Keep this stub for the raffle at 9 pm."
}

Save this script as prepare-ticket.py. It checks each field against the template's own limits before it writes a request, so a bad export fails here instead of at the API.

Python
"""Map one registration record to the saved Event ticket template."""
import json
import sys
from datetime import date
from pathlib import Path

LIMITS = {
    "organiser": 50, "event_name": 60, "tagline": 120, "doors_open": 20,
    "venue_name": 40, "venue_address": 60, "ticket_type": 18,
    "holder_name": 60, "ticket_number": 14, "seat": 8, "stub_note": 120,
}

fixture_path, template_id, version, output_path = sys.argv[1:]
try:
    reg = json.loads(Path(fixture_path).read_text())
    holder_name = f"{reg['first_name']} {reg['last_name']}".strip()
    data = {
        "organiser": reg["organiser_name"],
        "event_name": reg["title"],
        "tagline": reg["tagline"],
        "event_date": date.fromisoformat(reg["session_date"]).isoformat(),
        "doors_open": reg["doors_time"],
        "venue_name": reg["location_name"],
        "venue_address": reg["location_address"],
        "ticket_type": reg["tier"],
        "holder_name": holder_name,
        "ticket_number": str(reg["confirmation_code"]),
        "seat": str(reg["seat_assignment"]),
        "price": float(reg["amount_paid"]),
        "stub_note": reg["check_in_note"],
    }
    for name, limit in LIMITS.items():
        if not 1 <= len(data[name]) <= limit:
            raise SystemExit(f"field_too_long: {name}")
    if not 0 <= data["price"] <= 100000:
        raise SystemExit("invalid_price")
    request = {"template_id": template_id, "version": int(version),
               "data": data, "wait": False}
    Path(output_path).write_text(json.dumps(request, indent=2, allow_nan=False) + "\n")
except (KeyError, TypeError, ValueError, OSError):
    raise SystemExit("invalid_input") from None

Make the file

The mapping command writes a local request. The first curl command creates one PDF and costs one credit on success. Use the confirmation code as the Idempotency-Key, so a repeated webhook delivery never makes a second ticket:

Shell
umask 077
python3 "$THIRDS_WORK/prepare-ticket.py" \
  "$THIRDS_WORK/registration.json" \
  "$THIRDS_TEMPLATE_ID" "$THIRDS_TEMPLATE_VERSION" \
  "$THIRDS_WORK/request.json"
curl --fail-with-body --silent --show-error https://thirds.ai/v1/pdf \
  -H "Authorization: Bearer $THIRDS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: ticket-MN-016-0342' \
  --data @"$THIRDS_WORK/request.json"

Save the id from the response with your registration record. Read the job until status is succeeded. Replace the sample ID with yours.

Shell
curl --fail-with-body --silent --show-error \
  https://thirds.ai/v1/pdf/pdf_00000000000000000000000000000000 \
  -H "Authorization: Bearer $THIRDS_API_KEY"

Copy download.url from that response and add https://thirds.ai before it. The download needs no API key.

Shell
curl --fail --silent --show-error \
  'https://thirds.ai/v1/downloads/YOUR_SIGNED_DOWNLOAD_TOKEN' \
  --output "$THIRDS_WORK/ticket.pdf"
shasum -a 256 "$THIRDS_WORK/ticket.pdf"

The hash must match artifact.sha256 in the job, and the file size must match artifact.byte_size.

Open ticket.pdf and check the name, seat, and QR code before you email or print it. Keep one folder and one key per confirmation code. If a webhook delivery retries, send the same request with the same key, and you get back the same ticket instead of a duplicate.

Resolve errors

Code or stateNext step
field_too_long or invalid_priceFix the source field named in the error before you retry.
invalid_inputCheck the registration record for a missing or malformed field.
template_data_invalidCheck the field names and limits against your published template version.
idempotency_conflictUse the original request with its key. A changed registration needs a new key.

For polling deadlines, download links, and file retention, read Retry requests and download files.

Customize the ticket layout

You can copy the Event ticket template and change its branding, colours, or wording:

  1. Open the Event ticket template and click Use this template to save a copy to your account.
  2. In the editor, change the colours, fonts, and copy to match your event.
  3. Choose a saved brand kit if you want the same colours and logo across tickets, badges, and receipts.
  4. Click Save. Each save creates a new template version, so you can pin the exact version your integration renders.

Once saved, send your copy's template_id and version with each request. Read Use templates with your data for the full field syntax and how versions work.

Credit usage and plans

Every thirds.ai plan gives you every tool, so you only choose how many PDFs and images you make each month. 1 credit makes 1 PDF or image, and a failed render costs nothing.

  • One ticket PDF costs 1 credit.
  • Registering many attendees at once? Upload a CSV of rows to a saved template with CSV batch renders instead of calling the API once per row.

You start free with 50 credits a month, and you don't need a card. For larger venues, pricing starts with Starter at 2,500 credits a month.

Make the file this guide describes

Start from a real template, put in your own words and colours, and click one button to get the finished file.