Errors
All error responses share a common JSON envelope:
{ "message": "Human-readable, localized message.", "code": "optional_code" }
message is localized per the active locale (see Localization).
code is an optional machine-readable identifier.
Validation errors (422)
Validation failures add a field-keyed errors map (Laravel-compatible):
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email field is required."],
"password": ["The password field must be at least 8 characters."]
}
}
Status codes
| Status | Meaning |
|---|---|
400 | Malformed request |
401 | Not authenticated |
403 | Authenticated but not permitted (RBAC / policy / cross-tenant) |
404 | Not found, or not visible to the current tenant |
419 | CSRF token missing/expired (SPA flow) — refetch /sanctum/csrf-cookie |
422 | Validation failed (see above) |
429 | Rate limit exceeded (see Rate limiting) |
5xx | Server error (reported to Sentry) |
Tenant isolation and 404
Cross-tenant access does not leak existence: a resource that belongs to another
tenant is invisible under Row Level Security and returns 404, not 403.
Request id (debugging)
Every API response carries an X-Request-Id header. The value is also attached
to every structured log line for that request (request_id), so quoting it lets
support correlate your request with the server logs.
You may supply your own correlation id by sending an X-Request-Id request
header (alphanumerics, dot, underscore and hyphen; capped at 128 characters);
otherwise the server generates one. When reporting a problem, include the
X-Request-Id from the failing response.
Security headers
API responses include baseline security headers (X-Content-Type-Options,
X-Frame-Options, Referrer-Policy, a conservative Content-Security-Policy,
and Strict-Transport-Security over HTTPS). These are defensive and do not
affect normal client usage.