Create a realistic billing setup (rate + discount + allocation + subscription), generate a charge that references versioned configuration, and understand what happens as a billing cycle runs and charges become immutable.
This walkthrough uses the Billing & Ledger API endpoints defined in the service OpenAPI spec, including:
POST /rates,POST /rate-cardsPOST /allocation-configurationsPOST /subscriptionsPOST /chargesPATCH /charges/{chargeId}(mutable only)POST /charges/invoiceandPOST /charges/settle(settlement entry points)GET /accounts/{id}/balance(balance views)
Auth: All requests use a bearer token (JWT-derived).
Billing & Ledger references Profile Service entities.
You will need:
billableEntityId(who services were rendered to)- one or more
accountIdvalues (who pays)
If you are using split allocations, ensure the billable entity is associated to the accounts you plan to reference (typically managed in Profile Service).
Create a rate that represents a debit (e.g., “Monthly Program Fee”). Rates are versioned and reusable across subscriptions and charges.
curl -X POST "https://api.nelnetpay.com/billing/rates" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"description": "Monthly Program Fee",
"type": "TUITION",
"pricePerUnit": 15000,
"tags": {
"billing.category": "PROGRAM",
"billing.plan": "STANDARD"
}
}'✅ Save the returned id as rateId.
Discounts are modeled as rates with type=DISCOUNT. Discount rates can be fixed-amount or percentage-based.
curl -X POST "https://api.nelnetpay.com/billing/rates" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"description": "10% Loyalty Discount",
"type": "DISCOUNT",
"discountPercentage": 10,
"tags": {
"billing.discountType": "LOYALTY"
}
}'✅ Save the returned id as discountRateId.
Allocation configurations define financial responsibility routing. They contain ordered rules that resolve deterministically during settlement.
Here’s a simple “single responsible party” example (100% to one account).
curl -X POST "https://api.nelnetpay.com/billing/allocation-configurations" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"name": "Single Responsible Party",
"description": "100% responsibility assigned to the primary account",
"rules": [
{
"ruleType": "RESPONSIBLE_PARTY",
"accountId": "acc_123",
"percent": 100
}
],
"tags": {
"billing.allocation": "PRIMARY_ONLY"
}
}'✅ Save the returned id as allocationConfigurationId.
When the charge is invoiced/settled:
- Billing resolves these rules into journal lines
- caps/transfers can yield remainder behavior such as write-offs
- the resolved outcome is stored in Settled Charges and cannot be changed later
Rate cards make it easier for UIs and humans to browse “the menu” of rates.
curl -X POST "https://api.nelnetpay.com/billing/rate-cards" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"name": "Program Catalog",
"rateIds": [
{
"rateId": "'"$rateId"'",
"allocationConfigurationId": "'"$allocationConfigurationId"'"
}
]
}'Rate cards are for discovery only—you still pass explicit IDs when creating charges.
Subscriptions express intent (what to bill and how often). They do not automatically create charges; a scheduler/orchestrator does that (often the Recurring Billing service).
Example: a monthly subscription that bills on the 1st of the month, but the person starts mid-month.
curl -X POST "https://api.nelnetpay.com/billing/subscriptions" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"billableEntityId": "be_123",
"rateId": "'"$rateId"'",
"allocationConfigurationId": "'"$allocationConfigurationId"'",
"discountRateIds": ["'"$discountRateId"'"],
"billingFrequency": "MONTHLY",
"billingAnchorDay": 1,
"billingTimeZone": "America/Chicago",
"startDate": "2026-02-15",
"status": "ACTIVE",
"tags": {
"billing.subscriptionSource": "ADMIN_UI"
}
}'✅ Save the returned id as subscriptionId.
A participant joins on the 15th, but billing runs on the 1st.
In this case:
- the subscription captures the intent and start date
- billing logic computes the correct proration factor at charge creation or finalization time (depending on workflow)
Charges can be created:
- from a subscription (recommended): pass
subscriptionIdand let the service discover rate/allocation/discount references - directly: pass explicit
rateId,allocationConfigurationId, etc.
curl -X POST "https://api.nelnetpay.com/billing/charges" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"subscriptionId": "'"$subscriptionId"'",
"quantity": 1,
"eventDate": "2026-02-15",
"tags": {
"billing.chargeReason": "MONTHLY_FEE"
}
}'The service snapshots versions at creation time (rate, discount rates, allocation config, subscription) for audit safety.
Use accountId when billing directly to an account (for example, a one-time registration fee).
curl -X POST "https://api.nelnetpay.com/billing/charges" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"accountId": "acc_123",
"rateId": "'"$rateId"'",
"allocationConfigurationId": "'"$allocationConfigurationId"'",
"quantity": 1,
"eventDate": "2026-02-15"
}'Charges can be patched only in mutable statuses (for example PENDING or BILLED). Once invoiced, updates return a conflict.
curl -X PATCH "https://api.nelnetpay.com/billing/charges/$chargeId" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"quantity": 2,
"tags": {
"billing.updateReason": "CORRECTED_QUANTITY"
}
}'Billing cycles control when you stop editing and start producing final financial outcomes.
- Before invoicing/settlement: charges represent editable instructions and may be updated/cancelled.
- During invoicing or direct settlement:
- allocation rules are resolved
- resolved amounts and splits are persisted
- ledger entries may be created (depending on the settlement flow)
- charges become immutable and move to Settled Charges
Invoices service typically retrieves charges and then calls the settlement endpoint to mark them invoiced and create ledger artifacts.
curl -X POST "https://api.nps.com/v1/charges/invoice" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"chargeIds": ["'"$chargeId"'"],
"invoiceId": "inv_123"
}'Payment Widget may support “pay account balance” by settling charges directly after payment.
curl -X POST "https://api.nps.com/v1/charges/settle" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
"chargeIds": ["'"$chargeId"'"],
"paymentId": "pay_123"
}'Result: the system records settlement and the charge becomes immutable (Settled Charge).
Billing & Ledger can be used even without the invoices service.
- With invoices: balances reflect invoices/settlements and ledger outcomes.
- Without invoices: charges still accumulate. You can query outstanding charges and compute logical balances from charge state.
Example balance lookup:
curl -X GET "https://api.nelnetpay.com/billing/charges?accountId=acb123" -H "Authorization: Bearer $ACCESS_TOKEN"Tags can be applied at multiple points:
- rate tags (pricing categorization)
- allocation tags (routing categorization)
- subscription tags (intent and schedule metadata)
- charge tags (event-specific context)
Downstream records (charges, journal entries, exports, reporting) can carry derived tags and keep them nested for provenance (e.g., billing.rate.*, profile.billableEntity.*).