Référence technique
Le détail contractuel des endpoints que votre application appelle directement : codes de réponse, cas d'erreur, limites de débit. Pour le schéma exact, interrogeable et à jour en continu, la documentation Swagger est disponible sur todis.eu/docs, sans jeton requis.
Authentification et limites de débit
Toutes les routes de vérification exigent l'en-tête
Authorization: Bearer <jeton>. Un jeton
manquant, invalide, révoqué ou expiré renvoie 401.
Au-delà de la limite de votre plan (30 requêtes/seconde en
Starter, 60 en Standard), une requête renvoie 429.
Quand votre jeton arrive à moins de 7 jours de son expiration,
chaque réponse authentifiée porte en plus l'en-tête
X-License-Warning (ex. license expires in 3
day(s), renew soon) : un simple signal, la requête en
cours aboutit normalement.
Qui décide quelles informations sont divulguées
Chaque attribut d'une présentation SD-JWT VC (l'âge, un diplôme, un statut) est protégé par son propre digest salé dans le JWT signé. Le portefeuille ne joint à sa présentation que la valeur et le sel des attributs que l'utilisateur a accepté de révéler : les autres restent des empreintes non réversibles. Todis n'a techniquement accès qu'à ce qui est effectivement divulgué dans la présentation qu'il reçoit, jamais plus.
Avec POST /verify/sessions, le champ
claims de votre requête devient une requête DCQL
intégrée à la demande d'autorisation envoyée au portefeuille :
l'utilisateur voit et approuve exactement cette liste avant que
son portefeuille ne construise la présentation. Avec
POST /verify/sd-jwt-vc, cette étape a déjà eu lieu
avant que vous n'obteniez la présentation : Todis la vérifie et
vous renvoie son contenu tel quel.
POST /verify/sd-jwt-vc
Vérifie une présentation SD-JWT VC. Deux modes au choix dans le
corps de la requête : issuer_public_key_pem (clé de
l'émetteur fournie directement) ou country_code
(résolution automatique via le registre officiel de l'UE) ;
issuer_public_key_pem est prioritaire si les deux
sont présents.
200: présentation vérifiée,claimsrenseigné.400: niissuer_public_key_pemnicountry_codefourni.401: jeton de licence manquant, invalide, révoqué ou expiré.422: présentation malformée ou signature non vérifiée (verified: false, détail danserror).429: quota de requêtes du plan dépassé.502: résolution du registre de confiance de l'émetteur impossible (modecountry_codeuniquement, ex. registre européen temporairement indisponible).
POST /verify/mdoc
Vérifie une présentation mdoc (ISO/IEC 18013-5, notamment permis
de conduire mobile). L'ancre de confiance
(issuer_trust_anchor_pem) doit être fournie
explicitement dans le corps de la requête. Ce peut être
l'IACA qui a émis le signataire du document,
c'est le choix recommandé : la chaîne du signataire, embarquée
dans la présentation, est validée jusqu'à elle, signature et
fenêtre de validité comprises, ce qui vous dispense de suivre le
renouvellement des signataires. Ce peut aussi être le certificat
du signataire lui-même, si vous préférez l'épingler. Un seul
niveau est suivi, comme le prévoit ISO/IEC 18013-5. Il n'y a pas
de résolution automatique par pays pour ce format
(contactez-nous si un émetteur mdoc particulier vous concerne).
200: présentation vérifiée,doc_typeetnamespacesrenseignés.401: jeton de licence manquant, invalide, révoqué ou expiré.422: présentation malformée, certificat de confiance incorrect, ou signature non vérifiée.429: quota de requêtes du plan dépassé.
POST /hosted/sessions
Le parcours de vérification hébergé : ce service sert lui-même
la page présentée à l'utilisateur (QR code, lien vers le
portefeuille, textes en français, anglais ou espagnol). C'est
l'intégration recommandée quand vous ne voulez pas construire
d'écran de vérification, et elle est détaillée sur la page
Intégrations. Vous redirigez
l'utilisateur vers l'URL renvoyée, il revient sur votre
success_url, et vous confirmez toujours le résultat
côté serveur avec GET /verify/sessions/{id}.
success_url signifie vérification réussie,
jamais que l'utilisateur est majeur : une preuve d'âge
qui vaut false y renvoie aussi, comme tout résultat
vérifié. Lisez le résultat côté serveur
(claims.age_over_18 avec le raccourci
age_over_18) et appliquez-y votre propre règle.
C'est le seul endpoint qui accepte un webhook_url
(POST JSON signé en HMAC-SHA256 à l'issue du parcours, même si
l'utilisateur ferme la page) et les raccourcis
check ("age_over_18",
"identity"), disponibles sur tous les plans. Il
exige un déploiement avec identité de signature des requêtes,
ce qui est le cas du service hébergé.
Option dc_api. Avec
"dc_api": true, la page hébergée propose au visiteur
d'ouvrir son portefeuille par la Digital Credentials API du W3C,
quand son navigateur la prend en charge ; le QR code reste
affiché. Le protocole est l'Annexe C d'ISO/IEC 18013-7
(org-iso-mdoc) : seules les attestations mdoc
(ISO/IEC 18013-5) passent par ce chemin, et le PID au format
SD-JWT VC passe par le QR code. Avec
check: "age_over_18", ce chemin demande
l'attestation de preuve d'âge (eu.europa.ec.av.1),
sans repli sur le PID. La réponse du portefeuille est chiffrée
pour Todis et liée à l'origine https://verify.todis.eu :
vous n'avez aucune origine à déclarer. Le résultat,
GET /verify/sessions/{id} et le webhook sont
inchangés. Hors navigateur, par exemple dans une application
native, utilisez le QR code ou le lien profond.
201: parcours créé, avechosted_url,expires_atet le secret de webhook.400: URL de retour invalide,checkinconnu, ou champs de vérification incohérents.401: jeton de licence manquant, invalide, révoqué ou expiré.403: le plan du jeton n'inclut pas la grammaire DCQL complète (dcql_queryfourni par vous).429: quota de requêtes du plan dépassé.501: déploiement sans identité de signature, flux hébergé indisponible.
POST /verify/sessions
Crée une session de vérification OpenID4VP. Le corps de la
requête liste le type de credential demandé (vct)
et les attributs demandés (claims, chemins
simples, ex. "age_over_18"). La confiance dans
l'émetteur vient de country_code (SD-JWT VC, liste
officielle de l'UE) ou des registres d'ancres de ce déploiement,
listés par GET /trust/registries. La réponse contient une
requête d'autorisation OpenID4VP à présenter au portefeuille de
l'utilisateur (QR code ou lien profond) ; la construction exacte
de la chaîne du code QR ou du lien est détaillée dans notre
fichier d'intégration. La session expire 15
minutes après sa création, qu'elle ait reçu une réponse ou non.
Pour les besoins avancés (plusieurs credentials par session,
alternatives entre credentials ou entre attributs, valeurs
contraintes), le champ dcql_query accepte une
requête DCQL complète à la place de
vct/claims, et peut mélanger SD-JWT VC
et mdoc. Le champ credential_trust donne alors à un
credential SD-JWT VC son propre country_code, pour
vérifier dans une même session des credentials d'émetteurs de
pays différents (chaque entrée ne vaut que pour son credential,
jamais pour un autre). Voir le détail dans la
spécification OpenAPI.
encrypted_response vaut false par
défaut sur cet endpoint, contrairement au flux hébergé : c'est
donc ici que vous devez agir. Certains portefeuilles nationaux
n'acceptent rien d'autre qu'une réponse chiffrée
(response_mode=direct_post.jwt) et, sans elle, ne
renvoient aucune erreur : ils lisent la requête puis
abandonnent, et la session reste indéfiniment
pending. Si vous visez un portefeuille de
production, activez-le.
Sans chiffrement, redirect_uri est optionnel (il
sert au flux same-device) ; le chiffrement le rend obligatoire
(HAIP 1.0 §5.1). Cette adresse doit être joignable depuis
l'appareil qui exécute le portefeuille, et non depuis votre
serveur : en cross-device, le portefeuille la reçoit après
avoir transmis sa réponse et tente de l'ouvrir sur le
téléphone. Une adresse de bouclage
(http://localhost:...) y désigne le téléphone
lui-même. Le symptôme est déroutant : la vérification a
réussi, le résultat vous parvient, et c'est le téléphone qui
affiche une erreur.
curl -X POST https://verify.todis.eu/verify/sessions \
-H "Authorization: Bearer VOTRE_JETON" \
-H "Content-Type: application/json" \
-d '{
"vct": "https://example.eu/credentials/pid",
"claims": ["age_over_18"],
"country_code": "FR"
}'
201: session créée, avecsession_idetauthorization_request.400:claimsvide, ou aucune ancre valide ne couvre le type demandé.401: jeton de licence manquant, invalide, révoqué ou expiré.403: le plan du jeton n'inclut pas la grammaire DCQL complète (dcql_query, plan Premium).429: quota de requêtes du plan dépassé.502: résolution du registre de confiance impossible (modecountry_code).503: le registre d'ancres dont dépend la session n'a aucune base chargée.
GET /verify/sessions/{id}
Consulte le résultat d'une session créée par
POST /verify/sessions, à interroger après que
l'utilisateur a répondu avec son portefeuille (ou
périodiquement en attendant).
curl https://verify.todis.eu/verify/sessions/<session_id> \
-H "Authorization: Bearer VOTRE_JETON"
200: trois statuts possibles,{"status": "pending"},{"status": "verified", "claims": {...}}ou{"status": "failed", "error": "..."}. Pour une session mdoc (doctype/mdoc_claims),claimsimbrique les éléments sous leur espace de noms. Une session vérifiée porte aussitrust, d'où vient l'ancre qui l'a validée.401: jeton de licence manquant, invalide, révoqué ou expiré.404: session inconnue (identifiant invalide, ou session expirée après 15 minutes).429: quota de requêtes du plan dépassé.
GET /trust/registries
Liste, sans jeton, les ancres de confiance que ce déploiement
accepte : pour chaque registre (mdoc,
sd_jwt_vc), ses sources et ses ancres, avec les
types de credential que chacune couvre et les dates de validité
de son certificat, et les pays qu'un country_code
peut atteindre. Une source lue d'un VICAL, la liste signée des
autorités d'émetteurs mdoc, dit de quel VICAL elle vient, et
discarded liste les entrées de ce VICAL qui ne sont
pas devenues des ancres, avec leur raison : c'est là que se
comprend l'absence d'une ancre attendue. Pour signaler une ancre
manquante :
report_missing_anchor.
curl https://verify.todis.eu/trust/registries
200: ancres des registres et pays de la liste officielle de l'UE, réponse cachable (Cache-Control,ETag).304: inchangé depuis l'ETagprésenté dansIf-None-Match.
GET /usage
Consultez votre volume de requêtes directement avec votre jeton de licence, sans attendre un tableau de bord ni solliciter le support : utile pour vérifier votre consommation avant qu'un bloc Starter ne s'épuise, ou simplement suivre votre activité au fil du temps. Si vous avez racheté plusieurs blocs Starter, le total additionne automatiquement tous les jetons associés à votre adresse email, pas besoin de les cumuler vous-même.
curl https://verify.todis.eu/usage \
-H "Authorization: Bearer VOTRE_JETON"
200:{"client_ids": [...], "total_authenticated_requests": 42}, outotal_authenticated_requests: nullavec un champerrorsi les métriques sont temporairement indisponibles (le reste de la réponse reste fiable).401: jeton de licence manquant, invalide, révoqué ou expiré.429: quota de requêtes du plan dépassé.
GET /usage/history
L'historique quotidien de votre consommation, à partir du plan
Standard : totaux par jour sur 30 jours. Le plan Premium y
ajoute la ventilation par endpoint, les latences, une
profondeur de 400 jours et l'export CSV
(?days=30&format=csv). Aucune donnée brute de
vérification n'est conservée, seulement des agrégats.
200: historique JSON, ou CSV siformat=csv.401: jeton de licence manquant, invalide, révoqué ou expiré.403: le plan du jeton n'inclut pas l'historique (Standard et au-delà).429: quota de requêtes du plan dépassé.501: déploiement sans base de données (auto-hébergement sansDATABASE_URL).
Format de réponse (vérification directe)
Pour /verify/sd-jwt-vc et /verify/mdoc :
un champ verified (booléen) et, selon le résultat,
soit les informations divulguées (claims pour
SD-JWT VC, doc_type/namespaces pour
mdoc), soit un champ error texte. Jamais
l'intégralité du document d'identité, seulement les attributs
effectivement divulgués (voir ci-dessus).
Aller plus loin
Pour la vue d'ensemble (authentification, deux façons
d'intégrer, exemple curl), voir
Comment fonctionne l'API. Pour le
schéma exact des requêtes/réponses,
todis.eu/docs
(Swagger). Une question qui n'est pas couverte ici ?
contact@todis.eu.