Web Server API
The Rondo Sync web server (Fastify on port 3000) exposes a programmatic API for triggering syncs from external systems like Rondo Club.
Authentication
Section titled “Authentication”API endpoints use a shared API key, separate from the dashboard session auth.
Header: X-Sync-API-Key: {key}
The key is set via the SYNC_API_KEY environment variable on the sync server. Generate one with:
openssl rand -base64 32| Scenario | Status | Response |
|---|---|---|
| Missing header | 401 | { ok: false, error: "Missing X-Sync-API-Key header" } |
| Invalid key | 403 | { ok: false, error: "Invalid API key" } |
| Key not configured on server | 503 | { ok: false, error: "API key not configured on server" } |
Health endpoints
Section titled “Health endpoints”GET /health and GET /health/sync are public and require neither API-key nor dashboard-session authentication.
| Route | Purpose | Response |
|---|---|---|
/health | Dashboard server availability | HTTP 200, { "status": "ok", "timestamp": "…" } |
/health/sync | Aggregate completed-run coverage of configured schedules | HTTP 200, { "status": "ok" }, or HTTP 503, { "status": "overdue" } |
Sync health allows one hour of grace for interval jobs and four hours for other jobs. The latest completed run must have started at or after the due slot at that cutoff. Both success and partial count; a running job alone does not. Disabled/manual schedules and changed schedules before their first eligible slot are exempt. The response contains no pipeline or member details. See Error monitoring for GlitchTip reporting and Twelve’s schedule handling.
Endpoints
Section titled “Endpoints”POST /api/sync/individual
Section titled “POST /api/sync/individual”Syncs a single member from Sportlink to Rondo Club. Launches a Playwright browser session to fetch fresh data from Sportlink (person details, parents, functions, committees, and free fields), then syncs the member to WordPress. First, a targeted SearchMembers query looks up the exact KNVB ID with union team data enabled; an absent active match is retried with the inactive-member filter. The partial /general response is overlaid on this fresh result, so KernelGameActivities and AgeClassDescription no longer wait for the next bulk import. A missing or ambiguous member, an incomplete activity/age-class response, or a failed search aborts before person updates; an explicitly empty activity clears the previous value. Search navigation retries a stuck panel once and can refresh an expired session. Fresh parent data overrides the member’s record from the latest full snapshot before parent deduplication, so a newly added parent is created or updated immediately and linked to the child in both directions. Other snapshot members remain available so shared parents retain links to siblings.
Rate limit: 5 requests per minute.
Concurrency: Only one individual sync can run at a time (Sportlink uses TOTP, so only one Playwright session is possible).
Request
Section titled “Request”{ "knvb_id": "12345678"}| Field | Type | Required | Description |
|---|---|---|---|
knvb_id | string | Yes | KNVB member ID (4-10 alphanumeric characters, e.g. SYQG014) |
Responses
Section titled “Responses”Success (200)
{ "ok": true, "knvb_id": "12345678", "action": "updated", "rondo_club_id": 456}action value | Meaning |
|---|---|
created | New person created in Rondo Club |
updated | Existing person updated |
skipped | No changes detected (member already up to date) |
Errors
| Status | Condition | Response |
|---|---|---|
| 400 | Invalid or missing knvb_id | { ok: false, error: "Invalid knvb_id: must be 4-10 alphanumeric characters" } |
| 422 | Member not found in Sportlink data | { ok: false, error: "Member not found" } |
| 429 | Another sync already running | { ok: false, error: "Another individual sync is already in progress. Try again in a minute." } |
| 500 | Internal error | { ok: false, error: "Internal sync error" } |
Example
Section titled “Example”curl -X POST https://sync.example.com/api/sync/individual \ -H 'Content-Type: application/json' \ -H 'X-Sync-API-Key: your-api-key-here' \ -d '{"knvb_id":"12345678"}'Configuration
Section titled “Configuration”Add to the sync server’s .env:
SYNC_API_KEY=your-generated-key-hereAfter adding the key, restart the web server:
systemctl restart rondo-sync-webThe same key must be configured in Rondo Club’s .env so WordPress can call this endpoint.
Dashboard pipeline routes
Section titled “Dashboard pipeline routes”Pipeline history and manual starts use dashboard session authentication, not
X-Sync-API-Key. Since Rondo Sync 0.16.1, the overview, route validation and
display names share PIPELINE_CONFIG from lib/dashboard-queries.js, including
Twelve. Unknown pipeline names return 404; unauthenticated requests redirect to
/login.
GET /pipeline/:name: run history, 20 runs per page.pagemust be a positive integer or the route returns 400. Twelve history is/pipeline/twelve.POST /api/pipeline/:name/start: launchesscripts/sync.shfor that pipeline. Twelve launches with['twelve'], so this is a manual run outside the scheduled window checks. Success returns{ ok: true, pipeline: "twelve" }; an already running pipeline returns 409. Immediate launch failures return 500 and retain a per-launch log underlogs/dashboard-launch/.
A successful launch response does not establish that the import completed; check the tracked run and its outcome. See Twelve pipeline for reconciliation and checkpoint rules.
Dashboard schedule forms
Section titled “Dashboard schedule forms”GET /beheer/schedules and POST /beheer/schedules use dashboard session authentication. The POST requires the form’s session CSRF token and current configuration revision: missing/wrong CSRF returns 403, validation returns 400 with the draft preserved, and a stale revision returns 409. A valid save redirects to /beheer/schedules?saved=1. This form does not use X-Sync-API-Key. See Editable Sync Schedules for fields, allowed cadences and dispatcher behavior.