Errors
Errors return a JSON body with a stable code, a human-readable message, and a request ID for support. Never parse the message; branch on the code.
{ "error": { "code": "SIGNATURE_INVALID", "message": "The request signature did not match the endpoint secret.", "requestId": "req_8f2c9a" }}| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR |
The body or query parameters are malformed. |
| 400 | OUTBOUND_REQUEST_BLOCKED |
The destination URL is not allowed (for example, a private address). |
| 400 | REDIRECT_BLOCKED |
The destination redirected somewhere unsafe. |
| 401 | AUTHENTICATION_FAILED |
Missing or invalid API key or session. |
| 401 | SIGNATURE_INVALID |
The request signature did not match the endpoint secret. |
| 403 | AUTHORIZATION_DENIED |
The key lacks the required scope or workspace access. |
| 404 | ENDPOINT_NOT_FOUND |
The endpoint does not exist or was deleted. |
| 404 | DELIVERY_NOT_FOUND |
The delivery does not exist. |
| 409 | IDEMPOTENCY_CONFLICT |
The idempotency key was reused with a different request. |
| 409 | SLUG_TAKEN |
The workspace slug is already in use. |
| 409 | LAST_WORKSPACE |
A workspace cannot be deleted while it is the last one. |
| 409 | DELIVERY_NOT_RETRYABLE |
The delivery is in a state that cannot be retried. |
| 409 | DELIVERY_NOT_CANCELLABLE |
The delivery is already finished or in flight. |
| 409 | PLAN_LIMIT_EXCEEDED |
Creating this endpoint, destination, or member exceeds the plan. |
| 410 | ENDPOINT_DISABLED |
The endpoint is paused or archived. |
| 413 | PAYLOAD_TOO_LARGE |
The request body exceeds the endpoint limit. |
| 429 | RATE_LIMIT_EXCEEDED |
Too many requests. Honor Retry-After. |
| 429 | quota_exceeded |
The workspace used its plan quota on /in/*. See below. |
| 500 | INTERNAL_ERROR |
Unexpected server error; include the request ID when reporting. |
| 500 | DELIVERY_STUCK |
A delivery did not make progress and needs a manual retry. |
| 500 | OUTBOX_EXHAUSTED |
Fan-out retries were exhausted. |
Ingest responses
Section titled “Ingest responses”Ingest requests (/in/*) use the same envelope for rejections. A 202 means the event was stored; a 4xx means it was rejected and no event exists.
The plan quota uses a dedicated 429 body. It is not wrapped in error.code, so branch on the top-level error string:
{ "error": "quota_exceeded", "plan": "free", "upgrade_url": "https://hooks.events/dashboard/billing", "message": "Free plan request limit reached. Upgrade to Pro to keep receiving events."}X-Quota-Limit and X-Quota-Remaining describe the allowance. Free lifetime credit never resets, so this response has no Retry-After. See Rate limits for billing rules and for the per-IP 429.