Skip to content

The CreditRefPro API

A versioned, organization-scoped REST API under /v1. Everything on this page is read from the running implementation — the endpoints from the router, the scopes and errors from their own catalogues, the rate limits from the same settings the limiter reads. There is no separate document to fall out of date.

The same thing as JSON: /developers.json.

What the API can never do. It cannot reach a record the interface could not. Every request runs through the same rules as a person in a browser: a lender sees the borrowers who disclosed themselves to it, an employer sees the employees who authorised it, and an application sees what a borrower allowed and only for as long as they allow it. A scope is a ceiling, never a floor.

Authentication

OAuth 2.0
POST /v1/oauth/token, grant client_credentials. Tokens last 60 minutes. Send your key id and secret, get a short-lived token. A token may ask for a subset of your client's scopes and never more, and the narrower of the two always wins — so trimming a client narrows tokens already issued.
API key
Authorization: Bearer <key_id>:<secret>. For a batch job that makes one call a night and should not have to run a token exchange to make it.
Transport
The Authorization header only. A credential in a query string ends up in access logs, browser history and referrer headers.
Revocation
Revoking a client revokes every token it issued, in the same statement. A key revoked at four whose token works until five has not been revoked.

Rate limits

Your organization's metered API usage under the api_calls pricing rule. The band you are billed in is the band you are limited in. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Tier, Retry-After.

TierUp to (calls)Per minute
No tiers are configured, so every client gets the default of 120 calls a minute. Unauthenticated callers get 20.

Pagination

Every collection returns {data, page: {limit, has_more, next_cursor}}. Pass next_cursor back as cursor. Cursors are opaque: do not construct or parse one. Default 25, maximum 100.

Idempotency

Idempotency-Key on every state-changing endpoint, marked in the endpoint list. A repeat of the same key and the same body returns the original response, byte for byte. The same key with a different body is refused rather than answered, because it means your code reused a key and handing you the earlier result would have you believe a request succeeded that was never made. Only successful writes are remembered. Remembered for 24 hours.

Scopes

ScopeWhat it allowsGranted by
identity.read Know who this client is Read back which organization this client belongs to and what it may do. Grants no access to anybody's data. an administrator of your own organization
loan_requests.read Read loan requests shared with us Read the loan requests this lender has been made eligible for, and only those. It does not widen what the lender can already see. an administrator of your own organization
offers.read Read our own offers Read the offers this lender has made and what became of them. Never a rival's terms on the same request. an administrator of your own organization
offers.write Answer requests Submit, amend and withdraw offers on requests this lender may already answer, and record that it will not be offering on one. an administrator of your own organization
document_requests.write Ask a borrower for a document Ask for one more document against a request this lender may already answer. The borrower decides whether to send it, and saying no is an answer. an administrator of your own organization
facilities.read Read our own facilities Read the facilities this lender is owed, with their repayment ledger. an administrator of your own organization
facilities.write Report our own steps on a facility Record that this lender has reviewed a facility and that it has sent the funds. It moves no money — it records what the lender says it did. an administrator of your own organization
repayments.write Report repayments we received Record that money reached this lender, and report the outstanding balance on their own book. an administrator of your own organization
employees.read Read our own employees Read the employment records of employees who authorised this employer. Never their score, their borrowing or what they owe. an administrator of your own organization
deductions.read Read deduction instructions put to us Read the instructions this employer has been asked to operate. an administrator of your own organization
employments.write Confirm employment and report leavers Confirm that somebody works here and on what terms, and report when they stop. Reporting a leaver ends the payroll route for them; it does not cancel what they owe. an administrator of your own organization
payroll.write Submit payroll figures Push pay figures for employees who authorised this employer, for employers whose payroll system this platform cannot read from. an administrator of your own organization
deductions.write Report what payroll did Record that a deduction was withheld, or that it was not and why. an administrator of your own organization
disputes.read Read disputes we have to answer Read the disputes routed to this organization, with what the person said and every step of the investigation so far. Reading one is not answering it: a finding is made by a person who puts their name to it, on a record nobody can edit afterwards, and there is no endpoint for that. an administrator of your own organization
employers.act Act for employers who appoint us Ask an employer to let this bureau operate the above on its behalf, and act under the mandates that are granted. It reaches nothing until an employer says yes and never more than that employer allowed. an administrator of your own organization
borrower.access.request Ask a borrower for permission Put a request to a borrower asking them to let this application read parts of their profile. It reads nothing by itself, and the borrower decides. an administrator of your own organization
borrower.profile.read Your profile Who you are on CreditRefPro: the identifier you confirmed, your country, when you joined, and whether an employer here can operate a payroll deduction for you. the borrower, per request, and revocable by them at any time
borrower.income.read Your income Each income source you have recorded, its amount and how often it arrives, with how strongly each one is evidenced. the borrower, per request, and revocable by them at any time
borrower.assets.read Your assets Each asset you have recorded, what you say it is worth, and whether anything is pledged against it. the borrower, per request, and revocable by them at any time
borrower.obligations.read What you already owe Your recorded obligations: who to, how much is outstanding, and what you pay each period. the borrower, per request, and revocable by them at any time
borrower.verification.read How verified your profile is How complete your profile is and how strongly it is evidenced, without the figures themselves. the borrower, per request, and revocable by them at any time
borrower.score.read Your score and why it is what it is Your current score with its band, the reasons behind it, what is helping and what is holding it back, and how it has moved. the borrower, per request, and revocable by them at any time
borrower.affordability.read What you could afford The estimate of what you could repay each month, and the working behind it. It is an estimate and not an approval. the borrower, per request, and revocable by them at any time

Endpoints

MethodPathScopeIdempotency keyActs under
POST /api/v1/borrower-authorizations/{id}/release borrower.access.request required —
GET /api/v1/borrower-authorizations/{id} borrower.access.request — —
GET /api/v1/borrower-authorizations borrower.access.request — —
POST /api/v1/borrower-authorizations borrower.access.request required —
GET /api/v1/borrowers/{authorization}/affordability borrower.affordability.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/assets borrower.assets.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/income-sources borrower.income.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/obligations borrower.obligations.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/profile borrower.profile.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/score-history borrower.score.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/score borrower.score.read — a borrower's own authorization
GET /api/v1/borrowers/{authorization}/verification borrower.verification.read — a borrower's own authorization
GET /api/v1/deduction-authorizations deductions.read — —
GET /api/v1/deduction-schedule deductions.read — —
POST /api/v1/deductions/{id}/not-withheld deductions.write required —
POST /api/v1/deductions/{id}/submit deductions.write required —
POST /api/v1/deductions/{id}/withheld deductions.write required —
GET /api/v1/deductions/{id} deductions.read — —
GET /api/v1/deductions deductions.read — —
GET /api/v1/disputes/{id} disputes.read — —
GET /api/v1/disputes disputes.read — —
GET /api/v1/document-requests document_requests.write or loan_requests.read — —
POST /api/v1/employees/{id}/confirm employments.write required —
POST /api/v1/employees/{id}/payroll payroll.write required —
POST /api/v1/employees/{id}/terminate employments.write required —
GET /api/v1/employees/{id} employees.read — —
GET /api/v1/employees employees.read — —
GET /api/v1/employers/{mandate}/deduction-authorizations deductions.read — a payroll mandate an employer granted you
GET /api/v1/employers/{mandate}/deduction-schedule deductions.read — a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/deductions/{id}/not-withheld deductions.write required a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/deductions/{id}/submit deductions.write required a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/deductions/{id}/withheld deductions.write required a payroll mandate an employer granted you
GET /api/v1/employers/{mandate}/deductions/{id} deductions.read — a payroll mandate an employer granted you
GET /api/v1/employers/{mandate}/deductions deductions.read — a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/employees/{id}/confirm employments.write required a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/employees/{id}/payroll payroll.write required a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/employees/{id}/terminate employments.write required a payroll mandate an employer granted you
GET /api/v1/employers/{mandate}/employees/{id} employees.read — a payroll mandate an employer granted you
GET /api/v1/employers/{mandate}/employees employees.read — a payroll mandate an employer granted you
POST /api/v1/employers/{mandate}/payroll-runs payroll.write required a payroll mandate an employer granted you
POST /api/v1/facilities/{id}/funded facilities.write required —
POST /api/v1/facilities/{id}/outstanding repayments.write required —
POST /api/v1/facilities/{id}/repayments repayments.write required —
POST /api/v1/facilities/{id}/reviewed facilities.write required —
GET /api/v1/facilities/{id} facilities.read — —
GET /api/v1/facilities facilities.read — —
POST /api/v1/inbound/{provider} none — —
POST /api/v1/loan-requests/{id}/document-requests document_requests.write required —
POST /api/v1/loan-requests/{id}/offers offers.write required —
POST /api/v1/loan-requests/{id}/pass offers.write required —
GET /api/v1/loan-requests/{id} loan_requests.read — —
GET /api/v1/loan-requests loan_requests.read — —
GET /api/v1/me identity.read — —
POST /api/v1/oauth/token none — —
POST /api/v1/offers/{id}/amend offers.write required —
POST /api/v1/offers/{id}/withdraw offers.write required —
GET /api/v1/offers/{id} offers.read — —
GET /api/v1/offers offers.read — —
POST /api/v1/payroll-mandates/{id}/release employers.act required —
GET /api/v1/payroll-mandates/{id} employers.act — —
GET /api/v1/payroll-mandates employers.act — —
POST /api/v1/payroll-mandates employers.act required —
POST /api/v1/payroll-runs payroll.write required —
GET /api/v1/portfolio facilities.read — —

Errors

Every failure has a stable machine-readable code and a fixed sentence. The message never names a record, an organization or an identifier: a record that does not exist and one that is not yours answer identically, because telling them apart would confirm the existence of records a caller has no business knowing about. A request_id comes back on every error so a support conversation can happen without anybody quoting data.

CodeHTTPMessage
unauthenticated 401 This request carried no usable credential.
revoked 401 This credential has been revoked.
expired 401 This token has expired. Ask for a new one.
forbidden_scope 403 This client was not granted that.
not_found 404 No such record, or it is not one of yours.
invalid_request 400 This request could not be understood.
unprocessable 422 This request was understood and could not be carried out.
idempotency_conflict 409 That idempotency key was already used for a different request.
rate_limited 429 Too many requests. Slow down, or ask about a higher tier.
internal 500 Something went wrong at our end. Nothing was changed.

Webhooks

A delivery carries an event id, the event name, the type and id of the object, and a link. It carries no borrower data. Fetch the object through the API, where your scope and the borrower's own permissions are applied at the moment you read — a payload rendered when the event happened could otherwise carry data whose permission was withdrawn before it was delivered.

EventWhat it meansNeeds scope
loan_request.matched A borrower has disclosed a loan request to you and you may now answer it. loan_requests.read
offer.received An offer has been made on a request. Sent to the lender that made it, as confirmation that it landed. offers.read
offer.accepted A borrower has accepted an offer. Accepting is not an approval — your own review and your own disclosures still come after this. offers.read
offer.superseded An offer of yours is no longer live: withdrawn, expired, or the borrower took another one. offers.read
facility.activated A facility became live because its lender reported sending the funds. facilities.read
repayment.posted An entry was posted to a facility's repayment ledger. It says which of the three claims it is; it does not net them. facilities.read
dispute.raised Somebody disputes one of their records and it has been routed to you to answer. A claim on the record, not a finding — and the record it is about must not be acted on as settled while it stands. disputes.read
dispute.resolved A dispute stopped standing open — either the party it was routed to gave its finding, or the person who raised it withdrew it. The event says which, and an upheld one has a correction beside the original. disputes.read
verification.changed How strongly one of a borrower's records is evidenced has changed. Sent only where that borrower has allowed you to read the records in question. borrower.verification.read

Verifying a delivery

Signed content is <timestamp>.<nonce>.<raw body>, using HMAC-SHA256 with your endpoint secret. Timestamps outside 300 seconds are stale.

Compute the HMAC over the raw body you received, not over a body you parsed and re-serialised. Compare in constant time. Reject a timestamp outside the tolerance, and reject a nonce you have seen before — a replayed delivery carries a valid signature, which is what makes it a replay.

X-CreditRefPro-Signature
v1=<hex hmac-sha256>
X-CreditRefPro-Timestamp
seconds since the epoch, as we saw it
X-CreditRefPro-Nonce
single-use, per delivery
X-CreditRefPro-Event
the event name, so you can route without parsing
X-CreditRefPro-Attempt
which attempt this is

Retries

6 attempts, at 0s, 60s, 300s, 1800s, 7200s, 21600s after the first. Any 2xx. Answer quickly and do the work afterwards; we give up waiting after 10 seconds and will retry.

At least once. At least once. A delivery that times out after you committed it looks identical, from here, to one that never arrived — so it is retried and you will see it twice. Deduplicate on the event id.

Disabled after 20 consecutive failures, and we tell you when that happens.

Pushing to us

If you push to us, sign it the same way with the secret an administrator configured for your provider record. Unsigned or invalid deliveries are refused and logged; a nonce we have seen is refused whether or not its signature is good.

Sandbox

What it is
Your organization can have a sandbox: a separate organization holding synthetic borrowers, requests and facilities, with its own keys and its own webhook endpoints.
Separation
A sandbox key reaches sandbox records and nothing else. The separation is the same one that stops one lender reading another's rows, rather than a second mechanism layered on top — and a key whose environment disagrees with its organization is refused before any handler runs.
The data
Synthetic records are built through the same code paths real ones are, so they behave the same way. Names are prefixed, and addresses use a domain that cannot receive mail.
Webhooks
A sandbox endpoint hears sandbox events only. A test run will not page whoever is on call for production.

Worked examples

A lender running its own underwriting

Hear that a borrower has shown you a request, read what they disclosed, bid or pass, and service the facility afterwards.

  1. api.v1.token Exchange your key for a token.
  2. api.v1.loan_requests The requests you may answer. Ones you have passed on are out of the list by default.
  3. api.v1.loan_request The disclosed package. This writes a disclosure to the borrower's own audit trail, exactly as an officer opening it on a screen would.
  4. api.v1.offers.create Bid. State the rate and the term; the comparable totals are worked out here so two lenders' offers mean the same thing.
  5. api.v1.requests.pass Or say you will not be offering, with one of the published reasons.
  6. api.v1.facilities.funded After acceptance, report that you sent the money. We never hold or move it.
  7. api.v1.repayments.create Report repayments as they arrive, each with your own payment reference — that reference is what makes reporting the same payment twice harmless.

Worth subscribing to: loan_request.matched, offer.accepted, offer.superseded, repayment.posted

A payroll bureau acting for employers

Ask each employer to appoint you, then run their deductions and push their payroll figures without anybody opening a browser.

  1. api.v1.payroll_mandates.create Ask an employer, naming them by the reference they gave you. They decide, and they can end it at any time.
  2. api.v1.employer.employees Once granted, their staff — the ones who authorised that employer, and no more than the employer itself could see.
  3. api.v1.employer.payroll_runs.create Push a whole run. Each row answers for itself; one bad reference does not reject the file.
  4. api.v1.employer.deduction_schedule What to deduct this period, and who to remit each amount to. We never touch the money.
  5. api.v1.employer.deductions.withheld Report what payroll did — or did not, with a reason code.
  6. api.v1.employer.employees.terminate Report a leaver. This ends the payroll route; it does not cancel what they owe.

Worth subscribing to: repayment.posted

An application acting for a borrower

Ask a borrower to let you read parts of their profile, then read only what they allowed, for as long as they allow it.

  1. api.v1.borrower_authorizations.create Put a request to them. Nothing is sent to them by this call and nothing is readable yet; they find it on their own screen.
  2. api.v1.borrower_authorization Poll until it is active. Pending, active or closed — closed does not say whether they refused it or it expired.
  3. api.v1.borrower.income_sources Read what they allowed. Every material value arrives with what evidences it and when, on a named scale.
  4. api.v1.borrower.score The score always comes with its reasons. A number without them gets used as a verdict.
  5. api.v1.borrower_authorizations.release Hand the permission back when you are finished with it.

Worth subscribing to: verification.changed