Skip to main content
Les endpoints clients donnent à un CRM, un outil marketing ou un programme de fidélité un accès direct aux fiches clients du restaurant : il n’est pas nécessaire de passer par une réservation pour en créer ou en modifier une. La lecture demande CUSTOMERS_READ ; la création et la modification demandent CUSTOMERS_WRITE et CUSTOMERS_READ, car chaque écriture renvoie la fiche complète. Une fiche client est une donnée personnelle de bout en bout : il n’existe pas de vue masquée, n’accordez ces scopes qu’aux intégrations autorisées à la détenir. Les clients appartiennent au restaurant de la clé. Un groupe de restaurants a une fiche par restaurant.

La ressource client

GET /customers/{id} renvoie les coordonnées, les tags, la note interne, les custom_fields, l’état marketing (newsletter_consent, marketing_opt_outs), la blacklist (blacklisted) et des stats calculées à partir de ses réservations (nombre, no-shows, montant payé, jour de la dernière réservation). Les stats sont en lecture seule. Les champs personnalisés sont indexés par identifiant de champ. GET /customer-fields liste les champs du restaurant avec leur type et, pour une liste, les valeurs autorisées : Un champ NUMBER peut être borné par min / max, un MULTI_SELECT par max_items. Une écriture avec un champ inconnu ou une valeur qui ne convient pas renvoie 422, avec une entrée par champ fautif dans error.details.issues.

Créer un client

POST /customers cherche un client existant avant d’en créer un, dans cet ordre : même external_id, même numéro de téléphone (les écritures nationale et internationale d’un numéro se correspondent), même email (sans tenir compte de la casse). on_match décide de ce que fait une correspondance : Sans correspondance, le client est créé : 201 created.
Rejouer un appel qui a abouti retrouve le client qu’il a créé, à condition que l’appel porte un external_id, un téléphone ou un email. Ce n’est pas un verrou : deux appels identiques envoyés en même temps peuvent créer chacun un client, ne lancez donc pas vos nouvelles tentatives en parallèle. external_id est votre identifiant du client : stockez-le, il est comparé en premier et reste stable quand le client change de téléphone ou d’email. Il est écrit à la création et avec on_match: "update" ; une simple correspondance return_existing laisse la fiche intacte, external_id compris. Il est distinct des identifiants que d’autres canaux gardent sur le client (Réserver avec Google par exemple), que l’API n’affiche ni ne modifie. Un client blacklisté est renvoyé comme les autres, avec blacklisted: true.

Modifier un client

PATCH /customers/{id} ne change que les champs qu’il contient ; null vide un champ. tags remplace toute la liste, custom_fields ne change que les champs nommés. newsletter_consent: false retire le consentement. La fiche est partagée par toutes les réservations du client.

Garder une copie synchronisée

GET /customers liste les clients par updated_at puis id, avec une pagination par curseur (limit jusqu’à 100, 50 par défaut) :
  1. Premier passage : appelez sans updated_since et suivez pagination.next_cursor jusqu’à has_more: false.
  2. Gardez le updated_at du dernier client traité.
  3. Passages suivants : passez-le en updated_since et parcourez les pages.
updated_since est inclusif : le client à la frontière revient une fois, dédoublonnez sur id. Repartez à chaque passage quelques minutes avant le dernier updated_at gardé : une écriture validée tardivement peut porter un horodatage légèrement plus ancien. Les changements de stats ne modifient pas updated_at : relisez un client quand vous avez besoin de chiffres à jour. Un nouvel opt-out marketing le modifie. Le flux contient les clients qui existent. Un client supprimé dans EatNow, ou fusionné dans un autre par l’équipe, en disparaît : les webhooks customer.deleted et customer.merged le signalent. Sans webhooks, listez régulièrement tous les clients (sans updated_since) et supprimez les identifiants qui ne reviennent plus. GET /customers filtre aussi par email, phone, external_id et tag.

Clients et réservations

Une réservation n’a pas besoin d’un appel client préalable. POST /reservations accepte :
  • customer: { "id": "…" }, pour réserver pour un client déjà connu ;
  • les coordonnées du client directement, pour le retrouver ou le créer dans le même appel, rapproché par téléphone puis email (pas d’external_id ici). S’il est absent, customer.on_match suit le réglage du restaurant (rapprochement, sauf si le restaurant l’a désactivé). Deux différences avec POST /customers : return_existing peut encore enregistrer un consentement newsletter que le client vient de donner, et update ne vide jamais une coordonnée (un email ou un téléphone vide est ignoré).
Un client blacklisté fait refuser la réservation, qu’il soit lié par id ou retrouvé à partir des coordonnées. Le client n’est résolu qu’une fois les contrôles de la réservation passés : une réservation refusée ne crée ni ne modifie de client. GET /reservations?customer_id=… liste les réservations d’un client.

Suivre les changements en direct

Abonnez un endpoint à customer.created, customer.updated, customer.merged et customer.deleted (voir la référence webhooks) pour être prévenu des changements au fil de l’eau, suppressions et fusions comprises. Les événements portent le même objet client que GET /customers/{id}. Gardez la synchronisation incrémentale ci-dessus comme source de vérité : l’import CSV ne publie aucun événement, et une livraison manquée est rattrapée au passage suivant.