---
description: Read SETTO tournament data (tournaments, divisions/categories, teams, rounds, games, standings) for one organization through the read-only SETTO External API with an org API token.
globs: []
alwaysApply: false
---

# SETTO External API

Read-only HTTP API for one SETTO organization's padel, tennis and pickleball
tournaments. Apply this rule when the task mentions SETTO, a `setto_live_…` /
`setto_test_…` token, or a tournament page under `https://www.setto.io/t/<slug>`.

There is **no write access**. If the task asks to create a tournament, submit a
score or register a player, say the API cannot do it.

## Auth

```
Authorization: Bearer $SETTO_API_TOKEN
```

Read the token from the `SETTO_API_TOKEN` environment variable. Never print,
log or commit it, and never ship it to a browser. Tokens belong to an
organization and can only read that organization's tournaments; personal
tournaments are invisible.

## Base URL

`https://api-production-ea80.up.railway.app/v1`

## Endpoints (all `GET`, ids are UUID v4 `idx`)

| Endpoint | Query |
| --- | --- |
| `/v1/external/me` | — |
| `/v1/external/tournaments` | `status`, `sport`, `populate`, `limit` (1–100, default 25), `offset` |
| `/v1/external/tournaments/{idx}` | `populate` (default `club,divisions`) |
| `/v1/external/tournaments/{idx}/divisions` | `populate` (default `rounds`) |
| `/v1/external/tournaments/{idx}/teams` | `populate` (default `playerProfiles,division`), `division` |
| `/v1/external/rounds/{idx}` | `populate` |

```bash
curl -s -G https://api-production-ea80.up.railway.app/v1/external/tournaments \
  -H "Authorization: Bearer $SETTO_API_TOKEN" \
  -d status=IN_PROGRESS -d limit=25
```

`status`: `DRAFT`, `UPCOMING`, `IN_PROGRESS`, `COMPLETED`, `ARCHIVED`.
`sport`: `PADEL`, `TENNIS`, `PICKLEBALL`.

## Populate

Comma-separated; a relation appears only when requested or in the default list;
nested paths pull their parents in; an unknown value is a `400` whose message
enumerates the legal values. Allow-lists: <https://docs.setto.io/docs/concepts/populate.md>

## Pagination

Only `/tournaments` paginates (`limit`, `offset`, `meta.total`); the rest return the whole collection.

## Errors — `{ statusCode, code, message }`, branch on `code`

- `400` `INVALID_ID` (bad path uuid, never a `404`) or validation (`message` is
  a string array) → fix the request, never retry.
- `401` `MISSING_API_TOKEN` / `INVALID_API_TOKEN` / `EXPIRED_API_TOKEN` → ask
  the user for a new token; do not retry.
- `403` `INSUFFICIENT_SCOPE` → stop and report.
- `404` `TOURNAMENT_NOT_FOUND` / `ROUND_NOT_FOUND` → wrong id, or the resource
  belongs to another organization; the API never says which.
- `429` `RATE_LIMITED` → wait `Retry-After`, retry once. Limits: 120 req / 60 s per token and 300 req / 60 s per IP; a `304` from `If-None-Match` still spends one request.
- `5xx` → exponential backoff with jitter.

## Gotchas

- Hidden divisions and rounds come back with `isVisible: false`, not dropped.
- `DRAFT` and `ARCHIVED` tournaments are included unless you filter on `status`.
- No emails, phones or payment data are ever returned; players carry only
  `idx`, `firstName`, `lastName`, `avatar`.
- Standings `rank` is the stored rank; the public site layers head-to-head
  tiebreaks on top, so a rendered table can differ.

## More

Docs <https://docs.setto.io> · OpenAPI <https://docs.setto.io/openapi.json> ·
MCP <https://docs.setto.io/mcp> (prefer its tools when connected) · full skill <https://docs.setto.io/skills/setto-api/SKILL.md>
