API de Captura TruthScan
Crie um link de captura para o telefone, receba uma foto assinada e leia o resultado com sua chave de API.
Todos os endpoints ficam em https://truthscan.com. Envie o usuário final para captureLink. Seu backend não recebe o JPEG.
Autenticação
Crie uma chave secreta na sua conta.
Envie-a em toda chamada no cabeçalho x-api-key ou em Authorization: Bearer. A chave precisa pertencer a uma organização com acesso à API. O link aberto no telefone não usa essa chave.
Substitua YOUR_API_KEY pela sua chave secreta.
Criar uma sessão
Devolve um link de captura. inviteeEmail é obrigatório. expiresInSeconds padrão é 15 minutos e não passa de 7 dias. maxCaptures padrão é 20 e não passa de 20. webhookUrl é opcional. Quando enviado, precisa ser https e resolver para um endereço público. webhookSecret aparece só nesta resposta.
POST /api/capture/session/createDefina sendEmail como true para a TruthScan enviar o link por e-mail. Aí subject e message são obrigatórios. Coloque {link} na mensagem onde a URL de captura deve aparecer. emailSent é true só quando esse e-mail foi enviado. Caso contrário, envie captureLink você mesmo.
Guarde webhookSecret. Ele é a chave HMAC de capture.completed.
Exemplo de pedido
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"
}'Exemplo de resposta
{
"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
}Ler resultados
Consulte a sessão até capturesUsed aumentar, ou espere o webhook. As URLs de download expiram em uma hora.
Status da sessão
Validade, capturas usadas e a URL de webhook que você registrou.
GET /api/capture/sessions/{sessionId}Exemplo de pedido
curl https://truthscan.com/api/capture/sessions/SESSION_ID \
-H 'x-api-key: YOUR_API_KEY'Exemplo de resposta
{
"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 uma sessão, da mais nova para a mais antiga. Use page e limit (máximo 50). Cada item é um resumo mais downloadUrl. A localização da lista só inclui latitude, longitude e altitude. Busque o arquivo para precisão, direção e velocidade.
GET /api/capture/sessions/{sessionId}/assetsExemplo de pedido
curl 'https://truthscan.com/api/capture/sessions/SESSION_ID/assets?page=1&limit=20' \
-H 'x-api-key: YOUR_API_KEY'Exemplo de resposta
{
"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
}
}Obter uma foto
O mesmo registro da página de detalhe da captura, mais assetUrl do JPEG assinado. O exemplo mostra os campos em que você decide o fluxo. device também inclui tela, lista de câmeras, GPU e entrada. sensors.geolocation repete o objeto de localização.
GET /api/capture/assets/{assetId}Exemplo de pedido
curl https://truthscan.com/api/capture/assets/ASSET_ID \
-H 'x-api-key: YOUR_API_KEY'Exemplo de resposta
{
"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 do resultado
verificationState, trustTier e risk.flags são os valores para decidir o fluxo. assetUrl e downloadUrl expiram depois de uma hora.
| Campo | Significado |
|---|---|
| verificationState | trusted, valid_untrusted, invalid ou none |
| trustTier | standard ou high |
| risk.score | De 0 a 1. Começa em 1. Cada sinal do sensor reduz o valor. |
| 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, fuso, tela, câmera, conexão (tipo, downlink, RTT) e rede (IP público, ASN, organização, país, cidade, região) |
| sensors | motionSampleCount, gyroVariance, accelVariance e permissionDenied de câmera, geolocalização e movimento |
| timing | Horários do obturador, do envio, do recebimento no servidor e da assinatura, mais captureToSubmitMs |
Webhook
Depois de uma captura confiável, a TruthScan envia POST capture.completed para webhookUrl. Redirecionamentos não são seguidos. O corpo não inclui a localização precisa. Busque o arquivo para o registro completo.
Leia X-TruthScan-Signature. O formato é t=<segundos unix>,v1=<hex>. Calcule HMAC-SHA256 de t + "." + o corpo bruto com webhookSecret e rejeite timestamps com mais de cinco minutos de diferença.
Exemplo de corpo
{
"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"
}
}Limites de taxa
Os limites são compartilhados entre instâncias. Um 429 inclui Retry-After em segundos.
Limites
Cada chamada conta como um pedido contra o IP e contra a organização.
| Caminho | Balde | Limite |
|---|---|---|
| Criar e ler | IP | 600 / minuto |
| Criar e ler | Organização | 2.000 / minuto |
| Captura no telefone | IP | 60 / minuto |
| Captura no telefone | Organização | 2.000 / minuto |
Criar e ler cobre POST /api/capture/session/create e as três rotas GET de resultado. A captura no telefone cobre nonce, upload-url e assinatura. O telefone não envia sua chave de API.
Resposta limitada
Espere Retry-After e envie o pedido de novo.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 12
}O que ler
- Retry-After: segundos até a janela reiniciar
- retryAfter: o mesmo valor no corpo JSON
Boas práticas
- Consulte o status da sessão em vez de listar arquivos em um loop curto.
- Prefira o webhook quando puder receber callbacks HTTPS.
Erros
Chamadas com falha devolvem JSON com error e code.
401 e 403 significam que a chave não pode ser usada. 404 significa que a sessão ou o arquivo não está na sua organização.
| Código | Significado |
|---|---|
| MISSING_API_KEY | Sem x-api-key nem token Bearer |
| INVALID_API_KEY | Chave desconhecida, inativa ou sem organização |
| ORG_NO_API_ACCESS | O acesso à API da organização está desligado |
| INVITEE_EMAIL_REQUIRED | inviteeEmail estava ausente |
| INVITEE_EMAIL_INVALID | inviteeEmail não é um endereço válido |
| INVITE_MESSAGE_REQUIRED | sendEmail é true e message está ausente ou longo demais |
| INVITE_SUBJECT_REQUIRED | sendEmail é true e subject está ausente |
| WEBHOOK_URL_REJECTED | webhookUrl não é https público |
| SESSION_NOT_FOUND | Nenhuma sessão com esse id na sua organização |
| ASSET_NOT_FOUND | Nenhum arquivo com esse id na sua organização |
| RATE_LIMIT_EXCEEDED | Pedidos demais na janela atual |
Perguntas frequentes
Como a API de Captura entra no seu backend.