Lendfy API

A REST API for the whole loan book — the same API the Lendfy app runs on. Use it to bring customers in from another system, keep an external product in sync, or build your own tooling on top of your data.

Overview

The base URL for all requests:

https://api.lendfy.io/v1

Every request and response body is JSON (Content-Type: application/json). All traffic requires TLS. Responses include an X-Request-Id header — quote it when reporting an issue and we can find the exact request in our logs.

Authentication

Authenticate with your organization's API key as a Bearer token. Keys start with lfy_ and are scoped to your organization — you can only ever see your own data.

curl https://api.lendfy.io/v1/customers \
  -H "Authorization: Bearer lfy_your_api_key"
Keep keys server-side. An API key carries full access to your book. Never ship one in a browser, mobile app, or public repository. Contact Lendfy to issue or rotate keys.

Errors

Errors use one envelope everywhere — a machine-readable code, a human-readable message, and the request id for correlation. Some errors attach extra fields (for example existing_id on a duplicate conflict).

{
  "error": {
    "code": "duplicate_suspected",
    "message": "This looks like Maria Lopez, who is already on file…",
    "request_id": "req_8f31c2",
    "existing_id": "7b0c…"
  }
}
StatusCodeMeaning
401unauthorizedMissing or invalid API key.
404not_foundNo such resource in your organization.
409duplicate_suspectedCustomer create matched someone already on file.
422customer_incompleteCustomer create is missing required fields (name, SSN, DOB, email, phone, address, employer, pay amount, pay frequency, bank routing + account). The missing list names each one. PATCH stays field-by-field.
409idempotency_conflictIdempotency-Key reused with a different body.
422validation_errorA field failed validation — the message names it.

Idempotency

Every write (POST, PATCH) requires an Idempotency-Key header — any unique string, a UUID is perfect. If a request is retried with the same key and body, the stored response is replayed instead of the work running twice (the replay carries X-Idempotent-Replay: true). The same key with a different body returns 409 idempotency_conflict.

# a network timeout is safe to retry with the same key —
# the customer can never be created twice
curl -X POST https://api.lendfy.io/v1/customers \
  -H "Authorization: Bearer lfy_…" \
  -H "Idempotency-Key: 6f9c1a2e-4b7d-4c11-9a3f-2f8e5d6c7b01" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Maria","last_name":"Lopez"}'

Pagination

List endpoints are cursor-paginated. Pass limit (default 25, max 100); the response carries next_cursor — pass it back as cursor for the next page. A null cursor means you've reached the end.

{ "data": [ … ], "next_cursor": "MTcyNT…" }

Money, dates & PII

List & search customers

GET/v1/customers

Newest first. query matches name and email (case-insensitive contains), a 7+ digit string matches phone, and a 4-digit string matches SSN last-four.

ParameterDescription
querySearch text — name, email, phone digits, or SSN last-4.
limitPage size, 1–100. Default 25.
cursorCursor from the previous page.

Retrieve a customer

GET/v1/customers/{id}
{
  "id": "7b0c1d2e-…",
  "first_name": "Maria",
  "last_name": "Lopez",
  "email": "maria@example.com",
  "phone": "8185550142",
  "dob": "1990-04-12",
  "ssn_last4": "6789",
  "payroll_cadence": "biweekly",
  "bank_routing": "021000021",
  "bank_account_last4": "6789",
  "store_id": "1111…",
  "status": "active",
  "external_source": null,
  "external_ref": null,
  "created_at": "2026-08-10T20:14:03Z",
  "…": "address, employment, ID and consent fields omitted for brevity"
}

Create a customer

POST/v1/customers

Only first_name and last_name are required; everything else is optional and validated when present. Key fields:

FieldDescription
first_name required1–120 chars.
last_name required1–120 chars.
middle_name / suffixOptional identity extras.
dobYYYY-MM-DD. Borrowers must be 18+.
ssn9 digits (full) or 4 (last-four). Stored as last-4 + salted hash only.
email / phoneLight format check; phone normalizes to digits.
addr1, addr2, city, state, zipMailing address; 2-letter state.
payroll_cadenceweekly · biweekly · semimonthly · monthly
employer_name, employer_address, employment_start/end, pay_amount_centsEmployment & income — pay amount is PER PAYCHECK, in cents. Benefits customers put the program name in employer_name. Employer is required to originate.
payroll_cadence, last_pay_dateThe payday anchor. Responses include a derived read-only next_pay_date.
statusgood (default) · prospect · on_hold · payment_plan · active_military · bankrupt · legal · deceased · done_borrowing · inactive. Only good/prospect can originate loans.
bank_routing9 digits — the ABA check digit is verified.
bank_account4–17 digits. Only the last four are stored.
bank_account_type / bank_name / direct_depositInstrument details.
id_type, id_number, id_state, id_expiresGovernment ID.
sms_opt_in / email_opt_in / do_not_callContact consent flags.
store_idLocation the customer belongs to (must be yours).
external_source / external_refYour system's identifier — see importing.
allow_duplicateSet true to override a duplicate_suspected 409.

Duplicate protection

If the SSN — or name plus date of birth — matches a customer already on file, the create returns 409 duplicate_suspected with existing_id and existing_name so you can link to the existing record instead. If it really is a different person, re-send with "allow_duplicate": true.

Update a customer

PATCH/v1/customers/{id}

Partial update: only the fields present in the body change. An explicit null clears an optional field; names cannot be emptied. Sending a new ssn or bank_account replaces the stored last-four (the full numbers are still never stored). Every update is audited with before-and-after values.

Importing a book from another system

Bringing customers over from a previous LMS? Stamp each record with your identifiers so the two systems stay linkable:

Combine with Idempotency-Key per record and the duplicate protection above, and an import script that crashes halfway is safe to simply run again.

List loans

GET/v1/loans
ParameterDescription
customer_idOnly this customer's loans.
statusactive · paid · pending · charged_off · void · canceled
bucketServicing bucket: past_due · due_today · due_week · current · payment_plan · returned
limit / cursorPagination as above.

Loan rows carry principal, due date, the balance and days-past-due as most recently reported, and any scheduled collection (collection_scheduled_date / _method).

Retrieve a loan

GET/v1/loans/{id}

Loan ledger

GET/v1/loans/{id}/ledger

The loan's money history — disbursements, payments, adjustments — newest first, with the running balance. Amounts follow the cents convention: payments are negative, charges positive.

Other endpoints

The surface keeps growing with the product. Also available today:

Questions, keys, or a field you need that isn't here? Write to info@lendfy.io — the API is built alongside real lending operations and we ship fast.