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.
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) :
- Premier passage : appelez sans
updated_sinceet suivezpagination.next_cursorjusqu’àhas_more: false. - Gardez le
updated_atdu dernier client traité. - Passages suivants : passez-le en
updated_sinceet 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_idici). S’il est absent,customer.on_matchsuit le réglage du restaurant (rapprochement, sauf si le restaurant l’a désactivé). Deux différences avecPOST /customers:return_existingpeut encore enregistrer un consentement newsletter que le client vient de donner, etupdatene vide jamais une coordonnée (un email ou un téléphone vide est ignoré).
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.