Elk verzoek heeft een API-sleutel van het bedrijf nodig (Integraties → Webhooks & API in het dashboard), verzonden als Authorization: Bearer tt_live_…. Antwoorden en fouten zijn JSON.
curl "https://api.tapti.me/v1/company/reservations?status=confirmed&updatedSince=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer tt_live_…"
Authenticatie
Elk verzoek bevat een API-sleutel van het bedrijf als bearer-token. Maak er een aan in het dashboard — Integraties → Webhooks & API → REST API-sleutels — waar hij één keer wordt getoond en op de server alleen als SHA-256-hash wordt bewaard; een verloren sleutel wordt ingetrokken en vervangen, niet hersteld. Een bedrijf kan maximaal 10 actieve sleutels hebben.
- Alleen-lezen-sleutels kunnen elk GET-endpoint aanroepen en webhooks aan- en afmelden — een abonnement verstuurt niets wat de sleutel niet al kon lezen.
- Lees-schrijfsleutels kunnen daarnaast een statuswijziging van een reservering POSTen; een alleen-lezen-sleutel krijgt op die routes 403 Forbidden.
- Een sleutel hoort bij één bedrijf — elke lijst en elke opvraging is daar al toe beperkt, er is geen companyId-parameter.
Rate limits
50 verzoeken per minuut per sleutel, in een glijdend venster, gedeeld door alle endpoints. Ga je eroverheen, dan krijg je 429 Too Many Requests; wacht en probeer opnieuw in plaats van sneller te pollen — of abonneer een webhook en stop met pollen.
Endpoints
/v1/company
Alleen-lezen of lezen-schrijven
Het huidige bedrijf ophalen
Het bedrijf waartoe de sleutel behoort, en wat de sleutel zelf mag.
/v1/company/reservations
Alleen-lezen of lezen-schrijven
Reserveringen tonen
Tafelreserveringen van het bedrijf, vroegste starttijd eerst — of, als updatedSince is opgegeven, vroegst gewijzigd eerst.
| Parameter | Type | Betekenis |
|---|---|---|
status |
string | Kommagescheiden: pending, confirmed, seated, completed, no_show, cancelled. cancelled omvat beide annuleringsredenen. |
branchId |
integer | Beperken tot één vestiging. |
from |
ISO 8601-datum/tijd | Alleen reserveringen die op of na dit tijdstip beginnen (UTC als er geen offset is opgegeven). |
to |
ISO 8601-datum/tijd | Alleen reserveringen die vóór dit tijdstip beginnen. |
updatedSince |
ISO 8601-datum/tijd | Alleen reserveringen die sinds dit tijdstip zijn gewijzigd; sorteert dan oplopend op updatedAt, om te synchroniseren. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
page |
integer | Paginanummer, beginnend bij 1. Standaard 1. |
/v1/company/appointments
Alleen-lezen of lezen-schrijven
Afspraken tonen
Dezelfde vorm en filters als reserveringen, voor de module Afspraken.
| Parameter | Type | Betekenis |
|---|---|---|
status |
string | Kommagescheiden: pending, confirmed, completed, no_show, cancelled. |
branchId |
integer | Beperken tot één vestiging. |
from |
ISO 8601-datum/tijd | Alleen afspraken die op of na dit tijdstip beginnen. |
to |
ISO 8601-datum/tijd | Alleen afspraken die vóór dit tijdstip beginnen. |
updatedSince |
ISO 8601-datum/tijd | Alleen afspraken die sinds dit tijdstip zijn gewijzigd; sorteert oplopend op updatedAt. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
page |
integer | Paginanummer, beginnend bij 1. Standaard 1. |
/v1/company/reservations/{id}
Alleen-lezen of lezen-schrijven
Een reservering ophalen
Eén reservering, in dezelfde vorm als in webhooks, met de attributie erbij. 404 als het id niet bij het bedrijf van de sleutel hoort.
/v1/company/appointments/{id}
Alleen-lezen of lezen-schrijven
Een afspraak ophalen
Eén afspraak, met dezelfde vorm en regels als het opvragen van een reservering.
/v1/company/reservations/{id}/status
Alleen lezen-schrijven
De status van een reservering wijzigen
Bevestigen, plaatsen, afronden, als no-show markeren, annuleren of heropenen — triggert dezelfde webhooks en conversies als een wijziging op het bord. JSON-body: {"status": "..."}.
| Parameter | Type | Betekenis |
|---|---|---|
status |
string, verplicht | Een van: pending, confirmed, seated, completed, no_show, cancelled (canceled en canceled_by_venue worden ook geaccepteerd). |
/v1/company/appointments/{id}/status
Alleen lezen-schrijven
De status van een afspraak wijzigen
Hetzelfde als het reserveringsendpoint, zonder de status seated.
| Parameter | Type | Betekenis |
|---|---|---|
status |
string, verplicht | Een van: pending, confirmed, completed, no_show, cancelled (canceled en canceled_by_venue worden ook geaccepteerd). |
/v1/company/events
Alleen-lezen of lezen-schrijven
De eventfeed pollen
Elk reserverings-, bestel- en loyaliteitsevent, oudste eerst — dezelfde feed waaruit webhooks worden gesneden. Geef de laatste cursor die je zag mee als after om alleen het nieuwe op te halen, en type om alleen bepaalde soorten op te halen.
| Parameter | Type | Betekenis |
|---|---|---|
after |
integer-cursor | De cursor van het laatste event dat je verwerkte. Laat weg om vanaf het begin te starten. |
type |
string | Kommagescheiden eventtypes of families: booking.confirmed,order.placed, of order, loyalty.*. Laat weg voor alle events. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
/v1/company/orders
Alleen-lezen of lezen-schrijven
Bestellingen tonen
Tafel-, afhaal- en bezorgbestellingen van gasten en elke verkoop die aan de kassa is aangeslagen, oudste eerst — of, als paidSince is opgegeven, in de volgorde waarin ze betaald zijn. Een bestelling die nog op de vooruitbetaling wacht, wordt weggelaten.
| Parameter | Type | Betekenis |
|---|---|---|
status |
string | Kommagescheiden: open, paid. |
source |
string | customer (geplaatst door de gast) of staff (aangeslagen door het personeel). |
fulfilment |
string | Kommagescheiden: dine_in, takeaway, delivery. |
branchId |
integer | Beperken tot één vestiging. |
from |
ISO 8601-datum/tijd | Alleen bestellingen die op of na dit tijdstip zijn aangemaakt. |
to |
ISO 8601-datum/tijd | Alleen bestellingen die vóór dit tijdstip zijn aangemaakt. |
paidSince |
ISO 8601-datum/tijd | Alleen bestellingen die sinds dit tijdstip zijn betaald; sorteert oplopend op paidAt, om verkopen te synchroniseren. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
page |
integer | Paginanummer, beginnend bij 1. Standaard 1. |
/v1/company/orders/{id}
Alleen-lezen of lezen-schrijven
Een bestelling ophalen
Eén bestelling met regels, totalen en attributie, in de vorm waarin bestelwebhooks haar leveren.
/v1/company/loyalty/programs
Alleen-lezen of lezen-schrijven
Loyaliteitsprogramma's tonen
Elk stempelkaartprogramma: naam, status, benodigde stempels, de beloning en waar die geldt.
/v1/company/loyalty/cards
Alleen-lezen of lezen-schrijven
Spaarkaarten tonen
Kaarten van leden, in de vorm van het loyaliteitsobject. Een lid dat een kaart vol heeft, heeft die als completed met een nieuwe actieve kaart ernaast; customer.id is het lid.
| Parameter | Type | Betekenis |
|---|---|---|
programId |
integer | Beperken tot één programma. |
customerId |
integer | Beperken tot één lid. |
status |
string | Kommagescheiden: active, completed. |
updatedSince |
ISO 8601-datum/tijd | Alleen kaarten die sinds dit tijdstip zijn afgestempeld of voltooid; sorteert oplopend op updatedAt. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
page |
integer | Paginanummer, beginnend bij 1. Standaard 1. |
/v1/company/loyalty/rewards
Alleen-lezen of lezen-schrijven
Beloningen tonen
Beloningen die zijn verdiend door een kaart vol te maken, met hun code, vervaldatum en of ze zijn ingewisseld.
| Parameter | Type | Betekenis |
|---|---|---|
programId |
integer | Beperken tot één programma. |
customerId |
integer | Beperken tot één lid. |
status |
string | Kommagescheiden: issued, redeemed, expired. |
from |
ISO 8601-datum/tijd | Alleen beloningen die op of na dit tijdstip zijn uitgegeven. |
to |
ISO 8601-datum/tijd | Alleen beloningen die vóór dit tijdstip zijn uitgegeven. |
limit |
integer | Paginagrootte, 1–100. Standaard 50. |
page |
integer | Paginanummer, beginnend bij 1. Standaard 1. |
/v1/company/webhooks
Alleen-lezen of lezen-schrijven
Webhookabonnementen tonen
De abonnementen die deze sleutel heeft aangemaakt. Webhooks die in het dashboard zijn toegevoegd, staan hier niet bij.
/v1/company/webhooks
Alleen-lezen of lezen-schrijven
Een webhook abonneren
Begint events naar een URL te sturen — wat de app van een automatiseringsplatform aanroept wanneer een gebruiker een trigger aanzet. Geeft het abonnement en zijn ondertekeningsgeheim terug, één keer. JSON-body: {"url": "…", "events": ["order.placed"]}; Zapiers {"target_url": "…", "event": "…"} wordt ook geaccepteerd.
| Parameter | Type | Betekenis |
|---|---|---|
url |
string, verplicht | Het https-adres om naar te sturen (target_url wordt ook geaccepteerd). |
events |
array van strings, verplicht | Eventtypes, families zoals order.* of loyalty, of * voor alles (event wordt ook geaccepteerd, voor één type). |
description |
string | Een naam die in het dashboard wordt getoond. Standaard het platform en de naam van de sleutel. |
/v1/company/webhooks/{id}
Alleen-lezen of lezen-schrijven
Een webhook afmelden
Stopt het verzenden naar een abonnement dat deze sleutel heeft aangemaakt. Geeft 204 terug. Als je de sleutel intrekt, worden ook al zijn abonnementen verwijderd.
Het reserveringsobject
Een reservering en een afspraak komen in één gedeelde vorm: kind, id, status, source, guestInitiated, startsAt/endsAt/timezone (de eigen tijdzone van de vestiging), currency, company, branch, guest, attribution, conversionId, createdAt/updatedAt — plus partySize en table voor een reservering, service en specialist voor een afspraak. Het is precies wat een webhook levert, dus een ontvanger die het ene verwerkt, verwerkt ook het andere.
{
"data": {
"kind": "reservation",
"id": 4821,
"status": "confirmed",
"source": "online",
"guestInitiated": true,
"startsAt": "2026-09-12T20:00:00+03:00",
"endsAt": "2026-09-12T21:30:00+03:00",
"timezone": "Asia/Baku",
"currency": "AZN",
"company": { "id": 12, "name": "Cafe Aroma", "slug": "cafe-aroma", "country": "AZ" },
"branch": { "id": 3, "name": "Nizami st." },
"partySize": 4,
"table": { "id": 9, "label": "T3" },
"guest": { "name": "Maria K.", "phone": "…", "email": "…" },
"locale": "en",
"note": null,
"conversionId": "tt3f9c…",
"createdAt": "2026-09-10T08:12:04+00:00",
"updatedAt": "2026-09-12T20:00:03+00:00",
"attribution": { "channel": "widget", "utmSource": "instagram", "gclid": null, "fbclid": "IwAR2…" }
}
}
Het bestelobject
Een bestelling heeft overal dezelfde vorm — in de REST API, de bestelwebhooks en de eventfeed: kind ("order"), id, number (het bonnummer dat het personeel omroept), status (open of paid), source (customer of staff), guestInitiated, fulfilment, table, label, deliveryAddress, note, currency, timezone, company, branch, customer (het loyaliteitslid, als het personeel er een herkende), items (name, variant, quantity, unitPriceCents, subtotalCents), itemCount, subtotalCents, discountCents, deliveryFeeCents, tipCents, totalCents, valueCents (wat de bestelling waard is zonder fooi — het getal dat conversies rapporteren), paymentMethod, servedBy, conversionId, createdAt, paidAt en attribution. Bedragen staan altijd in de kleinste munteenheid.
{
"data": {
"kind": "order",
"id": 9170,
"number": 42,
"status": "paid",
"source": "customer",
"guestInitiated": true,
"fulfilment": "takeaway",
"table": null,
"label": "Ana",
"currency": "EUR",
"branch": { "id": 3, "name": "Vitosha Blvd" },
"customer": { "id": 311, "name": "Ana", "phone": "…" },
"items": [
{ "menuItemId": 88, "name": "Flat white", "variant": "Large",
"quantity": 2, "unitPriceCents": 450, "subtotalCents": 900 }
],
"itemCount": 2,
"subtotalCents": 900,
"discountCents": 0,
"deliveryFeeCents": 0,
"tipCents": 100,
"totalCents": 1000,
"valueCents": 900,
"paymentMethod": "online",
"conversionId": "tt81ad…",
"createdAt": "2026-09-12T12:04:10+03:00",
"paidAt": "2026-09-12T12:05:02+03:00",
"attribution": { "channel": "web", "utmSource": "google", "gclid": "Cj0K…" }
}
}
Het loyaliteitsobject
Elk loyaliteitsevent, elke kaart en elke beloning heeft één vorm: kind ("loyalty"), id (de kaart), timezone, company, branch (waar het gebeurde, als dat bekend is), program (id, name, stampsRequired, rewardTitle), customer (id, name, phone, locale), card (id, status, stampsCount, stampsRequired, stampsToReward, createdAt), stamp (bij een stempel: source, issuedBy, createdAt) en reward (bij een beloning: code, title, status, expiresAt, redeemedAt, redeemedBy), plus conversionId, het id waaronder een loyaliteitsaanmelding wordt gerapporteerd.
{
"kind": "loyalty",
"id": 5120,
"program": { "id": 4, "name": "Coffee card", "stampsRequired": 8, "rewardTitle": "Free coffee" },
"customer": { "id": 311, "name": "Ana", "phone": "…", "locale": "bg" },
"card": { "id": 5120, "status": "active", "stampsCount": 5,
"stampsRequired": 8, "stampsToReward": 3 },
"branch": { "id": 3, "name": "Vitosha Blvd" },
"stamp": { "source": "qr_scan", "issuedBy": "Ivan", "createdAt": "…" },
"reward": null,
"conversionId": "tt5c02…"
}
Eventfeed
Elke rij is { id, cursor, type, createdAt, previousStatus, data }. data bevat het onderwerp onder de sleutel die het voorvoegsel van het event noemt: data.booking, data.order of data.loyalty. Bewaar de hoogste cursor die je hebt verwerkt en geef die terug als after — er wordt nooit een rij overgeslagen of herhaald, dus hervatten na een crash is veilig. Negeer types die je niet verwerkt: er kunnen nieuwe bijkomen.
| Eventtype | Betekenis |
|---|---|
booking.requested | Een gast vroeg een reservering aan die op jouw bevestiging wacht. |
booking.confirmed | Een reservering is bevestigd — direct bevestigd aangemaakt, of later door het personeel bevestigd. |
booking.cancelled | Geannuleerd door de gast of door de zaak; status zegt door wie. |
booking.no_show | De gast is niet gekomen. |
booking.completed | Het bezoek is afgelopen. |
order.placed | De bestelling van een gast is bij de zaak binnengekomen — via de pagina, de QR-code van een tafel, afhalen of bezorgen; na de betaling als de zaak vooruitbetaling vraagt. |
order.paid | Een bestelling is afgerekend, ook verkopen die aan de kassa zijn aangeslagen. |
loyalty.member_joined | Een klant kreeg zijn eerste stempel in een programma. |
loyalty.stamp_added | Er is een stempel aan een kaart toegevoegd. |
loyalty.reward_issued | Een kaart is vol en de beloning is uitgegeven. |
loyalty.reward_redeemed | Een beloning is overhandigd. |
curl "https://api.tapti.me/v1/company/events?after=998&limit=100" \
-H "Authorization: Bearer tt_live_…"
Webhooks
Maak een endpoint aan onder Integraties → Webhooks & API — of koppel Zapier, Make, n8n of Pipedream onder Integraties → Automatisering — en TapTime POST een ondertekend JSON-event zodra een reservering, een bestelling of een spaarkaart verandert. Per endpoint kies je de events. Een bedrijf kan in totaal maximaal 50 webhooks, postbacks, chats en automatiseringskoppelingen hebben.
| Header | Betekenis |
|---|---|
TapTime-Event | Het eventtype, bijv. booking.confirmed. |
TapTime-Event-Id | Vast id van het event — gebruik het om een opnieuw verzonden levering te ontdubbelen. |
TapTime-Delivery | Id van deze specifieke leveringspoging. |
TapTime-Signature | t=<unix time>,v1=<hex HMAC-SHA256 van "t.rawBody">, ondertekend met het eigen geheim van het endpoint. |
- Elk 2xx-antwoord geldt als geleverd; een niet-2xx-antwoord of een time-out wordt opnieuw geprobeerd met oplopende wachttijd (1m, 5m, 15m, 1u, 3u, 6u, 12u) tot 8 pogingen — een 410 stopt de nieuwe pogingen.
- Redirects worden niet gevolgd.
- De URL moet in productie HTTPS zijn en mag niet naar een privé- of loopbackadres wijzen.
- Levering is at-least-once: verifieer altijd de handtekening en ontdubbel op TapTime-Event-Id.
{
"id": "evt_9f3c2a7e1b",
"type": "booking.confirmed",
"createdAt": "2026-09-12T19:02:11+03:00",
"test": false,
"data": { "booking": { "kind": "reservation", "id": 4821, "status": "confirmed", "…": "…" } }
}
const [t, v1] = req.headers["taptime-signature"]
.split(",").map(part => part.split("=")[1]);
const expected = crypto
.createHmac("sha256", process.env.TAPTIME_WEBHOOK_SECRET)
.update(`${t}.${rawBody}`)
.digest("hex");
const valid = crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(v1));
Webhookabonnementen
Automatiseringsplatforms zetten triggers aan en uit via de API, in plaats van de gebruiker een URL te laten plakken (‘REST hooks’): POST de eigen URL van het platform met de gewenste events, bewaar het teruggegeven id en DELETE het wanneer de trigger wordt uitgezet. Een abonnement is een gewone ondertekende webhook — het staat in het dashboard, gemarkeerd met de sleutel die het aanmaakte.
- Een sleutel ziet en verwijdert alleen de abonnementen die hij zelf aanmaakte; de sleutel intrekken verwijdert ze allemaal.
- Adressen op hooks.zapier.com, make.com en pipedream.net worden herkend en met de naam van het platform getoond; elk ander https-adres is een gewone webhook.
- Het ondertekeningsgeheim wordt één keer teruggegeven, in het antwoord op de POST; de eigenaar kan het in het dashboard opnieuw tonen.
curl -X POST "https://api.tapti.me/v1/company/webhooks" \
-H "Authorization: Bearer tt_live_…" -H "Content-Type: application/json" \
-d '{"url": "https://hooks.zapier.com/hooks/catch/123/abc/", "events": ["order.placed"]}'
Postbacks
Een GET of POST naar de eigen URL van je tracker met ingevulde macro's, voor platforms die postbacks via querystrings gebruiken in plaats van JSON-webhooks. Voor een bestelling is {value} de waarde van de bestelling zonder fooi en {order_id} haar id.
https://tracker.example/postback
?cid={click_id}&event={event}&status={status}
&payout={value}&cur={currency}
Beschikbare placeholders
{event}{event_id}{status}{booking_id}{booking_type}{order_id}{customer_id}{conversion_id}{value}{currency}{party_size}{branch_id}{timestamp}{utm_source}{utm_medium}{utm_campaign}{utm_term}{utm_content}{gclid}{fbclid}{msclkid}{ttclid}{click_id}
Fouten
Elke fout is JSON: { "error": "<code>", "message": "<leesbare tekst>" }.
| Status | fout | Betekenis |
|---|---|---|
| 401 | unauthorized |
De Authorization-header ontbreekt, of de sleutel is ongeldig of ingetrokken. |
| 403 | forbidden |
Een alleen-lezen-sleutel riep een schrijfendpoint aan (een statuswijziging). |
| 404 | not_found |
Er hoort geen reservering met dat id bij het bedrijf van de sleutel. |
| 409 | conflict |
De statuswijziging zou de tafel of het tijdslot dubbel boeken; er is niets gewijzigd. |
| 422 | invalid_status |
De statuswaarde in de body is geen van de geaccepteerde waarden voor dat reserveringstype. |
| 422 | invalid_type |
De type-parameter van de eventfeed noemt geen bestaand eventtype of bestaande familie. |
| 422 | invalid_url |
Een abonnements-URL is geen openbaar https-adres. |
| 429 | rate_limited |
Meer dan 50 verzoeken in een minuut voor deze sleutel. Vertraag en probeer opnieuw. |
API-toegang, webhooks en de automatiserings- en chatkoppelingen zitten bij elke module inbegrepen — geen extra kosten, geen apart developerabonnement.
Start een gratis proefperiode