# API e integração do tiro de precisão

> Orbit help — markdown twin of `/precisao/docs/api` for LLM agents (llmstxt.org). Human UI: /precisao/docs/api

A API pública do tiro de precisão permite que o sistema do seu clube crie provas, inscreva atletas e leia o resultado sem ninguém redigitar nada.

## A chave

  É a mesma chave dos outros módulos. O dono do clube cria em Entidade → Chaves de API; a secret aparece uma única vez, na criação. Guarde-a com o cuidado de uma senha.

```
Authorization: Bearer <access_key>.<secret>
```

- Escopo "leitura" permite só GET; "leitura e escrita" libera o resto.

- Tudo é escopado ao seu clube. Não existe requisição que alcance dado de outra entidade.

- Se o módulo não estiver habilitado para o clube, a resposta é 404 — a chave é válida, o recurso é que não existe para você.

## Os três eixos da prova

  Entender isto antes de integrar economiza a maior parte do retrabalho:

- **Modalidade** — o percurso de fogo. No cadastro do clube é reutilizável; dentro da prova é uma CÓPIA. Editar o cadastro depois não muda prova montada — é o que garante que resultado de prova encerrada nunca mude. É a ela que a inscrição aponta e por ela que o ranking agrupa.

- **Série** — o eixo de tempo: a rodada.

- **Turno** — o eixo de espaço: quem atira junto, com limite de vagas.

  **Uma inscrição por modalidade.** O mesmo atleta pode
  ter duas inscrições na mesma prova. Repetir a *mesma* modalidade é
  que devolve conflito.

## Um dia de prova, em quatro chamadas

```
BASE=https://seu-dominio/api/v1
AUTH="Authorization: Bearer $ACCESS.$SECRET"

# 1. criar a prova já com as modalidades do cadastro do clube
curl -s -X POST $BASE/precisao/provas -H "$AUTH" \
  -H 'Content-Type: application/json' \
  -d '{"nome":"1ª Etapa 2026","data_prova":"2026-03-14",
       "modalidades":["<uuid-da-modalidade>"],
       "external_id":"etapa-1-2026"}'

# 2. inscrever um atleta numa modalidade e num turno
curl -s -X POST $BASE/precisao/provas/$prova/inscricoes -H "$AUTH" \
  -H 'Content-Type: application/json' \
  -d '{"prova_modalidade_uuid":"<uuid>","turno_uuid":"<uuid>",
       "nome":"Fulano","email":"fulano@exemplo.com",
       "sexo":"M","data_nascimento":"1988-04-02",
       "external_id":"insc-4471"}'

# 3. abrir a prova e publicar os resultados
curl -s -X PATCH $BASE/precisao/provas/$prova -H "$AUTH" \
  -H 'Content-Type: application/json' \
  -d '{"status":"aberta","resultados_publicos":true}'

# 4. ler o ranking (agrupado por modalidade, com o desempate aberto)
curl -s $BASE/precisao/provas/$prova/resultados -H "$AUTH"
```

## external_id: o id do SEU sistema

  Todo recurso de prova e de inscrição aceita um external_id. Grave nele o id do registro do seu lado e você reencontra tudo sem precisar guardar os nossos uuid:

```
curl -s "$BASE/precisao/provas?external_id=etapa-1-2026" -H "$AUTH"
```

  Ele é único no escopo (clube para provas, prova para inscrições), o que o torna também uma chave de idempotência: reenviar o mesmo POST devolve conflito em vez de criar um duplicado silencioso. Enviar null desvincula; omitir o campo não mexe no que está gravado.

## Score não se escreve por aqui

  A fita nasce assinada no campo — HMAC do juiz, encadeado com o
  PIN do atleta quando a prova exige. Um POST de score criaria resultado sem
  essa cadeia, que é justamente o que o módulo existe para impedir. A API
  **lê** o resultado; quem o produz é o juiz na tela de
  lançamento.

  Pelo mesmo motivo, a API nunca emite nem devolve o PIN de um atleta. Existe só o caminho de apagá-lo, para que o atleta crie outro — o PIN é segredo dele, e um integrador que o conhecesse esvaziaria o sentido da assinatura do atleta.

## O que trava, e por quê

  Estas regras valem igual na tela e na API. Todas devolvem 409:

|
        Tentativa |
        Por quê |

|
        Mudar desempate ou exigência de PIN com score lançado |
        As duas fazem parte do que foi assinado; mudá-las reordenaria o ranking sem ninguém atirar. |

|
        Mudar o percurso da modalidade com score lançado |
        A fita assinada aponta para aqueles alvos. |

|
        Renumerar série com score lançado |
        O desempate por série final lê as séries pela ordem. |

|
        Trocar a modalidade de uma inscrição com score |
        Moveria uma fita assinada para outro percurso de fogo. |

|
        Excluir inscrição com score |
        Sobraria fita assinada sem dono. Use status "cancelada". |

|
        Publicar prova em rascunho |
        A estrutura ainda vai mudar; o link mostraria algo que não se confirma. |

|
        Alterar estrutura de prova encerrada |
        O resultado já circulou como oficial. |

|
        Passar do limite de vagas do turno |
        É o mesmo limite que a tela respeita. |

  Excluir uma prova exige, além disso, o nome exato dela em ?confirmacao= — a mesma zona de perigo da interface.

## Paginação, limites e erros

- Listagens aceitam ?page (a partir de 1) e ?per_page (padrão 25, máximo 100; nas inscrições, padrão 50 e máximo 200) e devolvem page, per_page e total.

- Rate limit por chave, com os headers X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset. Excedido devolve 429 com Retry-After — respeite-o em vez de repetir em loop.

- Erro é sempre JSON no mesmo formato: {"error": {"code": "not_found", "message": "Prova não encontrada."}}. A mensagem é escrita para ser lida por quem está integrando, não só registrada.

## Conferir o que a integração fez

  Cada prova expõe a própria trilha de auditoria, e as ações feitas por chave aparecem nela junto com as feitas na tela. É como se reconcilia o que o robô fez com o que a secretaria fez:

```
curl -s "$BASE/precisao/provas/$prova/auditoria?acao=precisao.prova_update" -H "$AUTH"
```

## Referência interativa

  Explore e teste os endpoints abaixo (Swagger UI). O JSON da especificação OpenAPI está em
  [openapi.json](/precisao/docs/api/openapi.json).

    Este guia cobre as provas de tiro de precisão (fogo circular e bullseye), nos moldes dos regulamentos SCTSL e CBTT.

---

HTML: /precisao/docs/api
Index: /llms.txt · Docs index: /docs/llms.txt
