Referencia técnica
El detalle contractual de los endpoints que tu aplicación llama directamente: códigos de respuesta, casos de error, límites de tasa. Para el esquema exacto, consultable y siempre actualizado, la documentación Swagger está disponible en todis.eu/es/docs, sin necesidad de token.
Autenticación y límites de tasa
Todas las rutas de verificación requieren la cabecera
Authorization: Bearer <token>. Un token
ausente, inválido, revocado o caducado devuelve
401. Superado el límite de tu plan (30
peticiones/segundo en Starter, 60 en Standard), una petición
devuelve 429.
Cuando a tu token le quedan menos de 7 días para caducar, cada
respuesta autenticada incluye además la cabecera
X-License-Warning (p. ej. license expires in
3 day(s), renew soon): solo un aviso, la petición en
curso se completa con normalidad.
Quién decide qué información se divulga
Cada atributo de una presentación SD-JWT VC (la edad, un título, un estado) está protegido por su propio digest salado en el JWT firmado. La cartera solo adjunta a su presentación el valor y el salt de los atributos que el usuario ha aceptado revelar: el resto permanece como una huella irreversible. Todis solo tiene acceso técnicamente a lo que realmente se divulga en la presentación que recibe, nunca más.
Con POST /verify/sessions, el campo
claims de tu solicitud se convierte en una
consulta DCQL integrada en la solicitud de autorización enviada
a la cartera: el usuario ve y aprueba exactamente esa lista
antes de que su cartera construya la presentación. Con
POST /verify/sd-jwt-vc, ese paso ya ha ocurrido
antes de que obtengas la presentación: Todis la verifica y te
devuelve su contenido tal cual.
POST /verify/sd-jwt-vc
Verifica una presentación SD-JWT VC. Dos modos en el cuerpo de
la petición: issuer_public_key_pem (clave del
emisor indicada directamente) o country_code
(resolución automática a través del registro oficial de la UE) ;
issuer_public_key_pem tiene prioridad si ambos
están presentes.
200: presentación verificada,claimsincluido.400: no se indicó niissuer_public_key_pemnicountry_code.401: token de licencia ausente, inválido, revocado o caducado.422: presentación malformada o firma no verificada (verified: false, detalle enerror).429: cuota de peticiones del plan superada.502: resolución del registro de confianza del emisor imposible (solo en modocountry_code, por ejemplo si el registro europeo no está disponible temporalmente).
POST /verify/mdoc
Verifica una presentación mdoc (ISO/IEC 18013-5, usada
especialmente para el permiso de conducir móvil). El ancla de confianza
(issuer_trust_anchor_pem) debe indicarse
explícitamente en el cuerpo de la petición. Puede ser la
IACA que emitió al firmante del documento, que es
la opción recomendada: la cadena del firmante, incrustada en la
presentación, se valida hasta ella, incluidas la firma y la
ventana de validez, lo que te evita seguir las renovaciones de
firmantes. También puede ser el certificado del propio firmante,
si prefieres fijarlo. Solo se sigue un nivel, tal como prevé
ISO/IEC 18013-5. No hay resolución automática por país para este
formato (contáctanos si
te concierne un emisor mdoc concreto).
200: presentación verificada,doc_typeynamespacesincluidos.401: token de licencia ausente, inválido, revocado o caducado.422: presentación malformada, certificado de confianza incorrecto o firma no verificada.429: cuota de peticiones del plan superada.
POST /hosted/sessions
El recorrido de verificación alojado: este servicio sirve él
mismo la página que ve el usuario (código QR, enlace a la
cartera, textos en francés, inglés o español). Es la integración
recomendada cuando no quieres construir una pantalla de
verificación, y está detallada en la página
Integraciones. Rediriges al
usuario a la URL devuelta, vuelve a tu
success_url, y siempre confirmas el resultado en el
servidor con GET /verify/sessions/{id}.
success_url significa verificación
superada, nunca que el usuario sea mayor de edad: una
prueba de edad que vale false también llega allí,
como cualquier resultado verificado. Lee el resultado en el
servidor (claims.age_over_18 con el atajo
age_over_18) y aplícale tu propia regla.
Es el único endpoint que acepta un webhook_url (un
POST JSON firmado con HMAC-SHA256 al concluir el recorrido,
incluso si el usuario cierra la página) y los atajos
check ("age_over_18",
"identity"), disponibles en todos los planes.
Requiere un despliegue con identidad de firma de solicitudes,
que el servicio alojado tiene.
La opción dc_api. Con
"dc_api": true, la página alojada propone al
visitante abrir su cartera mediante la Digital Credentials API
del W3C, cuando su navegador la admite; el código QR sigue
visible. El protocolo es el Anexo C de ISO/IEC 18013-7
(org-iso-mdoc): solo las atestaciones mdoc
(ISO/IEC 18013-5) pasan por esta vía, y el PID en formato
SD-JWT VC pasa por el código QR. Con
check: "age_over_18", esta vía pide la atestación de
prueba de edad (eu.europa.ec.av.1), sin recurrir al
PID. La respuesta de la cartera va cifrada para Todis y vinculada
al origen https://verify.todis.eu: no tienes ningún
origen que declarar. El resultado,
GET /verify/sessions/{id} y el webhook no cambian.
Fuera de un navegador, por ejemplo en una aplicación nativa, usa
el código QR o el enlace profundo.
201: recorrido creado, conhosted_url,expires_aty el secreto de webhook.400: URL de retorno inválida,checkdesconocido, o campos de verificación incoherentes.401: token de licencia ausente, inválido, revocado o caducado.403: el plan del token no incluye la gramática DCQL completa (undcql_queryindicado por ti).429: cuota de peticiones del plan superada.501: despliegue sin identidad de firma, flujo alojado no disponible.
POST /verify/sessions
Crea una sesión de verificación OpenID4VP. El cuerpo de la
petición indica el tipo de credential solicitado
(vct) y los atributos solicitados
(claims, rutas simples, ej.
"age_over_18"). La confianza en el emisor viene de
country_code (SD-JWT VC, lista oficial de la UE) o
de los registros de anclas de este despliegue, listados por
GET /trust/registries. La respuesta contiene una
solicitud de autorización OpenID4VP para presentar a la
cartera del usuario (código QR o enlace directo); cómo construir
exactamente la cadena del código QR o del enlace se detalla en
nuestro archivo de integración. La sesión
caduca 15 minutos después de su creación, haya recibido
respuesta o no. Para necesidades avanzadas (varios credentials
por sesión, alternativas entre credentials o entre atributos,
valores restringidos), el campo dcql_query acepta
una consulta DCQL completa en lugar de
vct/claims, y puede mezclar SD-JWT VC y
mdoc. El campo credential_trust da entonces a un
credential SD-JWT VC su propio country_code, para
verificar en una misma sesión credentials de emisores de países
distintos (cada entrada solo vale para su credential, nunca para
otro). Ver el detalle en la
especificación OpenAPI.
encrypted_response vale false por
defecto en este endpoint, a diferencia del flujo alojado: es
aquí donde debes actuar. Algunas carteras nacionales no aceptan
otra cosa que una respuesta cifrada
(response_mode=direct_post.jwt) y, sin ella, no
devuelven ningún error: leen la petición y abandonan, y la
sesión se queda en pending indefinidamente. Si
apuntas a una cartera de producción, actívalo.
Sin cifrado, redirect_uri es opcional (sirve al
flujo same-device); el cifrado lo hace obligatorio
(HAIP 1.0 §5.1). Esa dirección debe ser alcanzable desde el
dispositivo que ejecuta la cartera, no desde tu servidor: en
cross-device, la cartera la recibe tras enviar su respuesta e
intenta abrirla en el teléfono. Una dirección de bucle
(http://localhost:...) designa allí al propio
teléfono. El síntoma despista: la verificación funcionó, el
resultado te llega, y es el teléfono el que muestra un error.
curl -X POST https://verify.todis.eu/verify/sessions \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vct": "https://example.eu/credentials/pid",
"claims": ["age_over_18"],
"country_code": "FR"
}'
201: sesión creada, consession_idyauthorization_request.400:claimsvacío, o ningún ancla válida cubre el tipo solicitado.401: token de licencia ausente, inválido, revocado o caducado.403: el plan del token no incluye la gramática DCQL completa (dcql_query, plan Premium).429: cuota de peticiones del plan superada.502: no se pudo resolver el registro de confianza (modocountry_code).503: el registro de anclas del que depende la sesión no tiene ninguna base cargada.
GET /verify/sessions/{id}
Consulta el resultado de una sesión creada por
POST /verify/sessions, a interrogar después de que
el usuario haya respondido con su cartera (o periódicamente
mientras se espera).
curl https://verify.todis.eu/verify/sessions/<session_id> \
-H "Authorization: Bearer TU_TOKEN"
200: tres estados posibles,{"status": "pending"},{"status": "verified", "claims": {...}}o{"status": "failed", "error": "..."}. Para una sesión mdoc (doctype/mdoc_claims),claimsanida los elementos bajo su espacio de nombres. Una sesión verificada lleva ademástrust, de dónde viene el ancla que la validó.401: token de licencia ausente, inválido, revocado o caducado.404: sesión desconocida (identificador inválido, o sesión caducada tras 15 minutos).429: cuota de peticiones del plan superada.
GET /trust/registries
Lista, sin token, las anclas de confianza que acepta este
despliegue: para cada registro (mdoc,
sd_jwt_vc), sus fuentes y sus anclas, con los
tipos de credential que cubre cada una y las fechas de validez
de su certificado, y los países que un country_code
puede alcanzar. Una fuente leída de un VICAL, la lista firmada de
las autoridades emisoras mdoc, indica de qué VICAL procede, y
discarded lista las entradas de ese VICAL que no se
han convertido en anclas, con su motivo: ahí se entiende por qué
falta un ancla esperada. Para señalar un ancla que falta:
report_missing_anchor.
curl https://verify.todis.eu/trust/registries
200: anclas de los registros y países de la lista oficial de la UE, respuesta cacheable (Cache-Control,ETag).304: sin cambios desde elETagenviado enIf-None-Match.
GET /usage
Consulta tu volumen de peticiones directamente con tu token de licencia, sin esperar un panel ni contactar con soporte: útil para comprobar tu consumo antes de que se agote un bloque Starter, o simplemente para seguir tu actividad con el tiempo. Si has recomprado varios bloques Starter, el total suma automáticamente todos los tokens asociados a tu dirección de email, sin que tengas que sumarlos tú mismo.
curl https://verify.todis.eu/usage \
-H "Authorization: Bearer TU_TOKEN"
200:{"client_ids": [...], "total_authenticated_requests": 42}, ototal_authenticated_requests: nullcon un campoerrorsi las métricas no están disponibles temporalmente (el resto de la respuesta sigue siendo fiable).401: token de licencia ausente, inválido, revocado o caducado.429: cuota de peticiones del plan superada.
GET /usage/history
El historial diario de tu consumo, a partir del plan Standard:
totales por día durante 30 días. El plan Premium añade el
desglose por endpoint, las latencias, una profundidad de 400
días y la exportación CSV
(?days=30&format=csv). No se conserva ningún
dato bruto de verificación, solo agregados.
200: historial JSON, o CSV siformat=csv.401: token de licencia ausente, inválido, revocado o caducado.403: el plan del token no incluye el historial (Standard en adelante).429: cuota de peticiones del plan superada.501: despliegue sin base de datos (autoalojamiento sinDATABASE_URL).
Formato de la respuesta (verificación directa)
Para /verify/sd-jwt-vc y /verify/mdoc:
un booleano verified y, según el resultado, la
información divulgada (claims para SD-JWT VC,
doc_type/namespaces para mdoc) o un
campo de texto error. Nunca el documento de
identidad completo, solo los atributos realmente divulgados
(ver arriba).
Para ir más allá
Para la visión general (autenticación, dos formas de integrar,
un ejemplo con curl), consulta
Cómo funciona la API. Para el
esquema exacto de peticiones/respuestas,
todis.eu/es/docs
(Swagger). ¿Tienes una pregunta que no está aquí?
contact@todis.eu.