Cada petición necesita una clave de API de la empresa (Integraciones → Webhooks y API en el panel) enviada como Authorization: Bearer tt_live_…. Las respuestas y los errores son JSON.
curl "https://api.tapti.me/v1/company/reservations?status=confirmed&updatedSince=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer tt_live_…"
Autenticación
Cada petición lleva una clave de API de la empresa como token bearer. Créala desde el panel, en Integraciones → Webhooks y API → Claves de la API REST, donde se muestra una sola vez y se guarda en el servidor únicamente como hash SHA-256; una clave perdida se revoca y se sustituye, no se recupera. Una empresa puede tener hasta 10 claves activas.
- Las claves de solo lectura pueden llamar a todos los endpoints GET y suscribir y cancelar webhooks: una suscripción no envía nada que la clave no pudiera leer ya.
- Las claves de lectura y escritura también pueden hacer POST para cambiar el estado de una reserva; una clave de solo lectura recibe 403 Forbidden en esas rutas.
- Una clave pertenece a una sola empresa: cada listado y consulta ya está limitado a ella, no existe un parámetro companyId.
Límites de peticiones
50 peticiones por minuto y por clave, en ventana deslizante, compartidas por todos los endpoints. Si te pasas, recibes 429 Too Many Requests; espera y vuelve a intentarlo en lugar de consultar más rápido, o suscribe un webhook y deja de consultar.
Endpoints
/v1/company
Solo lectura o lectura y escritura
Obtener la empresa actual
La empresa a la que pertenece la clave y lo que la propia clave puede hacer.
/v1/company/reservations
Solo lectura o lectura y escritura
Listar reservas
Reservas de mesa de la empresa, de la hora de inicio más antigua a la más reciente; o, si se indica updatedSince, de la modificación más antigua a la más reciente.
| Parámetro | Tipo | Significado |
|---|---|---|
status |
cadena | Separados por comas: pending, confirmed, seated, completed, no_show, cancelled. cancelled incluye los dos motivos de cancelación. |
branchId |
entero | Limita a un local. |
from |
fecha y hora ISO 8601 | Solo reservas que empiezan en este momento o después (UTC si no se indica desfase). |
to |
fecha y hora ISO 8601 | Solo reservas que empiezan antes de este momento. |
updatedSince |
fecha y hora ISO 8601 | Solo reservas modificadas desde este momento; cambia el orden a updatedAt ascendente, para sincronizar. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
page |
entero | Número de página empezando por 1. Por defecto, 1. |
/v1/company/appointments
Solo lectura o lectura y escritura
Listar citas
Misma estructura y filtros que las reservas, para el módulo de Citas.
| Parámetro | Tipo | Significado |
|---|---|---|
status |
cadena | Separados por comas: pending, confirmed, completed, no_show, cancelled. |
branchId |
entero | Limita a un local. |
from |
fecha y hora ISO 8601 | Solo citas que empiezan en este momento o después. |
to |
fecha y hora ISO 8601 | Solo citas que empiezan antes de este momento. |
updatedSince |
fecha y hora ISO 8601 | Solo citas modificadas desde este momento; ordena por updatedAt ascendente. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
page |
entero | Número de página empezando por 1. Por defecto, 1. |
/v1/company/reservations/{id}
Solo lectura o lectura y escritura
Obtener una reserva
Una reserva, con la misma estructura con la que la envían los webhooks, incluida su atribución. 404 si el id no pertenece a la empresa de la clave.
/v1/company/appointments/{id}
Solo lectura o lectura y escritura
Obtener una cita
Una cita, con la misma estructura y las mismas reglas que la consulta de una reserva.
/v1/company/reservations/{id}/status
Únicamente lectura y escritura
Cambiar el estado de una reserva
Confirmar, sentar, completar, marcar como no presentado, cancelar o reabrir: dispara los mismos webhooks y conversiones que cambiarlo en el cuadrante. Cuerpo JSON: {"status": "..."}.
| Parámetro | Tipo | Significado |
|---|---|---|
status |
cadena, obligatorio | Uno de: pending, confirmed, seated, completed, no_show, cancelled (también se aceptan canceled y canceled_by_venue). |
/v1/company/appointments/{id}/status
Únicamente lectura y escritura
Cambiar el estado de una cita
Igual que el endpoint de reservas, sin el estado seated.
| Parámetro | Tipo | Significado |
|---|---|---|
status |
cadena, obligatorio | Uno de: pending, confirmed, completed, no_show, cancelled (también se aceptan canceled y canceled_by_venue). |
/v1/company/events
Solo lectura o lectura y escritura
Consultar el feed de eventos
Cada evento de reservas, pedidos y fidelización, del más antiguo al más reciente: el mismo feed del que salen los webhooks. Pasa el último cursor que viste como after para obtener solo lo nuevo, y type para obtener solo algunos tipos.
| Parámetro | Tipo | Significado |
|---|---|---|
after |
cursor entero | El cursor del último evento que procesaste. Omítelo para empezar desde el principio. |
type |
cadena | Tipos o familias de eventos separados por comas: booking.confirmed,order.placed, u order, loyalty.*. Omítelo para recibir todos los eventos. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
/v1/company/orders
Solo lectura o lectura y escritura
Listar pedidos
Pedidos de mesa, para llevar y a domicilio de los clientes y cada venta registrada en caja, del más antiguo al más reciente; o, si se indica paidSince, en el orden en que se pagaron. Un pedido que sigue esperando su pago por adelantado no se incluye.
| Parámetro | Tipo | Significado |
|---|---|---|
status |
cadena | Separados por comas: open, paid. |
source |
cadena | customer (lo hizo el cliente) o staff (lo registró el personal). |
fulfilment |
cadena | Separados por comas: dine_in, takeaway, delivery. |
branchId |
entero | Limita a un local. |
from |
fecha y hora ISO 8601 | Solo pedidos creados en este momento o después. |
to |
fecha y hora ISO 8601 | Solo pedidos creados antes de este momento. |
paidSince |
fecha y hora ISO 8601 | Solo pedidos pagados desde este momento; ordena por paidAt ascendente, para sincronizar ventas. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
page |
entero | Número de página empezando por 1. Por defecto, 1. |
/v1/company/orders/{id}
Solo lectura o lectura y escritura
Obtener un pedido
Un pedido con sus líneas, totales y atribución, con la misma estructura con la que lo envían los webhooks de pedidos.
/v1/company/loyalty/programs
Solo lectura o lectura y escritura
Listar programas de fidelización
Cada programa de tarjeta de sellos: nombre, estado, sellos necesarios, la recompensa y dónde se aplica.
/v1/company/loyalty/cards
Solo lectura o lectura y escritura
Listar tarjetas de fidelización
Las tarjetas de los miembros, con la estructura del objeto fidelización. Un miembro que completó una tarjeta la tiene en estado completed y otra nueva activa a su lado; customer.id es el miembro.
| Parámetro | Tipo | Significado |
|---|---|---|
programId |
entero | Limita a un programa. |
customerId |
entero | Limita a un miembro. |
status |
cadena | Separados por comas: active, completed. |
updatedSince |
fecha y hora ISO 8601 | Solo tarjetas selladas o completadas desde este momento; ordena por updatedAt ascendente. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
page |
entero | Número de página empezando por 1. Por defecto, 1. |
/v1/company/loyalty/rewards
Solo lectura o lectura y escritura
Listar recompensas de fidelización
Recompensas obtenidas al completar una tarjeta, con su código, su caducidad y si se canjearon.
| Parámetro | Tipo | Significado |
|---|---|---|
programId |
entero | Limita a un programa. |
customerId |
entero | Limita a un miembro. |
status |
cadena | Separados por comas: issued, redeemed, expired. |
from |
fecha y hora ISO 8601 | Solo recompensas emitidas en este momento o después. |
to |
fecha y hora ISO 8601 | Solo recompensas emitidas antes de este momento. |
limit |
entero | Tamaño de página, 1–100. Por defecto, 50. |
page |
entero | Número de página empezando por 1. Por defecto, 1. |
/v1/company/webhooks
Solo lectura o lectura y escritura
Listar suscripciones de webhooks
Las suscripciones que creó esta clave. Los webhooks añadidos en el panel no aparecen aquí.
/v1/company/webhooks
Solo lectura o lectura y escritura
Suscribir un webhook
Empieza a enviar eventos a una URL: es lo que llama la app de una plataforma de automatización cuando un usuario activa un disparador. Devuelve la suscripción y su secreto de firma, una sola vez. Cuerpo JSON: {"url": "…", "events": ["order.placed"]}; también se acepta el formato de Zapier {"target_url": "…", "event": "…"}.
| Parámetro | Tipo | Significado |
|---|---|---|
url |
cadena, obligatorio | La dirección https a la que enviar (también se acepta target_url). |
events |
array de cadenas, obligatorio | Tipos de evento, familias como order.* o loyalty, o * para todo (también se acepta event, para uno solo). |
description |
cadena | Un nombre que se muestra en el panel. Por defecto, la plataforma y el nombre de la clave. |
/v1/company/webhooks/{id}
Solo lectura o lectura y escritura
Cancelar la suscripción de un webhook
Deja de enviar a una suscripción que creó esta clave. Devuelve 204. Revocar la clave también elimina todas sus suscripciones.
El objeto reserva
Una reserva de mesa y una cita salen con una misma estructura compartida: kind, id, status, source, guestInitiated, startsAt/endsAt/timezone (la zona horaria del propio local), currency, company, branch, guest, attribution, conversionId, createdAt/updatedAt, más partySize y table en una reserva, y service y specialist en una cita. Es exactamente lo que entrega un webhook, así que un receptor que gestiona una gestiona las dos.
{
"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…" }
}
}
El objeto pedido
Un pedido tiene la misma estructura dondequiera que lo encuentres (la API REST, los webhooks de pedidos, el feed de eventos): kind ("order"), id, number (el número que canta el personal), status (open o paid), source (customer o staff), guestInitiated, fulfilment, table, label, deliveryAddress, note, currency, timezone, company, branch, customer (el miembro de fidelización, cuando el personal lo identificó), items (name, variant, quantity, unitPriceCents, subtotalCents), itemCount, subtotalCents, discountCents, deliveryFeeCents, tipCents, totalCents, valueCents (lo que vale el pedido sin la propina: la cifra que reportan las conversiones), paymentMethod, servedBy, conversionId, createdAt, paidAt y attribution. Los importes siempre van en unidades menores.
{
"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…" }
}
}
El objeto fidelización
Cada evento, tarjeta y recompensa de fidelización comparte una estructura: kind ("loyalty"), id (la tarjeta), timezone, company, branch (dónde ocurrió, cuando lo hay), program (id, name, stampsRequired, rewardTitle), customer (id, name, phone, locale), card (id, status, stampsCount, stampsRequired, stampsToReward, createdAt), stamp (en un sello: source, issuedBy, createdAt) y reward (en una recompensa: code, title, status, expiresAt, redeemedAt, redeemedBy), más conversionId, el id con el que se reporta un alta en fidelización.
{
"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…"
}
Feed de eventos
Cada fila es { id, cursor, type, createdAt, previousStatus, data }. data contiene el objeto bajo la clave que indica el prefijo del evento: data.booking, data.order o data.loyalty. Guarda el cursor más alto que hayas procesado y devuélvelo como after: nunca se salta ni repite una fila, así que es seguro reanudar tras un fallo. Ignora los tipos que no gestiones: pueden añadirse otros nuevos.
| Tipo de evento | Significado |
|---|---|
booking.requested | Un cliente ha pedido una reserva que espera tu confirmación. |
booking.confirmed | Una reserva está confirmada: creada ya confirmada o confirmada después por el personal. |
booking.cancelled | Cancelada por el cliente o por el local; status indica quién. |
booking.no_show | El cliente no se presentó. |
booking.completed | La visita ha terminado. |
order.placed | El pedido de un cliente ha llegado al local, desde la página, el código QR de una mesa, para llevar o a domicilio; después del pago cuando el local cobra por adelantado. |
order.paid | Cualquier pedido liquidado, incluidas las ventas registradas en caja. |
loyalty.member_joined | Un cliente ha recibido su primer sello en un programa. |
loyalty.stamp_added | Se ha añadido un sello a una tarjeta. |
loyalty.reward_issued | Se ha completado una tarjeta y se ha emitido su recompensa. |
loyalty.reward_redeemed | Se ha entregado una recompensa. |
curl "https://api.tapti.me/v1/company/events?after=998&limit=100" \
-H "Authorization: Bearer tt_live_…"
Webhooks
Crea un endpoint en Integraciones → Webhooks y API (o conecta Zapier, Make, n8n o Pipedream en Integraciones → Automatización) para que TapTime envíe por POST un evento JSON firmado en el momento en que cambia una reserva, un pedido o una tarjeta de fidelización. Tú eliges los eventos de cada endpoint. Una empresa puede tener hasta 50 webhooks, postbacks, chats y conexiones de automatización en total.
| Cabecera | Significado |
|---|---|
TapTime-Event | El tipo de evento, p. ej. booking.confirmed. |
TapTime-Event-Id | Id estable del evento: úsalo para descartar duplicados de un envío reintentado. |
TapTime-Delivery | Id de este intento de envío concreto. |
TapTime-Signature | t=<unix time>,v1=<hex HMAC-SHA256 de "t.rawBody">, firmado con el secreto propio del endpoint. |
- Cualquier respuesta 2xx se considera entregada; una respuesta que no sea 2xx o un tiempo de espera agotado se reintenta con espera progresiva (1 min, 5 min, 15 min, 1 h, 3 h, 6 h, 12 h) hasta 8 intentos; un 410 detiene los reintentos.
- No se siguen las redirecciones.
- La URL debe ser HTTPS en producción y no puede apuntar a una dirección privada o de loopback.
- La entrega es al menos una vez: verifica siempre la firma y descarta duplicados por 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));
Suscripciones de webhooks
Las plataformas de automatización activan y desactivan disparadores a través de la API en lugar de pedir al usuario que pegue una URL («REST hooks»): haz POST con la URL propia de la plataforma y los eventos que quiere, guarda el id devuelto y haz DELETE cuando se desactive el disparador. Una suscripción es un webhook firmado normal: aparece en el panel, marcada con la clave que la creó.
- Una clave solo ve y elimina las suscripciones que creó; revocar la clave las elimina todas.
- Las direcciones de hooks.zapier.com, make.com y pipedream.net se reconocen y se muestran con el nombre de la plataforma; cualquier otra dirección https es un webhook normal.
- El secreto de firma se devuelve una sola vez, en la respuesta al POST; el propietario puede volver a verlo en el panel.
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
Un GET o POST a la URL propia de tu tracker con las macros rellenadas, para plataformas que trabajan con postbacks por query string en lugar de webhooks JSON. En un pedido, {value} es el valor del pedido sin la propina y {order_id} su id.
https://tracker.example/postback
?cid={click_id}&event={event}&status={status}
&payout={value}&cur={currency}
Marcadores disponibles
{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}
Errores
Todos los errores son JSON: { "error": "<code>", "message": "<texto legible>" }.
| Estado | error | Significado |
|---|---|---|
| 401 | unauthorized |
Falta la cabecera Authorization o la clave no es válida o ha sido revocada. |
| 403 | forbidden |
Una clave de solo lectura ha llamado a un endpoint de escritura (un cambio de estado). |
| 404 | not_found |
Ninguna reserva con ese id pertenece a la empresa de la clave. |
| 409 | conflict |
El cambio de estado provocaría una doble reserva de la mesa o del hueco; no se ha cambiado nada. |
| 422 | invalid_status |
El valor de status del cuerpo no es uno de los aceptados para ese tipo de reserva. |
| 422 | invalid_type |
El parámetro type del feed de eventos no corresponde a ningún tipo ni familia de eventos. |
| 422 | invalid_url |
La URL de una suscripción no es una dirección https pública. |
| 429 | rate_limited |
Más de 50 peticiones en un minuto para esta clave. Reduce el ritmo y vuelve a intentarlo. |
El acceso a la API, los webhooks y las integraciones de automatización y chat se incluyen con cada módulo: sin coste adicional ni plan aparte para desarrolladores.
Empieza la prueba gratuita