Pagination
List endpoints use zero-indexed pagination.
Most list responses include a
pagination wrapper:
GET /locations/{id}/open_hours is not paginated.
Error response shapes
Three error envelope shapes occur on the API. Branch on the HTTP status code first, then read the matching shape below.Envelope A - modern
Returned by/check_ins, /users/me/*, and
/payment_intents/* on 400 validation and 404 resource_not_found
errors. Note this envelope is status-agnostic: the same shape
appears on both 400 and 404, so branch on the HTTP status, not the
envelope.
Invalid API key - 401 (envelope A)
Returned by/restaurants* and /locations* when the X-API-Key
header carries an expired or revoked key. (A missing key returns an
empty-body 401 instead — see below.)
Envelope C - routing layer 404
Returned when the path does not match a known route, such as typos, deprecated routes, or version-prefix mistakes.401 with empty body
A missing credential returns HTTP 401 with an empty body and aWWW-Authenticate: Bearer header — on both route families. Member
routes (/users/me/*, /payment_intents/*) return it when the bearer
is missing, malformed, or expired (the header carries
error="invalid_token" for a bad JWT);
Discovery routes (/restaurants*, /locations*, the /check_ins
venue feed including /check_ins/{id}) return it when the X-API-Key
header is absent. Do not try to parse JSON on these 401s —
read the WWW-Authenticate header. (An invalid API key is the one
exception that returns a JSON body; see above.)
403 insufficient scope
A valid bearer that lacks the scope a member route requires returns HTTP 403 with an empty body and aWWW-Authenticate: Bearer error="insufficient_scope" header. The
route exists; the token simply isn’t authorized for it, distinct
from the empty-body 401 (missing/invalid token) and from a 404
(route doesn’t exist). See Authentication → OAuth scopes.
Filtering GET /check_ins
GET /check_ins is an anonymized venue feed (no member identity) and
accepts optional filters
([restaurant, location, created_after, created_before]), but
a bare GET /check_ins with no filter is valid and returns the full
paginated set. (Earlier launch builds required at least one filter;
that requirement was dropped.)
Timestamp format
created_after and created_before take ISO 8601 strings:
2026-04-01T00:00:00Z. Epoch seconds are rejected with 400:
"Parse attempt failed for value [1715468700]".
Unknown query parameters
Unknown query parameters are silently ignored. A typo in a filter name, such as?restaurants=... instead of ?restaurant=..., is
treated as if the filter was not supplied, so /check_ins returns the
unfiltered set rather than an error, and you get more rows than you
expected rather than a 400.
Error code reference
With the SDK
The published@flynetdev/core SDK
normalizes every envelope above into a single FlynetError. Instead of
branching on HTTP status and matching envelope shapes, branch on
error.kind:
kind is one of unauthorized, insufficient_scope, forbidden,
not_found, validation, rate_limited, server, network, or
unknown. The SDK maps the raw responses for you: the empty-body 401 →
unauthorized, the 403 insufficient_scope → insufficient_scope, a 429
→ rate_limited, Envelope A 400 validation → validation, and a
failed request that never reached the API → network.