Bouw je eigen koppeling
Je catalogus, je vrije plaatsen, je bestellingen, je tickets en je scans kan een ander programma ook ophalen, en bestellingen kan het zelf aanmaken. Hieronder staat hoe dat werkt — eerst in gewone taal, daarna met de code erbij.
- Basisadres
- https://passavo.eu/api/v1
- Huidige versie
- 2026-09-20
- De beschrijving zelf
- openapi.json
Wat is een API, en heb je er een nodig?
Een API is een deur aan de achterkant van je account. Waar jij in je beheerscherm klikt, kan een programma langs die deur rechtstreeks je gegevens ophalen of erin schrijven — zonder dat er iemand aan een scherm zit.
Je hebt er alleen een nodig wanneer je iets wil dat het beheerscherm niet doet: cijfers in je eigen systeem, plaatsen op je eigen website, tickets uit een kassa die je al hebt. Kan je het klikken, dan is klikken sneller.
Wie zo’n koppeling bouwt — je webbouwer, je boekhoudpakket, iemand die kan programmeren — heeft twee dingen van je nodig: het adres hieronder en een sleutel die je zelf aanmaakt.
Wat je ermee kan bouwen
Vrije plaatsen op je eigen site
Toon op je eigen homepagina welke rondleidingen deze week nog plaats hebben, in je eigen vormgeving, met de cijfers die op dat moment kloppen.
Je boekhouding voeden
Laat je boekhouder of je boekhoudpakket zelf de verkoop van vorige maand ophalen, in plaats van elke maand een export door te sturen.
Een scherm aan de ingang
Een tablet in de inkomhal die toont welk tijdslot er zo begint en hoeveel plaatsen er nog vrij zijn. Eén oproep per minuut volstaat.
Verkopen vanuit je eigen systeem
Maak een bestelling aan vanuit het programma waarin je al werkt, stuur de koper een betaallink en krijg via een webhook te horen dat hij betaald heeft.
De API hoort bij de formule Pro
Sleutels maak je aan onder Instellingen → API-toegang. Dat scherm staat in je menu zodra je organisatie op die formule zit; overschakelen doe je zelf in je beheerscherm, onder Abonnement.
Bekijk de formules →Eerst veilig proberen
Naast een gewone sleutel (pv_live_) kan je een testsleutel aanmaken (pv_test_). Die werkt met je echte catalogus, tijdsloten en capaciteit, en kan ook bestellingen aanmaken. Zo’n testbestelling gaat nooit langs een betaalprovider — een testpagina speelt de betaling na —, telt niet mee in je omzet en wordt na 24 uur opgeruimd. Tot dan houdt ze echte plaatsen vast, dus werk liefst met een product dat je daarvoor aanmaakt. Wat buiten de sandbox valt, zoals webhooks beheren, weigeren we voor een testsleutel met de code sandbox.
Snelstart in vijf stappen
Van niets naar een koppeling die weet dat er betaald is. Elke stap duurt minuten, niet dagen.
-
Maak een sleutel aan
Open in je beheerscherm Instellingen → API-toegang en maak een sleutel met alleen de rechten die je nodig hebt. Het volledige token zie je één keer; bewaar het zoals je een wachtwoord bewaart.
-
Doe je eerste oproep
Vraag GET /me op met je sleutel in de Authorization-header. Je krijgt je organisatie, je formule en de rechten van je sleutel terug. Werkt dit, dan werkt de rest ook.
-
Maak een bestelling aan
Post de regels die je wil verkopen naar POST /orders, met een Idempotency-Key erbij — die is hier verplicht. Gaat je verbinding onderweg stuk, dan levert dezelfde oproep opnieuw geen tweede bestelling op. Het veld reserved_until zegt tot wanneer de plaatsen voor je vastgehouden worden.
-
Stuur de koper naar de betaallink
Vraag met POST /orders/{id}/checkout de betaallink op, samen met het adres waar de koper na het betalen terechtkomt. Stuur de koper naar die link of toon hem in je eigen pagina. De betaling loopt via de provider die je al gekoppeld hebt; zodra ze binnen is, worden de tickets aangemaakt en verstuurd.
-
Laat een webhook je verwittigen
Zet een adres van je eigen site klaar en registreer het. Zodra er betaald, geannuleerd of gescand is, krijg je dat daar binnen — je hoeft niet te blijven vragen of er al iets gebeurd is.
curl
curl "https://passavo.eu/api/v1/me" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/me');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/me', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/me", headers=headers)
data = response.json()["data"]
Authenticatie
Elk verzoek draagt je sleutel als bearer-token. Een sleutel hoort bij één organisatie: die organisatie is de hele wereld van dat verzoek, en je kan er nooit per ongeluk gegevens van iemand anders mee ophalen.
Authorization: Bearer pv_live_YOUR_API_KEY
Een gewone sleutel begint met pv_live_, een sandboxsleutel met pv_test_. Dat voorvoegsel is geen geheim maar een hulp: wie het in een logboek ziet, weet meteen waar het vandaan komt.
Het volledige token bestaat één moment, bij het aanmaken. Daarna bewaren wij er alleen een onomkeerbare afdruk (sha256) van. Kwijt is dus echt kwijt: trek de sleutel in en maak een nieuwe.
Rechten
Een sleutel zonder rechten mag niets. Dat is met opzet omgekeerd aan wat je zou verwachten: deze sleutels worden door klanten uitgedeeld aan tools van derden, en dan hoort de standaard "niets" te zijn en niet "alles".
- read
- Alles opvragen: je locaties, producten, prijzen, vrije plaatsen, kortingen, cadeaubonnen en jaarkaarten, en ook je bestellingen, tickets, kopers en de scans aan de ingang.
- write
- Bestellingen aanmaken, laten afrekenen, annuleren en terugbetalen; tickets intrekken of opnieuw versturen; bezoekers inchecken; cadeaubonnen en jaarkaarten uitgeven.
- webhooks
- Je eigen webhook-bestemmingen beheren.
- external_payment
- Een bestelling die geld kost als betaald markeren zonder betaalprovider, bijvoorbeeld na een overschrijving of een contante betaling (POST /orders/{id}/mark-paid). Een apart recht naast write, zodat je het alleen geeft aan een kassa of een boekhouding die je zelf beheert. Een bestelling van nul euro bevestigen kan ook zonder.
Als er iets misgaat
Elke fout heeft dezelfde vorm, met een code die vastligt en een tekst die vertaald is. Lees de code en niet de tekst: de tekst mag herschreven worden, de code nooit.
{
"error": {
"code": "validation_failed",
"message": "…",
"details": {}
}
}
| Code | Status | Wanneer |
|---|---|---|
| unauthenticated | 401 | Geen, onbekende, ingetrokken of vervallen sleutel. |
| forbidden | 403 | De sleutel klopt, maar mag dit niet. |
| plan_required | 403 | De formule van deze organisatie bevat de API niet. |
| sandbox | 403 | Een testsleutel probeert iets wat buiten de sandbox valt, zoals webhooks beheren. |
| not_found | 404 | Bestaat niet, of bestaat niet binnen deze organisatie. |
| validation_failed | 422 | De vraag zelf klopt niet: een onbekende filter, een ongeldige datum. |
| rate_limited | 429 | Te veel oproepen in één minuut. |
| conflict | 409 | Dezelfde Idempotency-Key werd al gebruikt voor iets anders. |
| server_error | 500 | Er ging iets mis aan onze kant. |
| slot_unavailable | 409 | Het tijdslot is vol, al begonnen of onbekend, of er werd er geen gekozen terwijl het product er een vraagt. |
| sold_out | 409 | Het product is uitverkocht of niet meer te koop. |
| discount_invalid | 422 | De kortingscode bestaat niet, is op, of geldt hier niet. |
| payment_provider_missing | 409 | De organisatie heeft geen betaalprovider gekoppeld. |
| not_cancellable | 409 | De bestelling kan in haar huidige toestand niet geannuleerd worden. |
| not_refundable | 409 | Er valt op deze bestelling niets (meer) terug te betalen, of ze werd buiten de betaalprovider om betaald. |
| check_in_duplicate | 409 | Deze code is al ingecheckt. |
| check_in_invalid | 404 | Deze code is onbekend. |
| check_in_wrong_day | 409 | Deze code is geldig, maar niet vandaag. |
| check_in_cancelled | 409 | Deze code hoort bij een geannuleerd of terugbetaald ticket. |
Een rij van een andere organisatie geeft altijd not_found en nooit een melding dat je er niet bij mag. Zou dat verschil zichtbaar zijn, dan kan iedereen met een geldige sleutel uitvissen hoeveel producten de buurman heeft door nummers af te lopen.
Paginering, sorteren en filteren
Lijsten komen per pagina binnen. Je vraagt niet om pagina drie maar om wat er ná de vorige pagina komt: het antwoord draagt een cursor, en die stuur je mee om verder te lezen. Zo mist een lijst nooit rijen wanneer er tijdens het bladeren iets bijkomt.
| Parameter | Wat het doet |
|---|---|
| page[size] | Hoeveel rijen per pagina; standaard 25, hoogstens 100. |
| page[cursor] | De next_cursor uit het vorige antwoord. Is hij leeg, dan was dat de laatste pagina. |
| sort | Waarop gesorteerd wordt. Een streepje ervoor keert de volgorde om. |
| filter[…] | Filteren op een veld dat het endpoint toestaat. |
| include | Relaties meenemen in hetzelfde antwoord, gescheiden door komma’s. |
Een parameter die het endpoint niet kent, is een fout en geen stilte. Wie zich verschrijft in de naam van een filter, hoort dat te horen — en niet ongemerkt de hele lijst ongefilterd terug te krijgen.
Er staat bewust geen totaal in het antwoord: bij tienduizenden rijen kost die telling meer dan de pagina zelf.
{
"data": [ … ],
"meta": { "next_cursor": "…" }
}
Twee keer versturen, één keer uitvoeren
Stuur bij elke oproep die iets aanmaakt of wijzigt een Idempotency-Key mee — een willekeurige unieke tekenreeks. Komt dezelfde oproep met dezelfde sleutel nog eens binnen, dan krijg je exact hetzelfde antwoord terug zonder dat er iets tweede keer gebeurt. Bij POST /orders is ze verplicht.
Idempotency-Key: 6f1c0b4a-6b2f-4a1b-9c7e-1f2d3e4a5b6c
Dezelfde sleutel met een andere inhoud levert conflict op en er gebeurt niets. Een bewaarde sleutel gaat 24 uur mee, en geldt per organisatie.
Versies
De versie staat in het pad (/api/v1) en verandert alleen bij een breuk; een tweede versie draait er dan naast en niet doorheen. Elke kleine toevoeging wordt gedateerd in de header Passavo-Version, nu 2026-09-20.
Wat er binnen deze versie bij mag komen: een veld, een endpoint, een nieuwe sorteer- of filterwaarde, een nieuwe foutcode. Bouw je koppeling dus zo dat een onbekend veld haar niet omver duwt.
Wat er nooit gebeurt binnen deze versie: een veld weghalen of hernoemen, een type wijzigen, een code een andere betekenis geven, of een parameter verplicht maken.
Snelheidslimiet
Een sleutel mag 120 oproepen per minuut doen. De limiet geldt per sleutel en niet per adres, zodat twee klanten achter dezelfde cloudprovider elkaar niet in de weg zitten.
Elk antwoord vertelt hoe je ervoor staat in X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Zit je eroverheen, dan krijg je rate_limited met een Retry-After erbij; wacht dan gewoon dat aantal seconden.
Codevoorbeelden
Hetzelfde verzoek in vier talen, per groep endpoints. Vervang YOUR_API_KEY door je eigen sleutel — en zet die nooit in code die een bezoeker kan downloaden.
Account
Wie ben ik en wat mag ik.
GET /api/v1/me
curl
curl "https://passavo.eu/api/v1/me" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/me');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/me', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/me", headers=headers)
data = response.json()["data"]
Catalogus
Locaties, producten en prijzen.
GET /api/v1/events
curl
curl "https://passavo.eu/api/v1/events" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/events');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/events', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/events", headers=headers)
data = response.json()["data"]
Beschikbaarheid
Tijdsloten en vrije plaatsen.
GET /api/v1/products/{id}/slots
curl
curl "https://passavo.eu/api/v1/products/12/slots" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/products/12/slots');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/products/12/slots', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/products/12/slots", headers=headers)
data = response.json()["data"]
Verkoop
Kortingen, cadeaubonnen en jaarkaarten.
GET /api/v1/discounts
curl
curl "https://passavo.eu/api/v1/discounts" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/discounts');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/discounts', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/discounts", headers=headers)
data = response.json()["data"]
Bestellingen
Bestellingen aanmaken, afrekenen, annuleren en terugbetalen.
GET /api/v1/orders
curl
curl "https://passavo.eu/api/v1/orders" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/orders');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/orders', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/orders", headers=headers)
data = response.json()["data"]
Tickets
Tickets opvragen, intrekken en opnieuw versturen.
GET /api/v1/tickets
curl
curl "https://passavo.eu/api/v1/tickets" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/tickets');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/tickets', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/tickets", headers=headers)
data = response.json()["data"]
Ingang
Inchecken aan de ingang.
GET /api/v1/check-ins
curl
curl "https://passavo.eu/api/v1/check-ins" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/check-ins');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/check-ins', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/check-ins", headers=headers)
data = response.json()["data"]
Klanten
Kopers, samengevat uit de bestellingen.
GET /api/v1/customers
curl
curl "https://passavo.eu/api/v1/customers" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/customers');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/customers', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/customers", headers=headers)
data = response.json()["data"]
Meta
De beschrijving van de API zelf.
GET /api/v1/openapi.json
curl
curl "https://passavo.eu/api/v1/openapi.json" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->get('https://passavo.eu/api/v1/openapi.json');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/openapi.json', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json"}
response = requests.get("https://passavo.eu/api/v1/openapi.json", headers=headers)
data = response.json()["data"]
Webhooks
Berichten die wij naar jouw server sturen.
GET /api/v1/webhook-deliveries
curl
curl "https://passavo.eu/api/v1/webhook-deliveries" \
-H "Authorization: Bearer pv_live_YOUR_API_KEY" \
-H "Accept: application/json"
PHP
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()
->withToken('pv_live_YOUR_API_KEY')
->get('https://passavo.eu/api/v1/webhook-deliveries');
$data = $response->json('data');
JavaScript
const response = await fetch('https://passavo.eu/api/v1/webhook-deliveries', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer pv_live_YOUR_API_KEY',
},
});
const { data } = await response.json();
Python
import requests
headers = {"Accept": "application/json", "Authorization": "Bearer pv_live_YOUR_API_KEY"}
response = requests.get("https://passavo.eu/api/v1/webhook-deliveries", headers=headers)
data = response.json()["data"]
Webhooks
Een webhook is het omgekeerde van een oproep: in plaats van dat jij blijft vragen of er al betaald is, sturen wij een bericht naar een adres van jou zodra het gebeurt. Dat scheelt duizenden oproepen per dag en je weet het meteen in plaats van bij de volgende ronde.
Je beheert je bestemmingen in je beheerscherm onder Instellingen → API-toegang, tabblad Webhooks, of via de API met een sleutel die het recht webhooks draagt. Elke bestemming heeft een eigen geheim; daarmee reken je na dat een bericht echt van ons komt.
De handtekening narekenen
Elk bericht draagt de header Passavo-Signature in de vorm t=<unix>,v1=<hmac-sha256>. t is het moment waarop wij tekenden; v1 is een HMAC-SHA256 met het geheim van die bestemming over de tijdstempel, een punt en de ruwe body. Reken hem zelf na en vergelijk in constante tijd — een gewone vergelijking verraadt via de tijd die ze kost hoeveel tekens er klopten. Weiger ook een bericht waarvan t meer dan 300 seconden van je eigen klok afwijkt: zo kan niemand een opgevangen bericht later opnieuw afspelen.
Reken na op de RUWE body, vóór je framework er json van maakt. Json opnieuw samenstellen zet sleutels in een andere volgorde en dan klopt geen enkele handtekening meer.
curl
# De handtekening narekenen vanaf de opdrachtregel.
# T is de waarde van t= uit de header Passavo-Signature,
# het resultaat hoort gelijk te zijn aan de waarde van v1=.
printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET"
PHP
// De RUWE body, niet $request->all().
$body = $request->getContent();
// "t=1758355200,v1=9f86d0…" uit elkaar halen.
$delen = [];
foreach (explode(',', (string) $request->header('Passavo-Signature')) as $stuk) {
[$sleutel, $waarde] = array_pad(explode('=', trim($stuk), 2), 2, '');
$delen[$sleutel] = $waarde;
}
$t = $delen['t'] ?? '';
$verwacht = hash_hmac('sha256', $t . '.' . $body, $secret);
// hash_equals vergelijkt in constante tijd; de tijdstempel
// houdt een opgevangen bericht tegen dat later opnieuw komt.
if (! ctype_digit($t) || abs(time() - (int) $t) > 300 || ! hash_equals($verwacht, $delen['v1'] ?? '')) {
abort(400);
}
JavaScript
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: de ruwe body als string, bv. via express.raw().
const header = request.headers['passavo-signature'] ?? '';
const delen = Object.fromEntries(header.split(',').map((s) => s.trim().split('=', 2)));
const verwacht = createHmac('sha256', secret).update(delen.t + '.' + rawBody).digest('hex');
const gekregen = delen.v1 ?? '';
const vers = /^\d+$/.test(delen.t ?? '')
&& Math.abs(Date.now() / 1000 - Number(delen.t)) <= 300;
const geldig = vers
&& verwacht.length === gekregen.length
&& timingSafeEqual(Buffer.from(verwacht), Buffer.from(gekregen));
Python
import hashlib, hmac, time
# raw_body: de ruwe body als bytes, bv. request.get_data() in Flask.
header = request.headers.get("Passavo-Signature", "")
delen = dict(stuk.strip().split("=", 1) for stuk in header.split(",") if "=" in stuk)
t = delen.get("t", "")
verwacht = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
# compare_digest vergelijkt in constante tijd.
geldig = (t.isdigit()
and abs(time.time() - int(t)) <= 300
and hmac.compare_digest(verwacht, delen.get("v1", "")))
De volledige lijst gebeurtenissen en hun inhoud staat in de referentie. →
Wat er verandert
Elke wijziging die het waard is om te noemen, krijgt een nieuwe datumversie. Staat je koppeling stil op een oude datum, dan blijft ze werken — de datum zegt alleen tegen welk gedrag ze gebouwd is.
-
2026-09-20
De eerste versie
Sleutels met rechten, een sandbox, één antwoordformaat en cursorpaginering. Lezen: je organisatie, locaties, producten, tijdsloten, events, kortingen, cadeaubonnen en jaarkaarten, bestellingen, tickets, kopers en scans. Schrijven: bestellingen aanmaken, afrekenen, annuleren en terugbetalen, tickets intrekken, bezoekers inchecken, cadeaubonnen en jaarkaarten uitgeven. En uitgaande webhooks, ondertekend per bestemming.