Skip to main content
POST
/
loans
cURL
curl --request POST \
  --url https://api.example.com/loans \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <api-key>' \
  --data '
{
  "id": "LOAN-12345",
  "customerId": "CUST-001",
  "originationDate": "2026-04-01",
  "totalBalance": 425,
  "amountDue": 125,
  "dueDate": "2026-05-01",
  "servicingStatus": "ACTIVE",
  "originalFundedAmount": 500,
  "originalTotalOwed": 650,
  "interestRate": 0.2999,
  "apr": 0.355,
  "startDate": "2023-12-25",
  "payoffAmount": 1,
  "totalPayoffAmount": 1,
  "balanceAtTransfer": 1,
  "preTransferPayments": 1,
  "isSettled": true,
  "chargeoffDate": "2023-12-25",
  "chargeoffAmount": 1,
  "autopayEnabled": true,
  "isVisibleInBorrowerPortal": true,
  "daysPastDueAtBoarding": 1,
  "daysSinceOriginationAtBoarding": 1,
  "externalLmsMetadata": {},
  "portfolioId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "paymentPlanMinPercentage": 0.5,
  "paymentPlanMaxPercentage": 0.5,
  "taskType": "Collections"
}
'
{
  "id": "<string>",
  "customerId": "<string>",
  "type": "<string>",
  "applicationStatus": "<string>",
  "originalFundedAmount": 123,
  "originalTotalOwed": 123,
  "interestRate": 123,
  "apr": 123,
  "originationDate": "2023-12-25",
  "startDate": "2023-12-25",
  "totalBalance": 123,
  "payoffAmount": 123,
  "totalPayoffAmount": 123,
  "balanceAtTransfer": 123,
  "preTransferPayments": 123,
  "amountDue": 123,
  "dueDate": "<string>",
  "servicingStatus": "<string>",
  "stopServicingReason": "<string>",
  "isSettled": true,
  "chargeoffDate": "<string>",
  "chargeoffAmount": 123,
  "autopayEnabled": true,
  "noCallReason": "<string>",
  "noTextReason": "<string>",
  "noEmailReason": "<string>",
  "isVisibleInBorrowerPortal": true,
  "daysPastDueAtBoarding": 123,
  "daysSinceOriginationAtBoarding": 123,
  "externalLmsMetadata": {},
  "portfolioId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "paymentPlanMinPercentage": 123,
  "paymentPlanMaxPercentage": 123,
  "taskType": "<string>"
}

Overview

Create or update (upsert) a loan pushed from an external partner. The loan is keyed on the combination of customerId and id (your external loan reference). If an active loan with the same external reference already exists for the customer, it is updated in place; otherwise a new loan is created. This endpoint is designed for collections intake — all loans must be created with servicingStatus: ACTIVE.

Upsert Behavior

POST /loans is an upsert. If you push a loan that already exists (matched on id + customerId among active loans), we update the existing record. Soft-deleted or deactivated (NOT_SERVICING) loans are not matched — a new active loan is created instead.
  • New loan: Returns 201 Created
  • Existing loan updated: Returns 200 OK
  • Concurrent duplicate: Returns 409 Conflict

Required Fields

FieldTypeDescription
idstringYour stable loan reference ID. Must be unique per customer among active loans.
customerIdstringMust match the id used when the customer was pushed via POST /customers.
amountDuenumberOrigination amount due. Must be positive. Immutable after creation — cannot be changed on subsequent upserts.
dueDatestringNext scheduled payment date (YYYY-MM-DD).
servicingStatusstringMust be ACTIVE on create. On upsert, can be set to NOT_SERVICING to stop servicing.
totalBalancenumberCurrent outstanding balance. Must be non-negative.
originationDatestringDate the loan was originated (YYYY-MM-DD). Required for cadence policy.

Validation Rules

Balance fields (non-negative)

All monetary fields must be >= 0 when provided: totalBalance, originalFundedAmount, originalTotalOwed, totalPayoffAmount, payoffAmount, balanceAtTransfer, preTransferPayments, chargeoffAmount. amountDue must be strictly positive (> 0).

Rate fields

interestRate and apr must be non-negative. Values are stored as decimals where 1.0 = 100%. High-interest loans can have values above 1.0 (e.g. 6.49 = 649% APR).

Chargeoff pairing

chargeoffDate and chargeoffAmount must both be set or both omitted. Setting only one returns a validation error.

Settlement percentages pairing

paymentPlanMinPercentage and paymentPlanMaxPercentage must both be set or both omitted. When set, both must be between 0 and 1, and min <= max. When both are omitted (null), the loan is pay-in-full only (no settlement discount).

Portfolio ID

portfolioId must be a valid UUID when provided. If omitted, defaults to the configured portfolio for your company.

Servicing status

servicingStatus must be ACTIVE on loan creation. To stop servicing on an existing loan, you can either upsert with servicingStatus: NOT_SERVICING (which also updates any other loan fields in the same call) or use the dedicated POST /loans/{id}/cancel-servicing endpoint. Both cancel all scheduled payments and communications.

Schedule creation

A collections schedule is automatically created on new loans based on the specified taskType. The customer must have a valid IANA timezone set. taskType defaults to Collections if not specified.

Request Body

{
  "id": "LOAN-12345",
  "customerId": "CUST-001",
  "type": "CASH_ADVANCE",
  "applicationStatus": "FUNDED",
  "originalFundedAmount": 500.00,
  "originalTotalOwed": 650.00,
  "interestRate": 0.2999,
  "apr": 0.3550,
  "originationDate": "2026-04-01",
  "startDate": "2026-04-01",
  "totalBalance": 425.00,
  "payoffAmount": 425.00,
  "amountDue": 125.00,
  "dueDate": "2026-05-01",
  "servicingStatus": "ACTIVE",
  "autopayEnabled": true,
  "isVisibleInBorrowerPortal": true,
  "portfolioId": "3607837d-2daf-4772-90db-638a2fa008df",
  "paymentPlanMinPercentage": 0.60,
  "paymentPlanMaxPercentage": 0.80,
  "taskType": "Collections"
}

Error Responses

Status CodeDescription
400Invalid enum value, servicingStatus not ACTIVE on create, mismatched amountDue on upsert, invalid timezone
404Customer with the given customerId not found
409Concurrent duplicate — another request created the same loan
422Validation error (negative balance/rate, unpaired chargeoff/settlement fields, invalid UUID, missing required field)
500Internal server error

Authorizations

X-API-Key
string
header
required

Body

application/json

Loan data

id
string
required

Your stable loan reference ID

Example:

"LOAN-12345"

customerId
string
required

External reference of a previously-pushed customer

Example:

"CUST-001"

originationDate
string<date>
required

Required. Date the loan was originated.

Example:

"2026-04-01"

totalBalance
number
required

Required. Current outstanding balance.

Required range: x >= 0
Example:

425

amountDue
number
required

Origination amount due. Must be positive. Immutable after creation.

Example:

125

dueDate
string<date>
required

Next payment date (YYYY-MM-DD)

Example:

"2026-05-01"

servicingStatus
enum<string>
required

Must be ACTIVE on create. Use cancel-servicing endpoint to stop.

Available options:
ACTIVE
type
enum<string>
Available options:
CASH_ADVANCE,
PERSONAL_LOAN,
INSTALLMENT_LOAN
applicationStatus
enum<string>
Available options:
PENDING,
APPROVED,
FUNDED,
WITHDRAWN,
REJECTED
originalFundedAmount
number
Required range: x >= 0
Example:

500

originalTotalOwed
number
Required range: x >= 0
Example:

650

interestRate
number

Non-negative decimal where 1.0 = 100% (e.g. 6.49 = 649%)

Required range: x >= 0
Example:

0.2999

apr
number

Non-negative decimal where 1.0 = 100%

Required range: x >= 0
Example:

0.355

startDate
string<date>
payoffAmount
number
Required range: x >= 0
totalPayoffAmount
number
Required range: x >= 0
balanceAtTransfer
number
Required range: x >= 0
preTransferPayments
number
Required range: x >= 0
isSettled
boolean
chargeoffDate
string<date>

Must be paired with chargeoffAmount

chargeoffAmount
number

Must be paired with chargeoffDate

Required range: x >= 0
autopayEnabled
boolean
Example:

true

noCallReason
enum<string>
Available options:
CUSTOMER_OPT_OUT,
LEGAL_HOLD,
BANKRUPT,
INCARCERATED,
DECEASED,
MILITARY,
FRAUD,
AGENCY_REQUEST,
COMPLAINT,
WRONG_NUMBER,
OTHER
noTextReason
enum<string>
Available options:
CUSTOMER_OPT_OUT,
LEGAL_HOLD,
BANKRUPT,
INCARCERATED,
DECEASED,
MILITARY,
FRAUD,
AGENCY_REQUEST,
COMPLAINT,
WRONG_NUMBER,
OTHER
noEmailReason
enum<string>
Available options:
CUSTOMER_OPT_OUT,
LEGAL_HOLD,
BANKRUPT,
INCARCERATED,
DECEASED,
MILITARY,
FRAUD,
AGENCY_REQUEST,
COMPLAINT,
WRONG_NUMBER,
OTHER
isVisibleInBorrowerPortal
boolean
Example:

true

daysPastDueAtBoarding
integer
Required range: x >= 0
daysSinceOriginationAtBoarding
integer
Required range: x >= 0
externalLmsMetadata
object
portfolioId
string<uuid>

Portfolio UUID for payment processor routing

paymentPlanMinPercentage
number

Min settlement percentage (0-1). Both min+max required together. NULL = pay-in-full.

Required range: 0 <= x <= 1
paymentPlanMaxPercentage
number

Max settlement percentage (0-1). Both min+max required together. NULL = pay-in-full.

Required range: 0 <= x <= 1
taskType
string

Schedule task type (e.g. Collections). Defaults to Collections for JustLoans.

Example:

"Collections"

Response

Existing loan updated

id
string

External loan reference ID

customerId
string

External customer reference ID

type
string
applicationStatus
string
originalFundedAmount
number
originalTotalOwed
number
interestRate
number
apr
number
originationDate
string<date>
startDate
string<date>
totalBalance
number
payoffAmount
number
totalPayoffAmount
number
balanceAtTransfer
number
preTransferPayments
number
amountDue
number
dueDate
string
servicingStatus
string
stopServicingReason
string
isSettled
boolean
chargeoffDate
string
chargeoffAmount
number
autopayEnabled
boolean
noCallReason
string
noTextReason
string
noEmailReason
string
isVisibleInBorrowerPortal
boolean
daysPastDueAtBoarding
integer
daysSinceOriginationAtBoarding
integer
externalLmsMetadata
object
portfolioId
string<uuid>
paymentPlanMinPercentage
number
paymentPlanMaxPercentage
number
taskType
string