Authentification

Deux habilitations aux pouvoirs très différents : la clé d’API qui identifie votre compte, et les jetons d’accès qu’elle émet.

Tout appel à l’API porte une habilitation de type porteur :

Authorization: Bearer <credential>

Il en existe deux sortes, et les confondre est l’erreur la plus coûteuse qui soit.

Avant l’une comme l’autre : choisissez l’hôte. L’api existe une fois par environnement, et une habilitation émise pour l’un est refusée par l’autre. Les exemples de cette page visent la Production.

EnvironnementBase de l'API
Productionhttps://api.saphere.ai
Testhttps://api.test.saphere.ai

Les deux sont entièrement séparés — comptes distincts, clés distinctes, données distinctes. Voir Environnements.

La clé d’API

Votre clé d’API identifie votre compte client. C’est un simple UUID.

POST /access-tokens HTTP/1.1
Host: api.saphere.ai
Authorization: Bearer 2428fbbc-d322-49cb-90a7-4e93401e67f8
Content-Type: application/json

Trois de ses propriétés méritent d’être dites franchement :

  • Elle n’a aucune portée. Elle ouvre toutes les routes de l’API — créer des mesures, les relire, les supprimer, émettre des jetons.
  • Elle n’expire pas. Rien dans la clé elle-même ne borne sa durée de validité.
  • La seule chose qui la borne est l’activité de votre compte. Révoquer une clé revient à désactiver le client, ce qui coupe au même instant toutes les intégrations qui l’utilisent.

La clé ne doit jamais atteindre un navigateur ni un paquet mobile

Une clé dans du code client livré est une clé entre les mains de quiconque ouvre les outils de développement, décompresse un APK ou lit un paquet depuis le cache de votre CDN. Comme la révoquer revient à désactiver le compte, une fuite n’est pas un incident mineur.

Le widget est conçu pour n’avoir jamais besoin de la clé : il demande à votre serveur un jeton court à la place. Prenez cette voie.

Les jetons d’accès

Un jeton d’accès est l’habilitation que vous remettez à un navigateur. Il est émis à partir de votre clé d’API par POST /access-tokens, et il est délibérément faible :

Clé d’APIJeton d’accès
PortéeToutUniquement les permissions qu’il nomme
ExpirationAucuneObligatoire, 48 heures au plus
RejouableToujoursSeulement si vous l’autorisez
RévocationDésactiver le compteDELETE /access-tokens/{id}
Sûr dans un navigateurNonOui, par conception

Une seule permission existe aujourd’hui, websocket.measurement : elle autorise l’ouverture d’une session de mesure en temps réel, et rien d’autre. Un jeton qui la porte ne peut pas lire vos mesures, ne peut rien supprimer, et ne peut pas émettre d’autres jetons.

Donnez-lui maxAttempts: 1 et il ouvre exactement une session. C’est la forme à retenir pour une intégration par widget : un jeton consommé par la mesure pour laquelle il a été créé, et sans valeur ensuite.

La valeur du jeton et son identifiant sont deux chaînes différentes

POST /access-tokens rend les deux. value est l’habilitation — elle commence par ivat-, elle n’est rendue qu’une fois, et c’est elle que le widget présente. id est un UUID qui nomme le jeton : c’est ce que vous passez pour le révoquer, et le présenter comme porteur n’ouvre rien.

Seul un condensé de value est conservé, de sorte qu’un vidage de base ne distribue pas de jetons utilisables.

Limitation de débit

Les requêtes sont comptées par habilitation, sur une minute glissante.

RouteLimite
POST /access-tokens10 par minute
Tout le reste120 par minute

L’émission est délibérément plus serrée que le reste : chaque appel écrit en base, et un jeton est censé être demandé une fois au début d’une mesure — jamais en boucle.

Les réponses portent les en-têtes x-ratelimit-* habituels. Dépasser une limite rend un 429.

Un diagnostic utile

La présence des en-têtes x-ratelimit-* sur une réponse indique que la requête a atteint une route et a été refusée par un garde. Leur absence sur un 404 indique qu’aucune route n’a correspondu — un mauvais chemin, plutôt qu’une mauvaise ressource.

Réponses d’erreur

Les erreurs sont en JSON, avec une exception qu’il faut connaître.

{ "statusCode": 400, "message": ["duration must be an interval"], "error": "Bad Request" }

Tout 404 est en text/plain, avec le corps Not Found. Cela vaut aussi bien lorsque le chemin ne correspond à aucune route que lorsque la route a refusé de trouver la ressource. Si vous analysez les corps d’erreur comme du JSON, traitez le 404 à part, sans quoi vous lèverez à l’analyse au lieu de traiter le statut.

StatutSignification
400Le corps ou la requête a échoué à la validation ; message énumère quoi.
401En-tête Authorization absent ou mal formé.
403L’habilitation ne porte pas la permission que la route exige.
404Ressource inexistante, ou qui n’est pas la vôtre. En texte brut.
429Limite de débit dépassée.
503L’instance déleste ; Retry-After indique quand revenir.

Introuvable et pas à vous sont la même réponse

Une ressource appartenant à un autre client rend exactement le même 404 qu’une ressource qui n’a jamais existé. C’est délibéré : personne ne peut se servir de l’API pour découvrir si un identifiant est réel.