Skip to main content

Troubleshooting

This guide covers common issues encountered during integration, with causes and solutions. For a full list of error codes, see the Error Codes reference.

Authentication Issues

API key rejected (403)

Symptoms: HTTP 403 with errorCode 324 ("Invalid client secret.") on every request.

Causes and solutions:

CauseSolution
Missing X-API-KEY headerAdd the header to all requests: -H "X-API-KEY: your-key"
Incorrect key valueVerify the key matches what was issued during onboarding
Using a production key against UAT (or vice versa)Use the key that matches your target environment
Key has been rotatedContact relay support for your current key
# Verify your key works with a simple health check
curl -v "https://api.preprod.ir.app/api/experian/agents/{agentId}/auth/status" \
-H "X-API-KEY: your-api-key"

SDK token exchange fails

Symptoms: SDK fails to initialize, onError fires immediately with an authentication error.

Causes and solutions:

CauseSolution
Invalid publishable key formatKeys must start with pk_live_ or pk_test_
Key is deactivatedContact support to verify your key status
Origin mismatchThe domain serving your page must match the allowed origins for your key
Mixed content (HTTP page loading HTTPS SDK)Serve your page over HTTPS
// Check for initialization errors
const sdk = Intellirent.init({
publishableKey: "pk_test_xxxxxxxxxxxxxxxx",
userContext: { userId: "user_123", userType: "CONSUMER" },
});

sdk.mount("#container", {
onError: (error) => {
// Log the error code for debugging
console.error(`SDK Error [${error.code}]: ${error.message}`);
},
});

Consumer locked out (423 Locked)

Symptoms: HTTP 423 on KBA or OTP submission.

Cause: The consumer has failed authentication repeatedly and Experian has blocked them.

Solution: The block duration is set by Experian — the error message includes the unblock time. Do not retry automatically — present the error to the user.

// Example 423 response
{
"payload": null,
"errors": [{
"errorCode": 300,
"errorOnField": null,
"errorMessage": "Experian user blocked from registration. Unblocks at 2026-07-08T18:50:00Z"
}]
}
tip

During testing, use the Test Consumers page for correct KBA answers to avoid triggering lockouts.

Consumer Registration Issues

Registration returns KBA questions

Symptom: POST /renters/{renterId} returns KBA questions instead of completing immediately.

This is expected behavior. Most consumers require identity verification via KBA or OTP before their credit report can be pulled. The flow is:

  1. Register consumer → API returns KBA/OTP challenge
  2. Submit KBA answers or OTP code → API returns verification result
  3. On success → pull the report

See the Getting Started guide for the complete flow.

KBA answers rejected

Symptoms: HTTP 422 with the Experian step-up payload returned.

Causes:

CauseSolution
Incorrect answers submittedVerify against Test Consumers for sandbox testing
Answer format mismatchSubmit integer choices wrapped in kbaAnswers objects (see below), not answer text
Session expiredRe-register the consumer to get fresh KBA questions
Consumer data mismatchEnsure the SSN, name, and address are accurate
# Submit KBA answers — use integer answer choices, not text
curl -X POST "https://api.preprod.ir.app/api/experian/renters/{renterId}/kba" \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"kbaAnswers": [
{ "answer": 1 },
{ "answer": 3 },
{ "answer": 2 }
]
}'
warning

Each failed KBA attempt counts toward Experian's lockout — repeated failures return HTTP 423 with the unblock time in the message. Do not retry programmatically.

OTP code not received

Symptoms: Consumer doesn't receive the OTP code after registration.

Solutions:

  1. Verify the phone number is correct and can receive SMS
  2. Check that the phone type matches (mobile, home, work) — OTP requires a mobile number
  3. Use the resend endpoint:
GET /renters/{renterId}/otp/resend
  1. In the test environment, OTP codes may be returned in the API response rather than sent via SMS

Report Retrieval Issues

Report expired

Symptoms: HTTP 400 with error code 138 (errorOnField: "tokenExpiration", message "Report has expired") on the REST API. On the SDK/platform path the same condition surfaces as HTTP 410 (SCREENING_REPORT_EXPIRED).

Cause: Reports expire 30 days after generation.

Solution: Re-register the consumer and complete identity verification to pull a fresh report.

# Check report expiry before attempting to retrieve
GET /renters/{renterId}/report-expiry

Payment not completed (HTTP 402) — Consumer API only

Symptoms: HTTP 402 with error code 285 when pulling a report via the Consumer API. The payload contains {reason, message}, e.g. reason: "NO_COMPLETED_PAYMENT".

Cause: The renter has not completed their per-transaction Stripe Checkout payment for this screening. This is unrelated to your organization's relay billing.

Solution: Direct the renter to complete payment, then retry. The condition clears automatically once payment completes — there's nothing for your integration to configure.

Credit balance exhausted (HTTP 402)

Symptoms: HTTP 402 with a structured {"error": {"code": "PAYMENT_REQUIRED", ...}} body when pulling a report, or the SDK onError callback fires with code: 'PAYMENT_REQUIRED' and recoverable: false.

Cause: Your organization is on the prepaid credit billing model and its credit balance has reached $0. The first pull of a report is blocked until billing is resolved.

Solution: This is deterministic — retrying will not succeed. Add credits to the account (or resolve the billing issue), then retry. Surface the condition to the account owner rather than the end consumer. See the Billing Technical Addendum and the SDK Billing guide.

Report not available after KBA

Symptoms: KBA succeeds but report retrieval returns an error.

Possible causes:

CauseSolution
Processing delayWait a few seconds and retry (1-2 retries max)
Consumer has a frozen credit fileUse test consumer "Samuel Bullock" (SSN 666660225) to simulate this scenario. A frozen file cannot be reported on.
Profile mismatchThe consumer's data doesn't match records on file — verify SSN, name, and address

Report content types

Reports can be returned as JSON or HTML:

# JSON (default for relay clients)
GET /renters/{renterId}/report
Accept: application/json

# HTML (rendered report)
GET /renters/{renterId}/report
Accept: text/html

# Or use the format query parameter
GET /renters/{renterId}/report?format=json
GET /renters/{renterId}/report?format=html

You can also exclude specific data sections:

# Exclude background data
GET /renters/{renterId}/report?excludeBackground=true

# Exclude housing court data
GET /renters/{renterId}/report?excludeHousingCourt=true

Payment Issues

Checkout session creation fails

Symptoms: POST /payment/checkout-session returns 400 or 500.

Common causes:

HTTP StatusCauseSolution
400Missing required fieldsInclude renterId, successUrl, and cancelUrl
400Amount below minimum feeAmount must be >= the configured relay fee
400Invalid redirect URLURLs must be HTTPS in production and valid URIs
409Duplicate paymentConsumer already paid within the last 30 days
500Stripe configuration errorContact support — your Stripe Connect setup may be incomplete
curl -X POST "https://api.ir.app/payment/checkout-session" \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"renterId": "renter-123",
"successUrl": "https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}",
"cancelUrl": "https://yourapp.com/cancel"
}'
tip

In the test environment, use Stripe's test card numbers (e.g., 4242 4242 4242 4242) to simulate payments.

Webhook not received

Symptoms: Checkout completes but your webhook endpoint isn't called.

Checklist:

  1. Verify your webhook endpoint is publicly accessible (not localhost)
  2. Webhook signature verification must use the correct endpoint secret
  3. Return HTTP 200 from your webhook handler — even on processing errors — to prevent Stripe retries
  4. Check Stripe Dashboard > Webhooks for delivery attempts and failures

Duplicate payment error (409)

Symptoms: HTTP 409 when creating a checkout session.

Cause: The consumer has already completed a payment within the last 30 days.

Solution: This is a safeguard against accidental double charges. If the consumer genuinely needs to pay again, contact support.

Credit balance exhausted (402)

Symptoms: Report requests return HTTP 402 with error code PAYMENT_REQUIRED; the embedded SDK delivers code: "PAYMENT_REQUIRED" with recoverable: false to onError.

Cause: Your account is on the prepaid credit billing model and its credit balance has reached $0. Accounts billed per transaction are not affected.

Solution: Add credits via your relay billing portal or account manager — service resumes automatically once the balance is restored. If you need help accessing your billing portal, contact integration support. Do not retry the request: unlike transient 5xx errors, a 402 returns the identical result on every attempt until credits are added. See the Billing Technical Addendum and the SDK Billing guide.

SDK Issues

Form doesn't appear

Symptoms: sdk.mount() resolves but no iframe is visible.

Checklist:

CheckFix
Container element exists in DOMEnsure the selector matches an existing element: document.querySelector('#container')
Container has dimensionsGive the container a min-height (e.g., 400px)
SDK script loadedVerify window.Intellirent exists before calling init()
Multiple mount callsOnly one form can be mounted at a time — call sdk.unmount() first
// Verify the SDK is loaded
if (typeof Intellirent === "undefined") {
console.error("Intellirent SDK not loaded. Check the script tag.");
} else {
const sdk = Intellirent.init({
publishableKey: "pk_test_xxxxxxxxxxxxxxxx",
userContext: { userId: "user_123", userType: "CONSUMER" },
});
await sdk.mount("#container");
}

Form loads but is blank or cut off

Symptoms: The iframe appears but shows no content or is clipped.

Solutions:

  • Use the onResize callback to adjust the container height dynamically:
sdk.mount("#container", {
onResize: ({ height }) => {
document.getElementById("container").style.height = `${height}px`;
},
});
  • Do not set overflow: hidden on the container element
  • Ensure no CSS max-height is constraining the container

SDK errors after navigation (SPA)

Symptoms: Errors when remounting the form after a client-side navigation in a single-page app.

Cause: The previous SDK instance was not cleaned up.

Solution: Always unmount or destroy before navigating away:

// React example
useEffect(() => {
const sdk = Intellirent.init({
publishableKey: "pk_live_xxxxxxxxxxxxxxxx",
userContext: { userId: user.id, userType: "CONSUMER" },
});

sdk.mount("#form-container", { onReady: () => setLoading(false) });

// Clean up on unmount
return () => {
sdk.destroy();
};
}, []);

See Lifecycle Events for complete cleanup patterns.

Network and Timeout Issues

Request timeouts

Symptoms: Requests hang and eventually time out.

Context: Screening API calls can take up to 20 seconds, particularly for identity verification and report generation.

Recommendations:

OperationRecommended Timeout
Token exchange10 seconds
Consumer registration30 seconds
KBA/OTP submission30 seconds
Report retrieval30 seconds
Payment checkout creation15 seconds

HTTP 503 / 504 errors

Symptoms: Intermittent 503 (Service Unavailable) or 504 (Gateway Timeout).

Causes:

CodeCauseAction
503Screening service temporarily unavailableRetry after 5-10 seconds (max 2 retries)
504Request exceeded gateway timeoutRetry once; if persistent, contact support

Retry strategy:

async function callWithRetry(fn, maxRetries = 2) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) throw error;
if (error.status === 503 || error.status === 504) {
await new Promise((r) => setTimeout(r, 5000 * (attempt + 1)));
continue;
}
throw error; // Don't retry other errors
}
}
}
warning

Only retry 503/504 errors. Do not retry 4xx errors (especially 401, 422, 423) — these indicate client-side issues that retrying won't fix.

Environment-Specific Issues

UAT vs Production differences

AspectUAT (api.preprod.ir.app)Production (api.ir.app)
API keysTest keys issued separatelyProduction keys from onboarding
Consumer dataUse Test Consumers onlyReal consumer data
Credit reportsSynthetic test dataReal credit data
PaymentsStripe test mode (test card numbers)Live payments
OTP deliveryMay be returned in API responseSent via SMS to consumer
Redirect URLsHTTP or HTTPS allowedHTTPS required

Common UAT mistakes

  • Using real SSNs in the test environment (use test consumer data instead)
  • Using production API keys against the UAT endpoint
  • Expecting OTP SMS delivery in the test environment
  • Using HTTP redirect URLs that will fail in production

Getting Help

If you're stuck:

  1. Check the Error Codes reference for your specific error code
  2. Review the API reference in the sidebar for endpoint-specific documentation
  3. Verify your request against the Getting Started examples
  4. Contact relay integration support with:
    • Your error code and HTTP status
    • The endpoint you're calling
    • Your environment (UAT or production)
    • A sanitized request/response (remove API keys and PII)
danger

Never include API keys, SSNs, or other sensitive data in support requests. Mask or redact before sharing.