# Public HTTP surface for agents

> There is no AI API. Read `/llms.txt`, this Markdown set, and the `#ai-readable-summary` block on HTML pages. JSON endpoints exist for the same imported matches the site shows.

Resumo: não existe `/api/ai/*`. O caminho certo para um agente é o Markdown e o bloco AI-readable. As APIs JSON repetem a prova importada e podem exigir o token público que o navegador recebe. Não contorne esse token.

Base: https://scoring.services/
This file: https://scoring.services/docs/api.md

## Prefer this order

1. `/llms.txt` for the map, or `/llms-full.txt` when the context window can hold the product model.
2. The topic file (`/docs/editor.md`, `/docs/scoring.md`, and the others).
3. The Markdown of a specific match or athlete, listed below. It carries the same text as the HTML page's `#ai-readable-summary`.
4. The HTML page's `#ai-readable-summary` for a stage. That block is server-rendered plain text.
5. JSON only when a number is missing from those texts.

Do not invent endpoints. Paths below are the public analysis surface. Account, billing, training-session and video-render APIs are private to the signed-in user and are out of scope here.

## Data in Markdown

No token. Short public cache. Each file links its HTML page in `Link: <...>; rel="canonical"`.

- `GET /match/{id}/{year}.md`, `GET /overall/{id}/{year}.md`, `GET /combinado/{id}/{year}.md`: what each page offers, plus counts on the hub. Standings stay in the JSON below.
- `GET /athlete/{id}/{year}/{athlete_id}.md`: one result of one shooter, stage by stage.
- `GET /list.md`: recent public matches.
- `GET /calendar.md`: upcoming events.
- `GET /elo.md`: top shooters per division, with the method. Computed from fully public matches only.
- `GET /clubes-tiro.md`: club counts by state. `?uf=SP` lists name and city. Phone and email are not included.
- `GET /coaching.md`, `GET /tools.md`: public tracks and tools.

A private, hidden or login-only match answers `404` here, with no login page. Sending `Accept: text/markdown` to a page that has one of these files answers `303` to it; a page without one answers HTML as usual.

## Token

Browser calls to the analysis JSON send a header `X-SS-Api-Token` that the HTML page issues for that visitor. A client with no page session can receive `401` with `{"error":"api_token"}` or `429` when rate limited. That control is intentional.

Do not scrape tokens, do not reuse another visitor's token, and do not look for a service credential. If the JSON is refused, cite the HTML AI-readable block or ask the user to open the page. There is nothing useful to bypass.

## Match catalog

- `GET /api/matches` lists imported matches for the catalog.
- `GET /api/last_update/{match_id}/{year}` is the freshness stamp for one match.
- `GET /api/match_status/{match_id}/{year}` and `GET /api/match_source/{match_id}/{year}` describe processing state and where the file came from.
- `GET /api/check_match_exists` checks whether a source is already imported.

Ids are the portal's import ids, not federation ids.

## One match

Replace `{id}` and `{year}`. Division and stage names must be URL-encoded.

- `GET /api/overall/{id}/{year}` place and percentage.
- `GET /api/combinado/{id}/{year}` stage audit.
- `GET /api/athletes/{id}/{year}` shooters.
- `GET /api/divisions/{id}/{year}` division names for the editor.
- `GET /api/division_athletes/{id}/{year}/{division}` scorecards for one division (what the Match Editor loads).
- `GET /api/stage_athletes/{id}/{year}/{stage}` one stage, many shooters.
- `GET /api/stage_by_division/{id}/{year}` stages grouped for navigation.
- `GET /api/best_stages/{id}/{year}` stages that weigh the most.
- `GET /api/heatmaps/stages/{id}/{year}/{division}` and `GET /api/heatmaps/{id}/{year}/{division}/{stage}` heatmap grids.
- `GET /api/athlete_details/{id}/{year}/{athlete_id}` one shooter.
- `GET /api/stage_details/{id}/{year}/{athlete_id}/{stage}` one shooter on one stage.
- `GET /api/competitors/{id}/{year}/{athlete_id}` peers used in comparisons.
- `GET /api/graph` comparison series for the charts. Query parameters select match, year and athletes; omit the call if the AI-readable block is enough.
- `GET /api/probabilities/{id}/{year}` descriptive spreads. They are not a prediction that a shooter "will win" the next match.
- `GET /api/probabilities/categories/{id}/{year}?division={name}` the categories available for those spreads in one division. `division` is required. Only real-time data answers; elsewhere it is `404`.

## Athletes across matches

- `GET /api/search_athlete?q={text}` finds names containing every word of `q`, in any order. Rate limited per IP; `429` carries `Retry-After`.
  - `q` shorter than 2 characters answers `[]`, an empty array.
  - Otherwise it answers an object: `{"athletes": ["..."], "unique_count": 1, "total_variations": 2, "grouped": true}`. At most 50 names. `%` and `_` in `q` are plain characters.
- `GET /api/athlete_matches/{athlete_name}` matches of one shooter. Needs the page token above.
  - `match=exact` is the default: same words in any order, ignoring case, accents and the `(DQ)` marker. "Jose Silva" does not return "Jose Carlos Silva".
  - `match=contains` matches every word by substring. `match=auto` tries exact, then contains.
  - The answer has `matches`, `match_mode`, and `truncated`, which is `true` when the 500-row limit cut the list.
- `GET /elo/api` ELO table. Filters: division, modality, source. This is not `/api/overall`.
- `GET /elo/api/history?division={name}&modality={id}&athlete_key={key}` the rating timeline of one shooter in one division. `athlete_key` comes from the `/elo/api` ranking.

Names are display strings from the import. Two people can share a name. Do not merge them without the match context.

## Import

`POST /api/psc/*`, `POST /api/winmss/*` and `POST /api/import_match` upload files or start an import. They are user actions, not read APIs. Do not POST a match file on behalf of a user unless that user asked to import it in the product. Reading `/docs/import.md` is the agent path.

## Errors

- `401` `api_token`: missing or expired public token. Stop. Use HTML or Markdown.
- `403`: wrong host or a tool that does not apply (the editor rejects TDT/IDPA).
- `404`: no such match, stage or athlete in the import.
- `429`: rate limit. Back off. Do not rotate tokens.

`GET /health` only reports process health. It is not a data endpoint.
