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/bnpl

Request body

FieldTypeDescription
consumerRequiredobjectIdentifies the consumer. See Consumer object below. Either consumer_id (for returning customers) OR full PII fields (for new consumers).
transactionRequiredobjectThe transaction being financed. See Transaction object below.
plan_preferencestringPreferred payment plan: pay_in_4, pay_in_6, or monthly. AltFiScore selects the best match if omitted.

Consumer object

FieldTypeDescription
consumer_iduuidUUID of an existing consumer in your tenant. Skip the rest of the consumer fields if provided.
first_namestringRequired for new consumers.
last_namestringRequired for new consumers.
emailstringRequired for new consumers. Used for verification flows.
phonestringE.164 format. Used for SMS verification when KYC tier is enhanced/hard.
dobstringISO 8601 date (YYYY-MM-DD). Required for new consumers.
ssn_last4stringLast 4 digits of SSN. Required for enhanced/hard KYC. Encrypted at rest.
addressobjectMailing address. line1, city, state, zip required for new consumers.

Transaction object

FieldTypeDescription
amountRequireddecimalTransaction amount. Use a string to avoid floating-point issues.
currencyRequiredstringISO 4217 currency code. Currently only USD is supported.
merchant_nameRequiredstringDisplay name of the merchant.
merchant_categoryRequiredstringCategory slug (e.g., food_beverage, furniture, electronics).
product_descriptionstringShort 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

The HTTP status indicates whether the API call succeeded, not whether the consumer was approved. A successfully-evaluated decline is HTTP 200 with outcome: "declined". See the errors page for genuine error cases.

Response fields

FieldTypeDescription
decision_iduuidUnique identifier for this decision. Store for reconciliation and later lookup.
application_iduuidIdentifier for the underlying application. Typically equals decision_id for first-time decisions.
statusstringcomplete or needs_consumer_action.
kyc_tierstringsoft, enhanced, or hard. Indicates the verification depth applied.
kyc_statusstringmatched, pending, or skipped.
outcomestringapproved, declined, or referred. null when status is needs_consumer_action.
approved_amountdecimalString. The amount AltFiScore approved. May be less than requested for risk-based offers.
aprdecimalString. Annual percentage rate as a percentage (e.g. "5.0" means 5%).
term_monthsintegerLength of the loan in months.
payment_schedulearrayArray of payment objects: { num, due_date, amount }.
tila_disclosureobjectTILA Reg Z disclosure block ready to display to the consumer.
decline_reasonsarrayArray of { code, reason } objects. Populated when outcome is declined or referred.
consumer_complete_urlstringURL the consumer must visit when status is needs_consumer_action. Null otherwise.
polling_urlstringRelative path to poll for completion. Combine with the base URL.
estimated_completion_secondsintegerRough estimate of how long verification will take.
expires_attimestampISO 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

Both POST and GET endpoints are safe to retry. The POST endpoint with the same Idempotency-Key returns the cached decision. The GET endpoint is naturally idempotent.

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 missing
  • 401 invalid_api_key — bad or missing API key
  • 409 idempotency_conflict — same idempotency key with a different body
  • 422 unprocessable_entity — request is well-formed but fails business rules (e.g., amount below product minimum)

Next steps