Reference
Error Codes
All API errors use a consistent JSON structure with machine-readable error codes. Use the code field — not the HTTP status — for programmatic error handling.
Error response format
Every error response from the XRNotify API has the same top-level structure:
{
"error": {
"code": "invalid_api_key",
"message": "The API key provided is invalid or has been revoked.",
"status": 401,
"request_id": "req_abc123"
}
}| Field | Description |
|---|---|
code | Snake_case string. Use this for programmatic error handling. |
message | Human-readable description suitable for logging. |
status | Mirrors the HTTP status code of the response. |
request_id | Unique identifier for this request. Include in support tickets. |
details | Optional array of per-field validation errors (validation_error only). |
retry_after | Seconds to wait before retrying (rate_limit_exceeded only). |
400Bad Request
| Code | Description |
|---|---|
bad_request | The request body is malformed or missing required fields. |
validation_error | One or more fields failed validation. Check the details array for per-field messages. |
The validation_error response includes a details array with one entry per failing field:
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"status": 400,
"request_id": "req_xyz789",
"details": [
{ "field": "url", "message": "Must be a valid HTTPS URL" },
{ "field": "event_types", "message": "Must contain at least one event type" }
]
}
}401Unauthorized
| Code | Description |
|---|---|
unauthorized | No API key was provided in the X-XRNotify-Key header. |
invalid_api_key | The key format is invalid, or the key does not exist in our system. |
api_key_expired | The API key has passed its configured expiration date. |
api_key_revoked | The API key was manually revoked via the dashboard or API. |
403Forbidden
| Code | Description |
|---|---|
forbidden | The API key is valid but lacks the required scope for this operation. |
plan_limit_exceeded | Your account has reached a plan limit (webhook count, monthly event volume, etc.). Upgrade your plan to continue. |
404Not Found
| Code | Description |
|---|---|
not_found | The requested resource does not exist. |
webhook_not_found | The webhook ID does not exist, or belongs to a different account. |
delivery_not_found | The delivery ID was not found. IDs are case-sensitive. |
409Conflict
| Code | Description |
|---|---|
conflict | A webhook already exists with the same URL and event types combination. Each (URL, event_types) pair must be unique within an account. |
422Unprocessable Entity
| Code | Description |
|---|---|
invalid_url | The webhook URL failed validation. Must be HTTPS. Localhost and private IP ranges are blocked. |
ssrf_blocked | The URL resolves to a private, loopback, link-local, or reserved IP address. This is a security measure. |
unsupported_event_type | One or more values in the event_types array are not recognised. Check the EventType union for valid values. |
429Too Many Requests
Rate limit errors include a Retry-After HTTP header and a retry_after field in the error body:
| Code | Description |
|---|---|
rate_limit_exceeded | You have exceeded your plan's request rate limit. Wait for the number of seconds indicated in Retry-After before retrying. |
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 23 seconds.",
"status": 429,
"request_id": "req_def456",
"retry_after": 23
}
}See the Rate Limits reference for per-plan limits and best practices for handling 429 responses.
500Internal Server Error
| Code | Description |
|---|---|
internal_error | An unexpected error occurred on our side. Retrying the request may succeed. If the error persists, contact support with the request_id. |
If you encounter a persistent 500 error, contact support at support@xrnotify.io and include the
request_id from the error response to help us diagnose the issue quickly.