Errors

Every error, on every endpoint, uses the same JSON envelope:

Error response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "issues": [ /* present only for VALIDATION_ERROR */ ]
  }
}

Error codes

StatusCodeMeaning
400BAD_REQUESTThe request was malformed in a way not covered by validation - e.g. an empty resolved message body.
400VALIDATION_ERRORThe request body failed schema validation. An `issues` array with field-level detail is included.
401UNAUTHORIZEDMissing, invalid, revoked, or expired credentials - API key or JWT.
402PAYMENT_REQUIREDThe account's SMS balance is insufficient to send.
403FORBIDDENAuthenticated, but missing the required permission or API key scope.
404NOT_FOUNDThe resource doesn't exist, or doesn't belong to your account.
409CONFLICTThe request conflicts with existing data - e.g. an email already registered.
429TOO_MANY_REQUESTSRate limit exceeded. See Rate limits for the specific limits.
503SERVICE_UNAVAILABLEA required upstream integration isn't configured or is temporarily unavailable.
500INTERNAL_ERRORAn unexpected server error. Safe to retry; contact support if it persists.

A note on 401 responses

Login and social-auth failures always return the same generic message - "Invalid email or password" - whether the email is unknown, the password is wrong, or the account only has social login configured. This is deliberate: it avoids letting an attacker use the error message to enumerate which emails have accounts.