Skip to main content

Error handling

All errors are returned in Problem JSON format (RFC 7807) with Content-Type: application/problem+json.

Structure

{
"type": "https://api-docs.maxpay.com.ar/errors/receivable-account-already-exists",
"title": "Receivable account already exists",
"status": 409,
"detail": "A collection account already exists for this client with the same currency.",
"instance": "/v1/receivable-accounts",
"code": "receivable-account-already-exists",
"requestId": "req_b3f8a1c2d4e5f6789012345678901234",
"existingResourceId": 1042
}
FieldTypeAlways presentDescription
typeURIYesURI identifying the problem type. Points to the error catalog.
titlestringYesShort summary in English. Stable across problem versions.
statusintegerYesCorresponding HTTP code.
detailstringYesHuman-readable description of the concrete problem. May vary between occurrences.
instancerelative URIYesPath of the request that generated the error.
codestringYesStable identifier of the error (kebab-case). Suitable for ERP mapping.
requestIdstringYesUnique request ID. Include in support reports.
invalidParamsarrayOnly on 400Detail of invalid fields.
Other fieldsvariableNoAdditional problem-specific context.

Validation errors (400)

When the problem is request format (missing fields, incorrect types, out-of-range values), the response includes invalidParams:

{
"type": "https://api-docs.maxpay.com.ar/errors/validation",
"title": "Validation error",
"status": 400,
"detail": "The request has invalid fields.",
"instance": "/v1/receivables",
"code": "validation",
"requestId": "req_...",
"invalidParams": [
{ "name": "amount", "reason": "Amount must be positive" },
{ "name": "currencyCode", "reason": "Currency code is required" }
]
}

HTTP categories

HTTPCategoryExamples
400Invalid request formatBad schema, wrong types, missing fields
401Authentication failedInvalid, expired, or missing token
403Authorized but no permissionMissing scope, resource belongs to another customer
404Resource doesn't existNonexistent or inaccessible ID
409State conflictResource already exists, idempotency-key reused with different body
422Business ruleAccount not enabled for debt, invalid state transition
429Rate limit exceededToo many requests per minute
500Internal errorBug in Max Pay
502/504External provider failureBanking provider unavailable, COELSA timeout

Full catalog

See Error catalog for the exhaustive list of codes, messages and context.

async function callMaxpay(path, options) {
const res = await fetch(`${BASE_URL}${path}`, options);

if (res.ok) return await res.json();

const problem = await res.json();
const requestId = problem.requestId;

switch (problem.code) {
case 'idempotency-key-reuse':
// Different body with the same key — integrator bug, alert
throw new IntegrationError(problem, requestId);

case 'receivable-account-already-exists':
// Resource already created — use the existing ID
return await callMaxpay(`/v1/receivable-accounts/${problem.existingResourceId}`, {
method: 'GET',
headers: options.headers
});

case 'rate-limit-exceeded':
const retryAfter = parseInt(res.headers.get('Retry-After'), 10);
await sleep(retryAfter * 1000);
return await callMaxpay(path, options);

case 'external-provider-unavailable':
case 'external-provider-timeout':
// Retry with exponential backoff
throw new RetryableError(problem, requestId);

default:
throw new MaxpayError(problem, requestId);
}
}

Reporting an incident

When reporting an error to support, always include the requestId and the code. This allows finding the full trace of the request in our logs.