Developerdocumentatie

TapTime REST API

Lees en wijzig tafelreserveringen en afspraken, lees bestellingen en spaarkaarten, poll de geordende eventfeed en ontvang ondertekende webhooks — vanuit je eigen CRM, kassasysteem, datawarehouse of automatiseringsplatform. Deze pagina is de volledige referentie; de gids over conversietracking legt uit waarom je ze zou gebruiken.

Basis-URLhttps://api.tapti.me AuthBearer token FormaatJSON Waarom gebruiken

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.

Bevestigde reserveringen die sinds een datum zijn gewijzigd
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

GET /v1/company Alleen-lezen of lezen-schrijven

Het huidige bedrijf ophalen

Het bedrijf waartoe de sleutel behoort, en wat de sleutel zelf mag.

GET /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.

ParameterTypeBetekenis
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.
GET /v1/company/appointments Alleen-lezen of lezen-schrijven

Afspraken tonen

Dezelfde vorm en filters als reserveringen, voor de module Afspraken.

ParameterTypeBetekenis
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.
GET /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.

GET /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.

POST /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": "..."}.

ParameterTypeBetekenis
status string, verplicht Een van: pending, confirmed, seated, completed, no_show, cancelled (canceled en canceled_by_venue worden ook geaccepteerd).
POST /v1/company/appointments/{id}/status Alleen lezen-schrijven

De status van een afspraak wijzigen

Hetzelfde als het reserveringsendpoint, zonder de status seated.

ParameterTypeBetekenis
status string, verplicht Een van: pending, confirmed, completed, no_show, cancelled (canceled en canceled_by_venue worden ook geaccepteerd).
GET /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.

ParameterTypeBetekenis
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.
GET /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.

ParameterTypeBetekenis
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.
GET /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.

GET /v1/company/loyalty/programs Alleen-lezen of lezen-schrijven

Loyaliteitsprogramma's tonen

Elk stempelkaartprogramma: naam, status, benodigde stempels, de beloning en waar die geldt.

GET /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.

ParameterTypeBetekenis
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.
GET /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.

ParameterTypeBetekenis
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.
GET /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.

POST /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.

ParameterTypeBetekenis
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.
DELETE /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.

GET /v1/company/reservations/4821 → data
{
  "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.

GET /v1/company/orders/9170 → data
{
  "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.

GET /v1/company/loyalty/cards → data[0]
{
  "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.

EventtypeBetekenis
booking.requestedEen gast vroeg een reservering aan die op jouw bevestiging wacht.
booking.confirmedEen reservering is bevestigd — direct bevestigd aangemaakt, of later door het personeel bevestigd.
booking.cancelledGeannuleerd door de gast of door de zaak; status zegt door wie.
booking.no_showDe gast is niet gekomen.
booking.completedHet bezoek is afgelopen.
order.placedDe 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.paidEen bestelling is afgerekend, ook verkopen die aan de kassa zijn aangeslagen.
loyalty.member_joinedEen klant kreeg zijn eerste stempel in een programma.
loyalty.stamp_addedEr is een stempel aan een kaart toegevoegd.
loyalty.reward_issuedEen kaart is vol en de beloning is uitgegeven.
loyalty.reward_redeemedEen beloning is overhandigd.
Synchroon blijven met de eventfeed
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.

HeaderBetekenis
TapTime-EventHet eventtype, bijv. booking.confirmed.
TapTime-Event-IdVast id van het event — gebruik het om een opnieuw verzonden levering te ontdubbelen.
TapTime-DeliveryId van deze specifieke leveringspoging.
TapTime-Signaturet=<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.
POST · application/json · booking.confirmed
{
  "id": "evt_9f3c2a7e1b",
  "type": "booking.confirmed",
  "createdAt": "2026-09-12T19:02:11+03:00",
  "test": false,
  "data": { "booking": { "kind": "reservation", "id": 4821, "status": "confirmed", "…": "…" } }
}
De handtekening verifiëren (Node.js)
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.
Een URL abonneren op nieuwe bestellingen
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.

Postback-URL met placeholders · GET of POST
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>" }.

StatusfoutBetekenis
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.
Een sleutel nodig?

API-toegang, webhooks en de automatiserings- en chatkoppelingen zitten bij elke module inbegrepen — geen extra kosten, geen apart developerabonnement.

Start een gratis proefperiode