API reference

Errors

Every error from the Credicorp API uses one envelope and a conventional HTTP status. Read the machine-readable code to branch your logic and the message for logs — never parse the prose. Quote the correlation_id when you contact support.

The API uses standard HTTP status codes: 2xx for success, 4xx when something about the request is wrong (and retrying unchanged won't help), and 5xx for the rare problem on our side. Failures relating to lending outcomes — a declined decision, a returned payment — are not API errors: those succeed with 2xx and carry the outcome in the resource body.

The error envelope

All errors share this shape. The top-level key is always error.

json
{
  "error": {
    "code": "validation_failed",
    "message": "The request failed validation.",
    "detail": [{ "field": "amount_pence", "reason": "required" }],
    "correlation_id": "cor_01J2K3M4N5P6Q7R8S9T0A1V2W3",
    "retryable": false
  }
}
FieldTypeDescription
codestringalwaysStable, machine-readable identifier for the specific error. Switch on this in your error handler.
messagestringalwaysHuman-readable explanation for logs. Wording may change — never parse it.
detailarrayoptPer-field validation detail. Each item carries reason (why it failed); field-specific items also carry field (dotted path, e.g. business.company_number). Omitted on errors that are not field-level.
correlation_idstringalwayscor_-prefixed saga ID. Also returned in the X-CC-Correlation-Id response header. Quote it to support.
retryablebooleanalwaysMachine signal: true means a retry (same body, same Idempotency-Key) may succeed. Do not infer this from HTTP status alone.
retry_after_msintegeroptAdvised wait in milliseconds before retrying. Mirrors the Retry-After response header (seconds). Present only when retryable is true and a specific wait is known.

Two tracing headers appear on every response: X-Request-Id (per-hop ID, present on successes too) and X-CC-Correlation-Id (the saga correlation_id, present on errors). Log both so a single line in your logs maps to a single line in ours. When contacting support, quote correlation_id — not X-Request-Id.

Error categories

Use the HTTP status for coarse routing, then switch on code for precise handling. The retryable field is the authoritative machine signal — never infer it from the status alone.

HTTP status map

The full set of statuses the API returns and how your client should treat each.

StatusWhenRetry?
200 OKRequest succeeded.
201 CreatedA resource was created (e.g. an application).
202 AcceptedAccepted for async processing (e.g. a disbursement queued).
401 UnauthorizedNo valid credential.No — refresh the token.
403 ForbiddenValid credential, insufficient scope.No — request the scope.
404 Not FoundUnknown ID, or hidden by your permissions.No.
409 ConflictState conflict, or an Idempotency-Key reused with a different body.No — reconcile first.
413 Payload Too LargeRequest body exceeds the platform size limit. Shrink the payload and retry.No — shrink the payload.
422 UnprocessableValidation failed — malformed JSON, a missing required field, or a value out of range.No — fix the request.
423 LockedThe resource is locked and cannot be mutated in its current state (e.g. a declined decision).No — check the resource state.
429 Too Many RequestsRate limit exceeded. Honour Retry-After.Yes — with back-off.
500 Server ErrorAn unexpected fault on our side.Yes — idempotent only.
503 UnavailableTemporary maintenance or overload.Yes — back-off; check status.

Common error codes

Switch on code for predictable handling. These are stable identifiers — new codes may be added, so treat an unknown code as a generic failure of its HTTP status class.

codeHTTPWhat it means
validation_failed422Request failed validation; detail[] carries per-field specifics (field path + reason).
unauthenticated401No valid credential, or credential is revoked or unrecognised.
token_expired401Bearer token has passed its expiry — refresh it.
insufficient_scope403Key not granted the scope this endpoint requires.
capability_denied403A staff capability or feature flag is required; check your permissions.
not_found404No object exists for the supplied ID, or it is hidden by your permissions.
conflict409State conflict, or an Idempotency-Key reused with a different body.
payload_too_large413Request body exceeds the platform limit. Reduce payload size — never retryable unchanged.
decision_locked423Decision is locked; only an owner-sudo release can move it.
rate_limited429Request rate over the quota; honour retry_after_ms and Retry-After.
internal_error500Unexpected fault. Retry idempotent calls; then contact support with the correlation_id.
service_unavailable503Temporary unavailability. Retry with back-off; check status.

A validation error in full

Posting to the MCP endpoint without the required method field returns 422 with validation_failed and a per-field detail array:

bash
curl -i "$CC_BASE/partner/v1/mcp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "jsonrpc": "2.0", "id": 1 }'

HTTP/2 422
X-CC-Correlation-Id: cor_01J2K3M4N5P6Q7R8S9T0A1V2W3
X-Request-Id: req_01J2K3M4N5P6Q7R8S9T0X1V2W3
Content-Type: application/json

{
  "error": {
    "code": "validation_failed",
    "message": "The request failed validation.",
    "detail": [{ "field": "method", "reason": "required" }],
    "correlation_id": "cor_01J2K3M4N5P6Q7R8S9T0A1V2W3",
    "retryable": false
  }
}

Handling errors in the SDKs

The SDKs raise typed exceptions for each error category, each exposing code, detail and correlationId. Catch the specific exception you can recover from and let the rest bubble.

php
use Credicorp\Exception\{InvalidRequestException, RateLimitException, ApiErrorException};

try {
    $result = $cc->mcp->call('tools/call', $params);
} catch (InvalidRequestException $e) {
    // $e->getCode() === 'validation_failed', $e->getDetail() has per-field info
    log_warning($e->getMessage(), $e->getCorrelationId());
} catch (RateLimitException $e) {
    sleep($e->getRetryAfter());   // then retry
} catch (ApiErrorException $e) {
    // transient — safe to retry idempotent calls with back-off
}
node
try {
  const result = await cc.mcp.call('tools/call', params);
} catch (err) {
  if (err.retryable && err.retryAfterMs) {
    await sleep(err.retryAfterMs); // then retry
  } else if (!err.retryable) {
    console.warn(err.code, err.detail, err.correlationId);
  } else {
    throw err;
  }
}
python
import credicorp

try:
    result = cc.mcp.call('tools/call', **params)
except credicorp.error.InvalidRequestError as e:
    log.warning("%s detail=%s (%s)", e.code, e.detail, e.correlation_id)
except credicorp.error.RateLimitError as e:
    time.sleep(e.retry_after_ms / 1000)   # then retry
except credicorp.error.APIError:
    pass  # transient — retry idempotent calls with back-off

Retry only idempotent requests on 5xx and 429, and always send the same Idempotency-Key so a retried POST can't double-create. See the idempotency guide.