Documentatie / Praticable Ticket Protocol (PTP) / v1
ProtocolspecificatieAPI-referentieGidsen
Documentatie / Praticable Ticket Protocol (PTP) / v1
STABLE · SINCE V1

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.

API-referentie

Deze pagina beschrijft het protocol zelf. Voor endpoints, parameters, response codes en voorbeelden van requests, zie de API-referentie →

§ 01

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
Huidige versie: 1

Alle payloads en endpoints bevatten een versie-identificatie. Ingrijpende wijzigingen vereisen een nieuwe hoofdversie. Issuers en scanners kunnen meerdere versies gelijktijdig ondersteunen tijdens overgangen.

§ 02

Terminologie

Issuer

Instantie die tickets aanmaakt en beheert. Eén issuer per klant.

Scanner

Clientapplicatie die tickets valideert bij de ingang van het evenement.

Ticket

Uniek, ondertekend legitimatiebewijs dat toegang verleent tot een specifiek evenement.

Check-in

De handeling waarbij een ticket bij de ingang als gebruikt wordt gemarkeerd.

Payload

Ondertekende gegevens die zijn gecodeerd in de QR-code van het ticket.

§ 03

Cryptografie

Ed25519 RFC 8032 64 bytes · Web Crypto API

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.

§ 04

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.

Format
tkt1.<base64url(payload_json)>.<base64url(signature)>
Maximale grootte

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

Payload JSON
{
  "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
}
Veld Beschrijving
v
integerrequired

Hoofdversie van het protocol (momenteel 1).

iss
stringrequired

Basis-URL van de issuer. Canonieke vorm: HTTPS, geen afsluitende slash. Wordt gebruikt voor discovery.

bijv. https://tickets.example.com
kid
stringrequired

Identificatie van de ondertekeningssleutel (eerste 8 hexadecimale tekens van de SHA-256-hash van de public key).

tid
stringrequired

Ticketidentificatie (ULID aanbevolen, uniek binnen de issuer).

eid
stringrequired

Evenementidentificatie (ULID aanbevolen, uniek binnen de issuer).

ev
objectrequired

Metadata van het evenement voor offline weergave: name (string), date (ISO 8601 UTC), venue (string). Uitsluitend informatief.

tt
stringoptional

Tickettype, voor weergavedoeleinden (bijv. "VIP", "Standard").

hn
stringoptional

Weergavenaam van de houder. Houd deze kort; geen persoonsgegevens behalve de weergavenaam.

iat
integerrequired

Unix-tijdstempel van uitgifte (seconden).

exp
integerrequired

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.

§ 05

Issuer discovery

Elke issuer MOET een discovery-document publiceren op het volgende adres:

GET {iss}/.well-known/ticket-issuer.json
Response · 200 OK
{
  "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]
}
  • keys KAN meerdere items bevatten tijdens rotatie. Scanners moeten elke niet-verlopen sleutel accepteren waarvan de kid overeenkomt met de payload.
  • valid_until: null betekent 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.
§ 06

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.

Tweestapsuitwisseling

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.

Setup QR format
tsetup1.<base64url(setup_json)>

Het voorvoegsel tsetup1. onderscheidt setup QR-codes van ticket QR-codes (tkt1.).

Setup JSON
{
  "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"
}
Veld Beschrijving
v
integerrequired

Hoofdversie van het protocol (momenteel 1).

iss
stringrequired

Basis-URL van de issuer. De scanner MOET het discovery-document ophalen van {iss}/.well-known/ticket-issuer.json.

enroll_url
stringrequired

Exact URL voor de enrollment-uitwisseling. MOET overeenkomen met scanner_enroll in het discovery-document.

token
stringrequired

Ondoorzichtige, onvoorspelbare enrollment token (≥ 128 bits entropie). Eenmalig gebruik.

eid
stringrequired

Evenement-ID waarvoor deze setup QR de scanner registreert.

event_name
stringoptional

Naam die aan de operator wordt getoond ter bevestiging. De officiële naam komt na enrollment van de issuer.

gate_id
stringoptional

Gate-identificatie. Indien aanwezig, is de API key beperkt tot deze gate.

expires_at
stringrequired

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 met UNAUTHORIZED.
  • 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.
§ 07

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:

1. Signature

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.

2. Tijd

Valideer de tijdsgrenzen iat/exp. Indien verlopen: afwijzen met expired.

3. Enrollment

Zoek actieve enrollments op aan de hand van de combinatie (iss, eid) uit de payload.

4. Match

Als er een overeenkomende enrollment bestaat: selecteer de API key, controleer de gate-compatibiliteit en roep vervolgens het check-in endpoint aan.

5. Geen match

Als geen enrollment overeenkomt: lokaal afwijzen met not_enrolled. Toon de evenementnaam uit ev.name.

6. Onbekend

Als de iss volledig onbekend is voor de scanner: afwijzen met unknown_issuer.

§ 08

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.

Aanbevolen lokale cache

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.

§ 09

Offline modus

Scanners MOETEN kunnen functioneren met wisselende connectiviteit. Hier is de verwerkingsvolgorde voor elke scan:

1

Verifieer de signature lokaal met de gecachte public keys van de issuer in iss.

2

Valideer de tijdsgrenzen iat/exp.

3

Zoek de overeenkomende enrollment voor (iss, eid) op in de lokale opslag. Indien niet gevonden: lokaal afwijzen.

4

Controleer de lokale cache op eerdere scans van deze tid. Indien gevonden: afwijzen als duplicaat.

5

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.

6

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.

§ 10

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 exp op het moment van scannen in het verleden ligt, en MOETEN waarschuwen bij onwaarschijnlijk toekomstige iat-tijdstempels.
  • Validatie van issuer-URL: het veld iss is 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_key voorkomt onbedoelde replay. Opzettelijke replay met een nieuwe key wordt gedetecteerd door de server-side tid-status en retourneert already_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_enroll endpoint MOET worden beperkt per IP en per token.
  • Verplicht TLS: scanners MOETEN elke setup QR afwijzen waarvan iss of enroll_url geen 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).
§ 11

Versiebeheer en evolutie

  • Het veld v in payloads en het veld protocol_version in 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).
Laatst bijgewerkt : 20 mei 2026