BNPL Decisions
Score a buy-now-pay-later transaction in real time. Returns an approval with TILA-compliant disclosures, a payment schedule, or a decline with ECOA reason codes. Sub-500ms p99 latency.
Create a decision
POST/v1/decisions/bnplRequest body
| Field | Type | Description |
|---|---|---|
consumerRequired | object | Identifies the consumer. See Consumer object below. Either consumer_id (for returning customers) OR full PII fields (for new consumers). |
transactionRequired | object | The transaction being financed. See Transaction object below. |
plan_preference | string | Preferred payment plan: pay_in_4, pay_in_6, or monthly. AltFiScore selects the best match if omitted. |
Consumer object
| Field | Type | Description |
|---|---|---|
consumer_id | uuid | UUID of an existing consumer in your tenant. Skip the rest of the consumer fields if provided. |
first_name | string | Required for new consumers. |
last_name | string | Required for new consumers. |
email | string | Required for new consumers. Used for verification flows. |
phone | string | E.164 format. Used for SMS verification when KYC tier is enhanced/hard. |
dob | string | ISO 8601 date (YYYY-MM-DD). Required for new consumers. |
ssn_last4 | string | Last 4 digits of SSN. Required for enhanced/hard KYC. Encrypted at rest. |
address | object | Mailing address. line1, city, state, zip required for new consumers. |
Transaction object
| Field | Type | Description |
|---|---|---|
amountRequired | decimal | Transaction amount. Use a string to avoid floating-point issues. |
currencyRequired | string | ISO 4217 currency code. Currently only USD is supported. |
merchant_nameRequired | string | Display name of the merchant. |
merchant_categoryRequired | string | Category slug (e.g., food_beverage, furniture, electronics). |
product_description | string | Short description of what is being purchased. |
Example request
Request body
{
"consumer": {
"first_name": "Marcus",
"last_name": "Chen",
"email": "marcus@example.com",
"phone": "+15551234567",
"dob": "1988-11-08",
"ssn_last4": "4521",
"address": {
"line1": "127 Tremont St",
"city": "Cambridge",
"state": "MA",
"zip": "02138"
}
},
"transaction": {
"amount": 850.00,
"currency": "USD",
"merchant_name": "Sunset Furniture",
"merchant_category": "furniture",
"product_description": "Mid-century walnut sofa"
},
"plan_preference": "pay_in_4"
}Response
The response status depends on the KYC tier AltFiScore assigns. Soft-tier decisions complete immediately. Enhanced and hard-tier decisions return needs_consumer_action with a URL the consumer must visit to complete verification.
Approved (soft KYC)
Status complete with outcome approved. Use the payment_schedule and tila_disclosure fields to present the offer to the consumer.
200 OK · approved
{
"decision_id": "8496f4b6-5514-4e0b-a6bf-6da6fb987448",
"application_id": "8496f4b6-5514-4e0b-a6bf-6da6fb987448",
"status": "complete",
"kyc_tier": "soft",
"kyc_status": "matched",
"outcome": "approved",
"approved_amount": "850.00",
"currency": "USD",
"apr": "0.0",
"term_months": 2,
"payment_schedule": [
{ "num": 1, "due_date": "2026-05-15", "amount": "212.50" },
{ "num": 2, "due_date": "2026-05-29", "amount": "212.50" },
{ "num": 3, "due_date": "2026-06-12", "amount": "212.50" },
{ "num": 4, "due_date": "2026-06-26", "amount": "212.50" }
],
"tila_disclosure": {
"amount_financed": "850.00",
"finance_charge": "0.00",
"total_of_payments": "850.00",
"apr": "0.0",
"payment_frequency": "biweekly"
},
"decline_reasons": [],
"expires_at": "2026-05-15T20:30:20.566665Z"
}Pending (enhanced/hard KYC)
Status needs_consumer_action. The consumer must visit consumer_complete_url to complete verification. Your application can either poll polling_url or wait for a webhook (coming Q3 2026).
200 OK · pending
{
"decision_id": "8496f4b6-5514-4e0b-a6bf-6da6fb987448",
"application_id": "8496f4b6-5514-4e0b-a6bf-6da6fb987448",
"status": "needs_consumer_action",
"kyc_tier": "hard",
"kyc_status": "pending",
"consumer_complete_url": "https://verify.altfiscore.com/c/8496f4b6...",
"polling_url": "/v1/decisions/bnpl/8496f4b6-5514-4e0b-a6bf-6da6fb987448",
"estimated_completion_seconds": 90,
"expires_at": "2026-05-15T21:00:00Z"
}Declined
Status complete with outcome declined. The decline_reasons array contains ECOA-compliant codes. See compliance for guidance on adverse action handling.
200 OK · declined
{
"decision_id": "f7e8d6c5-...",
"application_id": "f7e8d6c5-...",
"status": "complete",
"outcome": "declined",
"decline_reasons": [
{ "code": "AA-01", "reason": "Insufficient cash flow stability over the last 90 days." },
{ "code": "AA-07", "reason": "Outstanding obligations exceed approval thresholds." }
]
}A decline is still a 200
outcome: "declined". See the errors page for genuine error cases.Response fields
| Field | Type | Description |
|---|---|---|
decision_id | uuid | Unique identifier for this decision. Store for reconciliation and later lookup. |
application_id | uuid | Identifier for the underlying application. Typically equals decision_id for first-time decisions. |
status | string | complete or needs_consumer_action. |
kyc_tier | string | soft, enhanced, or hard. Indicates the verification depth applied. |
kyc_status | string | matched, pending, or skipped. |
outcome | string | approved, declined, or referred. null when status is needs_consumer_action. |
approved_amount | decimal | String. The amount AltFiScore approved. May be less than requested for risk-based offers. |
apr | decimal | String. Annual percentage rate as a percentage (e.g. "5.0" means 5%). |
term_months | integer | Length of the loan in months. |
payment_schedule | array | Array of payment objects: { num, due_date, amount }. |
tila_disclosure | object | TILA Reg Z disclosure block ready to display to the consumer. |
decline_reasons | array | Array of { code, reason } objects. Populated when outcome is declined or referred. |
consumer_complete_url | string | URL the consumer must visit when status is needs_consumer_action. Null otherwise. |
polling_url | string | Relative path to poll for completion. Combine with the base URL. |
estimated_completion_seconds | integer | Rough estimate of how long verification will take. |
expires_at | timestamp | ISO 8601 timestamp. The offer or pending state expires at this time. |
Poll a decision
GET/v1/decisions/bnpl/{decision_id}Returns the current state of a previously-created decision. Idempotent and safe to call repeatedly. Recommended polling interval: 2–3 seconds.
Poll a pending decision
curl 'https://api.altfiscore.com/v1/decisions/bnpl/8496f4b6-5514-4e0b-a6bf-6da6fb987448' \
-H 'Authorization: Bearer altfi_test_YOUR_KEY'While the consumer is completing verification, the response is identical in shape to the original — same needs_consumer_action body. Once verification completes, the response transitions to complete with a full approval or decline.
Use idempotency
Errors
Returns standard HTTP error codes. The full catalog is in the errors reference. Most common errors for this endpoint:
400 invalid_request— required field missing401 invalid_api_key— bad or missing API key409 idempotency_conflict— same idempotency key with a different body422 unprocessable_entity— request is well-formed but fails business rules (e.g., amount below product minimum)
Next steps
- Try this endpoint in the live demo
- Set up idempotent retries
- Handle declines with the compliance guide