Aller au contenu

Demander une garantie par carte

Certaines réservations valent de l’argent avant que quiconque ne s’assoie. Un séminaire, un groupe de trente au réveillon, une table retenue un jour férié : les réservations qu’un restaurant ne peut pas se permettre de voir s’évaporer. Pour celles-là, il configure une garantie — le client enregistre une carte, et le restaurant pourra la débiter si personne ne vient.

Vous pouvez réserver ces créneaux. Ce que vous ne pouvez pas faire, c’est prendre la carte.

Posez la question une fois par établissement, avant de construire quoi que ce soit. GET /v1/configuration porte card_guarantee_from_party_size :

  • null : aucune réservation faite maintenant, sur aucun service ni aucune date, ne se voit demander de carte. Soit le restaurant n’utilise pas de garantie par carte, soit il ne peut pas en prendre pour le moment. Supprimez l’étape carte pour cet établissement.
  • Un nombre : un service, à une date, peut demander une carte à un groupe de cette taille ou plus, et aucun service ne la demande à un groupe plus petit. Construisez l’étape carte, puis lisez la réponse exacte pour la date choisie par le client.

La réponse exacte est guarantee_from_party_size sur chaque service de GET /v1/availability : le plus petit groupe à qui ce service demande une carte à cette date, quel que soit le party_size de votre requête. null y signifie qu’aucun groupe ne se la voit demander sur ce service ce jour-là. guarantee, sur le même service, porte les montants, comme l’explique Lire le devis.

La valeur de l’établissement peut changer à tout moment sans aucun changement de votre côté : quand le restaurant active ou désactive les garanties, ou quand ses paiements par carte s’arrêtent ou reprennent. Si vous la conservez au-delà de la session d’un client, revalidez-la avec l’ETag.

Un restaurant dont les paiements par carte ne sont pas actifs ne demande aucune carte, et la création n’est pas refusée pour autant. La réservation est prise sans garantie : confirmed, ou pending sous validation manuelle. Les deux champs valent null dans cet état : ils décrivent déjà ce que fera la création.

L’étape carte se termine sur une page qui n’est pas la vôtre

Section intitulée « L’étape carte se termine sur une page qui n’est pas la vôtre »

C’est la contrainte, et elle décide de la forme du travail avant tout le reste.

Une réservation sur un créneau à garantie a besoin d’un moyen de paiement. Votre clé lit le devis — nature, montant, conditions d’annulation — et jamais le moyen de paiement derrière : aucune réponse faite à votre clé ne contient de secret client, d’identifiant de compte connecté ni de clé publiable. C’est une position assumée pour la v1 plutôt qu’un oubli.

Le client enregistre sa carte sur une page hébergée par Service, via un lien que nous lui envoyons par e-mail.

Un parcours censé s’achever à l’intérieur de votre propre paiement n’est pas constructible ici aujourd’hui. Voici ce que vous obtenez à la place :

  1. Votre création répond 201, réservation en awaiting_guarantee.
  2. Service envoie au client un lien de paiement.
  3. Le client enregistre une carte sur notre page.
  4. La réservation passe en confirmed, ou en pending quand le service demande au restaurant de valider ses réservations. reservation.created part à ce moment, avec ce statut.

Votre écran de confirmation annonce cet e-mail au client. Si votre produit promet un paiement d’un seul tenant, tranchez le cas des créneaux à garantie dès le premier jour : soit vous les orientez ailleurs, soit vous l’annoncez dans vos textes. Le découvrir une fois le tunnel construit coûte cher.

S’il vous faut la carte à l’intérieur d’un parcours que vous contrôlez, intégrez plutôt le widget de réservation. Il prend la carte dans la page, parce que la page est la nôtre.

?expand=modification_token sur la création renvoie l’identifiant propre du client pour sa page de réservation. Cette page n’est pas sur api.useservice.app. Elle est sur l’hôte de réservation, celui que nomme public_booking_url dans GET /v1/configuration (https://book.useservice.app/r/{slug} en production), et le lien est {public_booking_url}/{modification_token} : le même lien que portent les e-mails de confirmation et de demande de paiement.

Qui détient le jeton peut faire sur cette page tout ce que peut le client : consulter la réservation, la modifier, l’annuler et, tant que la réservation est en awaiting_guarantee, enregistrer la carte. Les données de la page portent ce sur quoi son formulaire de carte est monté. La promesse ci-dessus couvre donc votre clé, pas le jeton. Dès que vous demandez le jeton, vous détenez la clé du client pour sa réservation.

Traitez-le comme le mot de passe du client :

  • Ne le transmettez qu’au client, comme lien vers sa propre page.
  • Ne le journalisez jamais, et ne l’affichez jamais en texte.
  • Ne le gardez pas plus longtemps que nécessaire. Si vous donnez le lien au client une seule fois, ne le conservez pas ensuite.

Les points de terminaison derrière cette page appartiennent au widget et ne font pas partie de cette API. Construisez sur le lien, pas sur eux. Si vous n’envoyez au client aucun lien de votre part, n’utilisez pas l’expansion. Que Service envoie le lien dépend de guest_notifications, et dans ce parcours il l’envoie toujours : partner_managed est refusé sur un créneau qui demande une carte, donc les e-mails de confirmation et de demande de paiement le portent.

Portée Ce qu’elle sert ici
reservations:read Les disponibilités, seul endroit où le devis de garantie apparaît avant de réserver.
reservations:write La création.
reservations:write:override Uniquement pour lever la garantie avec override: ["guarantee_required"] à la création, c’est-à-dire réserver la table sans empreinte. Un PATCH ne peut pas la lever.
guests:read Uniquement pour ?expand=modification_token sur POST /v1/reservations, la clé du client pour sa page de réservation. Voir Le jeton du client est sa clé. Relire les coordonnées de la réservation que vous venez de prendre l’exige aussi.

Il n’existe pas de portée « garantie », parce qu’il n’existe pas de point de terminaison « garantie ». Débiter, rembourser et libérer une carte sont des actes du restaurant, faits depuis son back-office, et votre clé en lit le résultat sans le provoquer. Demander la carte, elle, est un champ de la création et du PATCH, et n’exige rien de plus que reservations:write — voir Demander une carte vous-même. Voir Portées.

GET /v1/availability porte un objet guarantee par service. null signifie qu’aucune carte ne sera demandée. Toute autre valeur décrit ce qu’une réservation dans ce service devra garantir, au tarif de la taille de groupe demandée.

{
"object": "availability_guarantee",
"mode": "imprint",
"currency": "EUR",
"per_guest_amount": 2000,
"amount": 60000,
"cancel_hours": 48
}
Champ Ce qu’il signifie
mode imprint aujourd’hui, et uniquement imprint. Voir Une empreinte, pas un débit.
per_guest_amount En unités mineures, par personne : 2000 vaut 20,00 €.
amount En unités mineures, pour la taille de groupe demandée. Le chiffre à afficher.
cancel_hours Nombre d’heures avant le service au-delà desquelles une annulation peut être débitée. null signifie qu’annuler est toujours gratuit et que seule une absence est débitée.

L’objet tient compte de la taille du groupe et n’est émis que si une empreinte se déclencherait réellement : l’honorer est ce qui évite d’annoncer une réservation gratuite puis d’en produire une qui attend une carte. Mettez le montant et les conditions d’annulation sur la page où le client choisit son heure.

Une empreinte enregistre la carte et autorise un montant. Aucun argent ne bouge au moment de la réservation. Le restaurant pourra en débiter tout ou partie ensuite — une absence, ou une annulation dans la fenêtre cancel_hours — et la garantie portée par la réservation rapporte les deux chiffres séparément :

amount est le plafond consenti, charged_amount ce qui a été prélevé et refunded_amount ce qui a été rendu. Tous les champs, et les états par lesquels passe une garantie, sont sur L’objet garantie.

Une valeur est à attendre d’avance : origin vaut staff_request sur toute réservation créée par votre clé. La carte est demandée par un lien de paiement, la même demande qu’un restaurant envoie depuis son back-office.

kind publie deux valeurs, imprint et prepayment. Seule imprint se produit aujourd’hui ; le paiement d’avance relève d’une phase ultérieure et aucun créneau ne le produit. Lisez le champ au lieu de le supposer, et traitez une valeur inconnue comme toute autre énumération extensible.

Aucun identifiant Stripe n’apparaît dans cet objet, ni aucun statut de contestation. Cela relève de la relation entre le restaurant et son prestataire de paiement, et vous n’en feriez rien.

Les cinq webhooks reservation.guarantee_* — demandée, débitée, remboursée, libérée, paiement échoué — sont la façon d’apprendre que quelque chose a bougé sans interroger l’API.

Fenêtre de terminal
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-12-31",
"start_time": "20:00",
"party_size": 30,
"guest": {
"first_name": "Marie",
"last_name": "Dupont",
"email": "[email protected]",
"language": "fr"
}
}'

Rien dans ce corps ne mentionne la garantie. C’est le créneau qui décide, et la réponse vous dit quelle voie a été prise : lisez status. awaiting_guarantee signifie que la table est retenue et que la carte n’est pas encore enregistrée.

Quatre conséquences de l’existence d’une réservation dans cet état.

Le client a 48 heures, ou moins. La demande de paiement porte une échéance de 48 heures, ramenée à l’heure réservée ou à l’échéance d’annulation gratuite si l’une des deux arrive plus tôt. L’expires_at de la garantie est le chiffre réel : lisez-le au lieu de calculer le vôtre.

La table est retenue pendant toute la fenêtre. Ce type de demande ne rend pas la table quand l’échéance passe ; il la garde jusqu’à ce que la demande soit résolue. Laisser traîner des demandes impayées coûte au restaurant de la capacité réelle.

Impayé veut dire annulé, automatiquement. Si le client n’enregistre jamais de carte, la demande expire et la réservation est annulée. L’annulation automatique est active, parce qu’un restaurant qui a configuré une garantie l’a fait précisément pour ne pas installer un groupe qui n’a rien garanti.

Votre 201 arrive avant le webhook reservation.created — et si le client ne paie jamais, ce webhook ne part jamais, ni aucun reservation.cancelled. Une réservation dont votre système n’a jamais entendu parler ne reçoit pas d’annulation. Recalez-vous sur l’API, comme l’explique Garder votre système synchronisé.

Le créneau décide de lui-même, et vous le pouvez aussi. Envoyez un objet guarantee sur une création ou sur un PATCH, et le client se voit demander une carte sur une réservation qui n’en aurait pris aucune. C’est la demande que l’équipe du restaurant émet depuis son back-office, aux mêmes conditions, et tout ce qui précède sur le lien de paiement, l’échéance et la table retenue s’y applique tel quel.

Fenêtre de terminal
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-12-31",
"start_time": "20:00",
"party_size": 6,
"guest": { "first_name": "Marie", "last_name": "Dupont", "email": "[email protected]" },
"guarantee": { "amount_cents": 6000, "deadline_hours": 24, "auto_cancel_on_expiry": true }
}'

Les trois clés sont facultatives, et {} demande une carte aux conditions du restaurant lui-même.

Clé Ce que son absence signifie
amount_cents Le montant que la politique de garantie du restaurant donne pour ce service et ce nombre de couverts.
deadline_hours 48 heures. Une échéance que vous envoyez est ramenée de la même façon — à l’heure réservée, et à l’échéance d’annulation gratuite quand elle est encore devant.
auto_cancel_on_expiry true : une demande impayée annule la réservation quand l’échéance passe.

amount_cents et deadline_hours sont des nombres entiers strictement positifs. Toute autre valeur est un 400 qui nomme guarantee.amount_cents ou guarantee.deadline_hours, et auto_cancel_on_expiry prend true ou false.

reservations:write suffit. Demander une carte ne retire rien au restaurant, ajoute une protection qu’il n’aurait pas autrement, et ne peut pas installer un groupe que ses règles écarteraient. Cette demande reste hors de reservations:write:override, la portée qui sert à passer outre ces règles. L’équipe peut retirer la demande depuis le back-office à tout moment.

Vous ne pouvez pas posséder les messages du client et demander une carte dans la même requête. Un guarantee envoyé à côté de guest_notifications: "partner_managed" est un 400 qui nomme guarantee. Le lien de paiement est un e-mail de Service, et partner_managed est votre instruction de ne pas écrire à ce client. Envoyez guest_notifications à service_managed, ou retirez guarantee.

Sur un PATCH, la carte est tarifée après application des modifications. Un même corps qui fait passer le groupe à six et demande une carte tarife la carte pour six. La réservation doit être à venir, confirmed et sans garantie ; guarantee_request_not_allowed refuse tous les autres états.

Créneau à carte et demande explicite divergent quand les paiements sont coupés. Sur un restaurant qui ne peut pas encaisser, un créneau qui demanderait une carte prend la réservation et la confirme sans aucune garantie — et vous n’apprenez rien, puisque rien n’a été refusé. Un guarantee explicite sur ce même restaurant est refusé par payments_suspended. Vous avez posé une question, vous obtenez une réponse.

Quatre refus appartiennent à cette demande : payments_suspended, guarantee_request_not_allowed, guarantee_guest_email_required et guarantee_amount_invalid. Une création sans adresse e-mail où envoyer le lien de paiement est refusée plus tôt, par email_required_for_guarantee, dont le message dit laquelle des deux voies a demandé la carte.

Modifier la réservation avant que la carte soit enregistrée

Section intitulée « Modifier la réservation avant que la carte soit enregistrée »

Un PATCH sur une réservation encore en awaiting_guarantee s’applique comme tout autre : le nouveau créneau est revérifié face aux règles, la table est choisie à nouveau, et la réservation reste en awaiting_guarantee jusqu’à l’enregistrement de la carte.

La demande de carte ne suit pas la modification. amount, cancel_deadline_at et expires_at restent exactement tels que votre création les a calculés, quoi que vous changiez. Un groupe passé de quatre à six sur une garantie par convive se voit toujours demander le montant pour quatre, et la page du client affiche toujours ce montant. Si la modification doit être retarifée, annulez la réservation et prenez-en une nouvelle.

Une modification qui ferait passer sous garantie une réservation sans carte est refusée, par un 422 guarantee_modify_requires_card. Une modification ne peut pas recueillir de carte, ni chez vous ni chez le client : sa propre page de réservation répond le même refus et lui dit d’annuler puis de réserver à nouveau. Rien n’est modifié dans les deux cas. Voir Vous ne pouvez pas augmenter un montant consenti pour le refus voisin, sur une réservation qui a déjà une carte.

Jusqu’à l’enregistrement de la carte — ou jusqu’à ce que la demande expire et que la réservation soit annulée — aucun événement reservation.* sur la réservation ne vous parvient, modifications comprises. Un PATCH dans cette fenêtre n’envoie aucun reservation.updated, et ni ?expand=events ni le flux d’événements ne le listent. Le client n’en est pas informé non plus : une modification faite avant la publication de la réservation ne lui envoie aucun message. Le reservation.created que vous recevez, comme la confirmation que reçoit le client, décrivent la réservation telle qu’elle est au moment de sa publication, pas telle qu’elle a été demandée au départ.

Une adresse e-mail est obligatoire ici, quoi que disent les indicateurs

Section intitulée « Une adresse e-mail est obligatoire ici, quoi que disent les indicateurs »

Une création sur un créneau à garantie sans adresse e-mail est refusée par email_required_for_guarantee. C’est le seul endroit de cette API où l’e-mail du client n’est pas facultatif.

La surprise est que les indicateurs consultatifs de la configuration ne l’atteignent pas. require_guest_email et require_guest_phone décrivent ce que le restaurant demande sur son propre formulaire et n’engagent rien ici : une création sans l’un ni l’autre est acceptée sur un créneau ordinaire. Sur un créneau à garantie, l’exigence est mécanique plutôt que réglementaire — le lien de paiement est un e-mail, et sans adresse il n’y a nulle part où l’envoyer et aucun moyen pour la réservation d’aboutir.

Il arrive dans violations aux côtés de guarantee_required, et l’ordre est voulu. email_required_for_guarantee vient en premier et n’est pas levable ; guarantee_required suit et l’est. En lisant de haut en bas, le premier remède rencontré est celui qui ne coûte aucun privilège supplémentaire :

  • Envoyez une adresse e-mail, ou réutilisez un guest_id dont la fiche en porte une.
  • Ou override: ["guarantee_required"], qui réserve la table sans aucune garantie. C’est le cas de la réservation téléphonique — on ne demande pas une empreinte de carte à quelqu’un au téléphone — et le restaurant ne détient alors rien contre une absence. Levez-la délibérément, et voyez Réserver malgré les règles pour ce qui est consigné quand vous le faites.

Un refus voisin l’accompagne : guest_notifications_required_for_guarantee, renvoyé quand vous affirmez guest_notifications: "partner_managed" — vous prenez en charge la communication client — sur un créneau à garantie. La garantie est un e-mail : lien de paiement, rappel, reçu. L’affirmation et le créneau ne peuvent pas être honorés ensemble. Retirez la suppression pour cette réservation, ou levez la garantie et il ne reste aucun lien de paiement à supprimer.

Faites passer un groupe de quatre à huit sur une réservation dont la carte a été enregistrée via le widget du restaurant : le PATCH est refusé par guarantee_consent_guest_only. Rien ne change — toute la modification est annulée, y compris tous les autres champs du même corps.

La raison n’est pas un modèle de permissions. Un montant plus élevé est un plafond plus élevé que le restaurant pourra un jour prélever, et l’augmenter demande l’accord du porteur de la carte sur le nouveau chiffre. Cet accord est une preuve archivée, donnée par cette personne. Votre clé n’est pas le client, et le personnel du restaurant non plus : personne ne peut l’affirmer à sa place.

Trois voies, que le refus nomme :

  • Faire un changement qui n’augmente pas le montant. Déplacer le jour ou l’heure s’applique, et l’échéance d’annulation gratuite suit le nouveau service. Réduire le groupe aussi : l’empreinte est retarifée à la baisse sans rien demander à personne, puisque le mandat existant, plus élevé, couvre déjà le chiffre inférieur.
  • Passer par le restaurant. Le personnel peut changer la taille du groupe, et la garantie conserve son plafond existant, plus bas.
  • Renvoyer le client vers sa propre page de réservation. C’est la seule surface capable de recueillir un mandat renouvelé. Construisez le lien à partir de public_booking_url sur la configuration et du modification_token demandé à la création — la même paire qu’utilise le tunnel de réservation.

Ce refus ne peut pas survenir sur une réservation créée par votre clé. Une réservation prise via le widget du restaurant porte une carte que le client a saisie lui-même en acceptant un montant, et un montant plus élevé est un nouvel accord que lui seul peut donner. Une réservation créée par votre clé porte une carte demandée par lien de paiement (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 n’est jamais retarifée ensuite : il n’y a rien que ce code puisse refuser. Vous le rencontrez tout de même, parce qu’une synchronisation vous livre les réservations prises par le widget du restaurant et que votre chemin de modification passe dessus.

Une carte demandée par lien de paiement garde le montant auquel elle a été demandée. Une création pour quatre sur une garantie de 10 € par convive demande 40 €, et le client enregistre la carte à 40 €. Faites passer le groupe à six une semaine plus tard : la modification s’applique et la carte reste à 40 €. La couverture n’est plus proportionnelle au groupe, si bien qu’une annulation tardive ou un client absent sur cette réservation ne peut être débité que de 40 € au plus. Un restaurant que l’écart gêne peut libérer la carte depuis son back-office et en demander une nouvelle au nouveau chiffre. Une réservation prise par le widget fait l’inverse : elle retarife, et demande au client d’accepter le montant plus élevé.

Une réservation sans carte rencontre un autre refus quand un PATCH entraînerait une garantie — un groupe qui dépasse la taille à partir de laquelle le restaurant demande une carte, ou un passage sur un service qui en demande une à chaque réservation. C’est guarantee_modify_requires_card, et rien n’est écrit. Aucune modification n’a d’étape de carte — ni la vôtre, ni la propre page de réservation du client, qui répond le même refus et lui dit d’annuler puis de réserver à nouveau. Seule une création recueille une carte : annulez puis réservez à nouveau, et la nouvelle réservation passe par l’étape de carte décrite plus haut.

Code Quand Que faire
email_required_for_guarantee 422, dans violations, overridable: false Envoyez une adresse e-mail, ou levez la garantie.
guarantee_required 422, dans violations, overridable: true La lever réserve la table sans empreinte.
guest_notifications_required_for_guarantee 422, param: "guest_notifications", overridable: false Retirez la suppression, ou levez la garantie.
guarantee_consent_guest_only 422 sur un PATCH L’une des trois voies ci-dessus.
guarantee_modify_requires_card 422 sur un PATCH, jamais levable Annulez puis réservez à nouveau — seule une création recueille une carte. La page du client répond le même refus.
guarantee_modify_locked 403 L’échéance d’annulation gratuite est passée sur une empreinte active. La réservation reste annulable, selon les conditions du restaurant.
guarantee_guest_email_required 422 L’étape de garantie a été atteinte sans e-mail de contact. Envoyez un client qui en a un.
guarantee_amount_invalid 422 La configuration du restaurant donne un montant nul ou négatif. Envoyez guarantee.amount_cents vous-même, ou signalez-le-lui.
payments_suspended 422 sur un guarantee que vous avez envoyé Le restaurant ne peut pas encaisser par carte. Retirez guarantee et prenez la réservation sans garantie.
guarantee_request_not_allowed 422 sur un guarantee que vous avez envoyé La réservation n’est pas à venir, confirmed et sans garantie. Relisez-la avant de redemander.

Garanties par carte est la référence pour tous ces codes.

Une démonstration réserve un créneau à garantie et photographie le 201. Cinq choses la séparent de ce qu’un restaurant acceptera près du réveillon.

Elle annonce le montant avant l’engagement du client. Le devis est sur les disponibilités précisément pour que le client voie le chiffre et les conditions d’annulation sur la page où il choisit son heure, pas dans un e-mail ensuite.

Elle affiche trois issues, pas une. confirmed, pending et awaiting_guarantee sont trois messages différents à donner au client, et seul le premier signifie que la table est à lui. Une réservation qui quitte awaiting_guarantee peut aboutir à l’un ou l’autre des deux autres. Un écran de confirmation à message unique se trompe deux fois sur trois chez un restaurant qui utilise les garanties.

Elle prévient le client de l’e-mail. Le lien de paiement est la prochaine chose à faire et elle se passe ailleurs. Un client qui ne sait pas qu’il doit l’attendre ne l’ouvre pas, et la réservation s’annule d’elle-même deux jours plus tard.

Elle envoie guest.language. Le lien de paiement, le rappel et le reçu partent dans la langue du client si vous la renseignez, et dans la langue principale du restaurant sinon. Une demande de paiement illisible est une demande de paiement sans suite.

Elle ne traite jamais awaiting_guarantee comme une réservation. Ni dans son propre reporting, ni dans un décompte de couverts, ni dans ce qu’elle envoie au client. C’est une table retenue contre une promesse qui n’a pas encore été tenue.