Status codes
| Status | Meaning |
|---|---|
200 OK | Request succeeded |
201 Created | Resource created |
202 Accepted | Event accepted for asynchronous delivery (ingest) |
204 No Content | Request succeeded, no response body (delete, logout, purge) |
400 Bad Request | Malformed input — invalid email, missing required field, invalid sort column, unknown enum value |
401 Unauthorized | Missing/invalid/expired token, missing API key, or invalid credentials |
403 Forbidden | Authenticated, but the caller's role or tenant doesn't allow this action |
404 Not Found | Resource doesn't exist, or (for cross-tenant lookups) doesn't exist for this tenant |
409 Conflict | Unique constraint violated — duplicate name, duplicate email, already-used invite |
422 Unprocessable Entity | Request is well-formed but rejected by a business rule (destination inactive, disposable email blocked) |
429 Too Many Requests | Rate limit exceeded |
Error body shape
Most error responses return a plain string body describing the problem, with a Content-Type of
text/plain or application/json depending on the endpoint:
"Email 'admin@example.com' is already in use."
A handful of endpoints (disposable-email rejection on tenant creation) return a small JSON object instead:
{
"error": "disposable_email",
"message": "Please use a permanent email address."
}
204 No Content and 202/201-with-body responses never include an error body — check the
status code first.
Multi-tenancy and 403 vs 404
HookSentry never leaks whether a resource exists outside the caller's tenant. As a rule:
- Looking up a resource by an ID nested under another resource you already proved you own
(e.g. a sender under a destination you fetched) returns
403 Forbiddenif it belongs to another tenant. - Looking up a top-level ID directly generally returns
404 Not Foundfirst if the record doesn't exist at all, then403 Forbiddenif it exists but belongs to another tenant.
Each endpoint in this reference states exactly which codes it returns.
Rate limiting
| Endpoint | Limit |
|---|---|
POST /api/v1/auth/login | 10 failed attempts per IP or per email / 5 minutes |
POST /api/v1/tenants | 5 requests per IP / hour |
POST /api/v1/invites/{token}/register | 10 requests per IP / hour |
Rate-limited responses return 429 Too Many Requests with no Retry-After header — back off and
retry after the window described in the endpoint's documentation.
Cloud-only anti-abuse checks:
On HookSentry Cloud, tenant creation additionally blocks disposable email domains
(422 Unprocessable Entity) and applies a device-fingerprint/Cloudflare Turnstile check that
can also return 429. These checks are inert on self-hosted deployments. See
Tenants for the request fields involved.