Authentification

Deux sortes d’habilitation, ce que chacune ouvre, et pourquoi une seule peut atteindre un navigateur.

Tout appel à l’api porte une habilitation, dans l’en-tête Authorization.

Authorization: Bearer <credential>

Il en existe deux sortes. Les confondre est l’erreur la plus coûteuse que l’on puisse commettre avec cette api.

D’abord, choisissez l’adresse. L’api existe une fois par environnement, et une habilitation délivrée 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 environnements sont complètement séparés : comptes distincts, clés distinctes, données distinctes. Voir Environnements.

La clé d’API

Votre clé d’API identifie votre compte. C’est un simple identifiant unique.

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

Trois choses à son sujet méritent d’être dites franchement.

  • Elle n’a aucune limite. Elle ouvre toutes les routes de l’api : créer des mesures, les relire, les supprimer, délivrer des jetons.
  • Elle n’expire pas. Rien dans la clé elle-même ne borne sa durée de validité.
  • La seule chose qui l’arrête est la désactivation de votre compte. Révoquer une clé revient à désactiver le compte, 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é livrée dans du code client est une clé que n’importe qui peut lire : en ouvrant les outils de développement du navigateur, en décompressant une application mobile, ou en récupérant vos fichiers depuis un cache. Et 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 de courte durée à la place. Prenez cette voie.

Les jetons d’accès

Un jeton d’accès est l’habilitation que vous pouvez remettre à un navigateur. Votre serveur le crée à partir de votre clé d’API, avec POST /access-tokens. 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

Deux permissions existent : measurement.scan, la mesure du widget Saphere Scan, et measurement.realtime, celle de la chaîne temps réel. Elles autorisent l’ouverture d’une session de mesure, et rien d’autre. Un jeton qui en porte une ne peut pas lire vos mesures, ne peut rien supprimer, et ne peut pas créer d’autres jetons.

Donnez-lui maxSessions: 1 et il ouvre exactement une session. C’est ce qu’une intégration par widget doit utiliser : 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 seule fois, et c’est elle que le widget présente. id nomme le jeton : c’est ce que vous passez pour le révoquer, et l’employer comme habilitation n’ouvre rien.

Seule une empreinte de value est conservée, de sorte qu’une copie de la base ne distribue aucun jeton utilisable.

Limitation de débit

Les requêtes sont comptées par habilitation et par route, sur une minute glissante. Chaque route tient un compte à elle, de sorte qu’une rafale sur l’une n’en ferme jamais une autre.

RouteLimite
POST /access-tokens20 par minute
Tout le reste300 par minute

La création de jetons 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.

Ces bornes sont dimensionnées pour les pointes plutôt que pour le régime courant : elles sont là pour qu’une rafale ne devienne pas une panne. Ce qui gouverne votre volume est votre quota, pas ce tableau.

Les réponses portent X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset, ce dernier en secondes. Dépasser une limite donne un 429, avec un en-tête Retry-After en secondes : la route reste fermée à cette habilitation pendant une minute au plus.

L’ouverture d’une mesure

La poignée de main qui ouvre une mesure Saphere Scan — qu’elle présente un jeton d’accès ou un identifiant de mesure — est comptée à part, par client et non par route :

Ce qui est comptéLimite
mesures ouvertes2 par seconde
mesures ouvertes20 par minute

Les deux fenêtres vont ensemble : la seconde étale ce que la minute autorise, une mesure occupant de la mémoire tant qu’elle dure. Une ouverture refusée par cette borne ne consomme pas le jeton d’accès qu’elle présentait : il reste présentable, et une nouvelle tentative un instant plus tard aboutit.

Ces valeurs laissent une marge large sur ce qu’un parcours utilisateur produit — une personne ne lance pas deux mesures dans la même seconde. Elles se rencontrent en automatisant : un banc d’essai qui enchaîne les mesures doit les espacer.

Un diagnostic utile

Si une réponse porte les en-têtes X-RateLimit-*, la requête a atteint une route et a été refusée par un contrôle. Si un 404 n’en porte aucun — et que son corps est le texte brut Not Found —, aucune route n’a correspondu : c’est le chemin qui est faux, plutôt que la 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 amounting to more than zero, such as 2h 30min, 45min or 90s, each unit appearing at most once, and to at most 48h"],
  "error": "Bad Request"
}

Un chemin qui ne correspond à aucune route répond en texte brut : un 404 dont le corps est Not Found, sans JSON. Toute autre erreur, y compris le 404 d’une ressource inexistante, est en JSON — { "message": "Not Found", "statusCode": 404 }. Si votre code analyse les corps d’erreur comme du JSON, regardez d’abord le Content-Type, sinon un chemin mal saisi échouera à l’analyse au lieu d’être traité par son statut.

StatutSignification
400Le corps ou la requête a échoué à la validation ; message énumère quoi.
401En-tête Authorization absent ou mal formé, clé d’API inconnue, ou compte désactivé.
404Ressource inexistante, ou qui n’est pas la vôtre. En texte brut seulement quand aucune route ne correspond au chemin.
406Votre en-tête Accept exclut le JSON sur une route qui répond en JSON. Envoyez application/json ou */*.
415Une route qui prend un corps JSON a reçu un autre Content-Type.
429Limite de débit dépassée ; Retry-After indique quand revenir.
500La requête n’a pas pu aboutir de notre côté.
503L’instance déleste ; Retry-After indique quand revenir.

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

Une ressource appartenant à un autre compte 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 savoir si un identifiant est réel.