Credit Lines API
In company mode a company holds one credit card and opens credit lines under it — one per holder, vehicle or branch. Each line is a spending instrument with its own cap. The card still owns everything financial: the limit, the billing cycle, the cut-off, interest, late fees, payments and the statement. A purchase made with a line posts to the company's card and is attributed to that line.
The tenant has to be in "mode": "company-lines" (see
Card Settings). The CMS Base URL is used (see
introduction).
Lines are not clients
A line has no client or borrower of its own: it hangs from the card. Use
label, referenceId and metadata to identify who or what it belongs to.
If each holder must be a debtor with their own identity and debt, that is a
client with their own card, not a line.
How to read this page
Every flow on this page, with its diagram and the exact order of the checks, is also in Card Flows.
The typical flow, in order:
- Open the lines and distribute the card's limit.
- Send each purchase with its line.
- Read how an overrun is reported.
- When interest or late fees eat the card's room, fit the lines to the card.
- Check how payments free the lines.
- Every write is safe to retry.
The Credit Line Object
A real line from a company card in staging, after a forced purchase broke its cap:
Attributes
| Attribute | Description | Type |
|---|---|---|
id | Line id | UUID |
accountId | The company card it belongs to | UUID |
referenceId | Optional idempotency key from the partner | String |
label | Who or what the line is for. Shown on the statement next to each purchase | String |
status | See Line Status | String |
creditLimit | The line's cap | Money |
consumed | The line's purchases, minus reversals of them, minus what the company's payments gave back to it. Interest and late fees are never in a line: they are charged to the card | Money |
availableCredit | creditLimit − consumed. Negative when the line is over its cap — it is never clamped at zero | Money |
held | Cupo retained by the line's active authorizations, already subtracted from availableCredit and effectiveAvailable. Not part of consumed | Money |
effectiveAvailable | What a purchase on this line could actually take today: availableCredit capped by what the card can still spend, never below zero, and 0 when the line or the card cannot buy. Use this one to show a holder their room. Read-only | Money |
Line Status
| Status | Spends | Holds its cupo |
|---|---|---|
enabled | Yes | Yes |
disabled | No (not even with force) | Yes — suspended, not surrendered |
closed | No | No — its cupo goes back to the company. Terminal |
Endpoints
List the Lines
Every line of the card, closed ones included, with consumed,
availableCredit and effectiveAvailable computed at read time. It returns
all of them in one array — it does not paginate. The numbers come from
account-level aggregates, not one query per line, so a card with hundreds of
lines is still one call.
Get One Line
The same view as the list, for one line. 404 if the line is not on this
card. Use it to check a single holder without reading all the lines.
Checking both caps before a purchase
There is no dry-run endpoint, and you do not need one: effectiveAvailable
is the answer. It is the line's room capped by what the card can spend, so a
purchase of X without force on that line passes both caps exactly when
X ≤ effectiveAvailable. The two numbers it combines are also available on
their own: the line's availableCredit, and the card's availableCredit on
GET /v1/card-accounts/{id}/summary. It does not check the date rules (a
future date, before the card opened, after expiresOn), which depend on the
request, not on the balances.
Open a Credit Line
| Field | Description | Type |
|---|---|---|
label | Who or what the line is for (required) | String |
creditLimit | The line's cap, in the card's currency (required) | Money |
referenceId | Optional idempotency key, unique per tenant | String |
metadata | Free-form partner data | Object |
201 with the line. The sum of the limits of the live lines (enabled and
disabled) cannot exceed the card's limit: a line that would break it answers
422 credit_line_over_assigned. No line can be opened while the card is over
its limit.
Change a Line's Limit
Body: { "creditLimit": { "amount": "2500.00", "currency": "USD" } }.
- Raising must still fit the card (
422 credit_line_over_assigned) and is refused while the card is over its limit (422 card_over_limit). - Lowering is always allowed, in any mode and with the card over its limit:
it is the way out. A line can go below what it consumed; its
availableCreditthen turns negative. - Setting the same value is a no-op.
Enable, Disable or Close a Line
Body: { "status": "enabled" | "disabled" | "closed" }. Closing returns the
line's cupo to the company; disabling does not. Closing the card closes all its
lines in the same operation.
Credit Relief
The card's situation and the way out of it. A real response from staging — a company card whose interest and late fees pushed it over its limit:
| Attribute | Meaning |
|---|---|
over | How much the card is past its limit. Zero when it is not |
idle | Each live line's unused cupo, largest first |
recoverable | Sum of idle: what can be freed without touching anybody's spending |
blocked | A signal: the card is over its limit and its idle line cupo would not even cover the overdraft. Any overdraft already refuses every non-forced purchase — the card cap never looks at the lines, so freeing idle lines does not unblock purchases; only a payment, a reversal, a higher card limit or force does. Derived, never stored |
outsideLines | The card's debt that no line carries: interest, late fees, and any purchase booked without a line |
unbacked | Cupo the lines still show as available that the card can no longer back. Positive means a warning |
fit | The reduction per line that brings unbacked to zero. See below |
Fit the Lines to the Card
No body. Lowers the lines as fit suggests, in one operation, and answers
the credit relief after the change. See
When the card cannot back the lines.
Purchases against a line
- A purchase must fit both caps, the line's and the card's. The line is
checked first, so the error names the line:
422 insufficient_line_credit("this holder has no room") before422 insufficient_credit("the company has no room"). - A card with live lines only spends through them: a purchase without
creditLineIdanswers422 credit_line_required. A card without lines keeps buying directly. - A
creditLineIdoutside company mode answers422 card_mode_mismatch; a line from another card,404; a line that is notenabled,412. force: truebooks the purchase even when it breaks a cap — for purchases a processor already authorized and settled, where refusing would only keep real money out of the ledger. It skips the cap, never the status: a disabled or closed line does not spend, forced or not.- A reversal of the purchase gives the cupo back to the same line.
See Credit Cards for the rest of the purchase contract (dates, idempotency, reversals).
No credit: the line or the card?
A purchase with a line passes two checks, in this order: the line (what
this holder has) and then the card (what the company has). Each refusal
says which one failed, in the code, the message and error.details:
details.scope | Meaning | What to do |
|---|---|---|
line | This holder ran out of cupo on their line | Raise the line, reduce another line to make room, or wait for a payment to free it |
card | The company card has no room — usually because interest and charges live on the card — even if the line still shows cupo | Register a payment or raise the card limit. Reducing lines does not help: the card cap never looks at the lines |
cardOverLimit is present only when the card is over its limit; the line keys
only when the purchase named a line. The codes are unchanged, so an
integration that already handles them keeps working; details is additive.
Show effectiveAvailable, not availableCredit, to a holder
availableCredit is the line's own room. effectiveAvailable caps it by what
the card can actually spend: in the example above Juan's line shows
availableCredit 1,045.25 and effectiveAvailable 0, so the holder knows
before trying that the purchase will be refused.
How an overrun is reported
There are two caps, and either can be broken — by a forced purchase, or (the card's) by interest and late fees. Each surface tells you which one:
| Where | What you see |
|---|---|
The purchase (metadata) | overLimit: true on every forced purchase that broke a cap, plus lineOverLimitAmount (how far past the line) and/or overLimitAmount (how far past the card). One filter on overLimit finds every exception |
| The event stream | card.purchase.over-limit, emitted next to card.purchase.registered, with creditLineId + lineOverLimit when the line broke, and overLimit + creditLimit when the card did |
| The line | availableCredit negative (-600.00 in the example above) |
| The card | availableCredit negative on the account and the summary; credit-relief.over positive |
| Credit relief | blocked, unbacked and outsideLines, see above |
A real forced purchase from staging that broke both caps:
A purchase that fits is never marked, even when sent with force: true: the
flag authorizes an exception, it does not declare one.
When the card cannot back the lines
Interest, late fees and other charges are charged to the card, never to a
line. So the card can run out of room while every line still shows cupo: the
lines are promising money the card no longer has. In the example above the
card owes 1,616.77 of charges (outsideLines), and Flota Sur still shows
1,000 available that no purchase will be able to use (unbacked).
unbacked > 0 is the warning. The ways out:
- Register a payment — it shrinks the card's debt.
- Raise the card's limit.
- Reduce the lines so the cupo they show fits what the card can back:
POST …/credit-lines/fit.
How fit is computed:
- It takes only idle cupo, so no line drops below what it consumed.
- It spreads the cut in proportion to each line's idle cupo; per-line
rounding is settled on the line with the most idle, so the cuts add up to
unbackedexactly. closeSuggested: truemarks a line with no spending whose whole cupo is unbacked. A limit cannot be zero, so fitting it means closing it — whichfitnever does on its own. Close it explicitly if you agree.
What POST …/credit-lines/fit guarantees:
- The plan is recomputed by the server at the moment of the call, never
taken from the request: between reading the suggestion and confirming it, a
payment or a purchase may have changed it. Show the operator the
fitfromcredit-relief, and the response tells you what was actually applied. - All the lines change in one transaction.
- Each changed line emits
card.credit-line.limit-updatedwith"reason": "fit-to-card", visible in the card's history. - Idempotent: on a card that already fits, it changes nothing.
- It removes the warning, not an overdraft: a card over its limit stays over — and refusing purchases — until a payment lands or its limit grows. And
unbackedstays above zero while acloseSuggestedline is left open. - It works in any mode, like closing or lowering a line: it is a way out.
How payments free the lines
The company pays the card, but the caps live on the lines, so each payment also decides whose consumption it settled. The release is decided when the payment lands and stored on it — never recomputed later.
- How much: the share of the card's debt the payment covered. Paying 50% of what the card owes frees 50% of what the lines consumed.
- Whose: the plan's
lineRestoration—proportional(default: every line in proportion to its consumption),oldest-firstornewest-first. - Under
creditRestoration: on-close, the freed cupo is usable again only after the next cut-off, the same as the card's. - Reversing the payment undoes its release.
- A payment can name the line it frees: see Payments directed to a line.
Worked example — lines Norte (consumed 3,000) and Sur (consumed 1,000), the card owes 4,000, the company pays 2,000 (50%):
| Policy | Norte frees | Sur frees |
|---|---|---|
proportional | 1,500 | 500 |
oldest-first (Norte spent first) | 2,000 | 0 |
newest-first | 1,000 | 1,000 |
Payments directed to a line
creditLineId says whose cupo the payment frees. It does not change how
the payment is imputed to the card: the card's statements are settled by the
plan's paymentAllocation, exactly as without a line — so the order a
regulator may require (charges-first) is always respected.
- The named line gets back the payment amount, capped by what it consumed and by what the card owed.
- Whatever is left is an ordinary payment over the remaining debt and frees
the other lines by
lineRestoration.
The release does not look at which purchases were paid
With charges-first, a payment of 500 can be imputed entirely to interest
and late fees — not one peso to the line's purchases — and the line still
gets 500 of cupo back. That is on purpose: a line measures cupo, not
accounting debt, and the real debt (including those charges) stays on the
card. If the card cannot back the freed cupo, credit-relief reports it as
unbacked.
An undirected payment frees payment / owed of every line's consumption.
Interest and late fees are in what the card owes but in no line, so they
dilute it: with 11,216.77 owed and 9,600 of line consumption, a 500
payment frees about 428 spread across the lines. Directed to one line, the same
500 frees 500 on that line.
Rules: company mode only (422 card_mode_mismatch); the line must be on this
card (404) and not closed (412; a disabled line is accepted). The
payment keeps creditLineId, the statement document names the line on the
payment row (byCreditLine stays consumption only), and reversing the payment
undoes its release.
Retries and idempotency
Every write in this API is safe to retry. The rules:
| Operation | Key | A retry |
|---|---|---|
| Open a line | referenceId (optional, unique per tenant) | returns the existing line with 200 instead of a second one |
| Purchase / payment | referenceId (required) | returns the existing transaction with 200 |
| Reverse a purchase / a payment | one reversal per transaction | returns the existing reversal with 200 |
| Change a line's limit or status | the target value | setting the value it already has is a no-op |
| Fit the lines | the server's plan | on a card that already fits, changes nothing |
Two responses mean "try again":
409 concurrent_modificationwith aRetry-Afterheader (seconds): another operation changed the same card at the same time. Wait and repeat the same request.- A timeout or a
5xx: you do not know whether the write landed. Repeat it with the samereferenceId— if it did land, you get the existing record back, never a duplicate.
Use a different referenceId per operation
The key is the identity of the operation. Reusing one for a different purchase or line returns the earlier record and silently ignores the new body. Derive it from your own id for that operation, never from a label.
Permissions
| Permission | Grants |
|---|---|
card.account.read | list lines, read credit relief |
card.account.write | open lines, change their limit and status, fit them to the card |
card.purchase.create | purchases against a line |
Error Handling
| Status | When |
|---|---|
400 | Malformed id or amount, currency different from the card's |
404 | Unknown card or line, or a line that belongs to another card |
412 | The line is not enabled (purchases), or is closed (changes) |
409 concurrent_modification | Concurrent change on the same card; retry after Retry-After |
422 card_mode_mismatch | Opening lines or attributing a purchase outside company mode |
422 credit_line_over_assigned | The live lines' limits would exceed the card's limit |
422 card_over_limit | Raising cupo while the card is over its limit |
422 credit_line_required | A purchase without creditLineId on a card that has live lines |
422 insufficient_line_credit | The purchase does not fit the line (and was not forced); details.scope = line |
422 insufficient_credit | The purchase does not fit the card (and was not forced), even if the line had room; details.scope = card |
422 card_limit_below_assigned | Lowering the card's limit below what is assigned to its lines |