Aller au contenu

Catalogue des événements

Voici les types d’événements auxquels vous pouvez vous abonner. Chaque événement utilise l’enveloppe standard ; les exemples ci-dessous montrent l’enveloppe complète avec un data.object représentatif.

Gérez les types d’événements inconnus avec souplesse — de nouveaux types peuvent être ajoutés au fil du temps.

Événement Quand il se déclenche
reservation.created Une réservation est créée, ou, sur un créneau à garantie, sa carte est enregistrée — son status est confirmed (auto-confirmée) ou pending (en attente d’approbation).
reservation.pending Une réservation confirmée a été modifiée, par le client ou par un PATCH, d’une façon qui exige une nouvelle validation.
reservation.confirmed Une réservation en attente est approuvée par la suite. Les réservations auto-confirmées ne déclenchent pas cet événement.
reservation.declined Une réservation en attente est refusée.
reservation.updated Les détails d’une réservation changent (taille du groupe, date, notes, …).
reservation.partially_seated Une partie du groupe, mais pas la totalité, a été installée.
reservation.seated Le groupe a été installé.
reservation.completed La réservation est terminée (le groupe est parti).
reservation.cancelled La réservation est annulée.
reservation.no_show Le groupe ne s’est pas présenté.
guest.created Une fiche client est créée.
guest.updated Les détails d’un client changent.
guest.deleted Un client est supprimé (par ex. effacement pour protection des données).
guest.merged Deux fiches client ont été fusionnées. L’identifiant absorbé renvoie désormais vers la fiche conservée.
guest.unmerged Une fusion a été annulée et la fiche absorbée rétablie.
reservation.guarantee_requested Une garantie par carte a été demandée pour une réservation.
reservation.guarantee_charged La carte a été débitée — en totalité ou en partie.
reservation.guarantee_refunded Un débit a été remboursé, en totalité ou en partie.
reservation.guarantee_released La garantie a pris fin sans aucun débit.
reservation.guarantee_payment_failed Une tentative de débit a échoué.
feedback.received Un client soumet un avis après une visite.
waitlist_entry.created Un client a rejoint la liste d’attente.
waitlist_entry.promoted Une demande en liste d’attente est devenue une réservation.
waitlist_entry.updated Un client a modifié sa demande en liste d’attente — le créneau, le nombre de couverts, ou les deux.
waitlist_entry.cancelled Une demande en liste d’attente a été retirée par le client ou par le restaurant.
waitlist_entry.expired Une demande en liste d’attente a expiré et a été clôturée.
hold.created Une option a été posée sur un créneau. La table est occupée, et personne n’a réservé.
hold.converted Une option est devenue la réservation qu’elle désigne. Comptez-la comme une libération de l’option.
hold.released Une option a été rendue avant son échéance.
hold.expired Une option a expiré.

Le data.object de chaque événement de réservation est une réservation complète, dans la même forme que celle renvoyée par la référence de l’API (en anglais) — l’instantané complet de la ressource, pas un diff, coordonnées et notes du client comprises (voir Les livraisons portent les données client). Il porte toujours company : l’entreprise pour le compte de laquelle la réservation est faite, sous la forme { "object": "company", "id": "cmp_…", "name": … }, ou null.

Seul reservation.updated porte data.previous_attributes ; les autres événements de réservation l’omettent. Lorsqu’il est présent, previous_attributes ne contient que les champs modifiés parmi party_size, service_date, guest_notes, internal_notes, excluded_from_shift_limits, guest (le client précédent sous forme de simple identifiant gst_) et company (l’objet de l’entreprise précédente, ou null s’il n’y en avait pas). company est absent lorsque l’entreprise précédente a été supprimée depuis. Les changements d’horaire sont reflétés dans les starts_at / ends_at de l’instantané, pas dans previous_attributes.

Une réservation est créée (via le widget, le back-office, une intégration, une promotion depuis la liste d’attente ou la conversion d’une option). Cet événement se déclenche une fois par réservation — vérifiez data.object.status pour les distinguer : confirmed signifie qu’elle a été auto-confirmée à la création (sans étape d’approbation ; cas typique du back-office et des réservations par défaut), tandis que pending signifie qu’elle nécessite une approbation et émettra ensuite reservation.confirmed ou reservation.declined.

Trois sortes de réservation ne le déclenchent pas à leur création :

  • Une réservation sur un créneau qui porte une garantie par carte. L’événement est retenu jusqu’à ce que le client enregistre sa carte, que le restaurant accepte la réservation sans carte, ou qu’un PATCH supprime le besoin de carte, et se déclenche à ce moment-là. Aucun autre événement reservation.* sur cette réservation n’est envoyé avant lui. Si la carte n’est jamais enregistrée et que la réservation est annulée, reservation.created ne se déclenche jamais, et reservation.cancelled non plus. Voir Les réservations à garantie arrivent dans le désordre.
  • Une option. La poser déclenche hold.created, pas cet événement ; la convertir déclenche les deux. Voir Une conversion déclenche deux événements.
  • Une réservation importée d’un autre système de réservation lorsque le restaurant est passé à Service. Les imports ne déclenchent aucun webhook reservation.*.
{
"id": "evt_1K8xQ2m4Vd0pErJ7sN1aZ9bQ",
"object": "event",
"type": "reservation.created",
"created": "2026-06-20T09:04:18Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "pending",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 4,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-20T11:04:18+02:00"
}
}
}

Une réservation déjà confirmée a été modifiée d’une façon qui exige une nouvelle validation du restaurant. Elle est repassée en pending. La modification peut venir du client, sur sa propre page de réservation, ou d’un PATCH /v1/reservations/{id} via cette API : un groupe plus grand, ou un passage sur un service où le restaurant valide les réservations.

Une modification d’une réservation déjà en pending ne déclenche pas cet événement. Elle arrive en reservation.updated, et la réservation reste en pending.

{
"id": "evt_H8iJ9kL0mN1oP2qR3sT4uV5w",
"object": "event",
"type": "reservation.pending",
"created": "2026-07-14T09:22:31Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_DA2ipZ7Enk01lpIhd0saMQ1e",
"status": "pending",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 4,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_FD7tTyOA9c-ZpPpeF0XMhkZI",
"company": null,
"tables": [],
"guarantee": null,
"metadata": {},
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-14T11:22:31+02:00"
}
}
}

Une réservation en attente est approuvée — le flux d’approbation manuelle. Cet événement se déclenche uniquement pour les réservations créées en pending ; une réservation auto-confirmée n’émet jamais reservation.confirmed : elle arrive une seule fois, en tant que reservation.created avec status: "confirmed".

{
"id": "evt_2M9yR3n5We1qFsK8tO2bA0cR",
"object": "event",
"type": "reservation.confirmed",
"created": "2026-06-20T09:30:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "confirmed",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 4,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-20T11:30:00+02:00"
}
}
}

Une réservation en attente est refusée.

{
"id": "evt_3N0zS4o6Xf2rGtL9uP3cB1dS",
"object": "event",
"type": "reservation.declined",
"created": "2026-06-20T09:31:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "cancelled",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 4,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-20T11:31:00+02:00"
}
}
}

Les détails d’une réservation changent — par exemple la taille du groupe ou les notes. previous_attributes liste les champs modifiés et leurs valeurs précédentes (ici, la taille du groupe est passée de 4 à 6, la note du client a changé, le restaurant a écrit sa propre note et la réservation a été rattachée au compte d’une entreprise). Un changement portant sur le seul internal_notes déclenche cet événement comme n’importe quel autre changement.

{
"id": "evt_4O1aT5p7Yg3sHuM0vQ4dC2eT",
"object": "event",
"type": "reservation.updated",
"created": "2026-06-25T07:12:55Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "confirmed",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table; celebrating an anniversary",
"internal_notes": "Regulars — the corner banquette if it is free",
"guest": "gst_3Td9Lp0WqZ",
"company": {
"object": "company",
"id": "cmp_7Hk2Rw9XsB",
"name": "Atelier Morel"
},
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-25T09:12:55+02:00"
},
"previous_attributes": {
"party_size": 4,
"guest_notes": "Window table if possible",
"internal_notes": null,
"company": null
}
}
}

Une partie du groupe, mais pas la totalité, a été installée.

{
"id": "evt_5P2bU6q8Zh4tIvN1wR5eD3fU",
"object": "event",
"type": "reservation.partially_seated",
"created": "2026-06-27T18:05:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "partially_seated",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [
{
"object": "table",
"id": "tbl_QpZ2",
"name": "12",
"min_capacity": 2,
"max_capacity": 6,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-27T20:05:00+02:00"
}
}
}

Le groupe a été installé.

{
"id": "evt_6Q3cV7r9Ai5uJwO2xS6fE4gV",
"object": "event",
"type": "reservation.seated",
"created": "2026-06-27T18:08:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "seated",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [
{
"object": "table",
"id": "tbl_QpZ2",
"name": "12",
"min_capacity": 2,
"max_capacity": 6,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-27T20:08:00+02:00"
}
}
}

La réservation est terminée — le groupe est parti.

{
"id": "evt_7R4dW8s0Bj6vKxP3yT7gF5hW",
"object": "event",
"type": "reservation.completed",
"created": "2026-06-27T20:15:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "completed",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-27T22:15:00+02:00"
}
}
}

La réservation est annulée.

{
"id": "evt_8S5eX9t1Ck7wLyQ4zU8hG6iX",
"object": "event",
"type": "reservation.cancelled",
"created": "2026-06-26T16:00:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "cancelled",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-26T18:00:00+02:00"
}
}
}

Le groupe ne s’est pas présenté.

{
"id": "evt_9T6fY0u2Dl8xMzR5aV9iH7jY",
"object": "event",
"type": "reservation.no_show",
"created": "2026-06-27T18:45:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ",
"status": "no_show",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 6,
"service_date": "2026-06-27",
"starts_at": "2026-06-27T20:00:00+02:00",
"ends_at": "2026-06-27T22:00:00+02:00",
"guest_notes": "Window table if possible",
"internal_notes": null,
"guest": "gst_3Td9Lp0WqZ",
"company": null,
"tables": [],
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-27T20:45:00+02:00"
}
}
}

Une garantie par carte est une carte de paiement retenue sur une réservation — une empreinte débitée uniquement si le client n’honore pas la réservation, au titre de la politique de non-présentation du restaurant.

Ces cinq identifiants commencent par reservation. parce que le data.object est la réservation, qui porte la garantie sous la clé guarantee. Ils sont en revanche émis par le cycle de vie de la garantie et non par celui de la réservation, si bien qu’ils n’apparaissent pas dans le flux GET /v1/reservations/{id}/events, qui ne liste que les événements de réservation. Les webhooks sont le seul moyen de les observer.

Les montants sont des entiers exprimés dans la plus petite unité de la devise : 5000 vaut 50,00 €.

Une demande qui prend fin avant qu’une carte soit enregistrée passe en state: "withdrawn" et ne déclenche aucun des cinq : la réservation a été annulée, le restaurant a retiré la demande, ou une modification a rendu la carte inutile. Rien n’a été débité. Vous lisez l’état sur la réservation.

Le restaurant a demandé une carte au client sur une réservation qui existe déjà. La réservation repasse en awaiting_guarantee, l’état de la garantie est awaiting_card, et aucun montant n’a été débité. origin vaut staff_request, card et consented_at valent null tant que le client n’a pas enregistré de carte, et expires_at est l’échéance de paiement.

Une création via cette API sur un créneau à garantie ne déclenche pas cet événement. Cette réservation n’est publiée qu’une fois la carte enregistrée ou la demande retirée : vous l’apprenez par reservation.created, avec la garantie déjà active, ou withdrawn quand un PATCH a supprimé le besoin de carte. Son origin vaut aussi staff_request : la carte est demandée par le même lien de paiement.

{
"id": "evt_L1mN2oP3qR4sT5uV6wX7yZ8a",
"object": "event",
"type": "reservation.guarantee_requested",
"created": "2026-07-11T09:12:04Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"status": "awaiting_guarantee",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 2,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"company": null,
"tables": [],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "awaiting_card",
"origin": "staff_request",
"amount": 5000,
"currency": "EUR",
"charged_amount": 0,
"refunded_amount": 0,
"card": null,
"cancel_deadline_at": "2026-07-17T20:00:00+02:00",
"expires_at": "2026-07-13T11:12:04+02:00",
"consented_at": null,
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-11T11:12:04+02:00"
},
"metadata": {},
"created_at": "2026-07-09T18:40:12+02:00",
"updated_at": "2026-07-11T11:12:04+02:00"
}
}
}

La carte a été débitée.

{
"id": "evt_M2nO3pQ4rS5tU6vW7xY8zA9b",
"object": "event",
"type": "reservation.guarantee_charged",
"created": "2026-07-18T20:31:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"status": "no_show",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 2,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"company": null,
"tables": [],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "charged",
"origin": "online_booking",
"amount": 5000,
"currency": "EUR",
"charged_amount": 5000,
"refunded_amount": 0,
"card": { "brand": "visa", "last4": "4242" },
"cancel_deadline_at": "2026-07-17T20:00:00+02:00",
"expires_at": null,
"consented_at": "2026-07-11T11:12:04+02:00",
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
},
"metadata": {},
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
}
}
}

Un débit a été remboursé, en totalité ou en partie. refunded_amount est le cumul remboursé, et non le montant de ce remboursement précis : un second remboursement partiel augmente ce cumul plutôt que d’émettre un écart.

{
"id": "evt_N3oP4qR5sT6uV7wX8yZ9aB0c",
"object": "event",
"type": "reservation.guarantee_refunded",
"created": "2026-07-19T08:00:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"status": "no_show",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 2,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"company": null,
"tables": [],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "refunded",
"origin": "online_booking",
"amount": 5000,
"currency": "EUR",
"charged_amount": 5000,
"refunded_amount": 2500,
"card": { "brand": "visa", "last4": "4242" },
"cancel_deadline_at": "2026-07-17T20:00:00+02:00",
"expires_at": null,
"consented_at": "2026-07-11T11:12:04+02:00",
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
},
"metadata": {},
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
}
}
}

La garantie a pris fin sans débit — le client a honoré la réservation, ou le restaurant a levé l’empreinte. Rien n’a été prélevé ; charged_amount vaut 0.

{
"id": "evt_O4pQ5rS6tU7vW8xY9zA0bC1d",
"object": "event",
"type": "reservation.guarantee_released",
"created": "2026-07-18T22:30:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"status": "completed",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 2,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"company": null,
"tables": [],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "released",
"origin": "online_booking",
"amount": 5000,
"currency": "EUR",
"charged_amount": 0,
"refunded_amount": 0,
"card": { "brand": "visa", "last4": "4242" },
"cancel_deadline_at": "2026-07-17T20:00:00+02:00",
"expires_at": null,
"consented_at": "2026-07-11T11:12:04+02:00",
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
},
"metadata": {},
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
}
}
}

Une tentative de débit a échoué : carte expirée, refus, provision insuffisante. La garantie reste débitable jusqu’à la fermeture de sa fenêtre. Cet événement n’est pas nécessairement définitif : une tentative ultérieure peut réussir et émettre reservation.guarantee_charged.

{
"id": "evt_P5qR6sT7uV8wX9yZ0aB1cD2e",
"object": "event",
"type": "reservation.guarantee_payment_failed",
"created": "2026-07-18T20:31:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "reservation",
"id": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"status": "no_show",
"source": "widget",
"booking_channel": "website_widget",
"party_size": 2,
"service_date": "2026-07-18",
"starts_at": "2026-07-18T20:00:00+02:00",
"ends_at": "2026-07-18T22:00:00+02:00",
"guest_notes": null,
"contact_email": "[email protected]",
"contact_phone": "+33612345678",
"internal_notes": null,
"guest": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"company": null,
"tables": [],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "charge_failed",
"origin": "online_booking",
"amount": 5000,
"currency": "EUR",
"charged_amount": 0,
"refunded_amount": 0,
"card": { "brand": "visa", "last4": "4242" },
"cancel_deadline_at": "2026-07-17T20:00:00+02:00",
"expires_at": null,
"consented_at": "2026-07-11T11:12:04+02:00",
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
},
"metadata": {},
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T22:31:00+02:00"
}
}
}

Le data.object d’un événement de client est un client complet, dans la même forme que celle renvoyée par la référence de l’API (en anglais) ; ses notes sont les notes de l’équipe du restaurant sur la fiche client. Les événements de client ne portent pas previous_attributes — comparez avec votre propre copie stockée si vous avez besoin d’un diff.

Un client a un prénom, un nom ou les deux : first_name ou last_name peut valoir null, jamais les deux à la fois. Un client connu sous un seul nom le garde dans le champ où il a été enregistré.

Une fiche client est créée.

{
"id": "evt_A1bC2dE3fG4hI5jK6lM7nO8p",
"object": "event",
"type": "guest.created",
"created": "2026-06-20T09:04:18Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "guest",
"id": "gst_3Td9Lp0WqZ",
"first_name": "Marie",
"last_name": "Dupont",
"email": "[email protected]",
"phone": "+33612345678",
"notes": null,
"blacklisted": false,
"blacklist_reason": null,
"dietary_preferences": [],
"allergies": [],
"birthday": null,
"anniversary": null,
"vip": false,
"language": "fr",
"source": "widget",
"marketing_email_consent": true,
"marketing_email_consent_at": "2026-06-20T11:04:18+02:00",
"marketing_sms_consent": false,
"marketing_sms_consent_at": null,
"visit_count": 0,
"no_show_count": 0,
"cancellation_count": 0,
"first_visit_at": null,
"last_visit_at": null,
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-20T11:04:18+02:00"
}
}
}

Les détails d’un client changent. (Les seules statistiques de visite ne déclenchent pas cet événement — voir Pagination.)

{
"id": "evt_B2cD3eF4gH5iJ6kL7mN8oP9q",
"object": "event",
"type": "guest.updated",
"created": "2026-06-28T08:00:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "guest",
"id": "gst_3Td9Lp0WqZ",
"first_name": "Marie",
"last_name": "Dupont",
"email": "[email protected]",
"phone": "+33612345678",
"notes": null,
"blacklisted": false,
"blacklist_reason": null,
"dietary_preferences": ["vegetarian"],
"allergies": [],
"birthday": null,
"anniversary": null,
"vip": true,
"language": "fr",
"source": "widget",
"marketing_email_consent": true,
"marketing_email_consent_at": "2026-06-20T11:04:18+02:00",
"marketing_sms_consent": false,
"marketing_sms_consent_at": null,
"visit_count": 3,
"no_show_count": 0,
"cancellation_count": 0,
"first_visit_at": "2026-06-21T21:00:00+02:00",
"last_visit_at": "2026-06-27T20:00:00+02:00",
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-28T10:00:00+02:00"
}
}
}

Un client est supprimé (par exemple, une demande d’effacement pour protection des données). Le data.object est un instantané final du client complet (la même forme que toute autre charge utile de client) ; le client n’est plus récupérable depuis l’API par la suite.

{
"id": "evt_C3dE4fG5hI6jK7lM8nO9pQ0r",
"object": "event",
"type": "guest.deleted",
"created": "2026-06-29T07:00:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "guest",
"id": "gst_3Td9Lp0WqZ",
"first_name": "Marie",
"last_name": "Dupont",
"email": "[email protected]",
"phone": "+33612345678",
"notes": null,
"blacklisted": false,
"blacklist_reason": null,
"dietary_preferences": ["vegetarian"],
"allergies": [],
"birthday": null,
"anniversary": null,
"vip": true,
"language": "fr",
"source": "widget",
"marketing_email_consent": true,
"marketing_email_consent_at": "2026-06-20T11:04:18+02:00",
"marketing_sms_consent": false,
"marketing_sms_consent_at": null,
"visit_count": 3,
"no_show_count": 0,
"cancellation_count": 0,
"first_visit_at": "2026-06-21T21:00:00+02:00",
"last_visit_at": "2026-06-27T20:00:00+02:00",
"created_at": "2026-06-20T11:04:18+02:00",
"updated_at": "2026-06-28T10:00:00+02:00"
}
}
}

Deux fiches client ont été fusionnées — le plus souvent parce que la même personne avait été enregistrée deux fois. Le data.object est la fiche conservée.

data.previous_attributes n’est pas utilisé ici. L’identifiant de la fiche absorbée n’apparaît pas dans la charge utile : récupérez la fiche conservée si vous avez besoin de l’état fusionné, et notez que l’identifiant absorbé continue de répondre — GET /v1/guests/{identifiant_absorbé} renvoie la fiche conservée plutôt qu’une erreur 404, si bien que les références que vous détenez déjà restent valides.

{
"id": "evt_J9kL0mN1oP2qR3sT4uV5wX6y",
"object": "event",
"type": "guest.merged",
"created": "2026-07-02T14:05:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "guest",
"id": "gst_e_I4WiTGNl0S1v7W2G6M6ygk",
"first_name": "Marie",
"last_name": "Dupont",
"email": "[email protected]",
"phone": "+33612345678",
"notes": null,
"blacklisted": false,
"blacklist_reason": null,
"dietary_preferences": [],
"allergies": [],
"birthday": null,
"anniversary": null,
"vip": false,
"language": "fr",
"source": "manual",
"marketing_email_consent": false,
"marketing_email_consent_at": null,
"marketing_sms_consent": false,
"marketing_sms_consent_at": null,
"visit_count": 4,
"no_show_count": 0,
"cancellation_count": 0,
"first_visit_at": "2026-05-02T20:00:00+02:00",
"last_visit_at": "2026-06-27T20:00:00+02:00",
"created_at": "2026-05-01T10:00:00+02:00",
"updated_at": "2026-07-02T16:05:00+02:00"
}
}
}

Une fusion a été annulée et la fiche absorbée rétablie comme client distinct. Le data.object est la fiche rétablie, celle qui avait été absorbée.

Ensuite, l’identifiant rétabli renvoie de nouveau vers lui-même et non vers la fiche conservée. Si vous aviez mis en cache la correspondance issue de guest.merged, supprimez-la.

{
"id": "evt_K0lM1nO2pQ3rS4tU5vW6xY7z",
"object": "event",
"type": "guest.unmerged",
"created": "2026-07-03T09:30:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "guest",
"id": "gst_FD7tTyOA9c-ZpPpeF0XMhkZI",
"first_name": "Marie",
"last_name": "Dupond",
"email": "[email protected]",
"phone": null,
"notes": null,
"blacklisted": false,
"blacklist_reason": null,
"dietary_preferences": [],
"allergies": [],
"birthday": null,
"anniversary": null,
"vip": false,
"language": "fr",
"source": "widget",
"marketing_email_consent": false,
"marketing_email_consent_at": null,
"marketing_sms_consent": false,
"marketing_sms_consent_at": null,
"visit_count": 1,
"no_show_count": 0,
"cancellation_count": 0,
"first_visit_at": "2026-06-01T20:00:00+02:00",
"last_visit_at": "2026-06-01T20:00:00+02:00",
"created_at": "2026-06-01T18:00:00+02:00",
"updated_at": "2026-07-03T11:30:00+02:00"
}
}
}

Un client soumet un avis après une visite. Le data.object est un objet-valeur d’avis intégré (ce n’est pas une ressource d’API récupérable) portant les notes et le commentaire, ainsi que des références sous forme de simples identifiants vers le client et la réservation auxquels il se rapporte.

{
"id": "evt_D4eF5gH6iJ7kL8mN9oP0qR1s",
"object": "event",
"type": "feedback.received",
"created": "2026-06-28T11:20:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "feedback",
"id": "fb_7hn2qp",
"overall_rating": 5,
"food_rating": 5,
"service_rating": 4,
"comment": "Lovely evening, great service.",
"responded_at": "2026-06-28T13:20:00+02:00",
"guest": "gst_3Td9Lp0WqZ",
"reservation": "resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ"
}
}
}

Les champs overall_rating, food_rating, service_rating et comment peuvent chacun valoir null lorsque le client ne les a pas fournis. Utilisez cet événement pour inviter les clients à laisser un avis, acheminer les avis vers la bonne équipe, ou synchroniser les notes dans vos propres outils.

Une demande en liste d’attente est la demande d’un client d’être prévenu si une table se libère à une date de service donnée. Ce n’est pas une réservation et elle ne retient aucune table.

Le data.object de chaque événement de liste d’attente est une demande en liste d’attente complète, dans la forme que renvoie l’API.

La réservation qu’une proposition ouverte retient porte le statut awaiting_guest : une vraie table, retenue pour un seul client jusqu’à sa réponse. Son pendant awaiting_guarantee retient une table le temps qu’une carte soit enregistrée.

Les deux ne vous sont pas visibles de la même façon. Une empreinte demandée sur une réservation que vous connaissez déjà reste dans votre flux et c’est elle que porte reservation.guarantee_requested. Derrière un awaiting_guest, rien n’a jamais été publié. La réservation est retirée de tous les points d’entrée : la liste, le flux updated_since, la récupération par identifiant et le flux d’événements. ?status=awaiting_guest est accepté et ne renvoie rien. Une proposition refusée par le client, ou arrivée à échéance, devient cancelled et reste masquée sous ce statut également.

Lisez awaiting_guest comme un statut qui ne vous sera jamais envoyé. Il figure dans l’énumération de la référence parce que celle-ci liste tous les statuts qu’une réservation peut prendre en interne. Il ne vous parvient avec une réservation attachée qu’à un seul endroit : created_status sur l’entrée reservation.created du flux d’événements, une fois que le client a accepté.

reservation vaut null tant que la demande n’est pas devenue une réservation que vous pouvez récupérer. Une demande en cours de proposition porte bien une réservation en interne, mais celle-ci n’est pas encore publiée. Le champ reste null plutôt que de vous transmettre un identifiant qui renverrait une erreur 404. Une fois renseigné, GET /v1/reservations/{id} le résout.

Lorsqu’une demande devient une réservation, vous recevez à la fois waitlist_entry.promoted et l’événement habituel reservation.created, dont le source vaut waitlist.

C’est délibéré : un consommateur qui ne traite que les événements reservation.* reste complet et voit la réservation apparaître comme n’importe quelle autre, tandis qu’un consommateur qui suit la file apprend de quelle demande elle provient. Si vous traitez les deux, dédupliquez sur l’identifiant de la réservation — les deux événements décrivent une seule réservation.

Un client a rejoint la liste d’attente. Le status vaut active et reservation vaut null : rien n’est retenu.

{
"id": "evt_D4eF5gH6iJ7kL8mN9oP0qR1s",
"object": "event",
"type": "waitlist_entry.created",
"created": "2026-07-11T09:12:04Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "waitlist_entry",
"id": "wl_Os1uJlHmul2SpNy_0Ht1V6kY",
"status": "active",
"joined_via": "widget",
"service_date": "2026-07-18",
"party_size": 2,
"window_start": "19:00",
"window_end": "21:00",
"window_start_day_offset": 0,
"window_end_day_offset": 0,
"shift_name": "Dinner",
"shift_start_time": "19:00",
"shift_end_time": "22:30",
"notes": null,
"reservation": null,
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-11T11:12:04+02:00",
"guest": "gst_z7p7NMpeGZcmL52fP-erOktL"
}
}
}

Une table s’est libérée et la demande est devenue une réservation. Le status vaut promoted et reservation porte l’identifiant de la réservation.

{
"id": "evt_E5fG6hI7jK8lM9nO0pQ1rS2t",
"object": "event",
"type": "waitlist_entry.promoted",
"created": "2026-07-16T16:40:22Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "waitlist_entry",
"id": "wl_Os1uJlHmul2SpNy_0Ht1V6kY",
"status": "promoted",
"joined_via": "widget",
"service_date": "2026-07-18",
"party_size": 2,
"window_start": "19:00",
"window_end": "21:00",
"window_start_day_offset": 0,
"window_end_day_offset": 0,
"shift_name": "Dinner",
"shift_start_time": "19:00",
"shift_end_time": "22:30",
"notes": null,
"reservation": "resv_1M__exJ2ZQ7jKENZxz28djsH",
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-16T18:40:22+02:00",
"guest": "gst_z7p7NMpeGZcmL52fP-erOktL"
}
}
}

Le client a modifié sa propre demande — le créneau d’arrivée, le nombre de couverts, ou les deux. La demande conserve son identifiant et sa place ; seul ce qu’elle demande change.

Il s’agit d’un seul appel, et non d’un retrait suivi d’une nouvelle inscription : le client ne perd jamais sa place, et il n’existe aucun intervalle pendant lequel la demande n’existe pas. Vous ne verrez pas de paire cancelled / created pour une modification, et l’identifiant d’une demande reste stable.

{
"id": "evt_Q6rS7tU8vW9xY0zA1bC2dE3f",
"object": "event",
"type": "waitlist_entry.updated",
"created": "2026-07-12T10:04:17Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "waitlist_entry",
"id": "wl_Os1uJlHmul2SpNy_0Ht1V6kY",
"status": "active",
"joined_via": "widget",
"service_date": "2026-07-18",
"party_size": 4,
"window_start": "19:30",
"window_end": "22:00",
"window_start_day_offset": 0,
"window_end_day_offset": 0,
"shift_name": "Dinner",
"shift_start_time": "19:00",
"shift_end_time": "22:30",
"notes": null,
"reservation": null,
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-12T12:04:17+02:00"
}
}
}

La demande a été retirée — par le client depuis son propre lien, ou par le restaurant. Le status vaut cancelled.

{
"id": "evt_F6gH7iJ8kL9mN0oP1qR2sT3u",
"object": "event",
"type": "waitlist_entry.cancelled",
"created": "2026-07-14T08:03:41Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "waitlist_entry",
"id": "wl_Os1uJlHmul2SpNy_0Ht1V6kY",
"status": "cancelled",
"joined_via": "widget",
"service_date": "2026-07-18",
"party_size": 2,
"window_start": "19:00",
"window_end": "21:00",
"window_start_day_offset": 0,
"window_end_day_offset": 0,
"shift_name": "Dinner",
"shift_start_time": "19:00",
"shift_end_time": "22:30",
"notes": null,
"reservation": null,
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-14T10:03:41+02:00",
"guest": "gst_z7p7NMpeGZcmL52fP-erOktL"
}
}
}

La demande a expiré : le service est passé sans qu’aucune table ne se libère. Le status vaut expired.

{
"id": "evt_G7hI8jK9lM0nO1pQ2rS3tU4v",
"object": "event",
"type": "waitlist_entry.expired",
"created": "2026-07-18T21:30:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "waitlist_entry",
"id": "wl_Os1uJlHmul2SpNy_0Ht1V6kY",
"status": "expired",
"joined_via": "widget",
"service_date": "2026-07-18",
"party_size": 2,
"window_start": "19:00",
"window_end": "21:00",
"window_start_day_offset": 0,
"window_end_day_offset": 0,
"shift_name": "Dinner",
"shift_start_time": "19:00",
"shift_end_time": "22:30",
"notes": null,
"reservation": null,
"created_at": "2026-07-11T11:12:04+02:00",
"updated_at": "2026-07-18T23:30:00+02:00",
"guest": "gst_z7p7NMpeGZcmL52fP-erOktL"
}
}
}

Une option est une prise sur un créneau. Elle occupe une vraie table sans être une réservation : elle ne porte aucun client, elle n’apparaît pas dans GET /v1/reservations, et la table est retirée de la vente tant que l’option vit. Les options se posent avec POST /v1/holds et se rendent avec DELETE /v1/holds/{id}, ou sont libérées par le restaurant depuis son back-office — voir la référence de l’API (en anglais).

Le data.object de chaque événement d’option est une option, pas une réservation. Il porte "object": "hold" et son propre jeu de champs, plus restreint. Aiguillez sur data.object.object si un même gestionnaire reçoit les deux.

L’identifiant d’une option partage son suffixe avec celui de la réservation qu’elle devient : hold_AbC devient resv_AbC.

Les deux horodatages ne désignent pas le même moment

Section intitulée « Les deux horodatages ne désignent pas le même moment »

starts_at et ends_at désignent le service que l’option couvre. expires_at désigne le moment où l’option expire. Un calcul d’occupation a besoin des deux : la table est indisponible sur le créneau de service, mais seulement jusqu’à l’expiration de l’option.

expires_at est la réponse du serveur, pas la vôtre. Une création peut demander une échéance et le serveur la borne — une option basket à 30 minutes au plus (10 quand aucune n’est demandée), une option commitment à 48 heures au plus, les deux à 5 minutes au moins — alors relisez-la sur hold.created ou sur la réponse de création plutôt que de supposer la valeur que vous avez envoyée. Elle vaut null sur toute option terminée, car une option qui est finie n’a plus d’échéance à honorer.

Lorsqu’une option devient une réservation, vous recevez à la fois hold.converted et reservation.created — même groupe, même instant. Traitez hold.converted comme la libération de l’option et comptez les couverts sur le seul reservation.created, sans quoi le groupe est compté deux fois.

Poser une option ne déclenche aucun reservation.created : une table retenue reste hors du flux des réservations tant que personne n’a réservé.

Une option a été posée sur un créneau. Le status vaut held, reservation vaut null, et expires_at porte l’échéance fixée par le serveur.

{
"id": "evt_P3qR4sT5uV6wX7yZ8aB9cD0e",
"object": "event",
"type": "hold.created",
"created": "2026-07-04T17:12:40Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "hold",
"id": "hold_9pLr4Xm2Vd0",
"kind": "basket",
"status": "held",
"party_size": 4,
"service_date": "2026-07-11",
"starts_at": "2026-07-11T20:00:00+02:00",
"ends_at": "2026-07-11T22:00:00+02:00",
"expires_at": "2026-07-04T19:22:40+02:00",
"ended_at": null,
"reservation": null,
"tables": [
{
"object": "table",
"id": "tbl_QpZ2",
"name": "12",
"min_capacity": 2,
"max_capacity": 6,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"metadata": { "your_reference": "basket-8841" },
"created_at": "2026-07-04T19:12:40+02:00",
"updated_at": "2026-07-04T19:12:40+02:00"
}
}
}

metadata vous est renvoyé tel quel et est reporté sur la réservation lorsque l’option se convertit. Il est lisible par toutes les clés d’API que le restaurant a émises, pas seulement par celle qui l’a écrit — mettez-y des identifiants de rapprochement, pas des conditions commerciales.

L’option est devenue la réservation désignée par reservation. Le status vaut converted. expires_at et ended_at valent tous deux null : une option convertie ne s’est pas terminée, elle est devenue une réservation. kind indique toujours ce qu’était l’option.

{
"id": "evt_Q4rS5tU6vW7xY8zA9bC0dE1f",
"object": "event",
"type": "hold.converted",
"created": "2026-07-04T17:16:05Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "hold",
"id": "hold_9pLr4Xm2Vd0",
"kind": "basket",
"status": "converted",
"party_size": 4,
"service_date": "2026-07-11",
"starts_at": "2026-07-11T20:00:00+02:00",
"ends_at": "2026-07-11T22:00:00+02:00",
"expires_at": null,
"ended_at": null,
"reservation": "resv_9pLr4Xm2Vd0",
"tables": [
{
"object": "table",
"id": "tbl_QpZ2",
"name": "12",
"min_capacity": 2,
"max_capacity": 6,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"metadata": { "your_reference": "basket-8841" },
"created_at": "2026-07-04T19:12:40+02:00",
"updated_at": "2026-07-04T19:16:05+02:00"
}
}
}

L’option a été rendue avant son échéance, et sa table est de nouveau en vente. Le status vaut released et ended_at porte le moment où elle a été rendue.

Deux chemins y mènent : votre propre DELETE /v1/holds/{id}, et le restaurant qui libère l’option depuis son back-office, typiquement pour installer un client sans réservation à cette table. Le payload est le même dans les deux cas. Si vous recevez un hold.released que vous n’avez pas demandé, c’est que le restaurant a repris la table : l’option ne peut plus être convertie (POST /v1/reservations { hold_id } répond 404). Dites au client que l’heure n’est plus retenue, et réservez à neuf s’il la veut toujours.

Rendre une option déjà rendue ou déjà expirée n’écrit rien et ne déclenche rien : vous recevez cet événement une seule fois par option.

{
"id": "evt_R5sT6uV7wX8yZ9aB0cD1eF2g",
"object": "event",
"type": "hold.released",
"created": "2026-07-04T17:19:31Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "hold",
"id": "hold_9pLr4Xm2Vd0",
"kind": "basket",
"status": "released",
"party_size": 4,
"service_date": "2026-07-11",
"starts_at": "2026-07-11T20:00:00+02:00",
"ends_at": "2026-07-11T22:00:00+02:00",
"expires_at": null,
"ended_at": "2026-07-04T19:19:31+02:00",
"reservation": null,
"tables": [
{
"object": "table",
"id": "tbl_QpZ2",
"name": "12",
"min_capacity": 2,
"max_capacity": 6,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"metadata": { "your_reference": "basket-8841" },
"created_at": "2026-07-04T19:12:40+02:00",
"updated_at": "2026-07-04T19:19:31+02:00"
}
}
}

L’option a expiré et sa table est de nouveau en vente. Le status vaut expired et ended_at porte le moment où l’échéance a été actée.

{
"id": "evt_S6tU7vW8xY9zA0bC1dE2fG3h",
"object": "event",
"type": "hold.expired",
"created": "2026-07-04T17:25:00Z",
"livemode": true,
"api_version": "2026-06-27",
"data": {
"object": {
"object": "hold",
"id": "hold_4kNt7Yq1Wb3",
"kind": "commitment",
"status": "expired",
"party_size": 2,
"service_date": "2026-07-06",
"starts_at": "2026-07-06T12:30:00+02:00",
"ends_at": "2026-07-06T14:00:00+02:00",
"expires_at": null,
"ended_at": "2026-07-04T19:25:00+02:00",
"reservation": null,
"tables": [
{
"object": "table",
"id": "tbl_Rk71",
"name": "3",
"min_capacity": 2,
"max_capacity": 4,
"section": {
"object": "section",
"id": "sec_Lm8",
"name": "Main room",
"area_type": "indoor"
}
}
],
"metadata": {},
"created_at": "2026-07-02T19:20:11+02:00",
"updated_at": "2026-07-04T19:25:00+02:00"
}
}
}