Credit Card Flows
How the credit-card product behaves, step by step, with a diagram for each flow. Every rule here was checked against the code that runs it. For the request and response shapes see Credit Cards, Credit Lines, Card Products and Card Settings.
Interest and late fees live on the card, not on the lines
On a company card, credit lines only measure their own purchases. Interest, late fees and other charges are charged to the card. So the card can run out of room while the lines still show availability, and a card over its limit refuses every non-forced purchase, whatever the lines show. Reducing idle lines removes the warning, not the overdraft — see Fitting credit lines to the card.
The flows, in the order a card lives them:
- Card and billing-cycle lifecycle — statuses, cut-offs and the call sequence of a company card.
- Purchase on a company card through a credit line — every check, in order, and when the card refuses purchases.
- Interest and late fees — how they are computed and where they are charged.
- Fitting credit lines to the card — charges outside the lines, the warning, and shrinking or redistributing the lines.
- Payments — imputation, when the cupo comes back, how lines are freed, and payments directed to a line.
Card and billing-cycle lifecycle
A card account moves through four statuses: pending, enabled, disabled and closed. The status decides which operations the card accepts. Each card always has exactly one open statement, which is its current billing cycle. When the cut-off date passes, that statement is billed and the next cycle opens. Each billed statement then ends as paid, or goes overdue first and is paid later.
This page covers the two state machines, then the order of calls an integrator makes to run a company card with credit lines.
Card status
- A new card always starts as
pending. It accepts no purchases and no payments until you enable it. disablealso works on apendingcard, so you can suspend a card before it is ever activated.enableworks frompendingand fromdisabled.closeworks from any non-closed status, but only when the book is settled:outstanding − creditBalancemust be exactly zero. Otherwise it returns412. Closing the card does three things in the same transaction:- it closes every live credit line, with one
card.credit-line.status-changedevent per line (cause: card-closed); - it marks the open statement
paid; - it makes
closedfinal: no later call can reopen the card.
- it closes every live credit line, with one
- Every lifecycle call is idempotent. Enabling an enabled card, disabling a disabled one, or closing a closed one returns the card unchanged. Closing a closed card again also closes any credit lines left open on it.
| Operation | pending | enabled | disabled | closed |
|---|---|---|---|---|
| Purchase | no (412) | yes | no (412) | no (412) |
| Payment | no (412) | yes | yes | no (412) |
| Purchase or payment reversal | no (412) | yes | yes | no (412) |
enable | yes | no-op | yes | no (412) |
disable | yes | yes | no-op | no (412) |
close (net position = 0) | yes | yes | yes | no-op (closes leftover lines) |
| Change card limit | yes | yes | yes | no (412) |
| Open a credit line | yes | yes | yes | no (412) |
| Billing and accrual sweeps | run | run | run | skipped |
Suspending a card never suspends its debt. A disabled card still takes payments and reversals, and its cycles keep closing and accruing interest.
Statement status
| Status | Meaning | What moves it |
|---|---|---|
open | The current cycle. Purchases, interest and late fees post here. Nothing is due yet. | The cut-off. |
closed | Billed. dueAmount and minDue are frozen. The cycle is waiting for payment. | A payment, or the accrual sweep once dueDate has passed. |
overdue | Past dueDate and still owing. Accrues daily interest, plus a late fee if minDue was not met. | A payment or reversal that brings remainingDue to zero. |
paid | remainingDue is zero, or the cycle billed nothing. | Only a payment reversal sends it back to closed or overdue. |
Cut-off. A statement is billed on the first operation after its closeDate, which is the inclusive last day of the cycle. Two things perform the cut-off:
- The hourly
BillingCronsweep. - A lazy rollover inside any purchase, payment, reversal or accrual that touches the card first. Because of this, an operation that arrives between sweep runs still lands on the right cycle.
At cut-off, any credit balance is applied first. If nothing is left to pay, the statement goes straight to paid. Each new cycle's dates come from the previous closeDate, never from the day the sweep runs. So if the sweep was down, its next run creates one statement per missed cycle and leaves no gaps. A card opened mid-cycle gets a short first cycle that ends at the next cut-off.
Overdue. The AccrualCron sweep moves a closed statement that still owes money to overdue once its dueDate has passed. A payment runs the same accrual first, up to the day before the payment, so interest is never lost when the sweep is late. Unpaid balances are never billed again in a later cycle. Each statement keeps its own dueAmount and paidAmount, and the interest it generates posts on whichever cycle is open at the time.
Integrator journey: company card with credit lines
In company-lines mode, a company holds one card and opens a credit line under it for each holder. The card owns everything financial: the limit, the cycle, interest, payments and the statement. Each line is a spending cap. A purchase must fit both the line's cap and the card's.
| Step | Call | Rules enforced |
|---|---|---|
| 1 | PUT /v1/card-settings with mode: company-lines | You can always switch to company mode. Switching back to single returns 422 card_lines_still_open while any line is live. |
| 2 | POST /v1/card-products | The plan freezes its terms onto each card at issuance. The card needs an enabled product. |
| 3 | POST /v1/card-accounts | clientId is required. openedOn (YYYY-MM-DD, optional) backdates the first cycle and cannot be in the future. In single mode, a client with a non-closed card gets 422 card_client_has_card. Company mode has no such limit. |
| 4 | POST …/enable | pending becomes enabled. Only an enabled card can buy. |
| 5 | POST …/credit-lines (one per holder) | Requires company mode, or returns 422 card_mode_mismatch. The sum of live line limits must stay at or below the card limit (422 credit_line_over_assigned). No cupo can be assigned while the card is over its limit (422 card_over_limit). |
| 6 | POST …/purchases with creditLineId | Once a card has live lines, a purchase without a line returns 422 credit_line_required. The line must be enabled. The purchase must fit the line first, then the card. force books an over-limit purchase but never skips the line status check. |
| 7 | GET …/credit-relief | Shows how far the card is over its limit, idle cupo per line, and cupo the lines promise but the card can no longer back. |
| 8 | POST …/payments (optional creditLineId) | Accepted on enabled and disabled cards. Applied to unpaid statements in waterfall order. Anything left over becomes credit balance. creditLineId requires company mode. |
| 9 | GET /v1/card-statements/{id}/document | The composed statement for one cycle, with its debits, credits and totals. |
Purchase on a company card through a credit line
POST /v1/card-accounts/{id}/purchases (permission card.purchase.create) books a consumption against the card. In a tenant running "mode": "company-lines", a company holds one card and spends through credit lines, one per holder. The purchase carries a creditLineId and must fit two caps: the line's, then the card's.
The checks run in a fixed order. Some run before the card account row is locked (they depend only on the request and the tenant config). The rest run under the lock, so two concurrent purchases cannot jointly overrun a cap. The first failing check answers and nothing is written.
Notes on the order:
- The
referenceIdreplay runs before every business check. Retrying a purchase that already went in returns it with200, even if the card was disabled or the tenant mode changed in between. A concurrent replay that loses the insert race also resolves to200with the existing row. - The date-in-future check runs before the lock. Opening day and
expiresOnneed the account, so they run under it. - Without
creditLineId, the card must have no live lines (enabledordisabled). A card with live lines in company-lines mode only spends through them. The mode is read only when the card has live lines. - The currency check runs before the line lookup. The lazy rollover and the card's spendable credit are resolved before both cap checks, so the purchase is measured against the correct cycle.
- Line cap first, then card cap. The line check comes first so the error names the line. The card check still runs after a forced line breach, so one forced purchase can break both caps.
forceskips caps only. It never skips card status, line status, opening day or expiry.- A purchase that fits both caps is never stamped, even when sent with
force: true.
Checks and responses
Each cap refusal carries error.details.scope (line or card); see No credit: the line or the card?.
| # | Check | Where | Failure |
|---|---|---|---|
| 0 | Idempotency-Key header replay (24h) | before the handler | cached 2xx replayed; same key with a different body: 422 |
| 1 | Body, date, creditLineId well formed | handler | 400 |
| 2 | referenceId present, amount > 0 | before lock | 400 |
| 3 | referenceId already booked | before lock | 200 existing transaction |
| 4 | creditLineId only in company-lines mode | before lock | 422 card_mode_mismatch |
| 5 | Date not after today | before lock | 422 card_tx_date_in_future |
| 6 | Card account exists | lock | 404 |
| 7 | Card status enabled | under lock | 412 |
| 8 | Date not before opening day | under lock | 422 card_tx_before_opening |
| 9 | Date not after expiresOn (force does not skip) | under lock | 422 card_expired |
| 10 | No creditLineId on a card with live lines | under lock | 422 credit_line_required |
| 11 | Currency matches the card | under lock | 400 |
| 12 | Line belongs to this card | under lock | 404 |
| 13 | Line enabled (force does not skip) | under lock | 412 |
| 14 | Amount fits the line's available | under lock | 422 insufficient_line_credit, or over-limit with force |
| 15 | Amount fits the card's spendable | under lock | 422 insufficient_credit, or over-limit with force |
| — | Booked | — | 201 |
When the card refuses purchases
A card refuses a purchase for one of two reasons. The instrument is out of service: not enabled, expired, or the line is not enabled. Or the money does not fit: the line cap or the card cap. Only the money checks can be overridden, and only with force.
Card spendable is the card cap the purchase is checked against:
available = creditLimit − outstanding + creditBalance. It is derived and never stored.- Under
terms.creditRestoration: "on-close", the cupo that payments and credit-restoring reversals freed during the open cycle is held back until the cut-off. Sospendable = available − held, and a payment does not reopen the card until the cycle closes. - Spendable can go negative after a forced purchase. While it is negative, every non-forced purchase fails the card cap.
blocked is a signal, not a gate
GET /v1/card-accounts/{id}/credit-relief reports:
over: how much the card is past its limit.recoverable: the idle cupo of the live lines.blocked:over > 0andrecoverable ≤ over.
blocked is derived on every read and never stored. The purchase path never reads it. The actual refusal is always the cap check (insufficient_credit / insufficient_line_credit).
| Situation | What refuses | What unblocks |
|---|---|---|
Card pending / disabled | 412 on status | POST …/enable. A closed card cannot be re-enabled. |
Purchase date after expiresOn | 422 card_expired | Nothing on this endpoint. force does not skip it. |
Line disabled / closed | 412 on line status | Re-enable a disabled line. force does not skip it. |
| Line cap exhausted | 422 insufficient_line_credit | A payment that releases cupo to the line, a reversal of the line's purchase, raising the line limit, or force. |
Card spendable exhausted (incl. blocked) | 422 insufficient_credit | A payment (held until the cut-off under on-close), a reversal, raising the card limit (PUT …/credit-limit), or force. |
| Card over its limit, raising a line | 422 card_over_limit on the line write | Pay or raise the card limit first. Lowering a line is always allowed. |
Freeing idle lines does not reopen the card cap. Lowering or fitting a line (POST …/credit-lines/fit) changes what the lines promise, not the card's spendable. Its effect is to remove unbacked cupo, so lines stop showing room the card no longer has. It does not let a purchase through while the card is over its limit.
Interest and late fees (the accrual sweep)
Once a cycle is billed at its cut-off, its dueAmount is frozen and the cardholder has termDays to pay it. If money is still owing after the due date, the accrual sweep charges two things against that statement: a one-time late fee when the minimum payment was missed, and daily interest on what remains unpaid until it is paid. These charges do not change the overdue statement itself: they are posted as new transactions on the cycle that is open when they accrue, so they show up on the next bill. The sweep runs every hour but is idempotent, so each day of interest and each late fee is posted once.
Rules
- Due date = cut-off +
termDays; the due date itself is the last day to pay without charges. - Minimum payment =
min(dueAmount, max(minPaymentPercent x dueAmount, minPaymentFloor)), frozen at cut-off. - Late fee: evaluated once, the first sweep after the due date. If payments dated on or before the due date did not reach the minimum, one charge of
penaltyRate% of what was still owed at the due date. Referencepen:{statementId}. - Interest: for every day after the due date,
interestRateprorated to one day ({"period":360,"rate":"36"}= 0.1% per day) ondueAmountminus payments dated up to that day. Referenceint:{statementId}:@YYYYMMDD. - Tax (IVA):
interestTaxon interest;penaltyTaxon the late fee (inheritsinterestTaxwhen absent). Computed per charge and added on top: the transactionamountis gross andtaxAmountshows the tax inside it. - Where charges land: on the cycle open when they accrue, with
metadata.sourceStatementIdpointing at the overdue cycle. They are billed at the next cut-off and, if that bill goes unpaid, accrue interest as part of it. - Card, not credit line: in company mode, interest and late fees are charged to the card account. They never use up a credit line, which only counts purchases and reversals.
- Stopping and corrections: a payment that zeroes the statement marks it
paidand interest stops. A backdated payment (dated before the days already charged, or covering the minimum by the due date) gives back the excess through a compensatingreversal(lpc:...). Charges are never edited.
Worked example (termDays 10, minPaymentPercent 10, penaltyRate 5%, interestRate 360/36, interestTax 21%; illustrative figures)
| Date | Event | Charge (net + tax = gross) |
|---|---|---|
| Mar 14 | Cut-off bills 10,000.00; minDue 1,000.00; due date Mar 24 | — |
| Mar 24 | Nothing paid | — |
| Mar 25 | Statement becomes overdue; minimum missed: late fee 5% x 10,000 | 500.00 + 105.00 = 605.00 |
| Mar 25–28 | Daily interest 0.1% x 10,000 | 10.00 + 2.10 = 12.10 per day |
| Mar 28 | Cardholder pays 10,000.00, statement paid | interest stops |
| Apr 14 | Next cut-off bills 605.00 + 4 x 12.10 = 653.40 of finance charges | — |
Mar 28 is charged if that day's sweep ran before the payment was registered (the usual case with an hourly sweep). A payment registered before the sweep cancels that day's interest.
Fitting credit lines to the card (credit-relief and fit)
On a company card, the card owns everything financial: limit, cycle, interest, late fees and payments. The credit lines are only spending caps. A line's consumed counts its own purchases only. Interest, late fees and other card-level charges post to the card, not to any line.
So once the card's whole limit is assigned to lines, those charges eat room on the card that no line knows about. The lines keep showing available cupo that the card no longer has. GET /v1/card-accounts/{id}/credit-relief reports this gap as unbacked. POST /v1/card-accounts/{id}/credit-lines/fit lowers the lines until it is gone.
1. The money picture
Card limit 10,000. Four lines whose limits add up to exactly 10,000, since the sum of live line limits can never exceed the card limit. The lines spend 9,600. One line is 600 over its own cap because of a forced purchase. The card is also charged 1,616.77 of interest and late fees.
What credit-relief returns for this card:
| Field | Value | How it is computed |
|---|---|---|
over | 1,216.77 | −(card available), when negative |
outsideLines | 1,616.77 | card limit − card available − Σ line consumption |
idle / recoverable | 1,000.00 | Σ (limit − used) of the live lines with positive idle. Only Fleet South qualifies |
unbacked | 1,000.00 | recoverable − max(card available, 0) |
blocked | true | over > 0 and recoverable ≤ over. Freeing all idle cupo still would not cover the overdraft |
fit | Fleet South: cut 1,000 → 2,000 | see below |
Because the line cap is checked first, a purchase on Fleet South is not refused by the line, which shows 1,000 of room. It is refused by the card, which has none. That is what the unbacked warning means.
2. What the operator or integrator does
Fit removes the warning (unbacked). It does not clear the overdraft. In the example the card stays 1,216.77 over after the fit, and with no idle cupo left it is still blocked. Only a payment or a higher card limit gets it back under the limit.
3. What POST …/credit-lines/fit does on the server
How the plan is built (planLineFit):
- Only idle cupo (
limit − used> 0) is taken, so no line goes below what it spent. Lines at or over their cap are left alone. - The cut is proportional to each line's idle:
cut = idle / recoverable × unbacked, rounded per line. Ifunbackedis at leastrecoverable, every idle line loses all of its idle. - Rounding cents go to the line with the largest idle, never past its idle. The cuts add up to
unbackedexactly. - A line whose suggested limit would be 0 (no spending and all of it unbacked) is marked
closeSuggested. A limit cannot be 0, and fit never closes a line. Close it yourself withPUT …/status. - Idempotent: once the lines fit, a second call changes nothing and emits no events.
- Works in any tenant mode and while the card is over its limit, because lowering is the way out.
4. Gates on line and card limits
- Disabled lines still hold their cupo, because a suspended line is not a surrendered one. Closed lines give it back. A closed line cannot be reopened.
- Raising a line or opening one is refused with
card_over_limitwhile the card is over its limit. Lowering a line is always allowed, even below its own consumption. Fit itself never cuts below consumption. card_over_limitgates lines only. Raising the card's own limit has no over-limit gate.- Opening and raising lines also require the tenant to be in
company-linesmode. Otherwise they return422 card_mode_mismatch. Lowering, closing and fit work in any mode.
5. Worked examples: lines before and after fit
A. Charges on top of a fully assigned card (the scenario above). Card limit 10,000, card available −1,216.77, unbacked 1,000.
| Line | Limit before | Used | Idle | Cut | Limit after |
|---|---|---|---|---|---|
| Fleet North | 3,000.00 | 3,000.00 | 0.00 | — | 3,000.00 |
| Fleet South | 3,000.00 | 2,000.00 | 1,000.00 | 1,000.00 | 2,000.00 |
| Sales | 2,000.00 | 2,600.00 | −600.00 | — | 2,000.00 |
| Management | 2,000.00 | 2,000.00 | 0.00 | — | 2,000.00 |
| Card | Σ 10,000.00 | 9,600.00 + 1,616.77 charges | Σ 9,000.00 · unbacked 0 · still over 1,216.77 |
B. Proportional split with no overdraft. Card limit 10,000. Charges have already used 3,000, so the card can still spend 5,000. The lines promise 8,000 of idle cupo, so 3,000 is unbacked.
| Line | Limit before | Used | Idle | Share of idle | Cut | Limit after |
|---|---|---|---|---|---|---|
| A | 5,000.00 | 1,000.00 | 4,000.00 | 50% | 1,500.00 | 3,500.00 |
| B | 5,000.00 | 1,000.00 | 4,000.00 | 50% | 1,500.00 | 3,500.00 |
After the fit, idle is 2,500 + 2,500 = 5,000, which is exactly what the card can spend.
Payments: allocation, credit restoration and directed payments
A payment on a credit card does two separate things, and they are decided by three separate product settings frozen on the account's terms:
- Allocation (
terms.paymentAllocation) decides which debt the money settles: which billed statements, and within them, consumption or finance charges. - Line restoration (
terms.lineRestoration, company-lines mode only) decides whose cupo comes back: how much of what each credit line consumed is freed. - Credit restoration (
terms.creditRestoration) decides when the freed cupo can be spent again: immediately, or only after the cycle's cut-off.
A payment can optionally be directed to one credit line with creditLineId. Directing
a payment never changes the allocation; it only changes who gets the cupo back first.
How a payment is registered
Key points:
- Idempotency.
referenceIdis the caller's key: replaying it returns the existing payment with200and changes nothing. - Catch-up accrual first. Before applying the payment, the service accrues interest up to the day before the payment date. Otherwise a payment that settles a cycle would take it out of the unpaid list and the days since the last sweep would never be charged.
- Allocation waterfall.
oldest-cycle(default) settles each unpaid statement in full, oldest cut-off first.charges-firstfirst covers the interest and late fees of every unpaid statement, oldest first, and only then consumption. A statement that reaches zero becomespaid, which stops its daily interest. Anything left over becomes credit balance. - The line release is decided once. It is computed with what was owed right before
the payment (
owedBefore = outstanding − creditBalance) and stored on the payment asmetadata.lineReleases. It is never recomputed. - Backdated payments. If the payment date is earlier than charges the accrual sweep already posted, those charges are compensated in the same transaction.
How a payment frees cupo on the credit lines
Undirected payment. The freed total is min(payment, owed) / owed applied to every
line's consumption. Interest and late fees are part of owed but belong to the card, not
to any line, so they dilute the release: with 11,216.77 owed and 9,500.00 of line
consumption, a 500.00 payment frees about 423 across the lines, not 500. The policy
(proportional, oldest-first, newest-first) only decides who gets that total, never
how much it is.
Directed payment. The named line is freed first: up to the payment amount, capped by
what the line consumed and by what was owed. Whatever is left of the payment is treated
as a normal payment over the remaining debt and frees the other lines under
lineRestoration. If the named line holds no consumption, the whole payment behaves as an
undirected one.
The line measures cupo, not accounting debt
The release to a directed line does not look at which purchases the payment settled.
Under charges-first, a 500.00 payment can be imputed entirely to interest and late fees,
with nothing applied to the line's purchases, and the line still gets 500.00 of cupo back.
This is intended: a credit line is a spending cap, not a ledger. The real debt (purchases,
interest and late fees) stays on the card and keeps following paymentAllocation.
Other rules:
- The line must belong to this card (
404) and must not beclosed(412). Adisabledline is accepted: a suspended line can still be paid. creditLineIdon a payment only exists in company-lines mode; in any other mode the request fails with422 card_mode_mismatch.- The payment row keeps
creditLineId. The statement document shows the line's label on the payment row, butbyCreditLinestays consumption-only: a directed payment is never counted as a line's spend. - Reversing the payment undoes its release: reversed payments stop counting in the line's consumption.
- Under
creditRestoration: "on-close", the cupo a payment frees on a line (directed or not) only becomes spendable after the cycle's cut-off, exactly like the card's own available credit.
Worked example
Company card in company-lines mode, lineRestoration: "proportional". Juan's line holds
1,500.00 of consumption and Ana's 8,000.00. Those 9,500.00 of purchases were billed on the
oldest unpaid cycle; 1,716.77 of interest and late fees were billed on later cycles. Owed
before the payment: 11,216.77. All figures are illustrative.
| Payment | paymentAllocation | Where the money is imputed | Juan freed | Ana freed | Juan still holds | Ana still holds |
|---|---|---|---|---|---|---|
| 500.00, undirected | oldest-cycle | 500.00 to the oldest cycle (purchases) | 66.86 | 356.61 | 1,433.14 | 7,643.39 |
| 500.00, undirected | charges-first | 500.00 to interest and late fees | 66.86 | 356.61 | 1,433.14 | 7,643.39 |
| 500.00, directed to Juan | oldest-cycle | 500.00 to the oldest cycle (purchases) | 500.00 | 0.00 | 1,000.00 | 8,000.00 |
| 500.00, directed to Juan | charges-first | 500.00 to interest and late fees, 0.00 to purchases | 500.00 | 0.00 | 1,000.00 | 8,000.00 |
| 2,000.00, directed to Juan | charges-first | 1,716.77 to charges, 283.23 to purchases | 1,500.00 | 411.66 | 0.00 | 7,588.34 |
How to read it:
- The release column never changes with
paymentAllocation: allocation decides what debt is settled, release decides whose cupo comes back. - Undirected:
500 / 11,216.77 = 4.4576%of each line's consumption, rounded per line. The charges dilute it to 423.47 freed in total. - Directed to Juan: Juan gets the full 500.00, Ana nothing, even when every peso went to interest and late fees.
- 2,000.00 directed to Juan: Juan is freed in full (1,500.00). The remaining 500.00 is a
normal payment over
11,216.77 − 1,500.00 = 9,716.77and frees500 / 9,716.77of Ana's 8,000.00. - In every 500.00 case the card's debt drops to 10,716.77.
Request: a payment directed to a credit line
date (YYYY-MM-DD, defaults to today) and metadata are optional. Omit creditLineId
for an undirected payment. The response is the payment transaction: applications shows
where the money was imputed, creditLineId echoes the line, and
metadata.lineReleases records the cupo freed per line, for example
{ "<creditLineId>": "500.00" }. Replaying the same referenceId returns 200 with the
original payment.