Skip to content

Errors ​

Errors are JSON. Most carry a message, and validation failures carry a Laravel-shaped errors object.

json
{
  "message": "The given data was invalid.",
  "errors": {
    "ideas.0.headline": ["The ideas.0.headline field is required."]
  }
}

Status codes ​

CodeMeaning
200Success
201Created (register, media upload, member invite)
204Success, no body (DELETE /device-tokens)
401Missing, invalid, expired or revoked token
403Wrong or missing brand, you lack permission, or the account is unverified
404Not found, or belongs to a brand you are not in
409A setup flow expired (passkey registration, 2FA setup)
422Validation failed, or a plan limit was reached
429Throttled
500Server error
503Maintenance mode

Unauthenticated requests ​

A missing, invalid, expired or revoked token returns 401 with this body:

json
{
  "remark": "unauthenticated",
  "status": "error",
  "message": { "error": ["Unauthorized request"] }
}
js
const response = await fetch(url, { headers })

if (response.status === 401) {
  throw new Error('Postlyra token is invalid or revoked.')
}

If you integrated before 2026-08-17

These responses used to carry HTTP 200 with the same body, so clients were told to branch on body.remark === 'unauthenticated' instead of the status code. That check still works (the body is unchanged), but the status is now correct, and response.status === 401 is the check to write. Two sibling responses were corrected in the same change: an unverified account now answers 403 (remark: "unverified") and maintenance mode answers 503 (remark: "maintenance_mode_enabled"), both of which also used to be 200.

404 versus 403 ​

Resources are scoped to a brand. Asking for a draft, idea or contact that belongs to a different brand returns 404, not 403, because from your side that id does not exist. A 403 means the brand itself was rejected, which almost always means a wrong X-Postlyra-Brand value.

Plan limits ​

Hitting a plan allowance returns 422, sometimes with a flag:

json
{ "message": "You have reached your plan limit. Upgrade for more.", "limit_reached": true }

You will meet this on POST /invitations (member seats, where pending invitations count) and POST /media/assets (storage). See Entitlements to read your allowances before you hit them.

The Posts API adds a couple of its own structured 422 codes: preflight_failed (a connector rejected the post, with per-account issues) and tier_denied (your token's access level does not cover what you asked for).

Postlyra, by MAVA Design