Skip to content

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 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.