Jetons d'accès
Émettre l’habilitation courte et cadrée que le widget présente — et la révoquer.
Un jeton d’accès est ce que vous remettez à un navigateur à la place de votre clé d’API. Il nomme ce qu’il a le droit de faire, il expire, et vous pouvez le rendre à usage unique.
Créer un jeton
POST /access-tokens
Authorization: Bearer <apiKey>
Content-Type: application/json
{
"description": "Mobile application, iOS and Android",
"permissions": [{ "name": "websocket.measurement", "maxAttempts": 1 }],
"duration": "15min"
}
Champs de la requête
| Champ | Type | Requis | Remarques |
|---|---|---|---|
description | chaîne | non | Texte libre, jusqu’à 255 caractères. Uniquement là pour vous aider à reconnaître le jeton plus tard. |
permissions | tableau | oui | Au moins une entrée. Nommer deux fois la même permission est refusé. |
duration | chaîne | oui | Un intervalle. 48h au plus, et strictement positif. Jusqu’à 64 caractères. |
Permissions
Une seule permission existe aujourd’hui.
{ "name": "websocket.measurement", "maxAttempts": 1 }
| Champ | Type | Remarques |
|---|---|---|
name | "websocket.measurement" | Autorise l’ouverture d’une session de mesure en temps réel. |
maxAttempts | entier ≥ 1, ou null | Nombre de sessions que le jeton peut ouvrir. |
`maxAttempts` n'a pas de valeur par défaut
Omettez-le — ou envoyez null — et le jeton ne porte aucune limite sur le nombre de sessions qu’il ouvre. Sa durée devient la seule chose qui le borne, et il est rejouable jusqu’à son expiration.
C’est un choix légitime, mais il doit être délibéré. Pour une intégration par widget, écrivez maxAttempts: 1 et émettez un jeton neuf pour chaque mesure.
Une tentative est consommée par l’ouverture d’une session, non par l’aboutissement d’une mesure. Un utilisateur qui ouvre la caméra puis s’en va a consommé le jeton. C’est pourquoi le widget en demande un nouveau au début de chaque tentative plutôt que de le mettre en cache.
Durées
duration s’écrit comme un intervalle — un nombre, une unité, éventuellement répétés :
30s 15min 2h 48h
2h 30min 1d 1d 12h
Unités acceptées :
| Unité | Écriture |
|---|---|
| Secondes | s, sec, secs, second, seconds |
| Minutes | m, min, mins, minute, minutes |
| Heures | h, hr, hrs, hour, hours |
| Jours | d, day, days |
`m` signifie minutes
Il ne signifie pas mois. Les mois s’écrivent mon. Cela suit la grammaire d’intervalle qu’emploie la base de données, et c’est la chose la plus susceptible d’être mal comprise ici.
En pratique, le piège se referme rarement : tout ce qui s’exprime en semaines ou en mois dépasse le plafond de 48 heures et est refusé d’emblée.
Deux règles supplémentaires :
- Une unité ne peut apparaître qu’une fois, toutes ses orthographes comptant comme la même unité.
1d 12hest valide ;1d 1dayne l’est pas. - Le plafond est de 48 heures. Un jeton existe pour la mesure qu’il ouvre, non pour la durée d’un contrat. Il est imposé à la fois lors de la validation de votre requête et sur la table elle-même.
Réponse
201 Created
{
"id": "8f1c2d34-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"value": "ivat-XCq7mR4vT8pLnW2yZbJ6sD9eA1gUiO5rN0kMxPq7Yt",
"description": "Mobile application, iOS and Android",
"permissions": [{ "name": "websocket.measurement", "maxAttempts": 1 }],
"expiresAt": "2026-08-24T15:12:44.000Z",
"createdAt": "2026-08-24T14:57:44.000Z"
}
| Champ | Ce que c’est |
|---|---|
id | Nomme le jeton. À repasser pour le révoquer. N’ouvre rien. |
value | L’habilitation. Commence par ivat-. C’est elle que vous remettez au widget. |
expiresAt | Résolu depuis la durée, par la base de données, à l’insertion. |
`value` n'est rendu qu'une seule fois
Seul son condensé est conservé. Aucune route ne peut vous le redonner. Si vous le perdez, révoquez le jeton et émettez-en un autre.
Il s’ensuit que value ne devrait pas être écrit dans vos journaux. id et expiresAt peuvent l’être sans risque — ils n’ouvrent rien.
expiresAt vaut exactement createdAt plus la durée demandée, résolue par la base de données plutôt que par votre horloge ou par la nôtre.
Révoquer un jeton
DELETE /access-tokens/{id}
Authorization: Bearer <apiKey>
200 OK en cas de succès. Le {id} est l’id de la réponse de création — non value.
Une seule réponse pour quatre situations
Un identifiant inconnu, un jeton déjà révoqué, un jeton appartenant à un autre client, et un identifiant bien formé qui n’a jamais existé rendent tous le même 404 — en texte brut. Personne ne peut sonder des jetons qui ne lui appartiennent pas.
Un identifiant mal formé rend 400 à la place : il est refusé par la validation avant toute recherche.
Un jeton expiré reste supprimable. L’expiration masque un jeton à toute lecture, mais elle ne retire pas la ligne, et faire le ménage derrière soi reste possible.
Exemple complet
Les commandes ci-dessous s’adressent à la Production. Les mêmes appels sur le Test ne diffèrent que par la base :
| Environnement | Base de l'API |
|---|---|
| Production | https://api.saphere.ai |
| Test | https://api.test.saphere.ai |
Un jeton émis sur l’un n’est jamais valable sur l’autre — voir Environnements.
# Émettre un jeton à usage unique, valable un quart d'heure
curl -sX POST https://api.saphere.ai/access-tokens \
-H "Authorization: Bearer $SAPHERE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "web widget",
"permissions": [{ "name": "websocket.measurement", "maxAttempts": 1 }],
"duration": "15min"
}'
# Le révoquer par anticipation
curl -sX DELETE https://api.saphere.ai/access-tokens/8f1c2d34-5a6b-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer $SAPHERE_API_KEY"
Quand un jeton est refusé
Le serveur de mesure refuse une poignée de main sans dire pourquoi — délibérément, pour qu’un jeton rejeté ne puisse pas être sondé pour en connaître le motif. Déroulez la liste :
| Vérification | Comment le savoir |
|---|---|
| Expiré ? | Comparez expiresAt à maintenant. Les jetons sont émis par mesure, et ce n’est pas un hasard. |
| Déjà consommé ? | Avec maxAttempts: 1, ouvrir une session le consomme. Une nouvelle tentative demande un nouveau jeton. |
| Révoqué ? | Le DELETE est irréversible. |
| Client désactivé ? | Tous les jetons d’un client inactif cessent de fonctionner d’un coup. |
id envoyé à la place de value ? | La cause la plus fréquente. id est un UUID ; value commence par ivat-. |
Relayer la réponse entière convient très bien
Si votre point d’accès relaie la réponse de l’API telle quelle, le widget y prélèvevalue de lui-même. Ne relayer que l’id est le bogue d’intégration classique — l’appel réussit, et toutes les mesures sont refusées.Limite de débit
POST /access-tokens est limité à 10 appels par minute et par clé d’API, plus serré que les 120 généraux. Émettez un jeton par mesure, au moment où la mesure démarre.