API reference
Eight operations across seven paths. Every one of them is listed here; there is no undocumented surface.
Requests and responses#
| Convention | Detail |
|---|---|
| Base URL | https://{host}/v1. The host is issued at onboarding |
| Authentication | X-Api-Key header on every request. Details |
| Content type | application/json on every request body and response |
| Idempotency | Idempotency-Key on every POST. Keys retained 24 hours |
| Correlation | 400 and 500 responses carry Request-Id. Quote it when asking us about a call |
| Unknown fields | Rejected, not ignored. A misspelled field returns 400 naming it in param |
| Timestamps | RFC 3339, UTC, e.g. 2026-07-06T09:14:22Z |
| Money | Always a value and an explicit currency. Never a bare number |
POST /v1/assessments returns 202 and nothing else. There is no
inline-result response to branch on, in either mode. The outcome arrives on a callback, and
the GET endpoints are the recovery path.
Errors#
Every error has the same shape: a type you can branch on, a code
carrying the specificity, a human message, the offending param
where one applies, and a doc_url.
| Type | HTTP | When, and what to do |
|---|---|---|
authentication_error | 401 | Key missing, malformed or revoked. Do not retry |
invalid_request_error | 400, 404, 409 | Malformed field, unknown country, or an operation illegal in the current state. Fix and resend |
consent_error | 402 | A required consent was not attested. The remedy is with the borrower, not the payload |
entitlement_error | 403 | Well formed, but your tenant is not entitled to a country or source the request needs. Retrying will not help. Not used for a market-wide gap, which caps the outcome instead of refusing the request |
idempotency_error | 409 | A key was reused with a different body. Use a new key, or resend the original body |
rate_limit_error | 429 | Back off using Retry-After. Expected, not exceptional |
api_error | 500 | Our side. Safe to retry with the same idempotency key |
Create an assessment#
POST /v1/assessments
Starts an assessment of one applicant against one policy version. Returns 202
in both modes.
Body
| Field | Required | Notes |
|---|---|---|
mode | Required | screening or full |
policy_version | Required | A policy version belonging to you. One version per lending product |
applicant | Required | Country and the identifiers that market uses. Secondary identifiers go in additional_identifiers |
consents | Required | What the borrower agreed to, and when. You attest, we record |
representatives | Optional | One entry per person the policy screens. Each carries its own ref, which every check on that person reports back in subject |
related_companies | Optional | Companies connected to the applicant that the policy screens, such as a parent or a company sharing a director. Each carries its own ref, reported back in subject |
request | Optional | Amount, currency, term. Required for a supportable amount to mean anything |
partner_data | Optional | Data only the lender holds, such as relationship length or an internal list result. Several eligibility and fraud signals cannot compute without it |
custom_inputs | Conditional | Whatever the policy version declares. An undeclared name or an out-of-range value returns 400, never a silent pass |
previous_assessment_id | Optional | Set when re-evaluating a case. Links the new assessment to the old one |
metadata | Optional | Opaque string pairs, echoed back everywhere. Never interpreted, never used in evaluation |
Responses
| Code | Meaning |
|---|---|
202 | Accepted and running. status is screening, result is null |
400 | Malformed, or an identifier that does not match the published pattern for the country |
402 | A required consent was not attested |
403 | Your tenant is not entitled to something the request needs, such as a country or a source your keys do not reach. A source unavailable in the market for everyone does not return 403: the assessment runs and the outcome is capped instead |
409 | Idempotency key reused with a different body |
429 | Rate limited |
List assessments#
GET /v1/assessments
Cursor-paginated, newest first, scoped to you. This is the recovery path after an outage: the consumer that missed a callback is the one that does not have the id.
| Query parameter | Notes |
|---|---|
status | One of the six lifecycle states |
country | Two-letter code |
created_after | RFC 3339. The parameter to use after an outage |
limit | 1 to 100, default 25 |
starting_after | A next_cursor from a previous page. Opaque, so do not parse it |
Retrieve an assessment#
GET /v1/assessments/{id}
The full envelope: status, coverage, the inputs as submitted, and result once
there is one. Safe to poll, though callbacks are the intended path.
Retrieve the result#
GET /v1/assessments/{id}/result
Just the evaluated output. Returns 409 while the assessment is still running,
rather than an empty object.
A complete worked result for each market ships with the contract:
api/examples/ee/assessment.json, api/examples/lv/assessment.json
and api/examples/pl/assessment.json. Each is a full envelope carrying every
ratio, check, indicator, credit exposure entry and reason code that market can produce, and each is validated
against this specification on every build.
Attach bank data#
POST /v1/assessments/{id}/bank-data
The single permitted append to an assessment. Legal only in
awaiting_bank_data; 409 in every other state, including after a
successful submission.
| Field | Required | Notes |
|---|---|---|
provider | Required | labelled or unlabelled, meaning whether transactions arrive with a category. Not a vendor name |
accounts | Required | One entry per account, with its currency. At most 50. We never ask for a full account number |
transactions | Required | Amounts unsigned, with an explicit direction. At most 50,000, and the body must stay under 10 MB |
declared_from ยท declared_to | Optional | The window your upstream says it covers. We measure the window that actually arrived and report both back in coverage |
Sign conventions differ between upstreams, and an inverted sign is invisible in a
cash-flow total. Send the magnitude and say debit or credit.
List countries#
GET /v1/countries
Every market the contract declares, with what each one can do. A market whose adapter is
not built appears here with status: declared, never absent and never
present-but-failing at runtime.
Example response: api/examples/countries.json.
Retrieve a country#
GET /v1/countries/{code}
Identifier schema, rating scales and industry schemes for one market. Read it before sending a case: the identifiers a market keys on and the scales its sources grade on are not the same in any two markets, and a floor written on one market's scale means nothing on another's.
Example responses:
ee,
lv,
pl.
What runs at the first stage is not published here. It is a property of the policy version
and is read from GET /v1/policies/{version}.
Retrieve a policy version#
GET /v1/policies/{version}
The policy configuration, as it was applied: the band boundaries per ratio, the thresholds per condition, the evidence floors, the ordered decision rules, the rating floor per market, the custom inputs the product declares, and which conditions make up the first stage.
Every outcome names the policy_version that produced it, and superseded
versions stay readable for as long as any assessment cites them, so a decision taken months
ago can be replayed against the rules that took it.
Tenant-scoped. You read the policies you own and no others; no boundary belonging to any other tenant appears in any field of any response.
Versions are configured by Credit Atlas from the rules you supply. A change is requested through your account contact and returns a new version identifier. Existing versions are never edited in place.