Errors
RFC 7807 problem details, common status codes, and how to interpret error bodies.
All non-2xx responses are served as application/problem+json and follow RFC 7807 Problem Details for HTTP APIs.
Envelope
{
"type": "https://api.benjipays.com/problems/<slug>",
"title": "<short title>",
"status": 401,
"detail": "<human-readable explanation>",
"instance": "/v2/<path>",
"request_id": "5e3e7c8e-1a04-4f7e-9c1f-9e0c5b9b1a23",
"correlation_id": "trace-abc-123"
}| Field | Type | Description |
|---|---|---|
type | string (URI) | Identifies the problem class. Stable per error category. |
title | string | Short, human-readable summary of the class. |
status | integer | HTTP status code. |
detail | string | Explanation specific to this occurrence. |
instance | string (URI) | URI identifying this specific failure. |
request_id | string | Echoed X-Request-ID — included whenever the middleware has set it. |
correlation_id | string | Echoed X-Correlation-ID — included whenever the middleware has set it. |
Both request_id and correlation_id are always set by the middleware, so a single ID will identify a failure across your logs, the API's logs, and any support ticket. See Request Tracing.
Common statuses
| Status | When |
|---|---|
400 Bad Request | Validation error. May include an errors[] extension listing the offending fields. |
401 Unauthorized | Missing, invalid, or expired credentials; or organization API access is not active (disabled or missing). See Authentication. |
403 Forbidden | Wrong actor type, missing OAuth scope, insufficient mapping permission for the API key owner, or free trial ended without billing enabled. |
404 Not Found | Unknown route or tenant-scoped resource not visible to the caller. |
409 Conflict | Idempotency-key body conflict, or a domain conflict. See Idempotency. |
429 Too Many Requests | Rate limit exceeded, or an in-flight idempotent request with the same key. Includes Retry-After. See Rate Limits. |
500 Internal Server Error | Unexpected failure. Safe to retry idempotent requests. |
Examples
400 with field-level details
{
"type": "https://api.benjipays.com/problems/validation",
"title": "Validation error",
"status": 400,
"detail": "Request body failed validation",
"instance": "/v2/partners",
"errors": [
{ "path": ["body", "externalId"], "message": "Required" }
],
"request_id": "9c6d…",
"correlation_id": "trace-abc-123"
}401 unauthorized
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json{
"type": "https://api.benjipays.com/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid or missing authentication token",
"instance": "/v2/whoami"
}403 forbidden — wrong actor
{
"type": "https://api.benjipays.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Access denied. Required actor type: partner",
"instance": "/v2/partners/accounts"
}403 forbidden — missing scope
{
"type": "https://api.benjipays.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Insufficient scope",
"instance": "/v2/partners/accounts"
}403 forbidden — free trial ended without billing
Organization-scoped merchant routes (/v2/invoices, /v2/gateways, etc.) return this when the organization's free trial has ended and billing is not enabled:
{
"type": "https://api.benjipays.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Free trial has ended and billing is not enabled",
"instance": "/v2/gateways"
}429 too many requests
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 1{
"type": "https://api.benjipays.com/problems/too_many_requests",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded. Please retry after the specified time.",
"instance": "/v2/whoami",
"retry_after_seconds": 1
}Best practices
- Parse
application/problem+jsonresponses by theirstatusandtype— don't string-match ondetail. - Distinguish 401 (re-authenticate) from 403 (you can't do this with these credentials).
- Use
request_id/correlation_idfrom the body when filing support tickets. - Retry
429only after honoringRetry-After. Retry500only on idempotent operations. - Treat unknown problem
typeURIs as if they were just the HTTPstatus— the platform may add new categories.
