A Data API (antes documentada aqui como Server API) permite que o seu backend leia os dados de identificação que a TRACIO já coletou para o seu workspace: o histórico de um visitante, sessões individuais e contadores de velocity de janela curta. Algumas rotas também escrevem — registros de conta, campos personalizados e entradas de listas —, para que o seu backend possa enriquecer o que a TRACIO sabe.
Ela complementa os webhooks em vez de substituí-los:
| Webhooks | Data API | |
|---|---|---|
| Direção | A TRACIO envia para o seu endpoint | O seu backend consulta sob demanda |
| Momento | Conforme cada identificação acontece | A qualquer momento, dentro da sua janela de retenção |
| Ideal para | Reagir a um evento | Consultar dados durante uma decisão, preencher históricos, investigações |
Ambas as superfícies estão disponíveis a partir do plano Pro.
https://api.tracio.ai/v2Este é um host diferente do endpoint do navegador (edge.tracio.ai) e do painel
(app.tracio.ai). Os três são separados: o navegador fala com o edge usando a sua
chave pública, e o seu backend fala com a Data API usando a sua chave
secreta.
As rotas /v1 foram removidas. Use /v2: as rotas são as mesmas, e toda chave JSON
e todo parâmetro de query estão em snake_case (visitor_id, risk_score,
min_risk_score). As chaves dentro dos seus próprios dicionários fields e labels
são dados seus e mantêm a grafia original. O SDK JavaScript mantém a sua interface
em camelCase.
Toda requisição carrega a sua chave secreta como bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"A Data API é apenas de servidor para servidor. Os cabeçalhos CORS deliberadamente não são retornados, então um navegador não consegue chamá-la — é isso que mantém a sua chave secreta fora do código do lado do cliente. Nunca envie a chave secreta para o navegador.
Crie-a no painel, em API Keys, escolhendo o tipo secret.
tracio_sk_ seguido de 43 caracteres, 53 no total.
O painel a lista pelos primeiros caracteres, para que você consiga diferenciar as
chaves.404, exatamente como para dados que não existem.A rotação emite uma chave nova e mantém a antiga funcionando por 7 dias, para que você possa implantá-la sem indisponibilidade. Implante a chave nova, confirme que o tráfego migrou e deixe a antiga expirar. Chaves públicas não são rotacionáveis — elas não são segredos e ficam visíveis no código-fonte das suas páginas por design.
| Método | Caminho | Retorna |
|---|---|---|
GET | /v2/visitors/{visitor_id} | Histórico agregado de um visitante, mais a sessão mais recente dele |
GET | /v2/visitors/{visitor_id}/sessions | Lista paginada das sessões desse visitante |
GET | /v2/visitors/{visitor_id}/sessions/latest | A única sessão mais recente |
GET | /v2/visitors/{visitor_id}/velocity | Contadores de atividade em uma janela curta |
GET | /v2/sessions/{request_id} | Uma sessão, pelo identificador da requisição |
POST | /v2/accounts/metadata | Anexar nomes de exibição e e-mails aos seus IDs de conta |
POST | /v2/events/fields | Anexar campos personalizados a uma visita depois do carregamento da página |
POST | /v2/events/fields/delete | Apagar campos personalizados de uma visita — a visita em si não é alterada |
GET | /v2/lists | As listas do seu workspace: id, nome, tipo (a ação), modo, número de entradas |
POST | /v2/lists/{list_id}/entries | Adicionar um valor a uma lista, opcionalmente com um TTL |
POST | /v2/lists/{list_id}/entries/delete | Remover um valor de uma lista |
GET | /.well-known/webhook-keys | Chaves públicas da assinatura de plataforma dos webhooks (sem auth) |
A mesma chave e o mesmo host também atendem as APIs de decisão, todas em snake_case:
| Método | Caminho | Finalidade |
|---|---|---|
POST | /v2/transactions | Uma decisão síncrona APPROVE / REVIEW / DECLINE sobre um evento de negócio — Business e acima, exceto uma conversão de afiliado (account_register ou deposit com affiliate_id), disponível em qualquer plano com acesso à API |
GET | /v2/transactions/{transaction_id} | Reler uma decisão com o seu rótulo e o estado efetivo — Business e acima, exceto uma transação atribuída a um afiliado |
PUT, DELETE | /v2/transaction-labels | Definir ou remover o rótulo de resultado de até 50 transações — Business e acima |
GET | /v2/transaction-labels/value-set | Os valores de rótulo aceitos, com a sua polaridade — Business e acima |
POST | /v2/logins/assess | Avaliar um login: allow / challenge / review / deny com motivos |
POST, GET | /v2/accounts/{user_id}/trusted-devices | Marcar como confiável o dispositivo de uma visita para uma conta depois da sua verificação adicional, ou listar os dispositivos confiáveis da conta |
DELETE | /v2/accounts/{user_id}/trusted-devices/{device_ref} | Revogar a confiança em um dispositivo |
GET | /v2/affiliates/{affiliate_id} | O relatório de qualidade de um afiliado ao longo de 90 dias, com uma recomendação hold / review |
POST, GET | /v2/self-exclusions | Definir ou estender uma autoexclusão em uma conta, ou listá-las |
DELETE | /v2/self-exclusions/{user_id} | Encerrar uma autoexclusão antes do prazo |
Os resultados delas também chegam até você como eventos de webhook:
transaction_state_changed, login_risk, trusted_device_enrolled,
trusted_device_revoked e affiliate_threshold_crossed.
Uma barra final é aceita e ignorada. Um caminho desconhecido ou um método errado retorna o mesmo envelope de erro JSON que todo o resto, nunca uma página HTML ou de texto puro.
Toda leitura é limitada por uma janela de tempo, controlada por dois parâmetros de query opcionais:
| Parâmetro | Aceita |
|---|---|
from | YYYY-MM-DD ou um timestamp RFC 3339 completo |
to | YYYY-MM-DD ou um timestamp RFC 3339 completo |
to inclui aquele dia inteiro.400 invalid_request e a mensagem
time must be YYYY-MM-DD or RFC3339.meta, então verifique meta.from e meta.to em vez de supor que a sua
requisição foi atendida ao pé da letra.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"A resposta carrega o histórico agregado e embute a sessão mais recente, então o caso comum precisa de uma requisição em vez de duas:
{ "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "first_seen_at": "2026-05-02T10:11:12Z", "last_seen_at": "2026-07-25T08:00:00Z", "visits": 42, "incognito_visits": 3, "unique_ips": 5, "unique_countries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "max_risk_score": 63, "avg_bot_score": 12.5, "bot_sessions": 7, "last_decision": "real" }, "network": { "vpn_seen": false, "proxy_seen": false, "tor_seen": false, "datacenter_seen": true, "proxy_detected_seen": true, "last_isp": "Deutsche Telekom", "last_real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }, "last_session": { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "account_id": "user_8842", "account": { "id": "user_8842", "email": "ann@example.com", "display_name": "Ann Lee" }, "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "fields": { "order_ref": "A-1029" }, "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", "browser": { "name": "Chrome", "version": "126.0" }, "os": { "name": "macOS", "version": "14.5" }, "device": "desktop", "gpu": "Apple M2", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "connection_type": "residential", "proxy_detected": true, "real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" }, "asn": 3320 }, "screen": { "width": 2560, "height": 1600, "color_depth": 30, "pixel_ratio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "client_hints": { "architecture": "arm", "bitness": "64", "platform_version": "14.5.0" }, "extensions": [ { "slug": "ublock-origin", "name": "uBlock Origin", "category": "adblock", "risky": false, "store_url": "https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm" } ], "bot": { "result": "human", "score": 4.5, "antidetect_score": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "match_type": "exact", "match_confidence": 0.99 }, "device_info": { "device_id": "d_4f9c2e", "cross_browser": true, "confidence": 0.88, "linked_browsers": 2 }, "decision": { "action": "real", "risk_score": 12, "suspect_score": 3 } }, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| Campo | Significado |
|---|---|
visits, incognito_visits | Total de visitas na janela, e quantas foram em uma janela privada |
unique_ips, unique_countries | Endereços e países distintos observados na janela |
browsers, os, devices | Os ambientes distintos em que este visitante apareceu |
risk.max_risk_score | A maior pontuação de risco registrada na janela, 0..100 |
risk.last_decision | A decisão registrada para a visita mais recente |
risk.avg_bot_score, risk.bot_sessions | Média da pontuação de bot e o número de sessões de bot — Business e acima |
network.*_seen | Se um VPN, proxy, nó de saída Tor ou endereço de datacenter já foi visto para este visitante |
network.proxy_detected_seen | Se pelo menos uma visita na janela saiu por um proxy ou VPN à frente do navegador — veja network.proxy_detected em Fatos do dispositivo |
network.last_isp | O ISP mais recente — Business e acima |
network.last_real_ip | O endereço mais recente observado atrás de um proxy ou VPN (address, country, isp) — Business e acima; ausente quando nenhuma visita trouxe um |
last_session | O objeto de sessão completo da visita mais recente |
meta | O plano, a retenção dele em dias e a janela efetivamente aplicada |
Um visitante sem dados dentro da janela de retenção retorna 404 not_found com a
mensagem visitor not found in the retention window — isso não é um erro da sua
integração, significa que o visitante é novo ou que os dados dele expiraram.
A sessão carrega dois vereditos, e eles respondem a perguntas diferentes — se o cliente era automatizado, e o que o motor de risco concluiu no geral:
| Campo | Valores |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Presente quando bot.result é bot: uma ferramenta específica (playwright, puppeteer, selenium, jsdom, claude_computer_use…), um agente de IA verificado (ai_agent:<vendor>) ou uma família quando a ferramenta não é nomeada — automation, headless, antidetect, extension, privacy_browser, impersonation, other |
decision.action | real, fake, suspicious |
bot.score e decision.risk_score vão ambos de 0..100. No Business e acima,
guidance os transforma em recomendações por cenário na escada
allow → challenge → review → deny — veja
Guidance para o que cada degrau
significa. Valores e escalas coincidem com o corpo do webhook v3 da mesma visita, e
guidance é calculado pelas mesmas regras.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&min_risk_score=50"| Parâmetro | Padrão | Observações |
|---|---|---|
limit | 50 | Limitado a 500; um valor maior é ajustado ao limite, não rejeitado |
from, to | Retenção do plano | A janela de tempo compartilhada descrita acima |
cursor | — | Cursor de paginação opaco vindo da página anterior |
bot_result | — | Manter apenas as sessões com este veredito de bot |
min_risk_score | — | Manter apenas as sessões com esta pontuação de risco ou acima, 0..100 |
As sessões voltam da mais recente para a mais antiga:
{ "items": [ { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "next_cursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "has_more": true, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}A paginação é baseada em cursor. Não há parâmetro page nem offset: devolva o
next_cursor que você recebeu como cursor e siga enquanto has_more for true.
A última página pode não incluir next_cursor, então não torne esse campo
obrigatório.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v2/visitors/${visitorId}/sessions`) url.searchParams.set("limit", "500") if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${secretKey}` } }) if (!res.ok) throw new Error(`Data API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.has_more ? page.next_cursor : undefined } while (cursor)
return sessions}Trate o cursor como opaco — o conteúdo dele é um detalhe de implementação e pode
mudar. Um cursor que tenha sido editado é rejeitado com 400 invalid_request e a
mensagem malformed cursor.
Além do navegador e do sistema operacional obtidos do User-Agent, uma sessão carrega o que o navegador do visitante informa sobre a máquina, higienizado do nosso lado. Cada campo fica ausente quando a visita não trouxe esses dados, então trate cada um como opcional.
| Campo | Significado |
|---|---|
gpu | Modelo do adaptador de vídeo conforme informado pelo navegador (WebGL), normalizado para um nome legível — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer significa que não há GPU real (uma máquina virtual ou um ambiente headless); o Safari informa Apple GPU |
network.proxy_detected | O tráfego HTTP da visita e seus caminhos de rede brutos saem por redes diferentes — um proxy ou VPN à frente do navegador; dois endereços do mesmo provedor (NAT da operadora, uma segunda saída da mesma VPN) não contam; não é sinalizado quando os endereços não podem ser comparados (iCloud Private Relay, IPv4 ao lado de IPv6) |
network.real_ip.address, .country, .isp | O endereço público observado no caminho de rede bruto, ou seja, o endereço atrás do proxy ou da VPN, com o país e o ISP dele — Business e acima; ausente quando nenhum endereço desse tipo foi observado (country e isp ficam ausentes quando não puderam ser resolvidos) |
device_info.device_id, .cross_browser, .confidence, .linked_browsers | Presente quando uma identidade de dispositivo foi resolvida: um id estável do dispositivo físico comum aos navegadores que há nele, se esta visita chegou por um navegador diferente do anterior, a confiança dessa correspondência e quantos visitantes (navegadores) distintos compartilham o dispositivo — mais de um significa uma máquina sob várias identidades de navegador — Business e acima |
os_environment | O ambiente de desktop medido em uma máquina Linux (Mint 22+, Ubuntu 25.04+, Ubuntu, GNOME, KDE) — Business e acima; ausente quando não determinado |
spoofing | O que a visita afirmou em comparação com o que verificações independentes mediram (claimed, real, spoofed_axes de os, gpu, screen, network, browser; anonymous_browser com nomes de produto) — Business e acima; presente apenas quando uma falsificação foi detectada |
screen.width, .height, .color_depth, .pixel_ratio | Resolução de tela, profundidade de cor e device pixel ratio conforme informados pelo navegador — Business e acima |
locale.languages, locale.timezone | Os idiomas preferidos e o fuso horário do próprio navegador — ao contrário de geo.timezone, derivado do endereço IP; uma divergência entre os dois é um sinal comum de localização falsificada — Business e acima |
client_hints.architecture, .bitness, .model, .device_name, .platform_version | User-Agent Client Hints: arquitetura e bitness da CPU, código do modelo do dispositivo (Android, p. ex. SM-A556B) com seu nome comercial da lista de dispositivos do Google Play (device_name, p. ex. Samsung Galaxy A55 5G) e a versão exata da plataforma; apenas navegadores baseados em Chromium — Business e acima |
environment.virtual_machine, environment.hypervisor | Presente apenas quando o adaptador de vídeo se identificou como virtual; hypervisor é um dicionário fechado (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Um bloco ausente significa que não há tal indício — Business e acima |
extensions lista as extensões do navegador detectadas durante a visita — Pro e acima.
Cada entrada é um objeto:
| Campo | Significado |
|---|---|
slug | Identificador estável e legível por máquina da extensão, o mesmo valor que o webhook entrega |
name | Nome legível por humanos |
category | Classe geral — adblock, privacy, automation, wallet, vpn, devtools, other, e assim por diante |
risky | true para extensões associadas a automação, falsificação ou roubo de credenciais |
store_url | Link para a página da extensão na loja, quando conhecido |
Um achado só é reportado depois de passar pelas nossas verificações de confiabilidade — um ambiente que responde "instalada" a cada sondagem, ou um lote com mais de doze nomes, é descartado como não confiável. Portanto, uma lista vazia ou ausente significa "nada que pudemos confirmar", não "nenhuma extensão instalada". Leia-a como indício, não como inventário.
fields traz os pares chave-valor personalizados que a sua integração anexou à visita —
uma referência de pedido, o nome de um plano, o resultado de um pagamento. Chaves e valores são sempre
strings: um número ou booleano anexado volta em forma de texto. Como o restante
da sessão, está disponível em qualquer plano com acesso à API (Pro e acima) e
fica ausente quando a visita não tem campos.
fields é lido ao vivo a cada requisição, não copiado no momento da visita. Campos anexados
depois da visita (veja Anexar campos depois da visita)
também aparecem aqui, então, para a mesma visita, fields pode conter mais do que o webhook
entregou.
Não coloque dados pessoais, como nomes, endereços de e-mail ou números de telefone, nos campos personalizados — anexe no lugar uma referência que os seus próprios sistemas consigam resolver, e envie quem é a conta como um registro de conta (abaixo).
account é o seu registro da conta por trás da visita — o que você enviou por último para
esse account_id por meio de POST /v2/accounts/metadata:
{ "account": { "id": "user-42", "email": "ann@example.com", "display_name": "Ann Lee" }}| Campo | Descrição |
|---|---|
id | O seu ID de conta — o mesmo valor de account_id |
email | O endereço de e-mail que você enviou para a conta; ausente se vazio |
display_name | O nome de exibição que você enviou para a conta; ausente se vazio |
O registro é lido ao vivo a cada requisição, então uma renomeação aparece também em visitas antigas: o bloco descreve a conta como ela é agora, não como era no momento da visita. Ele está disponível em qualquer plano com acesso à API (Pro e acima). Os rótulos que você enviou com o registro não são retornados.
O bloco fica ausente quando a visita não tem conta vinculada, quando não há registro para ela (ou você o limpou enviando valores vazios) e — raramente — quando o registro não pôde ser lido: a sessão ainda é retornada por completo, em vez de a requisição falhar, então trate um bloco ausente como "desconhecido", não como "esta conta não tem e-mail". Os webhooks não carregam este bloco.
A mais recente:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Isso retorna um objeto de sessão puro — não um array, e não embrulhado em um envelope.
Um visitante sem sessões na janela retorna 404 not_found com
no sessions for this visitor in the retention window.
Ou pelo request_id, o identificador que também aparece no payload do webhook:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitor_id=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitor_id é opcional aqui, mas passá-lo quando você o conhece torna a busca
sensivelmente mais rápida.
identification.drift_score — a pontuação de comportamento suavizada em que se baseou o veredito de
account takeover, Business e acima — é retornado apenas por esta leitura de sessão única.
A deriva de comportamento é medida durante a visita, depois da identificação inicial,
por isso as rotas de lista e de sessão mais recente o omitem. Uma visita cujo comportamento nunca foi
avaliado não tem drift_score. O mesmo valor chega até você pelo
webhook account_takeover.
Uma visita pode ser entregue mais de uma vez por webhook: primeiro primary, depois late ou
correction, à medida que sinais mais lentos e indícios comportamentais chegam — e uma fase posterior
pode reverter o veredito anterior. A Data API responde a partir do registro
primary. As fases posteriores não são mescladas a ele, então, para uma visita corrigida, o
último webhook é a resposta mais nova. Se você age minutos depois da visita, prefira a
fase que recebeu por último a uma leitura nova aqui.
Uma coisa funciona ao contrário: assim que o detector de account takeover dispara para uma
visita, guidance aqui reflete isso, enquanto os corpos de webhook identification da mesma
visita não carregam esse eixo — ele segue na entrega dedicada account_takeover.
Velocity responde "quanto este visitante andou fazendo ultimamente" — o formato de credential stuffing, teste de cartões e cadastros em massa.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window aceita 1h, 24h ou 7d e o padrão é 24h. Qualquer outro valor é
rejeitado com 400 invalid_request e window must be one of: 1h, 24h, 7d.
{ "window": "1h", "events": 37, "unique_ips": 9, "unique_countries": 3, "unique_accounts": 12, "bot_events": 4, "meta": { "plan": "business", "retention_days": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}unique_accounts conta os valores linkedId distintos que você enviou para este
dispositivo — veja Vinculação de contas. bot_events é do
Business e acima; no Pro a chave fica ausente.
A TRACIO conhece as suas contas apenas pelo ID que o seu site passa como linkedId — uma string opaca
como usr_8412. Envie os rótulos legíveis uma vez, e cada tela do painel que
mostra essa conta passa a exibir o nome ou o e-mail em vez do ID bruto, e o objeto de sessão
os retorna no seu bloco account. Nada aqui afeta a
detecção ou a pontuação; é uma camada de exibição.
curl -s -X POST "https://api.tracio.ai/v2/accounts/metadata" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"accounts":[ {"account_id":"usr_8412","email":"ann@example.com","display_name":"Ann Lee","labels":{"tier":"vip"}}, {"account_id":"usr_9130","display_name":"Bob Ray"} ]}'{ "updated": 2 }fields. Um e-mail
ou um nome enviado aqui vive apenas no registro de conta do seu workspace: não é armazenado
por visita, não é escrito na página, e um único envio atualiza todas as visitas dessa
conta.400 invalid_request, e uma entrada inválida
rejeita o lote inteiro — o erro indica a posição dela. IDs de conta duplicados em
um mesmo lote não são problema: vale o último.Limites: 1.000 contas por requisição, corpo de 1 MiB, account_id ≤ 256 caracteres,
email ≤ 320, display_name ≤ 256, no máximo 16 rótulos com chaves ≤ 64 e valores
≤ 256 caracteres.
O navegador só consegue enviar o que a página sabe no momento da renderização — um ID de pedido, nunca o resultado de uma verificação de pagamento ou qualquer outra coisa que se resolva depois. Esta rota preenche essa lacuna: encontre a visita por um campo que ela já carrega e anexe mais campos a ela no servidor.
curl -s -X POST "https://api.tracio.ai/v2/events/fields" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"},"fields":{"amount":"120.00"}}'{ "updated": 1 }match encontra a visita por um campo que ela já tem, não por request_id, dentro
do workspace e do ambiente da sua chave.404 not_found — escreva
o campo logo depois que o resultado se resolver.updated conta visitas distintas. Ele passa de 1 apenas quando o seu match atinge
mais de uma visita — um par chave/valor que não é exclusivo de um pedido ou carrinho. Um
match que atinge mais de 100 eventos é recusado com 400 invalid_request em vez de
ser aplicado a um subconjunto arbitrário.server, e têm
prioridade sobre um valor com a mesma chave escrito pelo navegador.404 not_found, não um 200 vazio.Os limites são os mesmos do canal do navegador: 50 campos por chamada, chaves ≤ 40 bytes, valores ≤ 500
bytes — bytes, não caracteres —, charset das chaves A-Za-z0-9_.:-, corpo de 1 MiB.
Apaga campos personalizados — nunca a visita em si. Use para valores que você não precisa mais anexados a uma visita, como o total de um pedido. Webhooks já enviados e exportações já feitas não são alterados.
# by request_id — clears every field on this one visitcurl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"request_id":"9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31"}'
# by match — clears "order_ref" from every visit currently holding "A-1029"curl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"}}'{ "deleted": 1 }request_id e match são mutuamente exclusivos — enviar os dois, ou nenhum, resulta em
400 invalid_request. match usa a mesma janela de 24 horas e o mesmo teto de 100 eventos
da anexação.deleted conta os campos removidos da visita (por request_id) ou as
visitas das quais o campo foi removido (por match).404 not_found.As mesmas listas que os seus operadores gerenciam no painel, acessíveis a partir do seu próprio back
office. O tipo de uma lista é a sua ação sobre uma visita correspondente — block,
manual_review, challenge, allow — e o seu modo decide se a ação é
aplicada: observe apenas conta as correspondências, enforce a aplica em guidance (o eixo
list). As listas e o seu modo são criados no painel; a Data API lê
as listas e adiciona e remove entradas.
curl -s "https://api.tracio.ai/v2/lists" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX"[ { "id": "7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60", "name": "Chargeback accounts", "type": "block", "mode": "enforce", "entries_count": 128 }]# ban an account for 30 dayscurl -s -X POST "https://api.tracio.ai/v2/lists/7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60/entries" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"value":"usr_8412","value_type":"linked_id","ttl_minutes":43200}'{ "id": "02b4c8d1-6e7f-4a90-b1c2-d3e4f5a6b7c8", "value": "usr_8412", "value_type": "linked_id", "expires_at": "2026-10-19T12:00:00Z", "propagation": "immediate"}Remova uma entrada com o mesmo corpo em POST /v2/lists/{list_id}/entries/delete; a
resposta é { "removed": true, "propagation": "immediate" }.
value_type é aquilo com que o valor é comparado: linked_id, visitor_id,
device_id, ip, cidr ou country. Um valor que não é válido para o seu tipo resulta em
400 invalid_request.linked_id →
visitor_id → device_id → ip → cidr → country — e, dentro dele, vence a ação mais
rígida.ttl_minutes é opcional. Se omitido, aplica-se o TTL padrão da lista; sem ele,
a entrada nunca expira (expires_at é null). Adicionar a entrada de novo a renova.propagation diz quando a mudança entra em vigor: immediate, ou ttl — em
cerca de cinco minutos.200 com removed: false; o 404 é
reservado para uma lista desconhecida.Um campo ausente significa "sem dados", nunca zero. Campos sem valor são omitidos
por completo em vez de enviados como 0, "" ou null: um visitante novinho em
folha não tem match_confidence, e uma visita limpa não tem antidetect_score nem
suspect_score. A única exceção deliberada é bot.score, que está sempre presente
mesmo quando vale zero. Leia os campos de forma defensiva.
O payload depende do seu plano. O acesso à API começa no Pro, e o Pro recebe a
sessão base — identificadores, timestamp, URL, IP, user agent, navegador, SO, dispositivo, geo,
network, bot, identification e decision — junto com
identification.match_type, identification.match_confidence, bot.score,
bot.type, bot.antidetect_score, gpu, network.proxy_detected, extensions,
fields e account. Business e Enterprise adicionam geo.isp, network.asn, network.real_ip, decision.suspect_score,
identification.drift_score, reasons, behavior, guidance, device_info,
spoofing, os_environment, screen, locale, client_hints e environment.
Os campos em nível de pessoa (person_id, reputation, linked_accounts_count,
linked_visitors_count) são reservados a Business e Enterprise e aparecerão assim que
a camada de pessoa for ativada — hoje ela funciona em modo de observação e esses campos
não são entregues. A ausência de um campo de Business em um plano Pro não é um erro.
Detalhes internos em nível de sinal nunca são retornados, em nenhum plano: nomes de sinais individuais, seus pesos, os limiares por trás de um veredito, valores brutos de sinais e a decomposição das pontuações ficam do nosso lado. Uma pontuação que pode ser submetida a engenharia reversa até as suas entradas deixa de ser útil como defesa.
Toda falha usa um único envelope:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "request_id": "8f14e45fceea167a5a36dedd" }}Este request_id não é o identificador da visita. Dois valores diferentes dividem o
mesmo nome: dentro de um payload de sessão, request_id é o UUID da visita, o mesmo que
o webhook entrega; dentro de um envelope de erro, é um identificador de trace de 24
caracteres, cunhado a cada chamada HTTP. O identificador de trace também volta no
cabeçalho X-Request-Id em toda resposta, bem-sucedida ou não. Inclua-o quando entrar
em contato com o suporte — é assim que encontramos a sua chamada exata.
| HTTP | code | Significado |
|---|---|---|
| 400 | invalid_request | Um parâmetro está ausente ou malformado |
| 401 | unauthorized | A chave está ausente, inválida, revogada ou expirada |
| 402 | upgrade_required | O seu plano não inclui acesso à API |
| 404 | not_found | Nada correspondeu dentro da janela de retenção, ou pertence a outro ambiente |
| 405 | method_not_allowed | A rota existe, mas não para esse método |
| 429 | rate_limited | Requisições por segundo, ou a cota diária, excedidas |
| 500 | internal | Algo falhou do nosso lado |
| 503 | unavailable | Um armazenamento de apoio está temporariamente inacessível |
As verificações rodam em uma ordem fixa — chave, depois plano, depois limites —, então
uma requisição com uma chave ruim sempre reporta a chave primeiro, nunca um problema de
cota. Dados de outro workspace são indistinguíveis de dados que não existem: resultam em
404, não 403.
Dois casos de 401 são redigidos de forma diferente de propósito:
missing Authorization: Bearer <secret key> significa que o cabeçalho nunca chegou,
enquanto invalid or revoked API key significa que ele chegou e não correspondeu. O
402 carrega Data API requires the Pro plan or higher.
Repita 429 e 503 com uma pausa crescente; repetir outros 4xx não adianta — eles
significam que a própria requisição precisa ser corrigida.
As respostas em que a chave foi reconhecida carregam a sua situação atual (as respostas 401
e 402 não carregam, e o Enterprise, sem cota diária, não recebe cabeçalhos de
cota):
| Cabeçalho | Significado |
|---|---|
X-RateLimit-Limit | A sua cota diária; em uma recusa por segundo, o limite por segundo |
X-RateLimit-Remaining | Chamadas restantes hoje |
X-RateLimit-Reset | Horário Unix do reset — meia-noite UTC |
Retry-After | Segundos de espera, enviado apenas com um 429 — siga-o: ele é correto para os dois limites |
| Plano | Requisições por segundo | Requisições por dia | Profundidade do histórico |
|---|---|---|---|
| Free | Sem acesso à API | — | 7 dias |
| Pro | 10 | 10.000 | 30 dias |
| Business | 50 | 100.000 | 90 dias |
| Enterprise | 200 | Sem limite | 365 dias |