Skip to main content
Flynet uses two credentials for partner integrations. Which one you use depends on which routes you call.

Pick by route

The two credentials are not interchangeable. Sending an OAuth bearer to /restaurants (or the /check_ins venue feed) returns an empty-body 401 — no API key present. Sending an API key to /users/me/check_ins is rejected the same way: a member’s own history always needs their token. Both return 401 with the reason in the WWW-Authenticate header, not JSON.
From the kitchen - Reach for the API key first when you are building app-level discovery, such as a map of nearby restaurants, a catalog page, hours, or locations. Reach for OAuth when you are doing anything with a member’s wallets, tags, check-ins, or payments.

What each credential authorizes

The API key is issued per partner, per environment, and grants app-level read access to Discovery data. The OAuth access token acts on behalf of the member who completed the flow. Member routes resolve the subject from the token’s sub claim, so /users/me/* returns that member’s data, never another member’s.

OAuth scopes

Member routes are gated by scope. A token without the required scope returns 403 with WWW-Authenticate: Bearer error="insufficient_scope": the route still exists; the token simply isn’t authorized for it. These are OAuth (JWT) scopes, requested at /authorize. The /check_ins venue feed and /check_ins/{id} are API-key routes gated by the separate read:checkins scope minted on the key — not an OAuth scope. See API keys. The member who completed the OAuth flow is the token’s sub claim. See Identify the authenticated member.

Credential lifetimes

How credentials reach you

After your app is approved, Blackbird sends your credentials by email — a set scoped to staging. Production credentials are issued separately when you’re approved for live traffic; see Environments. If anything is missing or wrong, reply to the onboarding email; fixes are fast pre-launch. See OAuth for the full OAuth flow and API keys for server-to-server use.
Chef’s warning - client_secret, API keys, and flynet_merchant_id are secrets. Never expose them in client-side code, public repos, or screenshots. client_id and registered redirect URIs are public.

401 behavior

The 401 envelope is determined by the route family’s gating filter, not by which credential you happened to send.
  • OAuth-protected routes (/users/me/*, /payment_intents/*) return HTTP 401 with an empty body on missing, malformed, or expired bearer. This also happens if you accidentally send an API key here: the OAuth filter doesn’t see a bearer and rejects. Do not try to parse JSON.
  • API-key routes (/restaurants*, /locations*, the /check_ins venue feed, /check_ins/{id}) return the same empty-body 401 when the API key is missing. An invalid (expired or revoked) key is the one case that returns a JSON body — envelope A with code: "invalid_api_key". See Pagination + errors for the exact shapes.
See Debugging for every observed error_code mapped to cause and fix.

Choose your on-ramp

Hello API Key

One curl, one API key, confirm Discovery works.

Hello OAuth

One curl, one access token, confirm a member route works.