API de Captura de TruthScan
Cree un enlace de captura para el teléfono, reciba una foto firmada y lea el resultado con su clave de API.
Todos los endpoints están en https://truthscan.com. Envíe al usuario final a captureLink. Su backend no recibe el JPEG.
Autenticación
Cree una clave secreta en su cuenta.
Envíela en cada llamada en el encabezado x-api-key o como Authorization: Bearer. La clave debe pertenecer a una organización con acceso a la API. El enlace abierto en el teléfono no usa esta clave.
Sustituya YOUR_API_KEY por su clave secreta.
Crear una sesión
Devuelve un enlace de captura. inviteeEmail es obligatorio. expiresInSeconds es 15 minutos por defecto y no supera 7 días. maxCaptures es 20 por defecto y no supera 20. webhookUrl es opcional. Si se envía, debe ser https y resolverse a una dirección pública. webhookSecret solo aparece en esta respuesta.
POST /api/capture/session/createPonga sendEmail en true para que TruthScan envíe el enlace por correo. Entonces subject y message son obligatorios. Ponga {link} en el mensaje donde debe aparecer la URL de captura. emailSent es true solo cuando ese correo se envió. Si no, envíe captureLink usted mismo.
Guarde webhookSecret. Es la clave HMAC de capture.completed.
Ejemplo de solicitud
curl -X POST https://truthscan.com/api/capture/session/create \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"inviteeEmail": "person@example.com",
"expiresInSeconds": 900,
"maxCaptures": 1,
"webhookUrl": "https://example.com/hooks/capture"
}'Ejemplo de respuesta
{
"sessionId": "11111111-1111-4111-8111-111111111111",
"captureToken": "…",
"captureUrl": "/capture/11111111-1111-4111-8111-111111111111?token=…",
"captureLink": "https://truthscan.com/capture/11111111-1111-4111-8111-111111111111?token=…",
"expiresAt": "2026-10-07T16:00:00.000Z",
"expiresInSeconds": 900,
"maxCaptures": 1,
"webhookSecret": "…",
"emailSent": false
}Leer resultados
Consulte la sesión hasta que capturesUsed aumente, o espere el webhook. Las URL de descarga caducan al cabo de una hora.
Estado de la sesión
Caducidad, capturas usadas y la URL de webhook que registró.
GET /api/capture/sessions/{sessionId}Ejemplo de solicitud
curl https://truthscan.com/api/capture/sessions/SESSION_ID \
-H 'x-api-key: YOUR_API_KEY'Ejemplo de respuesta
{
"sessionId": "11111111-1111-4111-8111-111111111111",
"expiresAt": "2026-10-07T16:00:00.000Z",
"maxCaptures": 1,
"capturesUsed": 1,
"inviteeEmail": "person@example.com",
"webhookUrl": "https://example.com/hooks/capture",
"createdAt": "2026-10-07T15:45:00.000Z"
}Listar fotos
Fotos de una sesión, de la más reciente a la más antigua. Use page y limit (máximo 50). Cada elemento es un resumen más downloadUrl. La ubicación de la lista solo incluye latitud, longitud y altitud. Obtenga el archivo para precisión, rumbo y velocidad.
GET /api/capture/sessions/{sessionId}/assetsEjemplo de solicitud
curl 'https://truthscan.com/api/capture/sessions/SESSION_ID/assets?page=1&limit=20' \
-H 'x-api-key: YOUR_API_KEY'Ejemplo de respuesta
{
"assets": [
{
"id": "22222222-2222-4222-8222-222222222222",
"verificationState": "trusted",
"trustTier": "standard",
"risk": { "score": 0.95, "flags": ["geolocation_low_accuracy"] },
"capturedAtUtc": "2026-10-07T15:50:00.000Z",
"submittedAtUtc": "2026-10-07T15:50:01.200Z",
"signedAtUtc": "2026-10-07T15:50:03.000Z",
"captureToSubmitMs": 1200,
"geolocation": {
"latitude": 37.7749,
"longitude": -122.4194,
"altitude": 15,
"altitudeAccuracyMeters": null,
"accuracyMeters": null,
"headingDegrees": null,
"speedMetersPerSecond": null
},
"deviceModel": "iPhone",
"devicePlatform": "iOS",
"inviteeEmail": "person@example.com",
"thumbnailUrl": "https://…",
"downloadUrl": "https://…"
}
],
"pagination": {
"page": 1,
"totalPages": 1,
"total": 1,
"hasPreviousPage": false,
"hasNextPage": false
}
}Obtener una foto
El mismo registro que la página de detalle de la captura, más assetUrl del JPEG firmado. El ejemplo muestra los campos con los que decide el flujo. device también incluye pantalla, lista de cámaras, GPU y entrada. sensors.geolocation repite el objeto de ubicación.
GET /api/capture/assets/{assetId}Ejemplo de solicitud
curl https://truthscan.com/api/capture/assets/ASSET_ID \
-H 'x-api-key: YOUR_API_KEY'Ejemplo de respuesta
{
"id": "22222222-2222-4222-8222-222222222222",
"sessionId": "11111111-1111-4111-8111-111111111111",
"verificationState": "trusted",
"trustTier": "standard",
"risk": { "score": 0.95, "flags": ["geolocation_low_accuracy"] },
"inviteeEmail": "person@example.com",
"mimeType": "image/jpeg",
"assetUrl": "https://…",
"thumbnailUrl": null,
"capturedAtUtc": "2026-10-07T15:50:00.000Z",
"submittedAtUtc": "2026-10-07T15:50:01.200Z",
"serverReceivedAtUtc": "2026-10-07T15:50:02.000Z",
"signedAtUtc": "2026-10-07T15:50:03.000Z",
"createdAtUtc": "2026-10-07T15:50:03.100Z",
"captureToSubmitMs": 1200,
"timing": {
"capturedAtUtc": "2026-10-07T15:50:00.000Z",
"submittedAtUtc": "2026-10-07T15:50:01.200Z",
"serverReceivedAtUtc": "2026-10-07T15:50:02.000Z",
"signedAtUtc": "2026-10-07T15:50:03.000Z",
"captureToSubmitMs": 1200
},
"geolocation": {
"latitude": 37.7749,
"longitude": -122.4194,
"altitude": 15,
"altitudeAccuracyMeters": 10,
"accuracyMeters": 12,
"headingDegrees": null,
"speedMetersPerSecond": null
},
"deviceModel": "iPhone",
"devicePlatform": "iOS",
"device": {
"browserName": "Mobile Safari",
"platform": "iOS",
"timezone": "America/Los_Angeles",
"screenWidth": 390,
"screenHeight": 844,
"camera": { "width": 1920, "height": 1080, "facingMode": "environment" },
"connection": { "effectiveType": "4g", "downlinkMbps": 10, "rttMs": 50 },
"network": {
"publicIp": "203.0.113.10",
"country": "US",
"city": "San Francisco"
}
},
"sensors": {
"gyroVariance": 0.02,
"accelVariance": 0.11,
"motionSampleCount": 24,
"permissionDenied": {
"camera": false,
"geolocation": false,
"motion": false
}
}
}Campos del resultado
verificationState, trustTier y risk.flags son los valores para decidir el flujo. assetUrl y downloadUrl caducan al cabo de una hora.
| Campo | Significado |
|---|---|
| verificationState | trusted, valid_untrusted, invalid o none |
| trustTier | standard o high |
| risk.score | De 0 a 1. Empieza en 1. Cada señal del sensor lo reduce. |
| risk.flags | camera_permission_denied, geolocation_permission_denied, geolocation_missing, geolocation_low_accuracy, motion_permission_denied, motion_samples_missing, gyro_missing, zero_gyro_variance, accel_missing |
| geolocation | latitude, longitude, altitude, altitudeAccuracyMeters, accuracyMeters, headingDegrees, speedMetersPerSecond |
| device | Navegador, plataforma, user agent, zona horaria, pantalla, cámara, conexión (tipo, downlink, RTT) y red (IP pública, ASN, organización, país, ciudad, región) |
| sensors | motionSampleCount, gyroVariance, accelVariance y permissionDenied de cámara, geolocalización y movimiento |
| timing | Horas del obturador, del envío, de la recepción en el servidor y de la firma, más captureToSubmitMs |
Webhook
Tras una captura de confianza, TruthScan envía POST capture.completed a webhookUrl. No se siguen redirecciones. El cuerpo no incluye la ubicación precisa. Obtenga el archivo para el registro completo.
Lea X-TruthScan-Signature. El formato es t=<segundos unix>,v1=<hex>. Calcule HMAC-SHA256 de t + "." + el cuerpo sin procesar con webhookSecret y rechace marcas de tiempo con más de cinco minutos de diferencia.
Ejemplo de cuerpo
{
"id": "…",
"type": "capture.completed",
"data": {
"sessionId": "11111111-1111-4111-8111-111111111111",
"assetId": "22222222-2222-4222-8222-222222222222",
"verificationState": "trusted",
"trustTier": "standard",
"capturedAtUtc": "2026-10-07T15:50:00.000Z"
}
}Límites de tasa
Los límites se comparten entre instancias. Un 429 incluye Retry-After en segundos.
Límites
Cada llamada cuenta como una solicitud contra la IP y contra la organización.
| Ruta | Cubo | Límite |
|---|---|---|
| Crear y leer | IP | 600 / minuto |
| Crear y leer | Organización | 2.000 / minuto |
| Captura en el teléfono | IP | 60 / minuto |
| Captura en el teléfono | Organización | 2.000 / minuto |
Crear y leer cubre POST /api/capture/session/create y las tres rutas GET de resultado. La captura en el teléfono cubre nonce, upload-url y firma. El teléfono no envía su clave de API.
Respuesta limitada
Espere Retry-After y vuelva a enviar la solicitud.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 12
}Qué leer
- Retry-After: segundos hasta que se reinicia la ventana
- retryAfter: el mismo valor en el cuerpo JSON
Buenas prácticas
- Consulte el estado de la sesión en lugar de listar archivos en un bucle corto.
- Prefiera el webhook cuando pueda recibir callbacks HTTPS.
Errores
Las llamadas fallidas devuelven JSON con error y code.
401 y 403 significan que la clave no se puede usar. 404 significa que la sesión o el archivo no está en su organización.
| Código | Significado |
|---|---|
| MISSING_API_KEY | No hay x-api-key ni token Bearer |
| INVALID_API_KEY | Clave desconocida, inactiva o sin organización |
| ORG_NO_API_ACCESS | El acceso a la API de la organización está desactivado |
| INVITEE_EMAIL_REQUIRED | Faltaba inviteeEmail |
| INVITEE_EMAIL_INVALID | inviteeEmail no es una dirección válida |
| INVITE_MESSAGE_REQUIRED | sendEmail es true y message falta o es demasiado largo |
| INVITE_SUBJECT_REQUIRED | sendEmail es true y falta subject |
| WEBHOOK_URL_REJECTED | webhookUrl no es https público |
| SESSION_NOT_FOUND | No hay sesión con ese id en su organización |
| ASSET_NOT_FOUND | No hay archivo con ese id en su organización |
| RATE_LIMIT_EXCEEDED | Demasiadas solicitudes en la ventana actual |
Preguntas frecuentes
Cómo encaja la API de Captura en su backend.