Security Best Practices
This guide covers authentication, API key management, data handling, and compliance requirements for integrating with the relay platform.
Authentication Model
relay uses two authentication mechanisms depending on your integration path:
| Integration | Auth Method | Header | Token Lifetime |
|---|---|---|---|
| REST API (direct) | API Key | X-API-KEY | Permanent (until rotated) |
| Embedded SDK | Publishable Key + JWT | Authorization: Bearer <token> | 1 hour |
API Key Authentication (REST API)
All direct API calls require your API key in the X-API-KEY header:
curl -X GET "https://api.ir.app/api/experian/renters/{renterId}/report" \
-H "X-API-KEY: your-api-key"
Your API key is a secret credential — treat it like a database password.
Never expose your API key in client-side code, public repositories, or browser network requests. API keys grant full access to your account's data.
SDK Token Exchange (Embedded SDK)
The SDK uses a two-step authentication flow that keeps secrets server-side:
- Your page loads the SDK with a publishable key (safe for client-side use)
- The SDK exchanges the publishable key for a short-lived JWT token via
POST /auth/token/exchange - All subsequent SDK requests use the JWT in the
Authorizationheader - The JWT is origin-bound — it can only be used from the domain that requested it
Browser relay
│ │
│ SDK loads with pk_live_* │
│─────────────────────────────>│
│ POST /auth/token/exchange │
│ (publishableKey + Origin) │
│─────────────────────────────>│
│ JWT (1hr, origin-bound) │
│<─────────────────────────────│
│ All API calls use Bearer │
│─────────────────────────────>│
The SDK handles token refresh automatically. You do not need to manage tokens yourself.
POST /auth/token/exchange requires the X-Publishable-Key and Origin headers and returns:
{
"token": "<JWT>",
"token_type": "Bearer",
"expires_in": 3600,
"flags": { "someFeatureFlag": true }
}
flags is a set of server-evaluated feature flags for the account/key, delivered alongside the token so the embedded view can branch on account-level feature state without a separate round trip. Treat it as opaque, additive, and non-exhaustive — new flags may appear without notice and unrecognized keys should be ignored.
API Key Management
Key Types
| Key Prefix | Environment | Usage |
|---|---|---|
pk_live_* | Production | Publishable key — safe for client-side SDK |
pk_test_* | Test/Staging | Publishable key — safe for client-side SDK |
| API Key (no prefix) | Both | Server-side only — never expose to clients |
Storage Guidelines
Do:
- Store API keys in environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)
- Use different keys for each environment (development, staging, production)
- Restrict key access to the services that need them
Don't:
- Commit keys to version control (add to
.gitignore/.env) - Log API keys in application logs
- Pass keys as URL query parameters
- Share keys over unencrypted channels (email, Slack messages)
# Good: environment variable
export RELAY_API_KEY="your-api-key"
# Good: .env file (add .env to .gitignore)
RELAY_API_KEY=your-api-key
Key Rotation
If you suspect a key has been compromised:
- Contact relay support immediately to issue a new key
- Update all services that use the compromised key
- Verify the old key is no longer accepted
- Audit recent API activity for unauthorized access
Iframe Trust Model (SDK)
The Embedded SDK renders inside an iframe served from relay's domain. This architecture provides important security boundaries:
How It Works
- The SDK creates an iframe pointed at
https://embedded.ir.app(environment-specific variants:embedded.stage.ir.app,embedded.preprod.ir.app) - Your page and the iframe communicate via the browser's
postMessageAPI - The iframe validates the origin of every incoming message
- Sensitive data (SSN, credit reports) never touches your DOM
Origin Validation
The JWT issued during token exchange is bound to the Origin header of the requesting page. This means:
- A token obtained on
https://yourapp.comcannot be reused onhttps://attacker.com - The iframe validates that messages come from the expected parent origin
- CSRF attacks are mitigated because the token won't work cross-origin
What This Means for You
- You never handle raw PII — the iframe collects and submits sensitive data directly to relay
- Your page receives only results — application IDs, statuses, and scores (not SSNs or full reports)
- No PCI/PII compliance burden for SDK integrations — sensitive data stays within relay's domain
If your integration uses the REST API instead of the SDK, you are responsible for PII handling and should follow the Data Handling section below.
Handling Personally Identifiable Information (PII)
REST API integrations handle sensitive consumer data. Follow these practices to stay compliant.
Data Classification
| Classification | Examples | Handling |
|---|---|---|
| Highly Sensitive | SSN, credit report data, KBA answers | Encrypt at rest, never log, minimize retention |
| Sensitive | Name, DOB, address, phone, email | Encrypt at rest, mask in logs |
| Non-Sensitive | Screening IDs, status codes, timestamps | Standard handling |
Transmission Security
- All relay APIs require HTTPS (TLS 1.2+). HTTP requests are rejected.
- Validate TLS certificates in your HTTP client — do not disable certificate verification.
- Use the latest version of your HTTP client library to ensure current cipher suites.
Logging and Monitoring
// BAD: logging raw PII
console.log(`Registering renter: SSN=${ssn}, DOB=${dob}`);
// GOOD: log only identifiers
console.log(`Registering renter: id=${renterId}`);
- Never log SSNs, full credit report data, or KBA answers
- Mask or redact sensitive fields before logging (e.g.,
***-**-7315) - relay's API scrubs sensitive data from internal logs automatically
Data Retention
- Credit reports expire 30 days after generation
- Do not cache or store credit report data beyond what is necessary for your business purpose
- If you must store report data, encrypt it at rest and enforce access controls
- Delete consumer data when the business purpose has been fulfilled
Brute Force Protection
The API enforces rate limiting and Experian-driven lockouts on authentication-related endpoints:
| Protection | Behavior |
|---|---|
| Failed KBA/OTP attempts | Repeated failures cause Experian to block the user. The block duration is set by Experian — the error message includes the unblock time. |
| API rate limiting (SDK token exchange) | 60 requests per minute per publishable key (fixed one-minute window; per-key overrides available). Exceeding it returns HTTP 429 with a Retry-After header. |
When a consumer is locked out:
- The REST API returns HTTP 423 Locked (
errorCode 300) with the unblock time in the message - The SDK/platform path returns
SCREENING_TOO_MANY_ATTEMPTS(HTTP 429) - The block expires automatically at the time indicated; contact support if you need help sooner
Do not retry failed KBA or OTP submissions automatically. Each failed attempt counts toward the lockout threshold. Present errors to the user and let them correct their input.
FCRA Compliance (FR-9)
relay's credit reporting services are governed by the Fair Credit Reporting Act (FCRA). As an integrator, you must:
Permissible Purpose
- Only pull credit reports for a legally permissible purpose (e.g., tenant screening for a rental application)
- Document and retain evidence of the consumer's consent
- The SDK handles consent collection automatically; REST API integrators must collect consent independently
Consumer Disclosures
- Inform consumers that a credit report will be pulled before initiating the screening
- Provide consumers with a copy of their rights under the FCRA
- If an adverse action is taken based on report data, provide the required adverse action notice
Data Access Controls
- Restrict access to credit report data to authorized personnel only
- Maintain audit logs of who accessed report data and when
- The API enforces row-level security — each API key can only access its own account's data
Report Sharing
When sharing reports between consumers and organizations/agents:
Embedded SDK / platform path — sharing is consumer-consent driven. The embedded flow calls these endpoints and surfaces the results via the onReportShared / onReportRevoked SDK callbacks:
# Consumer consents to share their report
POST /reports/{screeningId}/consent-to-share
# Consumer revokes sharing (permanent — cannot be re-enabled for the same share)
POST /reports/{screeningId}/revoke-sharing
REST API path — partners link and unlink renters directly:
# Link a renter's report to an organization
PUT /api/experian/renters/{renterId}/organizations/{organizationId}
# Unlink (revoke access)
DELETE /api/experian/organizations/{organizationId}/renters/{renterId}
- Consumers control who can view their reports
- Revocation takes effect immediately; on the SDK path it is permanent
Compliance Checklist
Use this checklist to verify your integration meets security and compliance requirements:
API Key Security
- API keys stored in environment variables or secrets manager
- Keys are not committed to version control
- Different keys used for test and production environments
- Keys are not exposed in client-side code or logs
Data Handling
- All API calls use HTTPS
- SSNs and sensitive data are not logged
- Credit report data retention policy is defined and enforced
- PII is encrypted at rest if stored
Authentication
- Failed KBA/OTP attempts are not auto-retried
- HTTP 423 (Locked) responses are handled gracefully
- SDK publishable keys are used for client-side (not API keys)
FCRA Compliance
- Consumer consent is collected before screening
- Adverse action notice process is documented
- Access to report data is restricted and audited
- Report sharing respects consumer revocation
SDK-Specific
- SDK loads over HTTPS
-
onErrorcallback handles authentication failures gracefully - SDK is destroyed on user logout (
sdk.destroy()) - No attempts to access iframe contents directly
Next Steps
- Getting Started — Make your first API call
- Error Codes — Complete error reference
- Troubleshooting — Common integration issues and solutions