ArenaBRticket · API de Cronometragem
ArenaBRticket · Documentação Técnica

API de Cronometragem

Documentação técnica para empresas de cronometragem integrarem com a ArenaBRticket: consulta de atletas inscritos e envio dos resultados da prova.

Introdução

A API de Cronometragem permite que sistemas de empresas parceiras consultem os atletas inscritos de um evento (com número de peito e chip/RFID) e enviem de volta os resultados da prova em lote.

Cada credencial é criada por um organizador, dentro do painel dele (menu Organizador → Empresas de Cronometragem), e só enxerga os eventos desse organizador específico — nunca dados de outro organizador na plataforma.

Autenticação

Toda requisição deve incluir o header X-Api-Key com a chave fornecida pelo organizador. A chave é exibida uma única vez no momento da criação — se for perdida, é preciso criar uma nova.

X-Api-Key: tck_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Cada credencial tem permissões independentes: read_athletes (consultar atletas), submit_results (enviar resultados) e assign_chips (associar chip/RFID a um número de peito). Uma chamada sem a permissão necessária recebe 403.

Ambientes

Homologação e produção são projetos completamente independentes — URLs e credenciais diferentes em cada um.

AmbienteURL base
Produçãohttps://auth.arenabrticket.com.br
Homologaçãohttps://zowqyltqdbihtwtiboua.supabase.co

Limites de Requisições

Consulta de atletas: até 500 registros por página (per_page, padrão 200).
Envio de resultados: até 1.000 itens por requisição.
Associação de chips: até 1.000 itens por requisição.
Taxa de requisições: 60 requisições por minuto por credencial (janela fixa de 1 minuto), padrão de mercado. Chamadas acima do limite recebem 429 até a janela seguinte começar.

Precisa de um limite maior para o seu volume de integração? Fale com o organizador que emitiu sua credencial para que ele solicite o ajuste junto à plataforma.

Versionamento

A API está na sua primeira versão, sem prefixo de versão na URL. Mudanças que quebrem compatibilidade serão anunciadas no changelog abaixo com antecedência antes de qualquer alteração.

Códigos de Erro

StatusCódigoSignificado
400BAD_REQUESTParâmetro obrigatório ausente ou inválido.
401UNAUTHORIZEDChave ausente, inválida, suspensa, revogada ou expirada.
403UNAUTHORIZEDChave válida, mas sem a permissão necessária.
404NOT_FOUNDEvento não encontrado, ou não pertence ao organizador dono da chave.
405METHOD_NOT_ALLOWEDMétodo HTTP errado (confira GET vs. POST).
429-Limite de requisições por minuto excedido para essa credencial. Aguarde a janela seguinte.
503-Serviço temporariamente desabilitado pela plataforma.
500INTERNAL_ERRORErro inesperado — tente novamente; se persistir, contate o organizador.

GET Consultar Atletas Inscritos

/functions/v1/external-api-timing-athletes — parâmetros de query: event_id (obrigatório), modalidade_id, page, per_page. Requer a permissão read_athletes.

curl -X GET "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes?event_id=SEU_EVENT_ID" \
  -H "X-Api-Key: SUA_CHAVE_AQUI"
const res = await fetch(
  "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes?event_id=SEU_EVENT_ID",
  { headers: { "X-Api-Key": "SUA_CHAVE_AQUI" } }
);
const data = await res.json();
console.log(data.athletes);
interface Athlete {
  registration_id: string;
  bib_number: string | null;
  chip: string | null;
  participant_name: string;
  modality: { id: string; name: string; distance: string };
}

const res = await fetch(
  `https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes?event_id=${eventId}`,
  { headers: { "X-Api-Key": apiKey } }
);
const data: { athletes: Athlete[] } = await res.json();
<?php
$ch = curl_init("https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes?event_id=SEU_EVENT_ID");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-Api-Key: SUA_CHAVE_AQUI"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
print_r($data["athletes"]);
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "SUA_CHAVE_AQUI");

var response = await client.GetAsync(
  "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes?event_id=SEU_EVENT_ID"
);
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);
import requests

response = requests.get(
    "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-athletes",
    params={"event_id": "SEU_EVENT_ID"},
    headers={"X-Api-Key": "SUA_CHAVE_AQUI"},
)
data = response.json()
print(data["athletes"])

Resposta

{
  "meta": {
    "generated_at": "2026-07-20T12:00:00.000Z",
    "event": { "id": "...", "name": "Corrida de Verão" },
    "pagination": { "page": 1, "per_page": 200, "total_records": 1, "total_pages": 1, "has_next_page": false }
  },
  "athletes": [
    {
      "registration_id": "...",
      "registration_number": "INS-0000-0000-2689",
      "bib_number": "1023",
      "chip": "CHIP-ABC123",
      "participant_name": "Antonio Fagundes",
      "document": null,
      "modality": { "id": "...", "name": "Corrida 10km", "distance": "10km" }
    }
  ]
}

POST Enviar Resultados

/functions/v1/external-api-timing-results — corpo com event_id e um array results (até 1.000 itens). Requer a permissão submit_results.

curl -X POST "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results" \
  -H "X-Api-Key: SUA_CHAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "SEU_EVENT_ID",
    "results": [
      {
        "bib_number": "1023",
        "elapsed_time": "00:43:21",
        "gun_time": "00:43:45",
        "position": 15,
        "category_position": 3,
        "status": "FINISHER"
      }
    ]
  }'
const res = await fetch(
  "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results",
  {
    method: "POST",
    headers: {
      "X-Api-Key": "SUA_CHAVE_AQUI",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      event_id: "SEU_EVENT_ID",
      results: [
        { bib_number: "1023", elapsed_time: "00:43:21", position: 15, status: "FINISHER" },
      ],
    }),
  }
);
const data = await res.json();
console.log(data.summary);
interface ResultInput {
  bib_number?: string;
  chip?: string;
  elapsed_time?: string; // "HH:MM:SS"
  gun_time?: string;
  position?: number;
  category_position?: number;
  status?: "FINISHER" | "DNF" | "DNS" | "DSQ";
  penalties?: Record<string, unknown>;
}

async function submitResults(eventId: string, results: ResultInput[]) {
  const res = await fetch("https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results", {
    method: "POST",
    headers: { "X-Api-Key": apiKey, "Content-Type": "application/json" },
    body: JSON.stringify({ event_id: eventId, results }),
  });
  return res.json();
}
<?php
$payload = [
    "event_id" => "SEU_EVENT_ID",
    "results" => [
        ["bib_number" => "1023", "elapsed_time" => "00:43:21", "position" => 15, "status" => "FINISHER"],
    ],
];

$ch = curl_init("https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "X-Api-Key: SUA_CHAVE_AQUI",
    "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "SUA_CHAVE_AQUI");

var payload = new {
    event_id = "SEU_EVENT_ID",
    results = new[] {
        new { bib_number = "1023", elapsed_time = "00:43:21", position = 15, status = "FINISHER" }
    }
};

var content = new StringContent(
    JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"
);
var response = await client.PostAsync(
    "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results", content
);
Console.WriteLine(await response.Content.ReadAsStringAsync());
import requests

payload = {
    "event_id": "SEU_EVENT_ID",
    "results": [
        {"bib_number": "1023", "elapsed_time": "00:43:21", "position": 15, "status": "FINISHER"},
    ],
}

response = requests.post(
    "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-results",
    json=payload,
    headers={"X-Api-Key": "SUA_CHAVE_AQUI"},
)
print(response.json()["summary"])

Resposta

{
  "batch_id": "...",
  "processed_at": "2026-07-20T12:05:00.000Z",
  "summary": { "total": 1, "matched": 1, "pending_review": 0, "errors": 0 },
  "results": [{ "bib_number": "1023", "chip": null, "status": "matched" }]
}

Matching: cada item é casado por bib_number ou chip contra os inscritos do evento. Sem correspondência, o resultado fica marcado como pending_review pro organizador resolver manualmente — nunca é rejeitado nem descartado silenciosamente.

Correções: reenviar o mesmo bib_number no mesmo evento atualiza o resultado existente (idempotente). Envios só com chip (sem peito) sempre criam um novo registro — prefira sempre enviar o peito quando disponível.

POST Atribuir Chips

/functions/v1/external-api-timing-chip-assignments — corpo com event_id e um array assignments (até 1.000 itens). Requer a permissão assign_chips.

curl -X POST "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments" \
  -H "X-Api-Key: SUA_CHAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "SEU_EVENT_ID",
    "assignments": [
      { "bib_number": "1023", "chip": "CHIP-ABC123" }
    ]
  }'
const res = await fetch(
  "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments",
  {
    method: "POST",
    headers: {
      "X-Api-Key": "SUA_CHAVE_AQUI",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      event_id: "SEU_EVENT_ID",
      assignments: [{ bib_number: "1023", chip: "CHIP-ABC123" }],
    }),
  }
);
const data = await res.json();
console.log(data.summary);
interface ChipAssignment {
  bib_number: string;
  chip: string;
}

async function assignChips(eventId: string, assignments: ChipAssignment[]) {
  const res = await fetch("https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments", {
    method: "POST",
    headers: { "X-Api-Key": apiKey, "Content-Type": "application/json" },
    body: JSON.stringify({ event_id: eventId, assignments }),
  });
  return res.json();
}
<?php
$payload = [
    "event_id" => "SEU_EVENT_ID",
    "assignments" => [
        ["bib_number" => "1023", "chip" => "CHIP-ABC123"],
    ],
];

$ch = curl_init("https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "X-Api-Key: SUA_CHAVE_AQUI",
    "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "SUA_CHAVE_AQUI");

var payload = new {
    event_id = "SEU_EVENT_ID",
    assignments = new[] {
        new { bib_number = "1023", chip = "CHIP-ABC123" }
    }
};

var content = new StringContent(
    JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"
);
var response = await client.PostAsync(
    "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments", content
);
Console.WriteLine(await response.Content.ReadAsStringAsync());
import requests

payload = {
    "event_id": "SEU_EVENT_ID",
    "assignments": [
        {"bib_number": "1023", "chip": "CHIP-ABC123"},
    ],
}

response = requests.post(
    "https://auth.arenabrticket.com.br/functions/v1/external-api-timing-chip-assignments",
    json=payload,
    headers={"X-Api-Key": "SUA_CHAVE_AQUI"},
)
print(response.json()["summary"])

Resposta

{
  "batch_id": "...",
  "processed_at": "2026-07-21T12:05:00.000Z",
  "summary": { "total": 1, "updated": 1, "errors": 0 },
  "results": [{ "bib_number": "1023", "chip": "CHIP-ABC123", "status": "updated" }]
}

Matching: cada item é casado só por bib_number — é o único identificador confiável nesse momento, já que o chip ainda não foi associado. Um BIB não encontrado no evento vira erro naquela linha, sem travar o restante do lote.

Quando usar: normalmente antes da prova, na entrega de kit ou na largada, assim que a empresa de cronometragem souber qual chip foi entregue a qual atleta. Depois disso o endpoint de Enviar Resultados passa a conseguir casar também por chip, não só por peito.

Webhooks

Se a credencial tiver uma URL de webhook cadastrada (HTTPS obrigatório), toda vez que um lote de resultados for processado enviamos uma notificação POST:

{
  "event": "results.processed",
  "sent_at": "2026-07-20T12:05:00.000Z",
  "data": {
    "batch_id": "...",
    "summary": { "total": 1, "matched": 1, "pending_review": 0, "errors": 0 },
    "results": [{ "bib_number": "1023", "chip": null, "status": "matched" }]
  }
}

Se a primeira tentativa falhar (endpoint fora do ar, timeout, HTTP fora da faixa 2xx), a notificação entra numa fila de retentativa automática: novas tentativas com intervalo crescente (dobrando a cada vez, até um teto de 24h entre tentativas) por até 7 dias. Depois disso, se ainda não tiver sido entregue, a tentativa é encerrada.

Se as falhas persistirem, avisamos por e-mail o organizador e, se a credencial tiver um e-mail técnico cadastrado, também esse contato — tanto no aviso de falha persistente quanto no aviso final de encerramento após os 7 dias. O organizador pode acompanhar o histórico completo de cada entrega (incluindo o código HTTP e o erro de cada tentativa) e forçar um reenvio manual a qualquer momento pela tela de Auditoria de Webhooks no painel dele.

A resposta da chamada original de envio de resultados nunca é afetada por isso — a entrega do webhook (inclusive a fila de retentativa) é sempre assíncrona em relação à sua requisição.

Changelog

2026-07-21

Novo endpoint: Atribuir Chips (assign_chips) — associa chip/RFID a um número de peito antes da prova.
Webhooks passam a ter retentativa automática por até 7 dias, com aviso por e-mail em caso de falha persistente ou expiração.

2026-07-20

Lançamento: consulta de atletas, envio de resultados, webhooks e gestão de credenciais.