Praticable Ticket Protocol (PTP)
Open protocol voor het verifiëren van tickets uitgegeven door onafhankelijke instanties. Elke clientinstallatie fungeert als een autonome issuer; elke scanner die dit protocol implementeert, kan tickets van elke issuer verifiëren, zonder voorafgaande bilaterale integratie.
Deze pagina beschrijft het protocol zelf. Voor endpoints, parameters, response codes en voorbeelden van requests, zie de API-referentie →
Overzicht
Dit protocol definieert hoe een scanner een ticket valideert dat is uitgegeven door een onafhankelijke issuer. Issuers coördineren niet met elkaar en vereisen geen centrale autoriteit.
Het protocol is ontworpen met vier doelstellingen:
- Offline verificatie van de authenticiteit van tickets via cryptografische signatures
- Online check-in status met sterke consistentie om dubbel scannen te voorkomen
- Onafhankelijkheid van issuers: issuers coördineren niet met elkaar
- Key rotation zonder reeds uitgegeven tickets ongeldig te maken
Alle payloads en endpoints bevatten een versie-identificatie. Ingrijpende wijzigingen vereisen een nieuwe hoofdversie. Issuers en scanners kunnen meerdere versies gelijktijdig ondersteunen tijdens overgangen.
Terminologie
Instantie die tickets aanmaakt en beheert. Eén issuer per klant.
Clientapplicatie die tickets valideert bij de ingang van het evenement.
Uniek, ondertekend legitimatiebewijs dat toegang verleent tot een specifiek evenement.
De handeling waarbij een ticket bij de ingang als gebruikt wordt gemarkeerd.
Ondertekende gegevens die zijn gecodeerd in de QR-code van het ticket.
Cryptografie
Het signature-algoritme is Ed25519 (RFC 8032). De signatures van 64 bytes zijn compact genoeg om in een QR-code te passen naast de payload-gegevens, en verificatie is snel dankzij native ondersteuning in de Web Crypto API.
Levenscyclus van sleutels
Elke issuer genereert een sleutelpaar bij de inrichting. De private key wordt opgeslagen in de Worker secrets en verlaat nooit de issuer. De public key wordt gepubliceerd op het well-known endpoint. Meerdere actieve public keys kunnen naast elkaar bestaan tijdens rotatieperioden.
Sleutelidentificatie (kid)
Elke public key heeft een korte, unieke identificatie: de eerste 8 hexadecimale tekens
van de SHA-256-hash van de ruwe public key. Payloads verwijzen naar de
kid zodat scanners weten welke sleutel zij voor verificatie moeten gebruiken.
QR-payload-formaat
De QR-code bevat een compacte JSON-payload gecodeerd in base64url, gevolgd door de signature.
Het voorvoegsel tkt1. identificeert de protocolversie en onderscheidt ticket-QR-codes
van andere codes die een scanner kan tegenkomen.
tkt1.<base64url(payload_json)>.<base64url(signature)>
De totale QR-inhoud moet onder de ~400 tekens blijven om scanbaar te blijven
op camera's van lage kwaliteit bij weinig licht. Vermijd het onnodig vullen
van de velden ev en hn.
JSON-structuur van de payload
{
"v": 1,
"iss": "https://tickets.example.com",
"kid": "a1b2c3d4",
"tid": "01HXXX...ULID",
"eid": "evt_01HXXX...ULID",
"ev": {
"name": "Gala Concert",
"date": "2026-06-15T20:00:00Z",
"venue": "Royal Hall"
},
"tt": "VIP",
"hn": "J. Smith",
"iat": 1745000000,
"exp": 1765000000
} Hoofdversie van het protocol (momenteel 1).
Basis-URL van de issuer. Canonieke vorm: HTTPS, geen afsluitende slash. Wordt gebruikt voor discovery.
bijv. https://tickets.example.comIdentificatie van de ondertekeningssleutel (eerste 8 hexadecimale tekens van de SHA-256-hash van de public key).
Ticketidentificatie (ULID aanbevolen, uniek binnen de issuer).
Evenementidentificatie (ULID aanbevolen, uniek binnen de issuer).
Metadata van het evenement voor offline weergave: name (string), date (ISO 8601 UTC), venue (string). Uitsluitend informatief.
Tickettype, voor weergavedoeleinden (bijv. "VIP", "Standard").
Weergavenaam van de houder. Houd deze kort; geen persoonsgegevens behalve de weergavenaam.
Unix-tijdstempel van uitgifte (seconden).
Unix-verlooptijdstempel (seconden). Doorgaans ingesteld op een redelijke termijn na afloop van het evenement.
Signature
De signature wordt berekend over de exacte bytevolgorde van de base64url-gecodeerde payload
(vóór het tweede .-scheidingsteken), met de Ed25519 private key van de issuer.
Scanners verifiëren door de signature opnieuw te berekenen over dezelfde bytes.
Issuer discovery
Elke issuer MOET een discovery-document publiceren op het volgende adres:
{
"protocol_version": 1,
"issuer": "https://tickets.example.com",
"name": "Client business name",
"keys": [
{
"kid": "a1b2c3d4",
"alg": "Ed25519",
"public_key": "base64url-encoded-raw-32-byte-public-key",
"valid_from": "2026-01-01T00:00:00Z",
"valid_until": null
}
],
"endpoints": {
"check_in": "https://tickets.example.com/api/v1/tickets/check-in",
"status": "https://tickets.example.com/api/v1/tickets/{tid}/status",
"scanner_enroll": "https://tickets.example.com/api/v1/scanners/enroll"
},
"supported_versions": [1]
} keysKAN meerdere items bevatten tijdens rotatie. Scanners moeten elke niet-verlopen sleutel accepteren waarvan dekidovereenkomt met de payload.valid_until: nullbetekent momenteel actief zonder geplande vervaldatum.- Scanners MOETEN dit document cachen met een TTL van maximaal 1 uur, en MOETEN het opnieuw ophalen als verificatie mislukt met een onbekende
kid.
Scanner setup QR
Scanners worden geregistreerd voor een evenement door het scannen van een tijdgebonden setup QR-code die is gegenereerd via de beheerdersinterface van de organisator. De setup QR bevat een enrollment token die de scanner bij eerste gebruik inwisselt voor een langlevende API key, beperkt tot een of meer evenementen.
De enrollment-stroom gebruikt een token-naar-API-key-uitwisseling in plaats van een API key rechtstreeks in de QR in te sluiten. Hierdoor is de setup QR eenmalig te gebruiken en tijdgebonden; een gefotografeerde of gelekte QR wordt onbruikbaar zodra deze is verbruikt.
tsetup1.<base64url(setup_json)>
Het voorvoegsel tsetup1. onderscheidt setup QR-codes van ticket QR-codes (tkt1.).
{
"v": 1,
"iss": "https://tickets.example.com",
"enroll_url": "https://tickets.example.com/api/v1/scanners/enroll",
"token": "st_01HXXX...opaque-enrollment-token",
"eid": "evt_01HXXX...ULID",
"event_name": "Gala Concert",
"gate_id": "north",
"expires_at": "2026-06-16T02:00:00Z"
} Hoofdversie van het protocol (momenteel 1).
Basis-URL van de issuer. De scanner MOET het discovery-document ophalen van {iss}/.well-known/ticket-issuer.json.
Exact URL voor de enrollment-uitwisseling. MOET overeenkomen met scanner_enroll in het discovery-document.
Ondoorzichtige, onvoorspelbare enrollment token (≥ 128 bits entropie). Eenmalig gebruik.
Evenement-ID waarvoor deze setup QR de scanner registreert.
Naam die aan de operator wordt getoond ter bevestiging. De officiële naam komt na enrollment van de issuer.
Gate-identificatie. Indien aanwezig, is de API key beperkt tot deze gate.
ISO 8601 UTC-tijdstempel waarna de token ongeldig is.
Mogelijkheden van de scanner
Een scanner KAN actieve enrollments voor meerdere evenementen tegelijkertijd bevatten. Dit ondersteunt locaties met meerdere fases, opeenvolgende evenementen en festivals. Elke enrollment voegt een evenement toe aan de actieve set van de scanner zonder eerdere enrollments te vervangen.
Levenscyclus van API keys
- Scope: elke API key is gekoppeld aan precies een evenement (en optioneel een gate). Een scanner die voor meerdere evenementen is geregistreerd, beschikt over meerdere API keys.
- Vervaldatum: API keys verlopen op het
expires_at-tijdstempel dat in de enrollment-response wordt teruggegeven (doorgaans einde evenement + marge). Verlopen keys worden afgewezen metUNAUTHORIZED. - Intrekking: organisatoren kunnen een key op elk moment intrekken via de beheerdersinterface.
- Geen vernieuwing: als een API key tijdens een evenement verloopt, moet de scanner opnieuw worden geregistreerd via een nieuwe setup QR.
- Rotatie: om een gecompromitteerde key te vervangen, trekt de organisator de oude key in en geeft een nieuwe setup QR uit.
Verificatiestroom
Wanneer een ticket wordt gescand, MOET de scanner het bijbehorende evenement
automatisch detecteren door het veld eid uit de geverifieerde payload te lezen.
De operator selecteert geen evenement handmatig. Hier is de verwerkingsvolgorde:
Ontleed de QR-payload en verifieer de signature met de gecachte public keys van de issuer in iss. Als de signature niet klopt: afwijzen met invalid_signature.
Valideer de tijdsgrenzen iat/exp. Indien verlopen: afwijzen met expired.
Zoek actieve enrollments op aan de hand van de combinatie (iss, eid) uit de payload.
Als er een overeenkomende enrollment bestaat: selecteer de API key, controleer de gate-compatibiliteit en roep vervolgens het check-in endpoint aan.
Als geen enrollment overeenkomt: lokaal afwijzen met not_enrolled. Toon de evenementnaam uit ev.name.
Als de iss volledig onbekend is voor de scanner: afwijzen met unknown_issuer.
Consistentie en dubbel scannen
Het voorkomen van dubbel scannen wordt afgedwongen door de issuer via een Durable Object dat per evenement is afgebakend. Alle check-in-schrijfbewerkingen voor een bepaald evenement worden geserialiseerd via dit DO, waardoor precies een check-in slaagt, zelfs bij gelijktijdige requests van meerdere gates.
Scanners MOETEN een lokale cache bijhouden van recent gescande tid-waarden
(laatste 24 uur) en duplicaten aan de clientzijde afwijzen voordat het netwerk wordt
aangesproken. Dit vermindert de belasting en maakt directe afwijzing van duidelijke
duplicaten mogelijk, zelfs offline.
Offline modus
Scanners MOETEN kunnen functioneren met wisselende connectiviteit. Hier is de verwerkingsvolgorde voor elke scan:
Verifieer de signature lokaal met de gecachte public keys van de issuer in iss.
Valideer de tijdsgrenzen iat/exp.
Zoek de overeenkomende enrollment voor (iss, eid) op in de lokale opslag. Indien niet gevonden: lokaal afwijzen.
Controleer de lokale cache op eerdere scans van deze tid. Indien gevonden: afwijzen als duplicaat.
Doe de check-in API-aanroep. Bij succes: accepteren en lokaal registreren. Bij netwerkstoring: in de wachtrij plaatsen met de idempotency_key, optimistisch accepteren en op de achtergrond opnieuw proberen.
Wanneer de verbinding terugkeert, de wachtrij verwerken. De idempotentie van de issuer garandeert dat er geen inconsistentie ontstaat.
Issuers MOETEN check-in requests accepteren met scanned_at-tijdstempels
in het verleden (tot een redelijk venster, bijv. 48 uur) om in de wachtrij
geplaatste offline scans te ondersteunen.
Beveiliging
- Verplichte signature-verificatie voordat enige payload-inhoud wordt vertrouwd. Toon nooit houdernamen of evenementinformatie zonder eerst de signature te hebben geverifieerd.
- Tijdsvalidatie: scanners MOETEN tickets afwijzen waarvan
expop het moment van scannen in het verleden ligt, en MOETEN waarschuwen bij onwaarschijnlijk toekomstigeiat-tijdstempels. - Validatie van issuer-URL: het veld
issis een URL. Scanners MOETEN HTTPS afdwingen en MOETEN zich beperken tot een vertrouwenslijst van issuer-domeinen als de context dat vereist. - Opslag van API keys: scannerapplicaties MOETEN API keys opslaan in de beveiligde opslag van het platform (Keychain, Keystore).
- Rate limiting: issuers MOETEN check-in endpoints per scannercredential beperken om misbruik te voorkomen.
- TLS: alle endpoints MOETEN worden aangeboden via TLS 1.2+. HTTP-naar-HTTPS-doorverwijzingen zijn niet voldoende voor API-aanroepen.
- Replay-bescherming: de
idempotency_keyvoorkomt onbedoelde replay. Opzettelijke replay met een nieuwe key wordt gedetecteerd door de server-sidetid-status en retourneertalready_used.
Enrollment-beveiliging
- Vertrouwelijkheid van de setup QR: de setup QR is een bearer credential totdat deze is verbruikt. Deze MOET worden gegenereerd vlak voor het uitdelen van apparaten aan personeel, alleen worden weergegeven op vertrouwde schermen, en niet worden gefotografeerd of gedeeld via chat/e-mail.
- Token-entropie: tokens MOETEN ten minste 128 bits entropie bevatten van een cryptografisch veilige RNG.
- Rate limiting: het
scanner_enrollendpoint MOET worden beperkt per IP en per token. - Verplicht TLS: scanners MOETEN elke setup QR afwijzen waarvan
issofenroll_urlgeen HTTPS is. - Bevestiging door operator: de scanner MOET de
event_name, datum en issuernaam tonen, en uitdrukkelijke bevestiging vragen voordat het enrollment endpoint wordt aangeroepen. - Auditspoor: issuers MOETEN elke enrollment-poging loggen (geslaagd en mislukt).
Versiebeheer en evolutie
- Het veld
vin payloads en het veldprotocol_versionin het discovery-document bevatten de hoofdversie. - Kleine toevoegingen (nieuwe optionele velden) verhogen de hoofdversie niet. Scanners MOETEN onbekende velden negeren.
- Ingrijpende wijzigingen verhogen de hoofdversie. Issuers MOETEN de vorige versie blijven accepteren gedurende een redelijke overgangsperiode (bijv. 6 maanden).