Erreurs
L’API utilise les codes de statut HTTP conventionnels et renvoie une enveloppe d’erreur structurée à chaque échec, afin que vous puissiez vous appuyer sur des champs lisibles par une machine plutôt que d’analyser du texte.
Enveloppe d’erreur
Section intitulée « Enveloppe d’erreur »{ "error": { "type": "invalid_request_error", "code": "bad_request", "message": "limit must be a positive integer.", "param": "limit", "doc_url": "https://docs.useservice.app/api/errors#bad_request" }}| Champ | Description |
|---|---|
type |
Catégorie générale de l’erreur (voir ci-dessous). Toujours présent. |
code |
Un code machine spécifique et stable identifiant l’échec. Toujours présent. |
message |
Une explication lisible par un humain, en anglais quelle que soit la langue du restaurant, destinée à la journalisation et au débogage. Ne l’affichez pas aux clients et ne vous appuyez pas dessus pour brancher votre logique. |
param |
Le paramètre de requête en cause — présent uniquement lorsqu’un paramètre précis a causé l’erreur. Omis sinon (il n’est pas envoyé à null). |
doc_url |
Un lien direct vers l’entrée de ce code sur cette page (https://docs.useservice.app/api/errors#<code>). Toujours présent. |
violations |
Présent uniquement sur un refus lié aux règles de réservation. Une entrée par règle enfreinte parmi les contrôles atteints — voir Ce qu’un refus ne peut pas vous dire. |
Chaque réponse — succès comme erreurs — renvoie également l’en-tête
Service-Version qui l’a servie.
Types d’erreur
Section intitulée « Types d’erreur »type |
Signification |
|---|---|
invalid_request_error |
La requête était mal formée, refusée par une règle, ou visait quelque chose qui n’existe pas. Couvre les 400, 403, 404, 409 et 422. |
authentication_error |
La clé d’API est manquante, mal formée ou invalide (401). |
rate_limit_error |
Vous avez dépassé la limite de débit (429). |
api_error |
Un problème est survenu du côté de Service, ou chez un prestataire dont Service dépend (500, 502). |
type est dérivé du statut : c’est la branche grossière. code est celui sur
lequel écrire votre logique.
Chaque clé d’API porte une liste explicite de portées, choisie par le restaurant
au moment de la création de la clé. Les portées sont plates : une portée qui
paraît plus large n’en implique jamais une plus étroite, et une clé créée avant
l’existence des portées ne porte que reservations:read. Une clé ne peut pas
élargir ses propres portées après coup — le restaurant crée une nouvelle clé.
Un appel qui exige une portée absente de la clé est refusé par un
403 insufficient_scope, jamais par une réponse silencieusement appauvrie. Les
données client à l’intérieur d’une réponse que vous pouvez lire sont la seule
exception : les contact_email, contact_phone, guest_notes et
internal_notes d’une réservation, et les notes d’une entrée de file
d’attente, sont omis sans guests:read, pas refusés. Voir
Données client d’une réservation.
| Portée | Autorise |
|---|---|
reservations:read |
Lire les réservations et leurs événements, les disponibilités, la configuration, les salles, les options et la file d’attente. |
reservations:write |
Créer, modifier et annuler des réservations ; créer et libérer des options. Il n’existe pas de portée distincte pour les options : une clé qui peut réserver peut poser une option. |
reservations:write:override |
Trois champs. Envoyer le tableau override, qui lève une règle de réservation fixée par le restaurant. Positionner excluded_from_shift_limits: true, qui exclut définitivement une réservation des plafonds de couverts du service. Envoyer entire_section_id sur une création, qui réserve toutes les tables d’une section, y compris une section que le restaurant ne propose pas à la réservation en ligne. Séparée de reservations:write pour qu’une intégration ordinaire n’en fasse aucune des trois par accident. |
guests:read |
Lire les fiches clients : GET /v1/guests, la valeur guest de expand, et sa valeur modification_token sur POST /v1/reservations. Aussi les données client de toutes les réservations — y compris celles que la clé a créées — et de toutes les entrées de file d’attente. Le jeton est la clé du client pour sa page de réservation, étape carte comprise : voir Le jeton du client est sa clé. |
La portée requise par chaque point de terminaison
Section intitulée « La portée requise par chaque point de terminaison »| Point de terminaison | Portée |
|---|---|
GET /v1/availability, GET /v1/availability/months |
reservations:read |
GET /v1/configuration |
reservations:read |
GET /v1/sections |
reservations:read |
GET /v1/reservations, GET /v1/reservations/{id}, GET /v1/reservations/{id}/events |
reservations:read |
POST /v1/reservations, PATCH /v1/reservations/{id}, POST /v1/reservations/{id}/cancel |
reservations:write |
GET /v1/holds, GET /v1/holds/{id} |
reservations:read |
POST /v1/holds, DELETE /v1/holds/{id} |
reservations:write |
GET /v1/waitlist-entries, GET /v1/waitlist-entries/{id} |
reservations:read |
GET /v1/guests, GET /v1/guests/{id} |
guests:read |
Une entrée de file d’attente exprime une demande de table et ne porte aucune
fiche client propre : la file se lit avec reservations:read. Le client qu’elle
désigne, non : on l’atteint avec expand=guest et la réponse exige alors
guests:read, quel que soit le point de départ. La demande du client dans les
notes de l’entrée l’exige aussi, et elle est absente sans elle.
Portées exigées par un seul champ
Section intitulée « Portées exigées par un seul champ »Deux portées sont exigées par un élément de la requête plutôt que par le point
de terminaison, réparties sur quatre champs, et le refus nomme ce champ dans
param :
| Champ | Portée | param |
|---|---|---|
override: [...] sur une écriture |
reservations:write:override |
override |
excluded_from_shift_limits: true |
reservations:write:override |
excluded_from_shift_limits |
entire_section_id sur une création |
reservations:write:override |
entire_section_id |
expand=guest, et expand=modification_token sur POST /v1/reservations |
guests:read |
expand |
Une seule portée couvre les trois premiers, et chacun reste refusé séparément :
le param vous dit pour quel champ la clé a été écartée, pas quelle portée lui
manque. Porter reservations:write:override les accorde tous les trois d’un
coup.
C’est toute la raison d’être du param sur insufficient_scope. Sans param,
votre clé ne peut pas appeler ce point de terminaison et renvoyer la requête ne
sert à rien. Avec un param, votre clé peut l’appeler et seul ce champ est hors
d’atteinte : retirer le champ et renvoyer la requête est un réessai qui mérite
d’être automatisé.
Envoyer override: [] ou excluded_from_shift_limits: false ne demande rien :
aucune de ces deux valeurs n’exige la portée. Seule l’affirmation l’exige. Sur
une réservation de section entière, excluded_from_shift_limits: true est la
valeur par défaut : l’envoyer n’est refusé que là où entire_section_id le
serait déjà.
La vérification de entire_section_id a lieu avant la recherche de la section :
une clé sans la portée reçoit le 403, que l’identifiant existe ou non.
Refus liés aux règles de réservation
Section intitulée « Refus liés aux règles de réservation »Lorsqu’une création, une modification ou une option enfreint les règles de réservation du restaurant, la réponse a toujours la même forme :
- le
codede premier niveau vaut toujoursbooking_rules_violated, violationsporte une entrée par règle enfreinte,- le tableau est présent même lorsqu’une seule règle est enfreinte.
{ "error": { "type": "invalid_request_error", "code": "booking_rules_violated", "message": "The booking breaks one or more of this restaurant's booking rules. See `violations` for each one, and whether it can be overridden.", "doc_url": "https://docs.useservice.app/api/errors#booking_rules_violated", "violations": [ { "code": "party_size_too_large", "message": "Party size exceeds the maximum", "param": "party_size", "overridable": true }, { "code": "cutoff_passed", "message": "Reservation cutoff has passed", "overridable": true } ] }}Le code de premier niveau ne nomme jamais une règle, et le message de premier
niveau est la même phrase pour une violation et pour six. Lisez le tableau. Un
client qui lit le code de premier niveau fonctionne jusqu’au jour où une
réservation enfreint deux règles : il en renvoie une et se fait refuser par celle
qu’il n’a pas lue.
Lire une violation
Section intitulée « Lire une violation »| Champ | Description |
|---|---|
code |
La règle enfreinte. Unique dans une réponse : le même code n’apparaît jamais deux fois. |
message |
En anglais, pour vos journaux. Une phrase fixe par code, sans aucun chiffre : la limite franchie se lit sur GET /v1/configuration (min_party_size, max_party_size, max_advance_days) ou sur le service dans GET /v1/availability (effective_cutoff_minutes). La seule exception est start_time_off_grid, dont le message nomme l’heure envoyée et les horaires d’arrivée du service. |
param |
Le champ de requête à changer, lorsqu’un seul champ est en cause. Omis sinon. |
overridable |
Indique si cette violation peut être levée en renvoyant la requête avec ce code dans override. Toujours présent sur chaque entrée. |
conflicts |
Présent uniquement sur section_occupied : une entrée par table occupée, sous la forme table, start_time et end_time. |
Les violations ne portent pas de doc_url propre. Construisez-la à partir du
code, comme le fait l’enveloppe :
https://docs.useservice.app/api/errors#<code de la violation>.
Ce qu’un refus ne peut pas vous dire
Section intitulée « Ce qu’un refus ne peut pas vous dire »violations liste chaque règle enfreinte parmi les contrôles qui ont tourné, et
trois contrôles restent en dehors de ce groupe :
no_service_at_this_timeest vérifié en premier. Toutes les autres règles sont jugées par rapport à un service : lorsqu’aucun service ne couvre la date et l’heure, rien d’autre n’est vérifié et cette violation arrive seule.start_time_off_gridest vérifié ensuite. Un service couvre l’heure mais n’accueille pas d’arrivée à cette minute. Toutes les autres règles sont jugées par rapport à un horaire d’arrivée, si bien que cette violation arrive seule, elle aussi.- La recherche de table passe en dernier, une fois chaque règle respectée ou
levée. Ses refus,
no_table_availableetno_table_available_in_section, ne sont jamais listés à côté des règles qui la précèdent.
Une réservation refusée pour party_size_too_large puis renvoyée avec ce code
dans override peut ainsi revenir avec un second 422, qui nomme cette fois
no_table_available. La levée a fonctionné ; le restaurant n’a aucune table
pour ce groupe à cette heure. Un écran d’accueil qui propose « lever et
réserver » doit traiter cette seconde réponse et dire à l’opérateur que la
réservation ne peut pas être placée, sans lui proposer une nouvelle levée.
Passer outre une règle
Section intitulée « Passer outre une règle »overridable: true signifie qu’une clé portant reservations:write:override
peut renvoyer la réservation à l’identique avec ce code dans un tableau
override, et que la règle est alors levée au lieu d’être appliquée. La liste
des codes levables est fixée dans l’API, pas par restaurant.
curl -X POST "https://api.useservice.app/v1/reservations" \ -H "Authorization: Bearer $SERVICE_API_KEY" \ -H "Idempotency-Key: $IDEMPOTENCY_KEY" \ -H "Content-Type: application/json" \ -d '{ "service_date": "2026-10-04", "start_time": "20:00", "party_size": 9, "guest": { "first_name": "Marie", "last_name": "Dupont" }, "override": ["party_size_too_large"] }'Quatre points décident du succès de cet appel :
- Réessayez une fois, pas en boucle. Relevez les codes levables dans
violations, décidez, renvoyez une fois. Un second refus est un autre problème de réservation, pas une liste plus longue — le plus souvent la recherche de table, que le premier refus ne peut pas signaler. - Renvoyez le même
Idempotency-Keyque la première fois. La tentative refusée n’a rien créé, et la clé est ce qui empêche un délai d’attente de produire une double réservation. - Un code non levable donne un
400, pas un silence. Placerdate_in_pastdansoverriderefuse toute la requête avecbad_requestetparam: "override", et le message précise si le code est une vraie règle qui ne peut jamais être levée ou s’il ne désigne aucune règle. - Le tableau peut être envoyé par anticipation. Une levée n’est enregistrée
que lorsqu’elle a effectivement levé quelque chose : un
overridesur une réservation qui n’enfreint aucune règle ne coûte rien et n’est pas consigné.
Aucun service, ou un service complet
Section intitulée « Aucun service, ou un service complet »Trois refus sont assez proches pour être confondus : ils portent donc des codes
différents. Sur une écriture, tous trois arrivent dans violations, sous la
même ombrelle.
| Code | Situation | overridable |
param |
|---|---|---|---|
no_service_at_this_time |
Aucun service ne couvre cette date et cette heure. Le restaurant est fermé à ce moment-là, ou l’heure tombe entre deux services. | false |
absent |
start_time_off_grid |
Un service est ouvert mais n’accueille pas d’arrivée à cette minute : l’heure tombe entre deux horaires d’arrivée, ou après le dernier. | false |
start_time |
slot_no_longer_available |
Un service est ouvert et son plafond de couverts est atteint. | true |
party_size |
Les remèdes diffèrent, et c’est pourquoi les codes diffèrent. Pour
no_service_at_this_time et start_time_off_grid, relisez
GET /v1/availability et envoyez une heure qu’il publie : rien d’autre ne fera
aboutir cette réservation, et nommer l’un de ces codes dans override donne un
400. Pour slot_no_longer_available,
envoyez un groupe plus petit, une autre heure, ou la même réservation avec le
code dans override si votre clé détient la portée.
overridable répond toujours à la question sur laquelle vous agissez, et il est
présent sur chaque violation pour cela. Les codes diffèrent aussi pour que vous
puissiez journaliser, compter et alerter séparément : un restaurant fermé à
l’heure que votre système demande sans cesse n’est pas le même problème qu’un
restaurant qui affiche complet.
Authentification et accès
Section intitulée « Authentification et accès »auth_header_missing
Section intitulée « auth_header_missing »authentication_error · 401 Unauthorized. Aucun en-tête
Authorization: Bearer … n’a été envoyé. Ajoutez l’en-tête — voir
Authentification.
invalid_token
Section intitulée « invalid_token »authentication_error · 401 Unauthorized. Une clé a été fournie mais elle est
invalide, révoquée, expirée ou inconnue.
Créez une nouvelle clé si la
vôtre ne fonctionne plus. Renvoyer la même clé ne peut pas réussir.
plan_required
Section intitulée « plan_required »invalid_request_error · 403 Forbidden. La clé est valide et le restaurant
existe, mais son abonnement n’inclut pas l’accès à l’API. Il s’agit d’un état de
facturation et non d’un problème de permissions : élargir les portées de la
clé n’y changera rien, et créer une nouvelle clé non plus. Demandez au restaurant
de mettre son abonnement à niveau ; l’accès revient à la requête suivante, sans
modification de votre côté.
insufficient_scope
Section intitulée « insufficient_scope »invalid_request_error · 403 Forbidden. La clé est valide et le restaurant est
éligible ; la clé ne porte pas la portée exigée par cet appel. Le message nomme
la portée manquante.
Lisez param avant de réagir :
paramabsent — la clé ne peut pas appeler ce point de terminaison. Demandez au restaurant une clé portant la portée nommée dans le message. Réessayer ne change rien.paramprésent — la clé peut appeler le point de terminaison, et seul le champ nommé dansparamest hors d’atteinte. Retirez ce champ et renvoyez la requête.
Voir Portées exigées par un seul champ pour les quatre champs concernés.
Forme de la requête
Section intitulée « Forme de la requête »bad_request
Section intitulée « bad_request »invalid_request_error · 400 Bad Request. Un paramètre, un filtre, un curseur,
un en-tête ou un champ du corps est mal formé, inconnu ou manquant. param le
nomme. Causes fréquentes : une valeur expand inconnue, un filtre date mal
formé, un locale que le restaurant ne publie pas, un section_id ou un
entire_section_id qui n’est pas un identifiant sec_…, un entire_section_id
envoyé avec section_id ou hold_id, une duration autre que
until_shift_end ou envoyée sans entire_section_id, une valeur metadata qui n’est pas un objet, une entrée
override qui n’est pas un code levable, une valeur
excluded_from_shift_limits qui n’est pas un booléen JSON, une Service-Version
non prise en charge (param est le nom de l’en-tête, Service-Version), et un
corps de PATCH ne contenant aucun champ modifiable.
Les filtres de liste en refusent deux autres par leur nom. service_date sur
GET /v1/reservations ou GET /v1/holds est un 400 avec
param: "service_date" : le filtre est date, ou l’intervalle date[gte] et
date[lte]. Un status sur GET /v1/reservations hors de l’énumération
publiée est aussi un 400, et late n’en fait pas partie.
Un corps d’écriture est lu strictement. Un champ que le point de terminaison ne
lit pas est un 400 qui le nomme dans param, au lieu d’un champ ignoré en
silence. Dans guest, le nom est pointé (guest.vip), et un corps qui est un
tableau JSON plutôt qu’un objet reçoit param: "body". Les clés contenues dans
metadata ne sont jamais vérifiées.
Les champs d’écriture vont dans le corps JSON. Un champ envoyé dans la chaîne de
requête à la place — PATCH /v1/reservations/{id}?party_size=6 — est un 400
qui le nomme dans param, même quand le corps est vide, au lieu d’une
modification appliquée depuis l’URL. Les paramètres de requête qui ne sont pas
des champs d’écriture, comme expand, sont lus comme avant.
Trois autres viennent du contenu du corps :
- un corps qui n’est pas du JSON valide —
paramvautbody, - un
start_timequi n’est pas une heure de la journée, comme8pmou25:00—paramvautstart_time, - un
guest.phonequ’aucun opérateur ne pourrait acheminer —paramvautguest.phone. Un numéro sans indicatif de pays est lu dans le pays du restaurant.
Corrigez la requête. La renvoyer telle quelle échouera de la même manière.
PATCH /v1/reservations/{id} répond également bad_request lorsque le corps
porte un champ fixé à la création : guest, guest_id, guest_notifications,
metadata, hold_id, entire_section_id et duration — ou guarantee_consent_amount_cents, qui appartient à
la page de réservation du client et ne peut pas être envoyé en son nom (voir
guarantee_consent_guest_only). param nomme
le champ et message explique pourquoi. Ces champs sont refusés plutôt
qu’ignorés, afin qu’un partenaire qui envoie guest_notifications sur un PATCH
n’obtienne jamais un 200 laissant croire que le réglage a changé.
host_metadata_too_large
Section intitulée « host_metadata_too_large »invalid_request_error · 422 Unprocessable Content. L’objet metadata de
POST /v1/reservations ou de POST /v1/holds compte plus de 50 clés, ou
dépasse 4 096 octets une fois sérialisé. Le nom du code vient du widget de
réservation, qui partage cette limite. Envoyez moins : gardez dans metadata
une référence vers votre propre enregistrement, pas l’enregistrement.
validation_error
Section intitulée « validation_error »invalid_request_error · 422 Unprocessable Content. Une valeur était bien
formée mais l’enregistrement l’a refusée — le message porte sa propre objection.
Lisez le message, corrigez la valeur.
guest_fields_required
Section intitulée « guest_fields_required »invalid_request_error · 422 Unprocessable Content. La réservation ne porte
pas de first_name, ou pas de last_name. Les deux sont exigés sur une création
qui envoie un objet guest : une table dont personne ne porte le nom ne peut pas
être accueillie à la porte.
Une création qui identifie une fiche existante avec guest_id en est
dispensée. Les noms sont déjà sur cette fiche, et les redemander rendrait
guest_id inutile pour l’intégration même à laquelle il sert.
required_guest_fields, sur GET /v1/configuration, décrit la même règle.
require_guest_email et require_guest_phone sont publiés sur
GET /v1/configuration à titre indicatif : ils décrivent le formulaire du
restaurant et n’engagent pas cette API. L’adresse e-mail et le téléphone y
sont donc facultatifs, à une exception près, celle d’un créneau portant une
garantie par carte. Voir
email_required_for_guarantee.
Idempotence
Section intitulée « Idempotence »Idempotency-Key est exigé sur POST /v1/reservations et POST /v1/holds,
et facultatif sur PATCH /v1/reservations/{id} et
POST /v1/reservations/{id}/cancel. Envoyez une valeur unique par tentative, et
la même valeur lorsque vous réessayez cette tentative. Les clés sont mémorisées
pendant 72 heures.
Un nouvel essai d’une requête déjà terminée reçoit la réponse de cette requête :
le statut et le corps d’origine, à l’identique, l’en-tête Location quand
l’original en portait un, et l’en-tête Idempotent-Replayed: true. Une première écriture ne porte jamais cet en-tête.
C’est ce qui vous permet de distinguer « cet appel a créé la réservation » de
« un appel précédent l’a créée » avant d’agir sur un 201.
Le corps est celui enregistré la première fois, y compris tout expand demandé
par la première requête, quoi que demande le nouvel essai. Une clé ne lit pas
pour autant au-delà de ses portées : l’empreinte inclut la clé d’API qui a
envoyé la requête, si bien qu’aucune autre clé ne peut la rejouer, et les
portées d’une clé sont fixées à sa création.
idempotency_key_required
Section intitulée « idempotency_key_required »invalid_request_error · 400 Bad Request. Une écriture qui exige l’en-tête a
été envoyée sans lui. C’est un 400 et non un 422 parce que la réservation n’a
jamais été interprétée : ajoutez l’en-tête et renvoyez la requête inchangée.
idempotency_key_invalid
Section intitulée « idempotency_key_invalid »invalid_request_error · 400 Bad Request. L’en-tête était présent mais dépasse
la longueur maximale, ou contient des caractères de contrôle. Envoyez une chaîne
imprimable — un UUID par tentative est le choix habituel.
idempotency_request_in_progress
Section intitulée « idempotency_request_in_progress »invalid_request_error · 409 Conflict. Une requête antérieure portant cette
clé est encore en cours. Attendez un instant et réessayez avec la même clé.
Créer une nouvelle clé ici est la façon de produire une double réservation.
idempotency_key_reuse
Section intitulée « idempotency_key_reuse »invalid_request_error · 409 Conflict. Cette clé a déjà servi pour une requête
différente. La clé est l’empreinte d’une tentative : la réutiliser pour une
autre réservation est refusé plutôt que répondu par la première réservation.
Créez une nouvelle clé pour la nouvelle requête.
Deux clés générées indépendamment peuvent se heurter sur une même chaîne : les clés sont cloisonnées par restaurant et non par clé d’API, si bien qu’une autre intégration sur le même restaurant peut consommer une chaîne que vous auriez utilisée. Des valeurs aléatoires par tentative évitent ce cas.
Règles de réservation
Section intitulée « Règles de réservation »Sur POST /v1/reservations, PATCH /v1/reservations/{id} et POST /v1/holds,
tous les codes de cette section arrivent à l’intérieur de violations, sous
l’ombrelle booking_rules_violated, et jamais comme code de premier niveau.
GET /v1/availability est la seule exception. Il vérifie quatre de ces règles
avant de calculer un créneau, et répond à un échec par un 422 dont le code
de premier niveau est la règle, sans tableau violations ni param :
date_in_past,
party_size_too_small,
party_size_too_large et
advance_window_exceeded. Une lecture ne réserve
rien : il n’y a rien à lever, changez la requête. GET /v1/availability/months
ne renvoie aucun d’eux : il ne prend pas de nombre de couverts, et signale un
jour passé ou hors de la fenêtre par past ou outside_window au lieu de
refuser.
booking_rules_violated
Section intitulée « booking_rules_violated »invalid_request_error · 422 Unprocessable Content. L’ombrelle de tout refus
lié aux règles de réservation. Lisez violations ; voir
Refus liés aux règles de réservation.
slot_no_longer_available
Section intitulée « slot_no_longer_available »Levable. Un service couvre cette heure et il est complet : son plafond de
couverts n’a plus la place pour un groupe de cette taille. Envoyez un groupe plus
petit, une autre heure, ou la même réservation avec ce code dans override si
votre clé détient la portée. param vaut party_size.
Cela ne veut pas dire que le restaurant est fermé à ce moment-là : c’est
no_service_at_this_time. Voir
Aucun service, ou un service complet.
cutoff_passed
Section intitulée « cutoff_passed »Levable. La réservation en ligne pour ce service est close depuis un certain
nombre de minutes avant son début. La limite est propre à chaque service, et
celle réellement appliquée est publiée sous effective_cutoff_minutes sur chaque
service dans GET /v1/availability — lisez-la là plutôt que dans la valeur par
défaut du restaurant, qu’un service peut remplacer.
advance_window_exceeded
Section intitulée « advance_window_exceeded »Levable. La date dépasse l’horizon auquel le restaurant accepte les
réservations en ligne. max_advance_days sur GET /v1/configuration porte cet
horizon, ce qui vous permet d’écarter la date avant de l’envoyer.
party_size_too_small
Section intitulée « party_size_too_small »Levable. En dessous du minimum du restaurant. min_party_size figure sur
GET /v1/configuration. param vaut party_size.
party_size_too_large
Section intitulée « party_size_too_large »Levable. Au-dessus du maximum du restaurant. max_party_size figure sur
GET /v1/configuration. param vaut party_size.
slot_blocked
Section intitulée « slot_blocked »Levable. Le personnel a marqué ce créneau ou ce service comme complet depuis le back-office. La table peut être matériellement libre, et c’est pourquoi ce refus est levable : le restaurant a cessé de vendre le créneau plutôt que de manquer de place.
shift_not_online_bookable
Section intitulée « shift_not_online_bookable »Levable. Le service est ouvert et visible, et n’accepte aucune réservation en
ligne (son mode en ligne est l’affichage seul). Les disponibilités publient ces
services avec display_only, ce qui vous permet de les présenter comme une
information plutôt que de les proposer.
no_service_at_this_time
Section intitulée « no_service_at_this_time »Non levable. Ni un service ni une exception de date ne couvre cette date et
cette heure : il n’y a rien à réserver — le restaurant est fermé à ce moment-là,
ou l’heure tombe entre deux services. GET /v1/availability publie les heures
qui existent ; lisez-le et envoyez l’une d’elles.
Nommer ce code dans override donne un 400, et non un silence.
Il n’y a ici aucun arbitrage de capacité à lever : la réservation n’a aucun
service auquel appartenir, et aucune clé ni aucune portée n’y change quoi que ce
soit.
start_time_off_grid
Section intitulée « start_time_off_grid »Non levable. Un service est ouvert à cette heure mais n’accueille pas
d’arrivée à cette minute : l’heure tombe entre deux de ses horaires d’arrivée,
ou après le dernier. param vaut start_time. Le message, en anglais, nomme
l’heure envoyée et les horaires d’arrivée du service, par exemple
20:07 is not one of this service's seating times: it seats every 30 minutes from 19:00 to 21:00.
Envoyez un start_time que GET /v1/availability propose pour ce service.
Il est vérifié à la création, à la pose d’une option et sur un PATCH qui
change la date ou l’heure. Un PATCH qui conserve les deux n’est pas vérifié,
car l’équipe du restaurant peut placer une réservation à n’importe quelle
minute. La conversion d’une option n’est pas vérifiée non plus : l’option a déjà
réservé la place. Comme no_service_at_this_time,
il arrive seul, et le nommer dans override donne un 400.
date_in_past
Section intitulée « date_in_past »Non levable. La date précède le jour courant du restaurant. Un service déjà passé ne peut pas être vendu ; envoyez une autre date.
slot_elapsed
Section intitulée « slot_elapsed »Non levable. La date est valide et cette heure de la journée est déjà passée.
Distinct de date_in_past parce que le remède diffère : une heure plus tardive
le même jour, plutôt qu’un autre jour. param vaut start_time.
no_table_available
Section intitulée « no_table_available »Non levable. Les couverts étaient disponibles et aucune table ni combinaison de tables ne peut accueillir ce groupe à cette heure. Le mécanisme de levée ne couvre pas ce refus : toutes les façons de l’honorer produiraient une réservation placée là où personne ne l’a choisi, ou une réservation sans table. Essayez une autre heure, ou un autre nombre de couverts.
Nommer ce code dans override donne un 400 qui dit qu’il
s’agit d’une vraie règle jamais levable — et non un 400 qui dit que ce n’est
pas un code. Le jugement de capacité que vous pouvez lever est
slot_no_longer_available ; ce refus-ci signifie
que les couverts étaient là et qu’aucune table ne convient.
no_table_available_in_section
Section intitulée « no_table_available_in_section »Non levable. Le même refus, restreint par le section_id demandé : aucune
table desservie par ce service ne se trouve dans cette salle. Retirez
section_id et renvoyez la requête pour laisser le restaurant placer le groupe
où il veut, ou choisissez une autre salle dans GET /v1/sections.
Sur une réservation de section entière, le même code, avec
param: "entire_section_id", signifie que la section n’est pas dessinée sur le
plan de salle en vigueur pour ce service. Essayez une autre date ou une autre
section.
section_occupied
Section intitulée « section_occupied »Non levable. Une réservation de section entière (entire_section_id) a été
demandée alors qu’une partie de la section est déjà prise pendant la plage que
la réservation occuperait : une réservation sur l’une de ses tables, ou une
autre réservation de la section entière. Rien n’est placé ; une réservation de
section entière n’est jamais placée sur une partie de la salle. param vaut
entire_section_id.
La violation porte conflicts, une entrée par table occupée :
| Champ | Description |
|---|---|
table |
L’identifiant tbl_… de la table occupée. null lorsque la section n’a aucune table et qu’une autre réservation de la section entière l’occupe. |
start_time |
HH:MM, heure à partir de laquelle la table est prise. |
end_time |
HH:MM, heure jusqu’à laquelle elle est prise. null pour une réservation enregistrée sans heure de fin. |
Les entrées sont triées par table, puis par heure de début. Elles ne disent rien
de qui occupe la table. Essayez une autre heure ou une autre date, ou demandez au
restaurant de déplacer les réservations listées. Nommer ce code dans override
est un 400. Voir
Réserver une salle entière.
duplicate_booking
Section intitulée « duplicate_booking »Levable. Le même client détient déjà une réservation vivante sur le même créneau. L’idempotence ne couvre pas ce cas : elle attrape deux fois la même requête, pas deux requêtes différentes pour un même client — un client qui réserve une fois via le widget et une fois chez vous, ou un réessai dont la clé a été régénérée. Une personne qui reçoit légitimement à deux tables existe : c’est un avertissement et non un mur.
guarantee_required
Section intitulée « guarantee_required »Levable. Le créneau porte une garantie par carte, et celle-ci ne peut pas aboutir faute d’adresse e-mail joignant le client. Deux remèdes : envoyer une adresse e-mail du client, ou lever la règle, ce qui réserve sans la garantie. Levez-la délibérément : c’est tout l’intérêt lorsqu’une réservation est prise par téléphone, et le restaurant ne détient alors aucune carte en cas d’absence.
Ce code peut aussi être envoyé par anticipation, avant même d’avoir vu le refus.
guest_notifications_required_for_guarantee
Section intitulée « guest_notifications_required_for_guarantee »Non levable. Vous avez envoyé guest_notifications: "partner_managed" pour
un créneau portant une garantie par carte. La garantie est un e-mail : le
client reçoit un lien de paiement, un rappel et un reçu, et sans eux la carte
n’est jamais enregistrée, l’option expire et la réservation s’annule
automatiquement — après que vous lui avez annoncé que la table était à lui.
Deux remèdes, tous deux entre vos mains : abandonner la suppression pour cette
réservation, ou envoyer override: ["guarantee_required"], qui réserve sans la
garantie et ne laisse aucun lien de paiement à supprimer.
widget_disabled
Section intitulée « widget_disabled »Non levable. Le compte du restaurant n’autorise plus du tout les réservations
en ligne. Vous ne devriez pas atteindre ce code, puisque la même condition fait
échouer l’authentification sous plan_required : le rencontrer
signifie que l’état du compte a changé entre l’authentification et l’écriture.
Rien de votre côté ne le corrige.
État d’une réservation ou d’une option
Section intitulée « État d’une réservation ou d’une option »not_found
Section intitulée « not_found »invalid_request_error · 404 Not Found. Aucune ressource de ce nom pour le
restaurant de cette clé, ou aucun point de terminaison de ce nom. Voir
404, et non 403, entre restaurants.
Lorsque l’identifiant qui ne correspond à rien a été envoyé dans le corps ou
dans la chaîne de requête — guest_id, hold_id, section_id,
entire_section_id, company_id ou excluding — param nomme ce champ.
Un identifiant d’option déjà convertie ou libérée, par vous ou par le
restaurant depuis son back-office, répond lui aussi 404 sur
POST /v1/reservations { hold_id } : du point de vue de la conversion il
n’existe aucune option utilisable, et c’est ce qui empêche de dépenser deux fois
la même option.
reservation_finalized
Section intitulée « reservation_finalized »invalid_request_error · 409 Conflict. La vie de la réservation est terminée :
elle est completed ou no_show, ou (sur PATCH seulement) déjà cancelled.
Rien de ce que vous enverrez ne la changera. Lisez status et cessez d’écrire.
Annuler une réservation déjà annulée n’est pas ce cas : cela répond 200
avec le même objet terminal, puisque l’objectif est déjà atteint.
hold_already_converted
Section intitulée « hold_already_converted »invalid_request_error · 409 Conflict. DELETE /v1/holds/{id} sur une option
déjà devenue une réservation. Il n’y a plus rien à libérer, et la table reste
prise — par une réservation. Le message la nomme ; une option et sa réservation
partagent le suffixe de l’identifiant : hold_AbC est devenue resv_AbC.
Annulez cette réservation si vous vouliez rendre la table.
C’est délibérément un 409 et non le 404 que renvoie la conversion pour la
même option. DELETE pose la question « assurez-vous que ceci ne retient pas une
table », et un 404 vous dirait que l’option n’a jamais existé alors que vous
détenez en réalité une réservation confirmée.
section_booking_not_modifiable
Section intitulée « section_booking_not_modifiable »invalid_request_error · 409 Conflict. PATCH /v1/reservations/{id} sur une
réservation faite avec entire_section_id. Toute modification est refusée,
y compris guest_notes seul, car une modification replace le groupe sur une
table ou une combinaison et réduirait la réservation à une partie de la salle.
Deux corps reçoivent une réponse avant ce contrôle : un corps vide, et un corps
qui nomme un champ qu’aucun PATCH n’accepte, obtiennent chacun d’abord un
400. Annulez et réservez de
nouveau, ou demandez au restaurant de la modifier depuis son back-office. Le
lien de gestion du client est refusé de la même façon.
conflict
Section intitulée « conflict »invalid_request_error · 409 Conflict. Une transition de statut que cette API
n’avait pas anticipée. Chaque situation que vous pouvez atteindre possède
délibérément un code plus précis : celui-ci est le filet de sécurité, pas un code
sur lequel brancher. Relisez la réservation et regardez status.
Garanties par carte
Section intitulée « Garanties par carte »Certains créneaux exigent une empreinte de carte. Une création sur l’un d’eux
produit une réservation en awaiting_guarantee et envoie au client un lien de
paiement. Une fois la carte enregistrée, la réservation passe à confirmed, ou à
pending quand le service demande au restaurant de valider ses réservations.
Les codes ci-dessous sont les façons dont cela peut être refusé.
Une carte est demandée par deux voies. Le créneau la demande de lui-même, quoi
que vous envoyiez ; ou vous envoyez un objet guarantee sur une création ou un
PATCH et la demandez sur un créneau qui n’en aurait pris aucune. Voir
Demander une carte vous-même.
Les deux voies divergent quand les paiements par carte du restaurant ne sont pas
actifs. Le créneau ne demande rien, aucun code de cette section n’est renvoyé et
la création est prise sans garantie — voir
Cet établissement demande-t-il une carte ?
Un guarantee que vous avez envoyé est refusé, lui, par
payments_suspended.
email_required_for_guarantee
Section intitulée « email_required_for_guarantee »invalid_request_error · 422 Unprocessable Content. Le créneau porte une
garantie par carte et la réservation n’a aucune adresse e-mail vers laquelle
envoyer le lien de paiement. Envoyez guest.email, ou réutilisez un guest_id
dont la fiche en porte déjà une.
C’est le seul endroit où une adresse e-mail du client est obligatoire sur cette API. Partout ailleurs elle est facultative, et c’est pourquoi le code est spécifique plutôt qu’une erreur générique de champ manquant.
Sur une création qui va jusqu’à la vérification des règles de réservation, le
même refus arrive dans
violations aux côtés de guarantee_required, qui
propose l’autre remède. Sur une conversion d’option il arrive à plat, sans
tableau.
guarantee_guest_email_required
Section intitulée « guarantee_guest_email_required »invalid_request_error · 422 Unprocessable Content. La réservation a atteint
l’étape de garantie sans fiche client, ou avec une fiche sans adresse e-mail de
contact : le lien de paiement n’a nulle part où aller. Envoyez un client
accompagné d’une adresse e-mail.
Distinct de email_required_for_guarantee, qui
est vérifié plus tôt et signale le remède qui ne coûte rien. Une création sans
adresse reçoit ce refus plus précoce, quelle que soit la voie qui a demandé la
carte, et son message nomme la voie. Le code présent est la réponse à un
guarantee envoyé sur un PATCH dont la réservation n’a personne à qui écrire.
guarantee_amount_invalid
Section intitulée « guarantee_amount_invalid »invalid_request_error · 422 Unprocessable Content. La garantie configurée par
le restaurant pour ce créneau se résout à un montant nul ou négatif : il n’y a
rien à demander au client. C’est un problème de configuration du restaurant et
non de votre requête — signalez-le-lui, ou envoyez guarantee.amount_cents
vous-même et fixez le chiffre.
Votre propre guarantee.amount_cents ne produit jamais ce code : un montant nul,
négatif ou fractionnaire est refusé à l’entrée, par un 400 qui nomme
guarantee.amount_cents. Ce code n’est atteint que là où le montant a été laissé
à la politique du restaurant.
guarantee_request_not_allowed
Section intitulée « guarantee_request_not_allowed »invalid_request_error · 422 Unprocessable Content. La garantie n’a pas pu
être posée sur l’état de la réservation.
Sur un guarantee envoyé avec un PATCH, cet état est celui de la réservation :
une carte ne peut être demandée que sur une réservation à venir, confirmed et
sans garantie. Une réservation en attente de validation doit d’abord être
validée, et une réservation qui porte déjà une carte doit voir celle-ci libérée
depuis le back-office. Relisez la réservation avant de redemander.
Sur le chemin de création il s’agit d’une incohérence interne plutôt que d’un choix de votre requête : réessayez la création une fois, et signalez-le si cela se reproduit.
payments_suspended
Section intitulée « payments_suspended »invalid_request_error · 422 Unprocessable Content. Vous avez demandé une
carte avec guarantee, et le restaurant ne peut pas encaisser par carte : sa
configuration de paiement est inachevée, ou son compte a été restreint depuis.
Il n’existe aucun moyen de recueillir la carte, donc rien n’est écrit.
Le même restaurant répond autrement à un créneau à carte : la réservation est prise et confirmée sans garantie, et vous n’apprenez rien, puisque rien n’a été refusé. La demande explicite est le seul endroit où un compte suspendu est une erreur.
guarantee_consent_guest_only
Section intitulée « guarantee_consent_guest_only »invalid_request_error · 422 Unprocessable Content. Un PATCH aurait relevé le
montant garanti d’une réservation dont la carte a été enregistrée via le widget du
restaurant — augmenter le nombre de couverts en est la cause habituelle — et un
montant plus élevé, c’est un plafond plus élevé que le restaurant pourra un jour
débiter. Relever ce plafond exige l’accord du titulaire de la carte sur le nouveau
montant : cet accord lui appartient et ne peut pas être donné en son nom. Ni votre
clé ni le personnel du restaurant ne peuvent l’affirmer à sa place.
Rien n’a changé. La modification est entièrement annulée et la réservation est exactement telle qu’elle était, y compris tout autre champ envoyé dans le même corps.
Trois voies possibles :
- Faire un changement qui ne relève pas le montant. Déplacer le jour ou l’heure s’applique, l’échéance d’annulation gratuite suivant le nouveau service, et réduire le nombre de couverts également — l’empreinte est alors recalculée à la baisse sans rien demander à personne, le mandat existant, plus élevé, couvrant déjà le montant réduit.
- Passer par le restaurant. Le personnel peut modifier le nombre de couverts depuis le back-office ; la garantie conserve son plafond existant, plus bas.
- Envoyer le client sur sa propre page de réservation, seule surface capable
de recueillir un nouveau mandat.
GET /v1/configurationpubliepublic_booking_url, etPOST /v1/reservations?expand=modification_tokenrenvoie le jeton d’une réservation que vous avez créée.
Une réservation créée par votre clé ne peut pas atteindre ce code. La carte
d’une réservation prise par le widget a été saisie par le client, qui a accepté
un montant en l’enregistrant, et relever ce montant est un nouvel accord que lui
seul peut donner. La carte d’une réservation créée par votre clé a été demandée
par le lien de paiement du client (origin: "staff_request"), au montant que
les règles du restaurant fixent ou à celui que vous avez demandé. Aucun chiffre
n’a été soumis à l’accord de ce client, et la carte garde le montant auquel elle
a été demandée pour toute la vie de la réservation. Voir Modifier la réservation
avant que la carte soit
enregistrée
et Demander une carte
vous-même.
Vous rencontrez ce code tout de même, parce qu’une synchronisation vous livre les réservations prises par le restaurant via son propre widget, et que votre chemin de modification passe dessus aussi.
details.guarantee_amount_cents n’est pas publié dans l’enveloppe : le montant ne
vous est d’aucune utilité, puisqu’il n’existe aucun chiffre que vous pourriez
accepter.
guarantee_modify_locked
Section intitulée « guarantee_modify_locked »invalid_request_error · 403 Forbidden. La réservation porte une empreinte de
carte active et sa date limite d’annulation gratuite est passée : elle ne peut
plus être modifiée. Cette fenêtre est ce qui empêche un groupe de passer de huit
à deux une heure avant le service pour échapper à l’empreinte.
L’annulation reste possible, avec les frais prévus par les conditions du
restaurant. Ce 403 porte sur l’état de la réservation et non sur votre clé :
aucune portée n’y change rien.
guarantee_modify_requires_card
Section intitulée « guarantee_modify_requires_card »invalid_request_error · 422 Unprocessable Content. Un PATCH ferait passer
une réservation sans carte sous une garantie par carte : le groupe dépasse la
taille à partir de laquelle le restaurant demande une carte, ou la réservation
passe sur un service qui en demande une à chaque réservation. Un PATCH n’a
aucune étape qui recueille une carte : il est refusé avant toute écriture. Rien n’a changé, y compris les autres champs du même corps.
Il arrive à plat, pas dans violations, et il n’est jamais levable : le nommer
dans override donne un 400.
Annulez puis réservez à nouveau. La création est l’appel qui recueille une
carte : la nouvelle réservation arrive en awaiting_guarantee et le client
reçoit le lien de paiement. Si votre clé porte reservations:write:override, la
création peut à la place lever guarantee_required et
réserver sans aucune empreinte. Une modification qui n’entraîne aucune garantie
s’applique normalement.
Le lien de gestion du client ne contourne rien. Cette page répond le même refus : elle indique au client que rien n’a été modifié, et qu’il lui faut annuler puis faire une nouvelle réservation ou contacter l’établissement. Aucune modification ne recueille de carte, d’un côté comme de l’autre.
Une garantie libérée, retirée ou expirée compte comme une absence de carte. Deux
cas ne sont pas refusés : une réservation dont le créneau et le groupe actuels
demandent déjà une garantie qu’elle n’a pas, par exemple une réservation créée
en levant guarantee_required, et toute réservation chez un restaurant dont les
paiements par carte ne sont pas actifs à ce moment.
Transport et serveur
Section intitulée « Transport et serveur »rate_limited
Section intitulée « rate_limited »rate_limit_error · 429 Too Many Requests. Attendez l’intervalle Retry-After
puis réessayez. Les lectures et les écritures puisent dans deux budgets distincts
et RateLimit-Resource nomme celui que vous venez de dépenser — voir
Limites de débit.
payment_provider_error
Section intitulée « payment_provider_error »api_error · 502 Bad Gateway. Le prestataire de paiement était injoignable ou
a refusé pendant la prise d’empreinte. La réservation n’a pas été créée.
Réessayez avec le même Idempotency-Key, en espaçant les tentatives.
internal_error
Section intitulée « internal_error »api_error · 500. Une erreur inattendue du côté de Service. Réessayez avec un
délai exponentiel, et signalez-le-nous si le problème
persiste.
Codes de statut HTTP
Section intitulée « Codes de statut HTTP »| Statut | Signification |
|---|---|
200 OK |
La requête a réussi. |
201 Created |
Une réservation ou une option a été créée. |
304 Not Modified |
Un GET conditionnel dont le If-None-Match correspond. Voir Étendre les objets. |
400 Bad Request |
La requête n’a jamais été interprétée — une entrée mal formée ou inconnue. Vérifiez param. |
401 Unauthorized |
Clé d’API manquante ou invalide. Voir Authentification. |
403 Forbidden |
La clé, l’abonnement ou l’état de la réservation l’interdit. Réessayer n’y change rien. |
404 Not Found |
Aucune ressource de ce nom pour le restaurant de cette clé, ou aucun point de terminaison de ce nom. |
409 Conflict |
L’état de la ressource refuse l’opération, ou une clé d’idempotence est en cours ou déjà utilisée. |
422 Unprocessable Content |
La requête a été comprise et refusée par une règle. Les règles de réservation arrivent ici avec violations. |
429 Too Many Requests |
Débit limité. Voir Limites de débit. |
500 / 502 / 503 |
Un problème est survenu du côté de Service ou chez un prestataire. Réessayez avec un délai exponentiel. |
404, et non 403, entre restaurants
Section intitulée « 404, et non 403, entre restaurants »Si vous demandez une ressource qui existe mais appartient à un autre
restaurant, l’API renvoie 404 Not Found, et non 403 Forbidden. Du point
de vue de votre clé, la ressource n’existe pas. Cela évite de divulguer
l’existence des données d’autres restaurants. Un identifiant valide qui devrait
« fonctionner » selon vous mais qui renvoie 404 appartient presque toujours à
un autre restaurant, ou à une autre clé.
Gérer les erreurs
Section intitulée « Gérer les erreurs »- Branchez sur
code, repliez-vous surtype, ne vous appuyez jamais surmessage. - Sur un
422 booking_rules_violated, lisezviolationset branchez entrée par entrée suroverridable. Le code de premier niveau ne vous apprend rien d’autre. - Réessayez les
429aprèsRetry-After, et les500/502avec un délai exponentiel — sur une écriture, toujours avec le mêmeIdempotency-Key. - Réessayez
409 idempotency_request_in_progressavec la même clé. Tous les autres409exigent que vous relisiez d’abord la ressource. - Un
2xxportantIdempotent-Replayed: trueest la réponse d’une tentative précédente, pas une nouvelle écriture. Voir Idempotence. - Ne réessayez jamais un
400,401,403ou404inchangé. Chacun appelle une autre requête, une autre clé, ou une action du restaurant.
Codes absents de cette page
Section intitulée « Codes absents de cette page »Cette page liste chaque code renvoyé par l’API Service. Le back-office et le widget de réservation de Service partagent le même vocabulaire d’erreurs et ont leurs propres codes, pour les sessions du personnel, les plans de salle, l’inscription en liste d’attente et l’administration des cartes. Aucun n’atteint une clé d’API, et aucun n’est listé ici.
Un code renvoyé par cette API sans entrée sur cette page est un défaut. Signalez-le avec l’identifiant de requête et l’enveloppe complète.
Obtenir de l’aide
Section intitulée « Obtenir de l’aide »Chaque réponse porte un en-tête X-Request-Id, en cas de succès comme
d’échec, et sa valeur désigne cette requête précise dans les journaux de Service.
Consignez-la à côté de l’enveloppe reçue : c’est l’identifiant de requête que les
paragraphes ci-dessus vous demandent de citer.
Signalez un problème depuis la page de contact de Service, en citant cet identifiant et l’enveloppe complète.