Developer docs

TapTime REST API

Read and update table reservations and appointments, read orders and loyalty cards, poll the ordered event feed, and receive signed webhooks — from your own CRM, POS, data warehouse or an automation platform. This page is the full reference; the conversion-tracking guide covers why you’d use it.

Base URLhttps://api.tapti.me AuthBearer token FormatJSON Why use it

Every request needs a company API key (Integrations → Webhooks & API in the panel) sent as Authorization: Bearer tt_live_…. Responses and errors are JSON.

Confirmed reservations changed since a date
curl "https://api.tapti.me/v1/company/reservations?status=confirmed&updatedSince=2026-09-01T00:00:00Z" \
  -H "Authorization: Bearer tt_live_…"

Authentication

Every request carries a company API key as a bearer token. Create one from the panel — Integrations → Webhooks & API → REST API keys — where it is shown once and stored server-side only as a SHA-256 hash; a lost key is revoked and replaced, not recovered. A company can hold up to 10 active keys.

  • Read-only keys can call every GET endpoint, and can subscribe and unsubscribe webhooks — a subscription pushes nothing the key could not already read.
  • Read-write keys can also POST a booking status change; a read-only key gets 403 Forbidden on those routes.
  • A key belongs to one company — every list and lookup is already scoped to it, there is no companyId parameter.

Rate limits

50 requests per minute per key, on a sliding window, shared by every endpoint. Going over returns 429 Too Many Requests; back off and retry rather than polling faster — or subscribe a webhook and stop polling.

Endpoints

GET /v1/company Read-only or read-write

Get the current company

The company the key belongs to, and what the key itself can do.

GET /v1/company/reservations Read-only or read-write

List reservations

Table reservations for the company, oldest start time first — or, when updatedSince is set, oldest updated first.

ParameterTypeMeaning
status string Comma-separated: pending, confirmed, seated, completed, no_show, cancelled. cancelled matches both cancellation reasons.
branchId integer Restrict to one branch.
from ISO 8601 datetime Only bookings starting on or after this time (UTC if no offset given).
to ISO 8601 datetime Only bookings starting before this time.
updatedSince ISO 8601 datetime Only bookings changed since this time; switches the sort to updatedAt ascending, for syncing.
limit integer Page size, 1–100. Default 50.
page integer 1-based page number. Default 1.
GET /v1/company/appointments Read-only or read-write

List appointments

Same shape and filters as reservations, for the Appointments module.

ParameterTypeMeaning
status string Comma-separated: pending, confirmed, completed, no_show, cancelled.
branchId integer Restrict to one branch.
from ISO 8601 datetime Only bookings starting on or after this time.
to ISO 8601 datetime Only bookings starting before this time.
updatedSince ISO 8601 datetime Only bookings changed since this time; sorts by updatedAt ascending.
limit integer Page size, 1–100. Default 50.
page integer 1-based page number. Default 1.
GET /v1/company/reservations/{id} Read-only or read-write

Get a reservation

One reservation, in the same shape webhooks carry it, with its attribution. 404 when the id doesn't belong to the key's company.

GET /v1/company/appointments/{id} Read-only or read-write

Get an appointment

One appointment, same shape and rules as a reservation lookup.

POST /v1/company/reservations/{id}/status Read-write only

Change a reservation's status

Confirm, seat, complete, mark a no-show, cancel or reopen — fires the same webhooks and conversions as changing it on the board. JSON body: {"status": "..."}.

ParameterTypeMeaning
status string, required One of: pending, confirmed, seated, completed, no_show, cancelled (canceled and canceled_by_venue also accepted).
POST /v1/company/appointments/{id}/status Read-write only

Change an appointment's status

Same as the reservation endpoint, without the seated state.

ParameterTypeMeaning
status string, required One of: pending, confirmed, completed, no_show, cancelled (canceled and canceled_by_venue also accepted).
GET /v1/company/events Read-only or read-write

Poll the event feed

Every booking, order and loyalty event, oldest first — the same feed webhooks are cut from. Pass the last cursor you saw as after to fetch only what’s new, and type to fetch only some kinds.

ParameterTypeMeaning
after integer cursor The cursor from the last event you processed. Omit to start from the beginning.
type string Comma-separated event types or families: booking.confirmed,order.placed, or order, loyalty.*. Omit for every event.
limit integer Page size, 1–100. Default 50.
GET /v1/company/orders Read-only or read-write

List orders

Guests’ table, takeaway and delivery orders and every sale rung up at the register, oldest first — or, when paidSince is set, in the order they were paid. An order still waiting for its upfront payment is left out.

ParameterTypeMeaning
status string Comma-separated: open, paid.
source string customer (placed by the guest) or staff (rung up by staff).
fulfilment string Comma-separated: dine_in, takeaway, delivery.
branchId integer Restrict to one branch.
from ISO 8601 datetime Only orders created on or after this time.
to ISO 8601 datetime Only orders created before this time.
paidSince ISO 8601 datetime Only orders paid since this time; sorts by paidAt ascending, for syncing sales.
limit integer Page size, 1–100. Default 50.
page integer 1-based page number. Default 1.
GET /v1/company/orders/{id} Read-only or read-write

Get an order

One order with its lines, totals and attribution, in the shape order webhooks carry it.

GET /v1/company/loyalty/programs Read-only or read-write

List loyalty programs

Every stamp-card program: name, status, stamps required, the reward and where it applies.

GET /v1/company/loyalty/cards Read-only or read-write

List loyalty cards

Members’ cards, in the loyalty object shape. A member who filled a card has it completed and a fresh active one beside it; customer.id is the member.

ParameterTypeMeaning
programId integer Restrict to one program.
customerId integer Restrict to one member.
status string Comma-separated: active, completed.
updatedSince ISO 8601 datetime Only cards stamped or completed since this time; sorts by updatedAt ascending.
limit integer Page size, 1–100. Default 50.
page integer 1-based page number. Default 1.
GET /v1/company/loyalty/rewards Read-only or read-write

List loyalty rewards

Rewards earned by completing a card, with their code, expiry and whether they were redeemed.

ParameterTypeMeaning
programId integer Restrict to one program.
customerId integer Restrict to one member.
status string Comma-separated: issued, redeemed, expired.
from ISO 8601 datetime Only rewards issued on or after this time.
to ISO 8601 datetime Only rewards issued before this time.
limit integer Page size, 1–100. Default 50.
page integer 1-based page number. Default 1.
GET /v1/company/webhooks Read-only or read-write

List webhook subscriptions

The subscriptions this key made. Webhooks added in the panel are not listed here.

POST /v1/company/webhooks Read-only or read-write

Subscribe a webhook

Starts sending events to a URL — what an automation platform’s app calls when a user switches a trigger on. Returns the subscription and its signing secret, once. JSON body: {"url": "…", "events": ["order.placed"]}; Zapier’s {"target_url": "…", "event": "…"} is accepted too.

ParameterTypeMeaning
url string, required The https address to send to (target_url also accepted).
events array of strings, required Event types, families such as order.* or loyalty, or * for everything (event also accepted, for one).
description string A name shown in the panel. Defaults to the platform and the key’s name.
DELETE /v1/company/webhooks/{id} Read-only or read-write

Unsubscribe a webhook

Stops sending to a subscription this key made. Returns 204. Revoking the key removes all of its subscriptions too.

The booking object

A reservation and an appointment come out in one shared shape: kind, id, status, source, guestInitiated, startsAt/endsAt/timezone (the branch's own zone), currency, company, branch, guest, attribution, conversionId, createdAt/updatedAt — plus partySize and table for a reservation, service and specialist for an appointment. It's exactly what a webhook delivers, so a receiver that handles one handles both.

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…" }
  }
}

The order object

An order comes out in one shape wherever you meet it — the REST API, the order webhooks, the event feed: kind ("order"), id, number (the ticket staff call out), status (open or paid), source (customer or staff), guestInitiated, fulfilment, table, label, deliveryAddress, note, currency, timezone, company, branch, customer (the loyalty member, when staff identified one), items (name, variant, quantity, unitPriceCents, subtotalCents), itemCount, subtotalCents, discountCents, deliveryFeeCents, tipCents, totalCents, valueCents (what the order is worth without the tip — the number conversions report), paymentMethod, servedBy, conversionId, createdAt, paidAt and attribution. Money is always in minor units.

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…" }
  }
}

The loyalty object

Every loyalty event, card and reward shares one shape: kind ("loyalty"), id (the card), timezone, company, branch (where it happened, when there is one), program (id, name, stampsRequired, rewardTitle), customer (id, name, phone, locale), card (id, status, stampsCount, stampsRequired, stampsToReward, createdAt), stamp (for a stamp: source, issuedBy, createdAt) and reward (for a reward: code, title, status, expiresAt, redeemedAt, redeemedBy), plus conversionId, the id a loyalty sign-up is reported under.

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…"
}

Event feed

Each row is { id, cursor, type, createdAt, previousStatus, data }. data holds the subject under the key the event’s prefix names: data.booking, data.order or data.loyalty. Store the highest cursor you’ve processed and pass it back as after — it never skips or repeats a row, so it’s safe to resume after a crash. Ignore types you don’t handle: new ones may be added.

Event typeMeaning
booking.requestedA guest asked for a booking that waits for your confirmation.
booking.confirmedA booking is confirmed — made confirmed, or confirmed later by staff.
booking.cancelledCancelled by the guest or the venue; status says which.
booking.no_showThe guest did not come.
booking.completedThe visit is over.
order.placedA guest’s order reached the venue — from the page, a table’s QR code, takeaway or delivery; after the payment when the venue takes it upfront.
order.paidAny order settled, including sales rung up at the register.
loyalty.member_joinedA customer got their first stamp on a program.
loyalty.stamp_addedA stamp was added to a card.
loyalty.reward_issuedA card was completed and its reward issued.
loyalty.reward_redeemedA reward was handed over.
Stay in sync with the event feed
curl "https://api.tapti.me/v1/company/events?after=998&limit=100" \
  -H "Authorization: Bearer tt_live_…"

Webhooks

Create an endpoint under Integrations → Webhooks & API — or connect Zapier, Make, n8n or Pipedream under Integrations → Automation — to have TapTime POST a signed JSON event the moment a booking, an order or a loyalty card changes. You choose the events per endpoint. A company can have up to 50 webhooks, postbacks, chats and automation connections combined.

HeaderMeaning
TapTime-EventThe event type, e.g. booking.confirmed.
TapTime-Event-IdStable id for the event — use it to deduplicate a retried delivery.
TapTime-DeliveryId of this specific delivery attempt.
TapTime-Signaturet=<unix time>,v1=<hex HMAC-SHA256 of "t.rawBody">, signed with the endpoint's own secret.
  • Any 2xx response is treated as delivered; a non-2xx or a timeout is retried with backoff (1m, 5m, 15m, 1h, 3h, 6h, 12h) up to 8 attempts — a 410 stops retrying.
  • Redirects are not followed.
  • The URL must be HTTPS in production and may not point at a private or loopback address.
  • Delivery is at-least-once: always verify the signature and dedupe on 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", "…": "…" } }
}
Verify the signature (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));

Webhook subscriptions

Automation platforms switch triggers on and off through the API rather than asking the user to paste a URL (“REST hooks”): POST the platform’s own URL with the events it wants, keep the returned id, and DELETE it when the trigger is switched off. A subscription is an ordinary signed webhook — it shows in the panel, marked with the key that made it.

  • A key sees and removes only the subscriptions it made; revoking the key removes them all.
  • Addresses on hooks.zapier.com, make.com and pipedream.net are recognised and shown with the platform’s name; any other https address is a plain webhook.
  • The signing secret is returned once, in the response to the POST; the owner can reveal it again in the panel.
Subscribe a URL to new orders
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

A GET or POST to your tracker’s own URL with macros filled in, for platforms that speak query-string postbacks rather than JSON webhooks. For an order, {value} is the order’s value without the tip and {order_id} its id.

Postback URL with placeholders · GET or POST
https://tracker.example/postback
  ?cid={click_id}&event={event}&status={status}
  &payout={value}&cur={currency}

Available 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}

Errors

Every error is JSON: { "error": "<code>", "message": "<human-readable>" }.

StatuserrorMeaning
401 unauthorized The Authorization header is missing, or the key is invalid or revoked.
403 forbidden A read-only key called a write endpoint (a status change).
404 not_found No booking with that id belongs to the key's company.
409 conflict The status change would double-book the table or slot; nothing was changed.
422 invalid_status The status body value isn't one of the accepted values for that booking type.
422 invalid_type The event feed’s type names no event type or family.
422 invalid_url A subscription URL is not a public https address.
429 rate_limited More than 50 requests in a minute for this key. Slow down and retry.
Need a key?

API access, webhooks and the automation and chat integrations are included with every module — no extra fee, no separate developer plan.

Start a free trial