Match API reference

The matches API is intentionally open: any app can create a match and share its public page with players and families, with or without a connected club account. This page documents the write-protection and club-sync contract on top of that open API. All of it is optional and backward-compatible — existing integrations keep working exactly as before.

Creating a match

POST /api/matches

Body is the match JSON payload. id is optional — a UUID is generated if omitted.

Response 201
{
  "id": "abc123",
  "url": "https://tactiboard.es/match/abc123"
}

To protect a match from being overwritten by someone else, generate a random token on the device before the first request and send it as X-Write-Token on the POST that creates it:

POST /api/matches
X-Write-Token: 9f2c1a...e7
Content-Type: application/json

{ "id": "abc123", ... }
Generate and store the token before sending the request — don't wait for the response. This makes retries safe: if the request times out or the response is lost, resending the same POST with the same id and the same X-Write-Token simply upserts the match, instead of locking you out with a 403. Apps that don't send this header are unaffected: the match is created normally and behaves like a legacy match (see below).

Updating or re-sending a match

PUT /api/matches/:id
POST /api/matches (same id as an existing match)

Both behave the same way once a match exists: the stored data is replaced with the new payload (upsert).

Match stateHeader requiredResult
Doesn't exist yet (PUT-create)Created, unprotected — same as today
Exists, created without X-Write-Token (legacy)Updated normally — no protection
Exists, created with X-Write-TokenX-Write-Token: <token>Updated only if the token matches; otherwise 403
PUT /api/matches/abc123
X-Write-Token: 9f2c1a...e7
Content-Type: application/json

{ "id": "abc123", ... }
A match is protected only if its creator sent X-Write-Token on the POST that created it. Matches created without that header — including via the older PUT-create flow — remain unprotected: anyone with the id can still update them, exactly as before.

Club / CRM sync

If the payload includes clubId and teamId (plus optionally season, category, competition, playerStats, actions), the API syncs this data into the connected club's dashboard — players, stats, and match actions.

To authenticate this sync, send your club's app key (Dashboard → Connect → Generate App Key):

X-App-Key: <your club's app key>
Without a valid X-App-Key, the match itself is still saved and its public page works as normal. The CRM sync may be skipped, in which case the response includes "crmSync": "skipped_auth". Until this is fully enforced, unauthenticated syncs are logged but still processed — check Dashboard → Connect → Sync status for details.

Backward compatibility