Getting started

Authentication

Flyhalf has two credentials, one per surface. Authenticated /v1 endpoints take an API key as a bearer token. Ingest endpoints take an endpoint key plus an HMAC signature instead — covered in Signing events.

API keys

Mint keys in Settings → API keys. A key is a prefix followed by 40 random characters. Only its sha256 hash is stored — the plaintext is shown once, at creation. Copy it then; if you lose it, rotate the key rather than trying to recover it.

Pass the key as a bearer token on every authenticated request:

Authenticated request bash
curl https://api.flyhalf.run/v1/participants \
  -H "Authorization: Bearer fh_live_ab12cd34…"

Live and sandbox keys

The prefix is the mode. A fh_live_ key reads and writes live data; a fh_test_ key reads and writes sandbox data. There's no separate flag — the key you use decides which data dimension you touch. See Sandbox mode.

PrefixModeData
fh_live_liveProduction participants, events, messages.
fh_test_sandboxIsolated test data; comms render but never send.

Abilities

Sensitive routes are gated by abilities — scopes you grant per key. Leaving abilities empty when you create a key gives it full access (*); once you list specific abilities, the key is limited to exactly that list.

AbilityUnlocks
events:writePOST /v1/events
participants:readreading participant records (PII) — list, show, summary, ledger, badges
points:adjustPOST /v1/participants/{id}/points
redemptions:writePOST /v1/redemptions
participants:eraseDELETE /v1/participants/{id}
exports:writePOST /v1/exports (bulk data export)

Grant only what an integration needs — abilities are enforced, so a narrowed key is genuinely limited. A key that only reports leaderboard standings needs none of the above. Catalogue reads (badges, rewards, leaderboards) stay open to any valid key.

What the errors mean

Auth failures use the standard {error:{type,message}} envelope:

  • 401 authentication_error — no bearer token, an unrecognised prefix, or a key that's invalid or revoked. Fix: check the Authorization: Bearer header and that the key hasn't been rotated.
  • 403 permission_error — the key is valid but lacks the ability the route needs. Fix: grant the ability in Settings, or use a key that already has it.

Rotating without downtime

Rotation issues a fresh key and marks the old one revoked immediately. Deploy the new key first, confirm traffic on it, then rotate — a revoked key stops working the moment it's revoked, so overlap the two.

Every response carries an X-Request-Id; quote it when contacting support. Keys are also rate-limited — see Errors & rate limits.