For developers

The Flock Run API

Everything a partner needs to read a Flock Run start list, register somebody at the door, and check runners in. One key, five endpoints, no accounts to create.

Getting a key

Keys are issued by hand, one per company, by emailing run@wakasflockrun.com. Every key is scoped, which means it can do exactly what it was given permission to do and nothing else, and most keys are locked to a single event. Send the key on every request:

Authorization: Bearer flk_live_xxxxxxxxxxxxxxxxxxxxxxxx

The base URL is https://stmrnytvrmnsolronhfl.supabase.co/functions/v1/api. Everything answers JSON. A successful call returns ok: true, a failed one returns ok: false with an error string you can branch on and usually a hint written for a human.

We store a hash of your key, not the key

Which means we cannot read it back to you. If you lose it, we issue a new one and turn the old one off. Treat it like a password: server side only, never in a mobile app or a web page where somebody can read it.

What a key is allowed to do

ScopeWhat it opens up
events:readThe schedule, the times, the venue, how many are registered and how many have checked in.
registrations:readThe start list: first name, last initial, crew, status, whether they have arrived. No way to contact anybody.
registrations:read_contactAdds full name, email and phone. Issued only to companies working the event with us under contract, because runners are told their details are never sold.
registrations:writeRegister a runner, including walk ups at the venue.
checkin:writeMark a runner as arrived.

Call GET /me to see exactly what your own key carries. Ask for a scope you do not have and you get a 403 with missing_scope.

Read the event

curl https://stmrnytvrmnsolronhfl.supabase.co/functions/v1/api/events \
  -H "Authorization: Bearer $FLOCK_KEY"
{
  "ok": true,
  "events": [
    {
      "slug": "las-vegas",
      "name": "Las Vegas Flock Run",
      "city": "Las Vegas",
      "state": "NV",
      "date": "2026-09-25",
      "start_time": "08:00:00",
      "end_time": "10:00:00",
      "venue": null,
      "capacity": null,
      "status": "registration_open",
      "registered": 0,
      "checked_in": 0,
      "spots_left": null
    }
  ]
}

Read the start list

GET /registrations?event=las-vegas. Paginate with limit (up to 500) and offset. Pass since as an ISO timestamp to pull only what is new, which is how you keep a copy in sync without refetching everything.

curl "https://stmrnytvrmnsolronhfl.supabase.co/functions/v1/api/registrations?event=las-vegas&since=2026-09-01T00:00:00Z&limit=200" \
  -H "Authorization: Bearer $FLOCK_KEY"

Each row carries registration_id, first_name, last_initial, crew, status, source, registered_at, checked_in, checked_in_at, waiver_signed and waiver_version. If your key carries the contact scope you also get last_name, email and phone. The response tells you which shape you got in contact_details_included, so you never have to guess.

Register somebody at the venue

This is the walk up. Somebody turns up on the day, you take their details on a tablet, you show them the waiver, and this call puts them on the list and checks them in at the same time.

curl -X POST https://stmrnytvrmnsolronhfl.supabase.co/functions/v1/api/registrations \
  -H "Authorization: Bearer $FLOCK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "las-vegas",
    "first_name": "Jordan",
    "last_name": "Reyes",
    "email": "jordan@example.com",
    "phone": "(702) 555-0143",
    "crew": "The Pack",
    "emergency_name": "Alex Reyes",
    "emergency_phone": "(702) 555-0144",
    "waiver_accepted": true,
    "waiver_version": "NV-2026-09-04",
    "walk_up": true
  }'
The waiver is not optional

Send waiver_accepted: true and the waiver_version you actually showed on screen, or the call is refused. Show the runner the same text that is on the registration page for that state. We store when they agreed and which version, because a waiver nobody can prove was shown is not a waiver.

Crew is one of The Front, The Pack or The Stroll, and it is optional. Send walk_up: true for somebody standing in front of you, which also checks them in. Send check_in: false and leave off walk_up if you are registering ahead of the day.

If that email is already registered you get already_registered: true with the existing registration_id instead of a duplicate. If the event is at capacity the runner is put on the waitlist rather than turned away, and the response says so in status.

Check somebody in

By id if you scanned something, by email if you are looking them up at the door.

curl -X POST https://stmrnytvrmnsolronhfl.supabase.co/functions/v1/api/checkin \
  -H "Authorization: Bearer $FLOCK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"las-vegas","email":"jordan@example.com"}'
{
  "ok": true,
  "registration_id": "…",
  "name": "Jordan R",
  "crew": "The Pack",
  "status": "registered",
  "waiver_signed": true,
  "already_checked_in": false,
  "checked_in_at": "2026-09-25T15:02:11Z"
}

Checking in twice is safe. The second call returns already_checked_in: true and leaves the original time alone, so a double scan at a busy door does not corrupt anything.

Errors

CodeMeans
401missing_api_key or invalid_api_key. The key is absent, wrong, or has been turned off.
403missing_scope, or event_not_allowed if your key is locked to a different event.
400Something in the request is wrong. waiver_not_accepted, email_invalid, crew_invalid, event_required.
404unknown_event, not_registered, or an endpoint that does not exist.
429Too many calls. Every key has a per minute limit, 120 by default. Back off and retry.

What we log

Every call is written to our audit trail with your partner name, the endpoint, how many rows came back, and whether contact details were included. This is not a threat, it is how we can tell a runner exactly who has seen their information if they ever ask. Build like the log is public, because to the runner it effectively is.

Rules of the road

Questions, or you need a key: run@wakasflockrun.com.