Skip to main content

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 a WWW-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 a WWW-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_scopeinsufficient_scope, a 429 → rate_limited, Envelope A 400 validation → validation, and a failed request that never reached the API → network.