Authentification
L’API Service authentifie les requêtes avec une clé d’API, envoyée comme
jeton Bearer dans l’en-tête
Authorization.
GET /v1/reservations HTTP/1.1Host: api.useservice.appAuthorization: Bearer sk_live_YOUR_SECRET_KEYAvec curl :
curl https://api.useservice.app/v1/reservations \ -H "Authorization: Bearer $SERVICE_API_KEY"Les exemples de cette documentation lisent la clé depuis une variable
d’environnement SERVICE_API_KEY plutôt que de la coder en dur — gardez votre
vraie clé hors de l’historique du shell, des scripts et du contrôle de version.
Comment fonctionnent les clés
Section intitulée « Comment fonctionnent les clés »- Une clé appartient à un seul restaurant. Elle authentifie ce restaurant et ne renvoie jamais que les données de ce restaurant, ce qui explique qu’aucune requête ne porte d’identifiant de restaurant. Il n’existe pas de clé de groupe : un groupe de dix établissements détient dix clés, une par établissement, et chaque appel concerne un seul établissement.
- Les clés sont secrètes. Une clé commençant par
sk_live_porte les portées que le restaurant lui a accordées, ce qui peut inclure réserver, modifier et annuler pour le compte de ce restaurant. Traitez-la comme un mot de passe. - Les clés sont réservées au serveur. N’intégrez jamais une clé dans un navigateur, une application mobile, un dépôt public ou tout code côté client. Utilisez-la uniquement depuis votre backend — voir Aucune clé utilisable dans un navigateur.
- Les clés ne sont affichées qu’une fois. Le secret complet est affiché une seule fois, à la création de la clé. Seul un court suffixe (les 4 derniers caractères) est conservé et affiché par la suite, alors stockez la valeur complète de manière sécurisée dès la création. Si vous la perdez, créez une nouvelle clé.
- Plusieurs clés sont prises en charge. Un restaurant peut détenir plusieurs clés (par exemple, une par intégration), ce qui vous permet d’en faire tourner ou d’en révoquer une sans perturber les autres.
Obtenir une clé
Section intitulée « Obtenir une clé »Les clés d’API sont en libre-service depuis le back-office. Un propriétaire ou copropriétaire peut les gérer dans Paramètres → Développeurs :
- Ouvrez Paramètres → Développeurs et créez une nouvelle clé d’API.
- Choisissez les portées qu’elle doit porter. Accordez le minimum dont l’intégration a besoin ; vous ne pourrez pas en ajouter ensuite.
- Copiez le secret (
sk_live_…) — il n’est affiché qu’une fois, à la création. Stockez-le en lieu sûr avant de quitter la page. - Envoyez-le comme jeton Bearer, comme indiqué ci-dessus.
Chaque clé est épinglée à une version de l’API à sa création et peut être révoquée à tout moment depuis le même écran. Un restaurant peut détenir jusqu’à 10 clés — gardez-en une par intégration pour pouvoir les faire tourner ou les révoquer individuellement.
Il n’y a pas d’environnement de bac à sable ou de test distinct : toutes les
clés sont en production (sk_live_…) et agissent sur le vrai restaurant via
l’unique URL de base de production.
Une réservation que vous créez est une réservation pour laquelle le restaurant
dressera une table, et une annulation parvient au client. Répétez sur un
restaurant qui vous appartient.
Une clé ne porte pas automatiquement toutes les permissions du restaurant.
Chacune est créée avec une liste de portées, choisie à la création, et elle ne
peut pas l’élargir ensuite — pour accorder davantage, créez une deuxième clé.
Une clé créée avant l’existence des portées porte reservations:read, et c’est
aussi la valeur par défaut d’une nouvelle clé.
| Portée | Ce qu’une clé qui la porte peut faire |
|---|---|
reservations:read |
Lire les réservations et leurs événements de cycle de vie, les disponibilités, la politique de réservation, les sections, les options et la file d’attente. |
reservations:write |
Réserver, modifier et annuler. Également poser et rendre des options : il n’existe pas de portée distincte pour les options, car une clé qui peut réserver peut poser une option. |
reservations:write:override |
Trois choses, sur une seule portée. Envoyer override, qui réserve en passant outre une règle fixée par le restaurant. Envoyer excluded_from_shift_limits: true, qui exclut définitivement une réservation du plafond de couverts du service. Envoyer entire_section_id, qui réserve toutes les tables d’une section, y compris une section que le restaurant ne propose pas à la réservation en ligne. Voir Réserver une salle entière. |
guests:read |
Lire les fiches clients, déplier un client depuis une réservation, déplier le jeton de modification sur POST /v1/reservations, et lire les données client d’une réservation — sur toutes les réservations, y compris celles que votre clé a créées. Un jeton de modification ouvre la page de réservation du client en son nom, étape carte comprise : traitez-le comme son mot de passe. |
Les portées ne s’emboîtent pas. reservations:write n’implique pas
reservations:read, et reservations:write:override n’implique pas
reservations:write — demandez chacune de celles que l’intégration utilise.
reservations:write:override exige reservations:write à côté d’elle, et en
est délibérément séparée : elle permet à un restaurant de vous confier la
possibilité de réserver sans vous confier celle de passer outre ses propres
règles, d’exclure une réservation de ses plafonds de couverts ou de retirer une
salle de la vente.
Ces trois pouvoirs étaient trois portées jusqu’au 22 septembre 2026. Ils n’en
font plus qu’une. Les trois champs restent distincts — chacun est refusé
séparément, en nommant son propre param — mais un restaurant qui en accorde un
dit une seule phrase, et une clé qui porte la portée les porte tous les trois.
Certaines portées sont exigées par un champ, pas par un point de terminaison
Section intitulée « Certaines portées sont exigées par un champ, pas par un point de terminaison »reservations:write:override et guests:read peuvent être exigées par quelque
chose à l’intérieur de la requête plutôt que par le point de terminaison appelé.
Sur une création, un tableau override, excluded_from_shift_limits: true et
entire_section_id exigent chacun la première ; expand=guest exige la
seconde, depuis n’importe quel point de terminaison.
Le refus vous dit lequel des deux cas s’est produit. Un 403 insufficient_scope
qui nomme un param signifie que l’appel est à votre portée et que seul ce
champ ne l’est pas : retirer le champ et réessayer mérite d’être automatisé.
Sans param, la clé n’atteint pas du tout le point de terminaison et réessayer
n’y changera rien.
Erreurs est la référence : la portée exigée par chaque point de terminaison, les champs qui en exigent une, et l’enveloppe du refus.
Données client d’une réservation
Section intitulée « Données client d’une réservation »Une réservation porte les coordonnées données pour cette réservation
(contact_email, contact_phone), la demande du client lui-même
(guest_notes) et la note privée du restaurant à son sujet (internal_notes).
Ces quatre champs sont des données client, et guests:read est la seule chose
qui les lit. Une clé qui la porte les lit sur toutes les réservations, tels
qu’ils sont, si bien qu’une modification ultérieure par l’équipe du restaurant,
ou par le client depuis sa page de réservation, lui parvient aussi.
Une clé qui ne porte pas la portée n’en lit aucun, sur aucune réservation — y compris celles qu’elle a créées elle-même, et la réponse à la création qui les a faites. Vous renvoyer ce que vous venez d’envoyer ne vous apprendrait rien ; relire la même réservation une heure plus tard, si, et c’est pourquoi vos propres réservations ne font pas exception.
Là où ils sont retenus, les quatre champs sont absents de la réponse, pas
null. null a déjà un sens sur ces champs : contact_email: null indique
que la réservation suit les coordonnées de la fiche client. Vérifiez la présence
de la clé avant de lire sa valeur. Rien n’est refusé : aucun 403, et le reste
de la réservation est complet.
La règle vaut partout où une réservation est rendue : une liste, une lecture
unitaire, la réponse à une création, à un PATCH ou à une annulation, et les
changes des événements d’une
réservation. Un PATCH envoyé par une clé sans guests:read écrit
guest_notes et internal_notes et répond sans eux. Les notes d’une entrée
de file d’attente, la demande du client lui-même, relèvent de la même règle.
internal_notes est la note du restaurant lui-même et non les mots du client,
et vous l’écrivez autant que vous la lisez : c’est un champ de la création et du
PATCH, la note que l’équipe du restaurant saisit sur son propre formulaire de
réservation. La note privée d’une entrée de file d’attente ne figure sur aucune
surface partenaire.
Les notes d’une fiche client sont un autre champ : les notes que l’équipe du
restaurant tient sur la fiche du client. Le client ne les écrit pas. Elles vous
parviennent partout où la fiche client vous parvient : GET /v1/guests,
expand=guest et les événements guest.*.
Un point de terminaison de webhook n’est lié à aucune clé, et ses livraisons portent les données client en entier. Voir Les livraisons portent les données client.
Aucune clé utilisable dans un navigateur
Section intitulée « Aucune clé utilisable dans un navigateur »Toutes les informations d’authentification émises par cette API sont secrètes.
Il n’existe aucune variante publiable, et api.useservice.app ne répond à
aucune requête d’origine croisée venant d’une origine que vous contrôlez : une
clé ne peut pas fonctionner depuis une page que vous servez à des clients, même
si vous acceptiez de l’y exposer.
Un tunnel de réservation que vous construisez vous-même a donc besoin de votre propre serveur : le navigateur du client parle à votre backend, et seul votre backend détient la clé. Pour un parcours de réservation qui tourne entièrement dans le navigateur, sans backend, utilisez plutôt le widget de réservation — il ne demande aucune clé.
Erreurs d’authentification
Section intitulée « Erreurs d’authentification »Une clé manquante, mal formée, révoquée ou inconnue renvoie 401 Unauthorized
avec une enveloppe d’erreur. Il existe deux codes
d’authentification :
auth_header_missing— aucun en-têteAuthorization: Bearer …n’a été envoyé.invalid_token— une clé a été envoyée mais elle est invalide, révoquée, expirée ou inconnue.
{ "error": { "type": "authentication_error", "code": "invalid_token", "message": "The provided API key is invalid, revoked, or expired.", "doc_url": "https://docs.useservice.app/api/errors#invalid_token" }}(param est omis ici — aucun paramètre de requête n’est en cause.)