Credit Report Integration Guide
Step-by-step guide to generating credit reports, handling authentication challenges, and testing your integration.
Generating a credit report is a multi-step process. Depending on the credit bureau's risk assessment, the consumer may need to verify their identity before the report data is returned.
- Submit a generate request with the consumer's personal details and address.
- Check the response status — the report may complete immediately or require an authentication challenge.
- If a challenge is required, collect the OTP from the consumer and submit it.
- On successful verification, the credit report data is returned.
The flow branches based on the provider's response:
Statuses
| Status | Meaning |
|---|---|
complete | Report generated successfully. Credit data is included in the response. |
authentication-required | The provider requires identity verification before releasing data. An OTP has been sent via SMS. |
blocked | The provider blocked the request due to risk assessment. The report cannot proceed. |
retry | OTP code was incorrect. Check attemptsRemaining and prompt the consumer to try again. |
failed | Maximum OTP attempts exhausted or an unrecoverable error occurred. |
Before making credit report requests, you must embed our device fingerprinting script on your website. The script collects device and browser data that the credit report provider uses for risk assessment and identity verification.
Why is this required? The OTP authentication challenge relies on device fingerprinting data. Without the script loaded on the consumer's browser, the provider cannot verify the device and the OTP challenge will always fail.
1. Add the script tag
Include the following script on any page where the consumer will submit their details for a credit report. Place it as early as possible (e.g. in <head>) so the SDK has time to collect device data before the form is submitted.
<script src="https://api-next.checkio.co.uk/v1/fingerprints/device"></script>The script automatically initialises and begins collecting device data. No additional configuration is required.
2. Capture the Session ID
When the script is ready, it calls a callback on window.checkio.deviceFingerprint with the generated Session ID. Register this callback before the script loads:
<script>
window.checkio = window.checkio || {};
window.checkio.deviceFingerprint = function (data) {
// Store the session ID for use in your credit report request
console.log('Session ID:', data.sessionId);
};
</script>
<script src="https://api-next.checkio.co.uk/v1/fingerprints/device"></script>3. Send the Session ID
Pass the Session ID as the sessionId field in your POST /credit-reports request. This links the device fingerprint data to the credit report request at the provider.
Submit the consumer's details to begin the credit report process. Replace {tenantId} with your tenant ID.
curl -X POST 'https://api-next.checkio.co.uk/{tenantId}/v1/credit-reports' \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{
"sessionId": "sdk-generated-session-id",
"bureaus": ["equifax"],
"ipAddress": "192.168.1.1",
"searchType": "pcp",
"title": "Mr",
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1990-05-15",
"gender": "M",
"mobileNumber": "+447911123456",
"email": "john.doe@example.com",
"externalReference": "your-internal-ref",
"addresses": [
{
"type": "current",
"ukAddress": {
"buildingNumber": "42",
"buildingName": "Rose Court",
"subBuildingName": "Flat 3",
"thoroughfare": "High Street",
"district": "Westminster",
"townOrCity": "London",
"county": "Greater London",
"country": "GB",
"postcode": "SW1A 1AA"
}
}
]
}'Required Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | The Session ID generated by the device fingerprinting SDK on the consumer's browser. This links the device data to the credit report request. See Prerequisite: Device Fingerprinting above. |
bureaus | string[] | Credit bureaus to query: "equifax", "transunion", "experian" |
ipAddress | string | The consumer's IP address |
searchType | string | Search type (currently "pcp") |
firstName | string | Consumer's first name |
lastName | string | Consumer's last name |
dateOfBirth | string | Date of birth in YYYY-MM-DD format |
addresses | object[] | At least one address (see Address Fields below) |
Optional Fields
| Field | Description |
|---|---|
title | e.g. "Mr", "Mrs", "Ms" |
middleName | Consumer's middle name |
gender | "M" or "F" |
mobileNumber | Required if OTP challenges are expected |
email | Consumer's email address |
externalReference | Your internal reference for correlation |
Address Fields
Each entry in the addresses array has a type (e.g. "current") and a ukAddress object with the following fields:
| Field | Required | Description |
|---|---|---|
subBuildingName | * | Sub-unit within a building, e.g. "Flat 3", "Unit B" |
buildingName | * | Name of the building, e.g. "Rose Court" |
buildingNumber | * | House or building number, e.g. "42" |
thoroughfare | No | Street name, e.g. "High Street" |
district | No | District or locality, e.g. "Westminster" |
townOrCity | Yes | Town or city name, e.g. "London" |
county | No | County, e.g. "Greater London" |
country | No | Country code, e.g. "GB" |
postcode | Yes | UK postcode, e.g. "SW1A 1AA" |
* At least one of subBuildingName, buildingName, or buildingNumber must be provided to identify the property.
When the provider does not require identity verification, the credit report data is returned immediately.
{
"meta": {
"request": { "timeTakenMs": 2845 },
"wallet": { "balance": 9500, "cost": 500 }
},
"data": {
"status": "complete",
"challengeType": "none",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890",
"creditReport": {
"person": {
"title": "Mr",
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1990-05-15"
},
"residencies": [
{
"addressId": "addr-1",
"address": {
"type": "uk",
"ukAddress": {
"subBuildingName": "Flat 3",
"buildingName": "Rose Court",
"buildingNumber": "42",
"thoroughfare": "High Street",
"district": "Westminster",
"townOrCity": "London",
"county": "Greater London",
"country": "GB",
"postcode": "SW1A 1AA"
},
"addressLine1": "Flat 3, Rose Court, 42 High Street",
"lines": ["Flat 3, Rose Court", "42 High Street", "Westminster", "London", "Greater London", "SW1A 1AA"]
},
"yearsAtAddress": 3,
"monthsAtAddress": 6,
"isCurrent": true
}
],
"consumerData": {
"records": [
{
"title": "Mr",
"firstName": "John",
"lastName": "Doe",
"accountNumber": "****1234",
"accountType": "Credit Card",
"accountStatus": "Open",
"companyType": "Bank",
"companyName": "Example Bank plc",
"numberOfPayments": 24,
"repaymentPeriod": 24,
"payment": { "amount": 50000, "currency": "GBP" },
"startBalance": { "amount": 500000, "currency": "GBP" },
"balance": { "amount": 250000, "currency": "GBP" },
"startDate": "2022-01-15",
"bureaus": [
{ "id": "equifax", "ref": "EQF-REF-123456" }
]
}
]
},
"previousSearches": {
"providers": [
{
"companyName": "Example Bank plc",
"records": [
{ "date": "2024-06-01T14:30:00Z" }
]
}
]
}
}
}
}50000 means £500.00.When the provider requires identity verification, an OTP code is sent via SMS to the mobile number provided. The response includes a challengeId that you'll need for the next step.
{
"meta": {
"request": { "timeTakenMs": 1234 },
"wallet": { "balance": 9500, "cost": 500 }
},
"data": {
"status": "authentication-required",
"challengeType": "otp",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890",
"challenge": {
"challengeId": "019def34-5678-9012-cdef-ab3456789012"
}
}
}Important: Save both the creditReportId and challengeId from this response. You will need both to submit the OTP. The challenge expires after 5 minutes.
SMS billing: Each SMS sent (initial OTP and any resends) is billed separately as an SMS line item. SMS charges are captured immediately upon successful delivery, regardless of whether the credit report completes.
The provider may block a request based on risk assessment. This is a terminal state and the report cannot proceed.
{
"meta": {
"request": { "timeTakenMs": 890 },
"wallet": { "balance": 10000, "cost": 0 }
},
"data": {
"status": "blocked",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890"
}
}Once the consumer receives the SMS and provides the OTP code, submit it using the creditReportId and challengeId from the previous response.
curl -X POST 'https://api-next.checkio.co.uk/{tenantId}/v1/credit-reports/{creditReportId}/otp-challenge' \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{
"challengeId": "019def34-5678-9012-cdef-ab3456789012",
"code": "123456"
}'Success Response
On successful verification, the credit report data is returned:
{
"meta": {
"request": { "timeTakenMs": 3567 },
"wallet": { "balance": 9500, "cost": 0 }
},
"data": {
"status": "complete",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890",
"creditReport": {
"person": { ... },
"residencies": [ ... ],
"consumerData": { "records": [ ... ] },
"previousSearches": { "providers": [ ... ] }
}
}
}Retry Response (Invalid Code)
If the code is incorrect, you can retry. The consumer has a maximum of 3 attempts:
{
"meta": {
"request": { "timeTakenMs": 234 },
"wallet": { "balance": 9500, "cost": 0 }
},
"data": {
"status": "retry",
"errorMessage": "Invalid OTP code",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890",
"attemptsRemaining": 2
}
}Failed Response (Max Attempts)
After 3 failed attempts, the challenge is permanently failed:
{
"meta": {
"request": { "timeTakenMs": 145 },
"wallet": { "balance": 10000, "cost": 0 }
},
"data": {
"status": "failed",
"errorMessage": "Maximum OTP attempts exhausted",
"creditReportId": "019abc12-3456-7890-abcd-ef1234567890"
}
}If the consumer did not receive the SMS, you can request a resend. The OTP can be sent a maximum of 3 times per challenge (1 initial + 2 resends). Each resend resets the 5-minute expiry timer.
curl -X POST 'https://api-next.checkio.co.uk/{tenantId}/v1/credit-reports/{creditReportId}/resend-sms' \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{
"challengeId": "019def34-5678-9012-cdef-ab3456789012"
}'Response
{
"meta": {
"request": { "timeTakenMs": 1200 }
},
"data": {
"remainingSmsCount": 1,
"smsSentCount": 2
}
}Use test mode during development to simulate the full flow without hitting real credit bureaus or charging credits. Set test: true in the request body and use challengeOptions to control the behaviour.
Test with OTP Challenge
Simulates the OTP flow. No real SMS is sent and no SMS credits are charged. Use code 123456 to verify. You can also test the resend SMS endpoint — it simulates the resend (including the max 3 sends limit) without sending a real SMS.
{
"sessionId": "test-session-1",
"bureaus": ["equifax"],
"ipAddress": "127.0.0.1",
"searchType": "pcp",
"firstName": "Test",
"lastName": "User",
"dateOfBirth": "1990-01-01",
"mobileNumber": "+447000000000",
"addresses": [
{
"type": "current",
"ukAddress": {
"buildingNumber": "1",
"thoroughfare": "Test Street",
"townOrCity": "London",
"postcode": "SW1A 1AA"
}
}
],
"test": true,
"challengeOptions": {
"status": "challenge"
}
}Challenge Options
status | Behaviour |
|---|---|
"challenge" | Returns authentication-required. Submit OTP code 123456 to complete. |
"allow" | Completes immediately with synthetic report data. No challenge step required. |
"block" | Returns blocked status. Simulates a provider risk rejection. |
In test mode, the email field controls which synthetic data scenario is returned. Use a specific email prefix (the part before @) to select a scenario. Matching is case-insensitive and the domain part is ignored.
| Email prefix | Scenario | Records returned |
|---|---|---|
test001 | No records found | 0 consumer records, 0 previous searches |
test002 | Single PCP record | 1 active hire-purchase record |
test003 | Multiple mixed records | 3 records: 1 PCP (hire-purchase), 1 credit card, 1 mortgage |
test004 | All settled/closed | 2 settled accounts (PCP + credit card) with zero balance |
| anything else | Default | 4 records: 2 PCP, 1 credit card, 1 mortgage |
Example: Empty Report
Use test001@example.com to simulate a consumer with no credit history on file:
{
"sessionId": "test-session-1",
"bureaus": ["equifax"],
"ipAddress": "127.0.0.1",
"searchType": "pcp",
"firstName": "Test",
"lastName": "User",
"dateOfBirth": "1990-01-01",
"email": "test001@example.com",
"addresses": [
{
"type": "current",
"ukAddress": {
"buildingNumber": "1",
"thoroughfare": "Test Street",
"townOrCity": "London",
"postcode": "SW1A 1AA"
}
}
],
"test": true,
"challengeOptions": {
"status": "allow"
}
}challengeOptions to test different data sets through both the direct and OTP challenge flows.The following errors may occur during the credit report flow. All errors follow the standard error envelope format.
| HTTP | Code | When |
|---|---|---|
400 | validation-error | Missing or invalid fields in the request body |
402 | insufficient-funds | Wallet balance is too low for this operation |
404 | credit-report-not-found | The credit report ID does not exist |
404 | challenge-not-found | The challenge ID does not exist or does not belong to this report |
410 | challenge-expired | The OTP challenge has exceeded the 5-minute window |
429 | rate-limited | Too many requests |
Example Error Response
{
"code": "insufficient-funds",
"message": "Your wallet balance is insufficient for this operation"
}- Always provide a mobile number if using bureaus that may require OTP verification. Without it, the challenge cannot be delivered.
- Handle all three initial statuses (
complete,authentication-required,blocked) in your integration logic. - Use
externalReferenceto correlate credit reports with records in your own system. - Monitor
meta.wallet.balancein responses to track your credit usage and avoid402errors. - Implement timeout handling for OTP challenges. After 5 minutes the challenge expires and a new generate request is needed.
- Test all paths using test mode before going live. Use
challengeOptions.statusto exercise each branch.