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, y el resultado de la sesión solo te devuelve esos atributos, aunque la cartera divulgue más. 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. Con country_code, el emisor se resuelve mediante la lista de confianza de ese país, a través del registro oficial de la UE, entre sus proveedores cualificados de atestaciones, y solo para los vct que el registro SD-JWT VC clasifica qeaa, nunca un PID; sin él, mediante el registro SD-JWT VC de este despliegue (GET /trust/registries), cuya ancla debe cubrir el vct del credential. La respuesta indica de dónde viene el ancla (trust). Las anclas las mantiene Todis: la petición no aporta ninguna clave, y una petición que aún lleva issuer_public_key_pem se rechaza.

Un PID ya no se verifica indicando un país. Hasta la versión 4.5.0, un PID SD-JWT VC podía verificarse con country_code. Desde la versión 5.0.0, un PID enviado con country_code falla siempre, sea cual sea el país: la lista de confianza de un país solo verifica atestaciones cualificadas (vct clasificado qeaa). Verifica un PID sin country_code, contra los registros de este despliegue (GET /trust/registries).

En verify.todis.eu, hoy ningún tipo está clasificado qeaa: la vía country_code no verifica allí ningún credential. Clasificar un tipo cualificado es un dato del registro, no una versión del servicio.

  • 200: presentación verificada, claims y trust incluidos.
  • 400: la petición lleva issuer_public_key_pem, sea cual sea su valor; el error nombra el campo.
  • 401: token de licencia ausente, inválido, revocado o caducado.
  • 422: presentación malformada, firma no verificada, o emisor que ningún ancla acepta (verified: false, detalle en error). Un emisor rechazado se nombra: el detalle lleva el sujeto, la huella SHA-256, la autoridad emisora y el identificador de clave de esa autoridad del certificado presentado, lo suficiente para hacerlo declarar sin una ida y vuelta. Y si ese certificado encadena hacia un ancla que el registro ya tiene, pero que no está declarada para el tipo presentado, el detalle lo dice y nombra esa ancla y sus tipos: el remedio es entonces declarar un tipo, no obtener un certificado. Es información de certificado, pública; ninguna reivindicación del portador figura ahí. También, con country_code, un credential cuyo vct no está clasificado qeaa, que el error nombra con su categoría, o cualquier presentación cuando este despliegue no clasifica ningún tipo qeaa, lo que el error indica: en ambos casos, sin que se lea ninguna lista. También un credential cuyo typ no es ni dc+sd-jwt ni vc+sd-jwt, y un credential que lleva aud, sea cual sea su valor: sin sesión, Todis no es el destinatario de ningún credential. En una sesión, aud se acepta cuando designa la misma audiencia que el KB-JWT de la petición (el client_id en redirección).
  • 429: cuota de peticiones del plan superada.
  • 502: resolución del registro de confianza del emisor imposible (solo en modo country_code, para un tipo clasificado qeaa, por ejemplo si el registro europeo no está disponible temporalmente, o si la lista nacional está cerrada o ha pasado su próxima actualización, o si una lista no ha podido releerse correctamente desde hace 24 h, lo que el error indica).
  • 503: el registro SD-JWT VC no tiene ninguna base cargada, con o sin country_code, que solo verifica los tipos que ese registro clasifica.

POST /verify/mdoc

Verifica una presentación mdoc (ISO/IEC 18013-5, usada especialmente para el permiso de conducir móvil), contra el registro mdoc de este despliegue (GET /trust/registries), que mantiene las IACA de los emisores aceptados: la cadena del firmante, incrustada en la presentación, se valida hasta una de ellas, incluidas la firma y la ventana de validez, en un solo nivel tal como prevé ISO/IEC 18013-5, y el ancla retenida debe cubrir el doctype firmado. La respuesta indica qué ancla validó al emisor (trust). Las anclas las mantiene Todis: la petición no aporta ningún certificado, y una petición que aún lleva issuer_trust_anchor_pem se rechaza. Un emisor mdoc que falta en el registro se comunica a integration@todis.eu.

  • 200: presentación verificada, doc_type, namespaces y trust incluidos.
  • 400: la petición lleva issuer_trust_anchor_pem, sea cual sea su valor; el error nombra el campo.
  • 401: token de licencia ausente, inválido, revocado o caducado.
  • 422: presentación malformada, firma no verificada, o emisor que ningún ancla del registro acepta, nombrado como en POST /verify/sd-jwt-vc.
  • 429: cuota de peticiones del plan superada.
  • 503: el registro mdoc no tiene ninguna base cargada.

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.

Dos peticiones en la página. El botón «Abrir mi cartera» lleva la petición del mismo dispositivo: tras su respuesta, la cartera devuelve el navegador a la página con un código nuevo (el Response Code, OpenID4VP 1.0 §14.2), y la página libera entonces el resultado. Solo lo libera en el navegador que la abrió primero, que reconoce por una cookie todis_parcours; un retorno que llega a otro navegador hace fallar la sesión. El código QR lleva una petición cross-device aparte, para una cartera en otro dispositivo, sin retorno, y servida tres veces como máximo. Dentro de un iframe, el botón abre la página en una pestaña nueva. Dos consecuencias para ti: no abras hosted_url en un navegador antes de redirigir al usuario a ella, o el recorrido quedaría ligado a tu navegador y no al suyo; y el webhook session.verified de un retorno en el mismo dispositivo sale cuando la página libera el resultado, no cuando la cartera envía su respuesta.

Los campos purpose y cross_device. purpose (opcional, un texto no vacío de 300 caracteres como máximo) es la finalidad de la verificación, con tus palabras y en el idioma de la página: la página la muestra antes de que el visitante abra su cartera, precedida de «Finalidad indicada por el sitio:» (OpenID4VP 1.0 §6.2). Sin él, la página muestra la finalidad del atajo check (edad o identidad), y si no, la lista de los datos solicitados. cross_device (booleano, true por defecto) a false sirve una página solo del mismo dispositivo, sin código QR: HAIP 1.0 §5.1 lo recomienda cuando cuentas con la vinculación al navegador del usuario contra el phishing.

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. La página pasa al navegador, en una sola llamada, una petición por protocolo que la verificación permite, y la cartera responde a la que sabe atender. La primera es OpenID4VP 1.0 (openid4vp-v1-signed), firmada con el mismo certificado y portadora de la misma petición que el código QR: tanto el PID en formato SD-JWT VC como las atestaciones mdoc (ISO/IEC 18013-5) pasan por ella. Con check, el PID SD-JWT VC solo se pide si el registro SD-JWT VC de este despliegue lo cubre; el de verify.todis.eu no lo cubre, así que el recorrido pide únicamente las rutas mdoc. La segunda, solo para las atestaciones mdoc, es el Anexo C de ISO/IEC 18013-7 (org-iso-mdoc). Con check: "age_over_18", la petición OpenID4VP ofrece las mismas fuentes de edad que el código QR, la atestación de prueba de edad (eu.europa.ec.av.1) en primer lugar; la petición del Anexo C pide un solo documento, sin alternativa: el atributo age_over_18 de esa atestación, que el registro mdoc de verify.todis.eu cubre, como muestra GET /trust/registries. Una cartera que responde únicamente por el Anexo C y no tiene esa atestación no puede responder por el botón; el código QR sigue en la página. 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 de GET /verify/sessions/{id} y el webhook ganan dc_api_protocol, el protocolo con el que respondió la cartera. Fuera de un navegador, por ejemplo en una aplicación nativa, usa el código QR o el enlace profundo. Un despliegue en modo HAIP bloqueado (opción HAIP_VERROUILLE, desactivada por defecto) bloquea lo que Todis emite: solicitud firmada bajo x509_hash y respuesta cifrada. No restringe el protocolo que elige la cartera: la página sigue ofreciendo el Anexo C junto a OpenID4VP, para que una cartera que solo sabe responder por él pueda responder. Para ofrecer solo OpenID4VP, únicamente en la plataforma de integración, crea la sesión con la cabecera todis-essai-openid4vp: seul.

La opción revocation. Como en POST /verify/sessions, report por defecto o require: la conducta del recorrido ante un estado que no se ha podido establecer. El resultado de la sesión y el webhook session.verified llevan revocation, la conducta aplicada y lo que estableció la comprobación.

La opción profil_av. Con "profil_av": true, la página alojada ofrece además, en una pestaña aparte, la aplicación de verificación de edad del perfil europeo: un código QR y un enlace av:// que le pasan la petición por valor, sin firmar, a la atestación de prueba de edad (eu.europa.ec.av.1) sola, sin recurso al PID. Es la forma que esas aplicaciones exigen, y la única que aceptan: rechazan cualquier petición firmada. La primera pestaña sigue siendo la página de antes. Lo que pierdes con esta opción: la aplicación no puede autenticar a Todis, al no haber firma; la respuesta sigue ligada a esta petición y se verifica como las demás. El resultado, GET /verify/sessions/{id} y el webhook no cambian. La opción exige que una ruta del recorrido ofrezca esa atestación: con check: "age_over_18", es el caso cuando el registro mdoc del despliegue la cubre, cosa que hace el de verify.todis.eu (ver GET /trust/registries). En un despliegue cuyo registro no la cubre, la creación responde 400.

  • 201: recorrido creado, con hosted_url, expires_at y el secreto de webhook.
  • 400: URL de retorno inválida, check desconocido, campos de verificación incoherentes, purpose vacío o de más de 300 caracteres, o ancla de confianza, transaction_data o consulta DCQL mal formada aportados (mismas reglas que POST /verify/sessions).
  • 401: token de licencia ausente, inválido, revocado o caducado.
  • 403: el plan del token no incluye la gramática DCQL completa (un dcql_query indicado 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 cuyo vct está clasificado qeaa, lista oficial de la UE; cualquier otro tipo falla en la verificación, con un mensaje que lo nombra) 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.

Un PID ya no se verifica indicando un país. Hasta la versión 4.5.0, un PID SD-JWT VC podía verificarse con country_code. Desde la versión 5.0.0, un PID enviado con country_code falla siempre, sea cual sea el país: la lista de confianza de un país solo verifica atestaciones cualificadas (vct clasificado qeaa). Verifica un PID sin country_code, contra los registros de este despliegue (GET /trust/registries).

En verify.todis.eu, hoy ningún tipo está clasificado qeaa: la vía country_code no verifica allí ningún credential. Clasificar un tipo cualificado es un dato del registro, no una versión del servicio.

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.

revocation (opcional, report por defecto) fija la conducta de la sesión ante una revocación que no se ha podido comprobar: con report, la sesión se verifica y su resultado dice que el estado no se estableció, y por qué; con require, falla. Todis comprueba el estado de cada credential que declara uno (lista de estado de un SD-JWT VC, lista de estado o de identificadores de un mdoc), antes de concluir la sesión y en todos los transportes. Un credential revocado, suspendido o cuyo índice queda fuera de su lista hace fallar la sesión sea cual sea la conducta; un credential que no prevé ningún estado pasa con ambas. El claim status de un SD-JWT VC nunca se te devuelve. Disponible en todos los planes.

profil_av (opcional, false por defecto) pide además el transporte OpenID4VP del perfil europeo de verificación de edad: la respuesta lleva entonces profil_av, con wallet_uri, un enlace av:// que abre la aplicación de verificación de edad pasándole la petición por valor, y authorization_request, esa petición, sin firmar, bajo redirect_uri:, en direct_post en claro, a la atestación de prueba de edad (eu.europa.ec.av.1) sola. Es la única forma que esas aplicaciones aceptan; a cambio, la aplicación no puede autenticar a Todis. La petición por redirección no cambia. La sesión debe pedir esa atestación en mdoc (400 si no), y pedir solo elementos age_over_NN de su espacio de nombres: cualquier otra ruta se rechaza en la creación, con un 400 que la nombra. Este transporte lleva esa atestación y nada más: una respuesta enviada a su response_uri que lleve otra credencial se rechaza, mientras que la petición por redirección y la DC API siguen aceptando las demás fuentes de edad de la sesión.

redirect_uri: mismo dispositivo o código QR. Con redirect_uri (opcional, un URI absoluto, 400 si no), la sesión es un flujo del mismo dispositivo: tras su respuesta, la cartera devuelve el navegador a redirect_uri, con el parámetro response_code añadido, un secreto nuevo. El resultado verificado queda entonces retenido: GET /verify/sessions/{id} devuelve {"status": "pending", "response_code_required": true} mientras no hayas presentado ese código a POST /verify/sessions/{id}/response-code (ver más abajo). Sin redirect_uri, la sesión es cross-device (código QR): sin redirección, y el resultado se lee directamente. No pases, pues, redirect_uri para un código QR, el resultado quedaría retenido. encrypted_response: true no exige redirect_uri. Esa dirección debe ser alcanzable desde el dispositivo que ejecuta la cartera, no desde tu servidor: una dirección de bucle (http://localhost:...) designa allí al propio teléfono.

Consulta DCQL. dcql_query se controla en la creación (400): arrays presentes no vacíos (credentials, credential_sets, claims, claim_sets, options, values), identificadores de credential y de atributo hechos de letras, cifras, _ y -, ningún atributo pedido dos veces (regla que vale también para claims y mdoc_claims), values limitado a cadenas, enteros y booleanos, ruta mdoc hecha exactamente del espacio de nombres y luego del elemento, claim_sets solo con claims. Una ruta de atributo sigue el Claims Path Pointer de OpenID4VP 1.0 §7: un nombre designa una clave, null todos los elementos de un array, un entero el elemento de esa posición; una restricción values se cumple en cuanto uno de los valores seleccionados figura en ella. Con "multiple": true en un credential, cada presentación recibida bajo su identificador se verifica, y credentials.<id> así como trust.<id> pasan a ser arrays, una entrada por presentación retenida; sin él, una segunda presentación bajo el identificador, o un array vacío, hace fallar la sesión.

Autoridades indicadas a la cartera. Para cada credential que juzgará un registro de este despliegue, Todis añade él mismo a la petición un trusted_authorities de tipo aki: el identificador de clave de cada autoridad (IACA) que el registro acepta para su tipo. Una cartera que aplica este filtro solo propone entonces los credentials que dependen de ellas. No se añade nada para un credential juzgado por la lista europea (country_code), ni cuando un ancla candidata no puede designarse así (ancla con rol de firmante). Un trusted_authorities que aportas en dcql_query se transmite tal cual; un valor aki que no es base64url se rechaza (400). Es una ayuda para la cartera: la verificación juzga la confianza como antes. Por último, transaction_data se rechaza (400): Todis no envía datos de transacción a la cartera y no verifica sus huellas.

Intención de conservación. En una claim de un credential mso_mdoc de dcql_query, "intent_to_retain": true declara que tienes intención de conservar ese elemento más allá de la transacción (OpenID4VP 1.0 §B.2.4). Todis lo transmite a la cartera en cada transporte: tal cual en la solicitud OpenID4VP, y como IntentToRetain en la solicitud del Anexo C de ISO/IEC 18013-7; la cartera puede mostrarlo al usuario. Si falta, vale falso: Todis anuncia entonces a la cartera que el elemento no se conservará, lo que te compromete a no almacenarlo más tiempo del necesario para llevar a cabo la transacción (ISO/IEC 18013-5 §8.3.2.1.2.1). Es lo que anuncian siempre los campos simplificados y los atajos check. El campo nunca se ignora: en mdoc_claims, en una claim SD-JWT VC o con un valor que no es un booleano, se rechaza con un error que lo nombra.

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"]
  }'
  • 201: sesión creada, con session_id y authorization_request.
  • 400: claims vacío, ningún ancla válida cubre el tipo solicitado, redirect_uri que no es un URI absoluto, consulta DCQL mal formada, transaction_data aportado, intent_to_retain fuera de una claim mdoc de dcql_query, o la petición lleva issuer_public_key_pem, o una entrada de credential_trust que lo lleva; el error nombra el campo, sea cual sea el transporte 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 (modo country_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), claims anida los elementos bajo su espacio de nombres. Una sesión verificada lleva además trust, de dónde viene el ancla que la validó. Una sesión concluida mediante la Digital Credentials API lleva dc_api_protocol, openid4vp-v1-signed u org-iso-mdoc, el protocolo con el que respondió la cartera. Una sesión del mismo dispositivo (creada con redirect_uri) cuyo resultado espera su código devuelve {"status": "pending", "response_code_required": true}. Una presentación ligada a otra petición (KB-JWT de otro nonce o de otra audiencia, mdoc cuya firma del dispositivo no se verifica sobre la petición) rechaza la respuesta entera, incluso junto a un credential válido: el error empieza entonces por the response is rejected (OpenID4VP 1.0 §14.1.2). Una sesión verificada lleva además revocation: la conducta aplicada (policy) y lo que estableció la comprobación, status entre not_provided (ningún estado previsto), valid (con mechanism, list_issued_at y, cuando la lista se admitió bajo una declaración de transición del registro, signer_exception) y not_checked (con reason); valid y not_provided llevan también certificates, la revocación de la cadena de certificados del emisor por la CRL de su emisor, not_provided o checked; Una sesión concluida lleva también checks, la lista de las comprobaciones realizadas sobre el credential presentado, en el orden en que se completaron, cada una con su resultado (passed, not_checked o failed): issuer_signature, issuer_trust_anchor, credential_integrity, credential_validity, holder_binding, credential_type, requested_claims, credential_status, certificate_revocation. Una comprobación que no tuvo lugar no aparece, y aquella cuyo detalle está en otro campo nombra la ruta donde leerlo en reported_in: trust, revocation o revocation.certificates. Una sesión fallida lo lleva también: las comprobaciones que se completaron antes de la que falló, y luego esa última con failed; un fallo que no hizo juzgar ninguna presentación, un rechazo del titular por ejemplo, no lleva checks. Una sesión fallida lleva también code, la causa en un vocabulario cerrado junto a error: wallet_declined, wallet_error, credential_missing, presentation_invalid, requirement_not_met, trust_resolution_unavailable, credential_revoked, revocation_not_checked o unspecified.
  • 401: token de licencia ausente, inválido, revocado o caducado.
  • 404: sesión desconocida (identificador inválido, sesión caducada tras 15 minutos, o sesión creada con la licencia de otro cliente).
  • 429: cuota de peticiones del plan superada.

POST /verify/sessions/{id}/response-code

Libera el resultado de una sesión del mismo dispositivo, creada con redirect_uri. Tu página de retorno recibe response_code como parámetro: preséntalo desde tu servidor, para la sesión que tu usuario lanzó, cuyo identificador has guardado de tu lado (en su sesión de servidor, por ejemplo), nunca para una sesión deducida de la dirección de retorno. Es lo que impide que una petición reenviada a otra persona te entregue su identidad (OpenID4VP 1.0 §14.2, HAIP 1.0 §5.1). Una cartera que no sigue la redirección nunca libera el resultado: queda retenido hasta que la sesión caduca.

curl -X POST https://verify.todis.eu/verify/sessions/<session_id>/response-code \
  -H "Authorization: Bearer TU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"response_code": "<valor recibido en redirect_uri>"}'
  • 200: el mismo cuerpo que GET /verify/sessions/{id}, con el resultado liberado, que GET /verify/sessions/{id} devuelve después sin código.
  • 400: sesión sin retorno en el mismo dispositivo (creada sin redirect_uri), o sesión de una página alojada, donde es la página la que presenta el código.
  • 401: token de licencia ausente, inválido, revocado o caducado.
  • 403: ese código no es el de la sesión; no se libera nada.
  • 404: sesión desconocida, caducada, o creada con la licencia de otro cliente.
  • 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, las fuentes oficiales que este despliegue debe consultar, con la fecha y el resultado de su última lectura (official_sources), y los países que un country_code puede alcanzar. trust_list_cache muestra las listas de confianza que este despliegue guarda, verificadas, para country_code: la LOTL y cada país ya resuelto, con su número de secuencia, la huella del archivo leído, su última lectura correcta, la fecha hasta la que se sirve la copia, y si se sirve ahora y, si no, por qué. Una lista que no ha podido releerse correctamente desde hace 24 h deja de servirse, hasta la próxima relectura correcta. Cada ancla es un rol de un certificado, tal como lo declara una fuente: un mismo certificado figura una vez por cada fuente que le da un rol. Su usage vale issuance cuando valida credentials, y status cuando firma sus listas de estado, lo que nunca valida un credential. type_categories da la categoría de cada tipo que clasifica este despliegue, lo cubra un ancla o no (pid, qeaa, pub_eaa, eaa), que la respuesta de una verificación repite en trust.category; solo los tipos qeaa del registro SD-JWT VC se verifican por country_code. Una fuente leída de un VICAL, la lista firmada de las autoridades emisoras mdoc, indica de qué VICAL procede. Una fuente leída de una lista de entidades de confianza (LoTE, ETSI TS 119 602) lleva list, con la próxima actualización de la lista: pasada esa fecha, sus anclas dejan de aceptarse. discarded lista las entradas de esas listas 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. La página Anclas de confianza presenta estas anclas por país, leídas en directo en la misma ruta.

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 el ETag enviado en If-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}, o total_authenticated_requests: null con un campo error si 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 si format=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 sin DATABASE_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.