Credit Line — Transaction Feed
A feed-based credit line ("type": "feed" on the credit line object) is the line for partners who operate their own card or spend product. Your customer spends through you; you report every transaction to Slate through a feed; and at the end of each period Slate funds you the net the customer spent — that funding is a draw against the customer's line. The customer repays the draw through you within a free window, and only if they don't does it convert into a financed extension.
This is the third credit line mode Slate offers:
| Transaction feed (this guide) | Draws | Revolving | |
|---|---|---|---|
| The customer... | spends on a card/product you operate | takes draws when they need cash | spends on a card you operate |
| Slate funds | you, the net of each period | the customer, per draw | you, per statement cycle |
| Repayment | through you, per draw, free inside its window | fixed installments per draw | statement balance each period |
| Financing cost | none inside the window; factor fee only on unpaid remainders | the plan's factor rate, always | period fees per statement |
| You push | every purchase and payment, daily | nothing | every purchase and payment |
| Typical for | fuel/fleet cards, B2B spend wallets | payroll funding, working capital | card programs on statement billing |
All amounts are integer cents (e.g.
50000= $500.00). All dates areYYYY-MM-DD. Requests are authenticated with your private API key in thex-api-keyheader. Webhooks are signed — see Webhooks.
Lifecycle at a glance
The full journey is below: Steps 1–6 take a customer from creation to an active line (the same onboarding every credit line mode shares), and Steps 7–13 are the feed itself — the part unique to this mode.
Line ACTIVE ──► You report purchases and payments daily (the feed)
│ purchases consume availability, payments restore it
▼
Period boundary passes (e.g. weekly)
│
▼
Slate closes the period: net = purchases − payments
│ │
net ≤ 0 net > 0
nothing is created a DRAW is created for the net
the credit carries ──► webhook: credit-line.draw.created
into the next period │
▼
Slate wires you the funds
──► webhook: credit-line.draw.funded
the customer's free window starts
│
┌───────────────────┴───────────────────┐
▼ ▼
Repaid in full inside the window Window passes with a balance
──► credit-line.draw.repaid the remainder becomes an EXTENSION
costs the customer nothing with a factor fee
──► credit-line.draw.created
│
┌───────────────────┴─────────────┐
▼ ▼
Extension repaid Extension unpaid past due
financing-agreement.completed line SUSPENDED (card off)
──► credit-line.suspended
│
▼
Renegotiated with Slate
──► credit-line.reinstated
Key facts:
- Every draw is a financing agreement of the line. The period's net, the extensions, the renegotiated replacements — all of them live in the same agreements API, distinguishable by their terms.
- The window is free. A period draw carries no fee while inside its repayment window; the factor fee exists only for remainders that outlive it.
- Availability is consumed by principal, never by fees. A $5,000 extension at 1.01 means $5,050 owed, but $5,000 of limit in use.
- Your deposits never pay extensions. Payments in the feed net the period funding between Slate and you; extensions are owed to Slate and collected through their own channel.
- Slate closes periods and funds you — you never trigger funding; you only keep the feed accurate.
Step 1 — Create the customer
Create the customer with your own stable identifier (externalId) — it is your reconciliation key across every Slate object. A customer is either a BUSINESS or a PERSON.
curl -X POST "$BASE_URL/customers" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "BUSINESS",
"externalId": "cus_123",
"productType": "credit_line",
"business": {
"legalName": "Acme Corp",
"dba": "Acme",
"website": "https://acme.com"
}
}'
A customer is enrolled in exactly one product. If your organization has more than one product active at the same time, productType is required — it tells Slate which product this customer is being offered (here, "credit_line"). With a single active product you can omit the field.
All detail fields are optional at creation — whatever you don't provide, the customer fills in during the application. The more you provide upfront, the shorter their application. If the customer already exists (for example, they already use another Slate product), skip this step.
Webhook — customer.created confirms the customer exists; data.id is the customer id.
Step 2 — Attach financial data
Submit the customer's financial history so Slate can underwrite. The payload is a timeseries keyed by your externalId:
curl -X POST "$BASE_URL/attach-financial-data" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "cus_123",
"timeseries": [
{ "date": "2026-04-01", "data": { "revenue": 4200000, "spendVolume": 950000 } },
{ "date": "2026-05-01", "data": { "revenue": 4550000, "spendVolume": 1010000 } },
{ "date": "2026-06-01", "data": { "revenue": 4830000, "spendVolume": 1120000 } }
]
}'
The data object is free-form — send the metrics agreed with Slate for your integration (revenue is the baseline; a card program typically adds the customer's spend volume on your product). You can attach new data at any time to keep the picture current; fresher data means better offers and limits.
Webhook — business.financial-data.attached confirms ingestion; data.id is the financial data record id.
Step 3 — Slate issues a pre-approval
When the customer qualifies, Slate creates a pre-approval — a non-binding offer whose amount is the facility limit Slate is prepared to extend on the line. You receive:
Webhook — preapproval.created
{
"event_type": "preapproval.created",
"data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
"timestamp": "2026-06-18T16:32:00Z"
}
Fetch the customer's offers any time:
curl "$BASE_URL/v2/pre-approvals?externalId=cus_123&status=ACTIVE" \
-H "x-api-key: $API_KEY"
A pre-approval is ACTIVE until it either expires (webhook preapproval.expired) or the customer converts it by completing an application (webhook preapproval.consumed).
Step 4 — Offer the application in your product
The application flow is rendered by Slate components, authenticated with a short-lived session token you mint server-side for the logged-in customer:
curl -X POST "$BASE_URL/user-session-token" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "externalId": "cus_123" }'
{ "token": "eyJhbGciOi..." }
Never expose your API key to the browser, only the session token. Load the components bundle once per page:
<script async type="module" src="https://components.tryslatehq.com/slate.esm.js"></script>
You have two options, from least to most integration work:
Option A — slate-credit-line-banner (recommended). One component covers the journey — the offer, the application, and the line once it is live. It reads the customer's state on its own and renders the right thing:
<slate-credit-line-banner
env="live"
user-token="<session-token>"
></slate-credit-line-banner>
In a feed program the banner's job ends once the line is live. It carries the customer through applying for the line and shows the progress and status of that journey (offer → application in progress → in review → line active); once the line is live it renders availability and usage as a purely informative display. It performs no operations after that — no draw button ever appears on a feed-based line. Everything functional lives in your system: the card and its spend, presenting the customer's balance, collecting repayments, and handling extensions are all managed directly by you, through the feed and the agreement endpoints of this guide.
Option B — Your own banner + slate-application-form. Build the offer UI yourself and embed only the application when the customer engages:
<slate-application-form
id="application-form"
env="live"
user-token="<session-token>"
closable
></slate-application-form>
<script>
const form = document.getElementById("application-form");
form.addEventListener("submit", ({ detail }) => {
// detail.applicationId — the application was submitted
});
form.addEventListener("dismiss", ({ detail }) => {
// detail.status is "submitted" | "approved" | "rejected" — close the form
});
</script>
Drive your offer UI's state from the webhooks (preapproval.*, application.*, credit-line.*) or by polling GET /v2/pre-approvals and GET /credit-lines?externalId=.... The form renders as an iframe that fills its parent, so give the container an explicit height — see the Application Form component page for its properties, events and caveats.
Never embed
slate-application-formwith thedrawattribute in a feed program. That attribute opens the customer draw flow of the draws mode — a capability feed customers don't have; their spend surface is your card. The application form withoutdrawruns only the credit line application, which is exactly what a feed integration needs.
Step 5 — The customer applies for the line
Clicking your CTA opens Slate's application. The customer confirms their details, connects their bank account, verifies identity if required, and accepts the terms and the master agreement — the agreement that governs every future draw the periods will create. There is no amount or terms selection: the customer is applying for a line, not borrowing yet.
Webhook — application.submitted fires when they finish. Slate then reviews the application:
Webhook — credit-line.approved — Slate approved the application for the line. This fires before the line exists — data.id is the application id, which now carries the approved maximum facility limit (GET /v2/applications/{applicationId}). The line itself is created next (Step 6).
Step 6 — Slate activates the line
Once the application is approved, Slate activates the credit line for the agreed facility limit — you don't call anything. The line goes live with nothing owed and the full limit available:
Webhook — credit-line.created
{
"event_type": "credit-line.created",
"data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
"timestamp": "2026-06-22T14:03:11Z"
}
data.id is the credit line id — the id every subsequent credit line webhook refers to. Store it against your customer, or resolve it any time from your own identifier:
curl "$BASE_URL/credit-lines?externalId=cus_123" \
-H "x-api-key: $API_KEY"
This is the moment the feed starts: activate the customer's card and begin reporting their transactions (Step 7).
Program parameters
Three knobs shape the feed's rhythm, all agreed with Slate at program setup — never chosen per draw:
- Period cadence — how often periods close into draws (weekly by default, anchored to the line's start).
- Free window — how many days the customer has to repay a funded draw at zero cost.
- Extension terms — the factor fee and term applied to remainders that outlive their window.
Step 7 — The feed: report transactions
Report every customer transaction as it happens (or in a daily batch). A transaction is a purchase (customer spend — consumes availability) or a payment (a deposit the customer made to you — restores availability).
One at a time, in real time:
curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "txn_01J8ZQ",
"type": "purchase",
"amount": 12500,
"transactionDate": "2026-07-30",
"description": "Fuel — pump 4"
}'
{
"transaction": { "id": "...", "externalId": "txn_01J8ZQ", "type": "purchase", "amount": 12500, "status": "active" },
"availableCredit": 987500
}
Or a daily batch (up to 1,000, processed in order):
curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions/batch" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transactions": [
{ "externalId": "txn_01J8ZQ", "type": "purchase", "amount": 12500, "transactionDate": "2026-07-30" },
{ "externalId": "txn_01J8ZR", "type": "payment", "amount": 5000, "transactionDate": "2026-07-30" }
]
}'
The batch answers per item — accepted items are persisted, rejected items are not (and tell you why):
{
"results": [
{ "externalId": "txn_01J8ZQ", "status": "accepted", "reason": null },
{ "externalId": "txn_01J8ZR", "status": "accepted", "reason": null }
],
"availableCredit": 992500
}
The rules, identical in both endpoints:
externalIdis your idempotency key per line. Re-sending an id that already exists is rejected as a duplicate and nothing changes — retries are safe.- A purchase that exceeds the available credit is rejected and not persisted. Your card authorization should check availability first (Step 8) — the feed is the system of record, not the authorization gate.
- Payments are always accepted, even with the facility fully consumed — a deposit only ever helps. Inside a batch, an earlier payment makes room for a later purchase.
transactionDatemust be current: dates in the future (beyond one day of timezone tolerance) or before the line's start are rejected. Report daily — the feed is not a backfill channel.
Mis-reported something? Reverse it while it is still pending (not yet part of a closed period):
curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions/txn_01J8ZQ/reverse" \
-H "x-api-key: $API_KEY"
Once a period closes, its transactions belong to the draw — corrections from then on are payments against it, never reversals.
Step 8 — Availability: the facility state
Before authorizing spend, read the facility:
curl "$BASE_URL/credit-lines/{creditLineId}/facility" \
-H "x-api-key: $API_KEY"
{
"creditLineId": "019f8186-...",
"currency": "CAD",
"status": "active",
"facilityLimit": 1000000,
"availableCredit": 750000,
"activeExtensionsRemaining": 100000,
"pendingPeriodSpend": 150000,
"activeExtensions": [
{
"id": "019f81a0-...",
"fundedAmount": 150000,
"remainingBalance": 101000,
"principalRemaining": 100000,
"status": "IN_PROGRESS"
}
]
}
availableCredit— what the customer can spend right now. Every purchase you report lowers it immediately and every payment restores it; use this field as your authorization gate.activeExtensionsRemaining— the principal still outstanding across the line's active agreements (period draws already funded, extensions, renegotiations). Principal, not payback: fees never consume the limit.pendingPeriodSpend— the net of the feed not yet closed into a draw: purchases minus payments. It can be negative: a customer whose deposits exceed their spend is prepaid, and their availability goes above the limit by exactly that surplus. That is intentional — the surplus is their own money sitting with you, and it is consumed first at the next close.activeExtensions— each active agreement with its full debt (remainingBalance) and the part consuming the limit (principalRemaining).
Step 9 — Period close: the net becomes a draw
On a fixed cadence (weekly by default, anchored to the line's start), Slate closes the period: it takes every pending feed transaction, nets purchases − payments, and turns the result into a draw.
-
Net > 0 — a draw is created for exactly the net. It is a zero-fee agreement of the line: no factor, no charge — just the amount Slate will fund you.
Webhook —
credit-line.draw.created—data.idis the credit line;financingAgreementIdcarries the draw's agreement. Fetch it from the agreements API to see the amount. -
Net ≤ 0 — nothing is created and nothing is owed. The transactions stay pending and carry into the next period: if the customer prepaid 5,000 against 2,000 of purchases this week, and buys 4,000 next week, next week's draw is 1,000. The carry is consumed exactly once — a closed period's transactions are attached to their draw and never re-counted.
Step 10 — Funding: Slate wires you
Slate reviews the draw and executes the transfer to you. This is when the customer's clock starts:
Webhook — credit-line.draw.funded — the money is on its way to you, and the draw's free repayment window begins (its length is agreed in your program setup). Until this webhook, the draw exists but nothing is owed and no clock is running.
Step 11 — The free window: the customer repays through you
Inside the window the draw costs the customer nothing. They repay you, and you remit to Slate, reporting each remittance against the draw's agreement:
curl -X POST "$BASE_URL/financing-agreements/{financingAgreementId}/payments" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 200000,
"currency": "CAD",
"externalId": "remit_8f2c31"
}'
externalId is your idempotency key — re-sending the same one is a no-op, so retries are safe.
Webhook — financing-agreement.repayment fires on each reported payment. When the draw is fully covered inside its window:
Webhook — credit-line.draw.repaid — the draw is done at zero cost to the customer, its agreement completes (financing-agreement.completed fires too), and the limit it consumed is freed.
Do not report the customer's deposits twice. A deposit made before the period closes goes in the feed as a
payment(it nets the upcoming draw). A repayment of an already funded draw goes to the agreement payments endpoint. One flow of money, one report — which endpoint depends on whether the draw exists yet.
Step 12 — Extension: the remainder gets financed
If the window passes with a balance still owed, Slate converts the remainder automatically: the zero-fee draw is closed as restructured, and a new agreement is created for exactly the unpaid amount, now carrying a factor fee and a short single-payment term.
Webhook — credit-line.draw.created — same event as any draw creation; the financingAgreementId is the extension's agreement. From here it behaves like any financed agreement: report the payments you collect against it, and financing-agreement.completed fires when it clears.
The extension's principal keeps consuming the limit until repaid — the fee never does.
Step 13 — Delinquency: card off, renegotiate, card on
If an extension passes its due date unpaid (with a one-day operational buffer, so a payment made late on the due date never races the signal), Slate suspends the line:
Webhook — credit-line.suspended — deactivate the card. From this moment the feed rejects new transactions (INVALID_STATE) and no periods close; spend you allow after this signal is your exposure, not Slate's.
Getting back is a conversation: Slate agrees new terms with the customer and replaces every overdue extension with a single renegotiated agreement. When nothing overdue remains:
Webhook — credit-line.reinstated — reactivate the card; the feed accepts transactions again. The renegotiation also emits credit-line.draw.created with the replacement agreement.
Monitoring endpoints
| Endpoint | What it returns |
|---|---|
GET /credit-lines?externalId=cus_123 |
The customer's lines — resolve a line from your own customer id |
GET /credit-lines/{creditLineId}/facility |
Limit, available credit, pending net and every active agreement |
GET /credit-lines/{creditLineId}/transactions |
The feed as Slate sees it — filter by type, status, externalId, financingAgreementId (the draw a transaction was closed into) or date range |
GET /credit-lines/{creditLineId}/agreements |
The line's draws and extensions, newest first. Filter with status |
GET /v2/financing-agreements/{id} |
One draw or extension: amount, payments made, remaining balance |
Reconcile the feed nightly: GET .../transactions?dateFrom=...&dateTo=... against your own ledger, and reverse mis-reports before the period closes.
Webhooks summary
Subscribe to these events (see Webhooks for delivery and signature verification). Onboarding events (customer.created, preapproval.*, application.*, credit-line.approved) appear in Steps 1–6.
| Event | data.id refers to |
When it fires | What you should do |
|---|---|---|---|
credit-line.created |
Credit line | The line is live | Store the credit line id; start the feed |
credit-line.draw.created |
Credit line | A period closed into a draw, a remainder became an extension, or a renegotiation replaced one (financingAgreementId carries the agreement) |
Fetch the agreement and track it |
credit-line.draw.funded |
Credit line | Slate wired you a period draw — the free window starts | Start the customer's repayment clock in your product |
credit-line.draw.repaid |
Credit line | A draw was fully repaid inside its window | Show it settled — zero cost to the customer |
financing-agreement.repayment |
Financing agreement | A payment you reported was settled | Refresh the agreement's balance |
financing-agreement.completed |
Financing agreement | A draw or extension was fully repaid | Refresh availability — the limit freed up |
credit-line.suspended |
Credit line | An extension went unpaid past its due date | Deactivate the card — the feed now rejects |
credit-line.reinstated |
Credit line | Renegotiation cleared every overdue agreement | Reactivate the card — the feed accepts again |
Good practices
- Authorize against
availableCredit, report through the feed. The facility endpoint is your authorization gate; the feed is the system of record. A purchase the feed would reject should never have been authorized. - Treat webhooks as notifications, not payloads. They carry ids — fetch the current state from the API afterward.
- One money flow, one report. Pre-close deposits are feed
payments; post-funding repayments go to the agreement's payments endpoint. Double-reporting overstates what the customer paid. - Act on
credit-line.suspendedimmediately. Spend after the card-off signal is your exposure. - Expect prepaid customers. Negative pending net and availability above the limit are normal — it means the customer's deposits outpaced their spend.
- Reverse before close, pay after. A mis-reported transaction is reversible only while pending; once its period closes it belongs to a draw.