Documentación / Praticable Ticket Protocol (PTP) / v1
Especificación del protocoloReferencia de la APIGuías
Documentación / Praticable Ticket Protocol (PTP) / v1
ESTABLE · DESDE V1

Praticable Ticket Protocol (PTP)

Protocolo abierto para verificar tickets emitidos por instancias independientes. Cada despliegue del cliente actúa como un issuer autónomo; cualquier scanner que implemente este protocolo puede verificar tickets de cualquier issuer, sin necesidad de integración bilateral previa.

Referencia de la API

Esta página describe el protocolo en sí. Para endpoints, parámetros, códigos de respuesta y ejemplos de requests, consulte la referencia de la API →

§ 01

Descripción general

Este protocolo define cómo un scanner valida un ticket emitido por un issuer independiente. Los issuers no se coordinan entre sí y no requieren una autoridad central.

El protocolo está diseñado para cuatro objetivos:

  • Verificación offline de la autenticidad del ticket mediante signatures criptográficas
  • Estado de check-in online con consistencia fuerte para prevenir el doble escaneo
  • Independencia del issuer: los issuers no se coordinan entre sí
  • Rotación de keys sin invalidar tickets ya emitidos
Versión actual: 1

Todos los payloads y endpoints incluyen un identificador de versión. Los cambios incompatibles requieren una nueva versión mayor. Los issuers y scanners pueden soportar múltiples versiones simultáneamente durante las transiciones.

§ 02

Terminología

Issuer

Instancia de despliegue que crea y gestiona tickets. Un issuer por cliente.

Scanner

Aplicación cliente que valida tickets en la entrada del evento.

Ticket

Credencial única y firmada que otorga acceso a un evento específico.

Check-in

El acto de marcar un ticket como utilizado en la entrada.

Payload

Datos firmados codificados en el código QR del ticket.

§ 03

Criptografía

Ed25519 RFC 8032 64 bytes · Web Crypto API

El algoritmo de signature es Ed25519 (RFC 8032). Las signatures de 64 bytes son lo suficientemente compactas para caber en un código QR junto con los datos del payload, y la verificación es rápida con soporte nativo en la Web Crypto API.

Ciclo de vida de las keys

Cada issuer genera un par de keys en el momento del aprovisionamiento. La key privada se almacena en los secretos del Worker y nunca sale del issuer. La key pública se publica en el endpoint well-known. Pueden coexistir múltiples keys públicas activas durante los períodos de rotación.

Identificador de key (kid)

Cada key pública tiene un identificador corto y único: los primeros 8 caracteres hexadecimales del hash SHA-256 de la key pública sin procesar. Los payloads hacen referencia al kid para que los scanners sepan qué key utilizar para la verificación.

§ 04

Formato del payload QR

El código QR contiene un payload JSON compacto codificado en base64url, seguido de la signature. El prefijo tkt1. identifica la versión del protocolo y distingue los códigos QR de tickets de otros códigos que un scanner pueda encontrar.

Format
tkt1.<base64url(payload_json)>.<base64url(signature)>
Tamaño máximo

El contenido total del QR debe mantenerse por debajo de ~400 caracteres para seguir siendo escaneable con cámaras de gama baja en condiciones de poca luz. Evite rellenar innecesariamente los campos ev y hn.

Estructura JSON del payload

Payload JSON
{
  "v": 1,
  "iss": "https://tickets.example.com",
  "kid": "a1b2c3d4",
  "tid": "01HXXX...ULID",
  "eid": "evt_01HXXX...ULID",
  "ev": {
    "name": "Gala Concert",
    "date": "2026-06-15T20:00:00Z",
    "venue": "Royal Hall"
  },
  "tt": "VIP",
  "hn": "J. Smith",
  "iat": 1745000000,
  "exp": 1765000000
}
Campo Descripción
v
integerrequired

Versión mayor del protocolo (actualmente 1).

iss
stringrequired

URL base del issuer. Forma canónica: HTTPS, sin barra final. Se utiliza para el descubrimiento.

ej. https://tickets.example.com
kid
stringrequired

Identificador de la key de firma (primeros 8 caracteres hexadecimales del hash SHA-256 de la key pública).

tid
stringrequired

Identificador del ticket (se recomienda ULID, único dentro del issuer).

eid
stringrequired

Identificador del evento (se recomienda ULID, único dentro del issuer).

ev
objectrequired

Metadatos del evento para visualización offline: name (string), date (ISO 8601 UTC), venue (string). Solo informativo.

tt
stringoptional

Tipo de ticket, para fines de visualización (ej. "VIP", "Standard").

hn
stringoptional

Nombre del titular para visualización. Mantener breve; sin datos personales más allá del nombre para mostrar.

iat
integerrequired

Timestamp Unix de emisión (segundos).

exp
integerrequired

Timestamp Unix de expiración (segundos). Normalmente se establece con un margen razonable después de la finalización del evento.

Signature

La signature se calcula sobre la secuencia exacta de bytes del payload codificado en base64url (antes del segundo separador .), utilizando la key privada Ed25519 del issuer. Los scanners verifican recalculando la signature sobre los mismos bytes.

§ 05

Descubrimiento del issuer

Cada issuer DEBE publicar un documento de descubrimiento en la siguiente dirección:

GET {iss}/.well-known/ticket-issuer.json
Response · 200 OK
{
  "protocol_version": 1,
  "issuer": "https://tickets.example.com",
  "name": "Client business name",
  "keys": [
    {
      "kid": "a1b2c3d4",
      "alg": "Ed25519",
      "public_key": "base64url-encoded-raw-32-byte-public-key",
      "valid_from": "2026-01-01T00:00:00Z",
      "valid_until": null
    }
  ],
  "endpoints": {
    "check_in": "https://tickets.example.com/api/v1/tickets/check-in",
    "status": "https://tickets.example.com/api/v1/tickets/{tid}/status",
    "scanner_enroll": "https://tickets.example.com/api/v1/scanners/enroll"
  },
  "supported_versions": [1]
}
  • keys PUEDE contener múltiples entradas durante la rotación. Los scanners deben aceptar cualquier key no expirada cuyo kid coincida con el del payload.
  • valid_until: null significa actualmente activa sin expiración programada.
  • Los scanners DEBEN almacenar en caché este documento con un TTL de máximo 1 hora, y DEBEN volver a solicitarlo si la verificación falla con un kid desconocido.
§ 06

QR de configuración del scanner

Los scanners se registran en un evento escaneando un código QR de configuración con tiempo limitado, generado por la interfaz de administración del organizador. El QR de configuración contiene un token de enrollment que el scanner intercambia, en su primer uso, por una API key de larga duración con alcance a uno o más eventos.

Intercambio en dos pasos

El flujo de enrollment utiliza un intercambio de token a API key en lugar de incrustar una API key directamente en el QR. Esto hace que el QR de configuración sea de un solo uso y con tiempo limitado; un QR fotografiado o filtrado se vuelve inútil una vez consumido.

Setup QR format
tsetup1.<base64url(setup_json)>

El prefijo tsetup1. distingue los códigos QR de configuración de los códigos QR de tickets (tkt1.).

Setup JSON
{
  "v": 1,
  "iss": "https://tickets.example.com",
  "enroll_url": "https://tickets.example.com/api/v1/scanners/enroll",
  "token": "st_01HXXX...opaque-enrollment-token",
  "eid": "evt_01HXXX...ULID",
  "event_name": "Gala Concert",
  "gate_id": "north",
  "expires_at": "2026-06-16T02:00:00Z"
}
Campo Descripción
v
integerrequired

Versión mayor del protocolo (actualmente 1).

iss
stringrequired

URL base del issuer. El scanner DEBE obtener el documento de descubrimiento desde {iss}/.well-known/ticket-issuer.json.

enroll_url
stringrequired

URL exacta para el intercambio de enrollment. DEBE coincidir con scanner_enroll en el documento de descubrimiento.

token
stringrequired

Token de enrollment opaco e impredecible (≥ 128 bits de entropía). Un solo uso.

eid
stringrequired

ID del evento para el cual este QR de configuración registra el scanner.

event_name
stringoptional

Nombre mostrado al operador para confirmación. El nombre oficial proviene del issuer después del enrollment.

gate_id
stringoptional

Identificador de la puerta de acceso. Si está presente, la API key se limita a esta puerta.

expires_at
stringrequired

Timestamp ISO 8601 UTC a partir del cual el token deja de ser válido.

Capacidades del scanner

Un scanner PUEDE mantener enrollments activos para múltiples eventos simultáneamente. Esto permite recintos con múltiples etapas, eventos consecutivos y festivales. Cada enrollment añade un evento al conjunto activo del scanner sin reemplazar los enrollments anteriores.

Ciclo de vida de la API key

  • Alcance: cada API key está vinculada a exactamente un evento (y opcionalmente a una puerta de acceso). Un scanner registrado para múltiples eventos tendrá múltiples API keys.
  • Expiración: las API keys expiran en el timestamp expires_at devuelto en la respuesta de enrollment (normalmente fin del evento + margen). Las keys expiradas se rechazan con UNAUTHORIZED.
  • Revocación: los organizadores pueden revocar una key en cualquier momento desde la interfaz de administración.
  • Sin renovación: si una API key expira durante un evento, el scanner debe volver a registrarse mediante un nuevo QR de configuración.
  • Rotación: para reemplazar una key comprometida, el organizador revoca la key antigua y emite un nuevo QR de configuración.
§ 07

Flujo de verificación

Cuando se escanea un ticket, el scanner DEBE detectar automáticamente el evento correspondiente leyendo el campo eid del payload verificado. El operador no selecciona manualmente un evento. Este es el orden de resolución:

1. Signature

Analizar el payload del QR y verificar la signature con las keys públicas en caché del issuer en iss. Si la signature no se verifica: rechazar con invalid_signature.

2. Tiempo

Validar los límites temporales iat/exp. Si ha expirado: rechazar con expired.

3. Enrollment

Buscar los enrollments activos por la combinación (iss, eid) del payload.

4. Coincidencia

Si existe un enrollment coincidente: seleccionar la API key, verificar la compatibilidad de la puerta y luego llamar al endpoint de check-in.

5. Sin coincidencia

Si ningún enrollment coincide: rechazar localmente con not_enrolled. Mostrar el nombre del evento desde ev.name.

6. Desconocido

Si el iss es completamente desconocido para el scanner: rechazar con unknown_issuer.

§ 08

Consistencia y doble escaneo

La prevención de doble escaneo es aplicada por el issuer mediante un Durable Object con alcance por evento. Todas las escrituras de check-in para un evento dado se serializan a través de este DO, asegurando que exactamente un check-in tenga éxito incluso con requests concurrentes desde múltiples puertas.

Caché local recomendada

Los scanners DEBEN mantener una caché local de valores tid escaneados recientemente (últimas 24 horas) y rechazar duplicados del lado del cliente antes de acceder a la red. Esto reduce la carga y permite el rechazo instantáneo de duplicados obvios incluso en modo offline.

§ 09

Modo offline

Los scanners DEBEN poder operar con conectividad intermitente. Este es el orden de procesamiento para cada escaneo:

1

Verificar la signature localmente con las keys públicas en caché para el issuer en iss.

2

Validar los límites temporales iat/exp.

3

Buscar el enrollment coincidente para (iss, eid) en el almacenamiento local. Si no hay ninguno: rechazar localmente.

4

Comprobar la caché local en busca de escaneos previos de este tid. Si se encuentra: rechazar como duplicado.

5

Intentar la llamada a la API de check-in. En caso de éxito: aceptar y registrar localmente. En caso de fallo de red: poner en cola con la idempotency_key, aceptar de forma optimista y reintentar en segundo plano.

6

Cuando se restablezca la conectividad, vaciar la cola. La idempotencia del issuer garantiza que no haya inconsistencias.

Los issuers DEBEN aceptar requests de check-in con timestamps scanned_at en el pasado (dentro de un periodo razonable, ej. 48 h) para soportar escaneos offline en cola.

§ 10

Seguridad

  • Verificación de signature obligatoria antes de confiar en cualquier contenido del payload. Nunca muestre nombres de titulares ni información del evento sin verificar primero la signature.
  • Validación temporal: los scanners DEBEN rechazar tickets cuyo exp esté en el pasado en el momento del escaneo, y DEBEN advertir sobre timestamps iat inverosímilmente futuros.
  • Validación de URL del issuer: el campo iss es una URL. Los scanners DEBEN exigir HTTPS y DEBEN restringir a una lista de confianza de dominios de issuer si el contexto lo requiere.
  • Almacenamiento de API keys: las aplicaciones scanner DEBEN almacenar las API keys en el almacenamiento seguro de la plataforma (Keychain, Keystore).
  • Límites de frecuencia: los issuers DEBEN aplicar límites de frecuencia en los endpoints de check-in por credencial de scanner para prevenir abusos.
  • TLS: todos los endpoints DEBEN servirse sobre TLS 1.2+. Las redirecciones HTTP a HTTPS no son suficientes para llamadas a la API.
  • Protección contra replay: la idempotency_key previene el replay accidental. El replay intencional con una key nueva será detectado por el estado del tid en el servidor y devolverá already_used.

Seguridad del enrollment

  • Confidencialidad del QR de configuración: el QR de configuración es una credencial portadora hasta que se consume. DEBE generarse justo antes de distribuir los dispositivos al personal, mostrarse solo en pantallas de confianza y no ser fotografiado ni compartido por chat o correo electrónico.
  • Entropía del token: los tokens DEBEN tener al menos 128 bits de entropía de un RNG criptográficamente seguro.
  • Límites de frecuencia: el endpoint scanner_enroll DEBE tener límites de frecuencia por IP y por token.
  • TLS obligatorio: los scanners DEBEN rechazar cualquier QR de configuración cuyo iss o enroll_url no sea HTTPS.
  • Confirmación del operador: el scanner DEBE mostrar el event_name, la fecha y el nombre del issuer, y solicitar confirmación explícita antes de llamar al endpoint de enrollment.
  • Pista de auditoría: los issuers DEBEN registrar cada intento de enrollment (éxito y fallo).
§ 11

Versionado y evolución

  • El campo v en los payloads y el campo protocol_version en el documento de descubrimiento llevan la versión mayor.
  • Los cambios aditivos menores (nuevos campos opcionales) no incrementan la versión mayor. Los scanners DEBEN ignorar campos desconocidos.
  • Los cambios incompatibles incrementan la versión mayor. Los issuers DEBEN continuar aceptando la versión anterior durante un periodo de transición razonable (ej. 6 meses).
Última actualización : 20 de mayo de 2026