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.
Authentication
- OAuth 2.0
-
POST /v1/oauth/token, grantclient_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.
| Tier | Up 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
| Scope | What it allows | Granted 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
| Method | Path | Scope | Idempotency key | Acts 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.
| Code | HTTP | Message |
|---|---|---|
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.
| Event | What it means | Needs 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.
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.
-
api.v1.tokenExchange your key for a token. -
api.v1.loan_requestsThe requests you may answer. Ones you have passed on are out of the list by default. -
api.v1.loan_requestThe disclosed package. This writes a disclosure to the borrower's own audit trail, exactly as an officer opening it on a screen would. -
api.v1.offers.createBid. State the rate and the term; the comparable totals are worked out here so two lenders' offers mean the same thing. -
api.v1.requests.passOr say you will not be offering, with one of the published reasons. -
api.v1.facilities.fundedAfter acceptance, report that you sent the money. We never hold or move it. -
api.v1.repayments.createReport 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.
-
api.v1.payroll_mandates.createAsk an employer, naming them by the reference they gave you. They decide, and they can end it at any time. -
api.v1.employer.employeesOnce granted, their staff — the ones who authorised that employer, and no more than the employer itself could see. -
api.v1.employer.payroll_runs.createPush a whole run. Each row answers for itself; one bad reference does not reject the file. -
api.v1.employer.deduction_scheduleWhat to deduct this period, and who to remit each amount to. We never touch the money. -
api.v1.employer.deductions.withheldReport what payroll did — or did not, with a reason code. -
api.v1.employer.employees.terminateReport 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.
-
api.v1.borrower_authorizations.createPut a request to them. Nothing is sent to them by this call and nothing is readable yet; they find it on their own screen. -
api.v1.borrower_authorizationPoll until it is active. Pending, active or closed — closed does not say whether they refused it or it expired. -
api.v1.borrower.income_sourcesRead what they allowed. Every material value arrives with what evidences it and when, on a named scale. -
api.v1.borrower.scoreThe score always comes with its reasons. A number without them gets used as a verdict. -
api.v1.borrower_authorizations.releaseHand the permission back when you are finished with it.
Worth subscribing to:
verification.changed