Skip to main content
When a request fails, Membership.io returns an errors array.

Request id

Every handled error includes a request id in meta.request_id and usually in the X-Request-ID response header. Include this id when reporting a bug or support issue.

Validation errors

Validation errors use status 422. When available, source.pointer points to the invalid request body field.
The pointer follows the shape of the body you sent. A request with a JSON:API envelope points at /data/attributes/<field>. A flat body points at /<field>. Check which shape the endpoint takes in the generated reference. An explicit null on a field that cannot be empty is also a 422. See Updating with PATCH for the endpoints where this applies.

Unknown routes and unsupported methods

A path that no endpoint serves returns 404. A path that exists, called with a method it does not support, returns 405. Both use the same errors array as every other error, with Content-Type: application/vnd.api+json, meta.request_id in the body, and the same id in the X-Request-ID header.
These errors have no code. Branch on the HTTP status. A 405 carries an Allow header that lists every method the path supports, sorted and comma separated. For example, PUT /api/v1/teams answers 405 with Allow: GET, POST. HEAD appears only on paths that serve it. A 404 from a missing resource, such as an unknown hub id, is a different error. It comes from the endpoint and is unchanged.
Before 2026-10-05, a 404 or 405 for an unknown path or method returned {"detail": "Not Found"} or {"detail": "Method Not Allowed"}, and Allow could list only some of the supported methods. If your client reads detail at the top level, read errors[0].detail instead. See the breaking changes log.

Common statuses

The mio CLI writes a smaller envelope to stderr. When stderr is not a terminal, each error carries only status, detail and meta.exit_code — not the API’s code, title or meta.request_id (as of v0.23.0). meta.exit_code is safe to branch on in every version (2 usage, 3 auth, 4 not found, 6 rate limited, 7 server). From v0.13.0, status is the status the API actually returned, so it tells 403 from 401 and 409 from 422. Through v0.12.1 it was rebuilt from the exit code, so 403 read "401", 409 and 422 both "400", and 503 "500"; check mio version before branching on it. A failure that never reached the API, such as a missing flag or no API key, has no HTTP status, so its status is derived from the exit code ("400" for 2, "401" for 3).A flag a command declares required is checked before credentials: if it is missing, the command exits 2 with required flag(s) "<flag>" not set and sends nothing, even when no API key is set. v0.23.0 moved about thirty commands to this check. On v0.22.0 and earlier, about half of them, including products create, products prices create, events create and events rsvp set, checked credentials first (for events, the member token), so the same call exited 3 when none was set. The rest already exited 2, with a message starting missing required flag. A requirement worded differently, such as --title is required to create a playlist, can still be checked after the key, so that command exits 3 when no key is set.
From v0.24.0 (the release after v0.23.0), the CLI keeps the API’s error members. When stderr is not a terminal, errors[] has one entry for each error object the API sent, with every member it sent, such as code, title, source and meta.request_id. Not every error carries all of them: a schema-validation 422 has no code. meta.exit_code is added to the error’s meta instead of replacing it. errors[0].status is still the response’s HTTP status, and errors[0].detail is still the CLI’s own message, which can add command context, a hint: line, and the messages of every error in the response, so branch on code where the API sends one, not on detail. With --raw, stderr is the API’s own error document: detail is the API’s text and status is the one in the body. The CLI adds meta.exit_code to each error object, and the HTTP status to any error object that has no status. A failure that never reached the API, or an answer that is not a JSON:API error document, keeps the smaller status, detail, meta.exit_code envelope. On a terminal the CLI still prints a one-line Error: message, and exit codes do not change.

Rate limit errors

When a per-endpoint rate limit is exceeded the response carries code rate_limited and a Retry-After header with the number of seconds until the window resets.
See Rate limits for the full list of limited endpoints and their thresholds.