Credit Line — Revolving

A revolving credit line is a spending facility behind a card you operate. Your platform records the customer's activity (purchases, payments, rebates) in real time, and Slate handles the billing: it groups activity into billing periods, generates an immutable statement at the end of each period, and manages the line's lifecycle (active → suspended → reactivated) based on whether the minimum payment is met.

This is one of the two credit line types Slate offers:

Revolving (this guide) Draws
The customer... spends on a card you operate takes draws when they need cash
Repayment statement balance each period fixed installments per draw
Billing 7-day periods, statements, minimum payments none — each draw has its own schedule
You push every purchase and payment, in real time nothing
Typical for fuel/fleet cards, B2B spend payroll funding, working capital on demand

This guide walks through the full flow end to end: which endpoints to call, which webhooks to listen to, and what your platform is expected to do at each step.

All amounts in the Credit Line API are integer cents (e.g. 50000 = $500.00). All dates are YYYY-MM-DD. All requests are authenticated with your private API key in the x-api-key header. Webhooks are delivered and signed, see Webhooks for signature verification.


Lifecycle at a glance

You create the customer ──► You attach financial data ──► Slate underwrites


                                  Pre-approval issued ──► webhook: preapproval.created

                              you present the offer            │
                              and the customer applies         │

Application approved ──► You activate the line ──► ACTIVE

                        ┌────────────────────────────┘

              Billing period (7 days)
              customer spends / pays / receives rebates
                        │  period ends

              Statement generated  ──►  webhook: credit-line.statement-ready


              Grace period (3 days) — customer pays the minimum

          ┌─────────────┴──────────────┐
          ▼                            ▼
   Minimum covered              Minimum NOT covered
   line stays ACTIVE            line is SUSPENDED
   next period continues        webhook: credit-line.suspended → block the card


                                Customer pays the missed minimum
                                line auto-reactivates
                                webhook: credit-line.reinstated → re-enable the card

Key facts:

  • Billing periods are 7 days, anchored to the anchorDate you set when activating the line, followed by a 3-day grace period to pay the statement's minimum.
  • Statements are immutable snapshots: once generated, a statement never changes. Payments received afterward appear on the next period's statement.
  • If a balance is carried past its due date, Slate bills a carried balance fee into the next period. It appears as a fee transaction on the line.
  • Suspension and reactivation are automatic. You don't call any endpoint to reactivate, a sufficient payment does it.

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 or money facility this customer is being offered (here, "credit_line"). With a single active product you can omit the field: the customer is assigned the product your program was set up with when you started with Slate.

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, "orders": 310 } },
      { "date": "2026-05-01", "data": { "revenue": 4550000, "orders": 335 } },
      { "date": "2026-06-01", "data": { "revenue": 4830000, "orders": 342 } }
    ]
  }'

The data object is free-form — send the metrics agreed with Slate for your integration (revenue is the baseline). You can attach new data at any time to keep the picture current; fresher data means better offers.

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 a credit 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 — Present the offer

Everything the customer sees is rendered by Slate components, authenticated with a short-lived session token that 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 for implementing the application flow, from least to most integration work:

One component covers the entire journey — the offer, the activation, and the line once it is live. It reads the customer's state on its own and renders the right thing, opening Slate's flows when clicked. Drop it in and you're done.

<slate-credit-line-banner
  env="live"
  user-token="<session-token>"
></slate-credit-line-banner>
Customer state What the banner shows Button
Pre-approved, no line yet "Activate your credit line" Activate → opens the application
Application in progress "Finish activating your credit line" Resume → reopens where they left off
Application submitted "We're reviewing your application"
Line active Available amount and how much of the limit is in use Draw funds → opens the draw flow
Line paused The line and its balance, draws disabled
No offer and no line Renders nothing (takes no space)

The banner only speaks credit line: if the customer's offer belongs to the capital product, it renders nothing. That makes it safe to embed on a page every customer sees — each customer gets their product's banner, never the wrong copy.

The banner inherits your brand colors from the branding you configure with Slate. See the Credit Line Banner component page for all its properties and caveats.

Option B — Your own banner + slate-application-form

Build the banner/offer UI yourself and open Slate's application form when the customer engages. You drive your banner's state by listening to the webhooks (preapproval.*, application.*) or by polling GET /v2/pre-approvals and GET /v2/applications, and embed only the application itself:

<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>

The form renders as an iframe that fills its parent, so give the container an explicit height. Omit finance-request-id for Capital, the form automatically loads the application tied to the customer's pre-approval. See the Application Form component page for all its properties, events and caveats.

Step 5 — The customer applies and is approved

The customer completes the application inside Slate's flow — confirming their details and connecting their bank account. Screens already covered by the data you sent when creating the customer are skipped. Slate reviews the application, and when it's approved you receive:

Webhook — credit-line.approved

{
  "event_type": "credit-line.approved",
  "data": { "id": "9c5f8f4e-...-application-uuid" },
  "timestamp": "2026-06-20T16:32:00Z"
}

data.id is the application id. The application now carries an approved maximum facility limit. Fetch it if you need the details:

curl "$BASE_URL/v2/applications/{applicationId}" \
  -H "x-api-key: $API_KEY"

Your platform can now activate the credit line.

Step 6 — Activate the credit line

Create the line from the approved application. You choose the facility limit (up to the approved maximum), the currency, and the anchor date — the day the first billing period starts. All subsequent periods are derived from this date, so pick it deliberately (usually "today" or the customer's go-live date).

curl -X POST "$BASE_URL/applications/{applicationId}/credit-line" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "facilityLimit": 1500000,
    "currency": "CAD",
    "anchorDate": "2026-06-22"
  }'

Response

{
  "id": "019f8186-7021-73ea-a169-b12cd581c38c",
  "applicationId": "9c5f8f4e-...",
  "customerId": "0195b7d2-...",
  "facilityLimit": 1500000,
  "currency": "CAD",
  "status": "active",
  "anchorDate": "2026-06-22",
  "createdAt": "2026-06-22T14:03:11.000Z",
  "updatedAt": "2026-06-22T14:03:11.000Z"
}

The line is active and its first billing period is open. You also receive:

Webhook — credit-line.created

{
  "event_type": "credit-line.created",
  "data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
  "timestamp": "2026-06-22T14:03:11Z"
}

From here on, data.id in every credit line webhook is the credit line id.

Step 7 — Record transactions in real time

Every time something happens on the card, push it to Slate as it happens through a single endpoint:

POST /credit-lines/{creditLineId}/transactions
Field Type Description
type "purchase" | "payment" | "rebate" What kind of transaction this is
amount integer Amount in cents, always positive
transactionDate string YYYY-MM-DD — the date the transaction happened
externalId string (optional) Your transaction id, for reconciliation
description string (optional) Free-text description (e.g. merchant name)

Transaction types

Type What it is Rules
purchase Customer card spend Rejected while the line is suspended or the period the date falls in is already closed
payment Money the customer paid toward the line Accepted at any time — including while suspended. Payments count toward the minimum payment and can automatically reactivate a suspended line
rebate A credit funded by you (the partner), e.g. a fuel rebate Reduces the outstanding balance, but does not count toward the customer's minimum payment and does not pay down fees

Examples

A purchase:

curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "purchase",
    "amount": 15000,
    "transactionDate": "2026-06-25",
    "externalId": "txn_8f2c31",
    "description": "PETRO-CANADA 38989"
  }'

A customer payment:

curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payment",
    "amount": 250000,
    "transactionDate": "2026-06-28",
    "externalId": "etransfer_a91b",
    "description": "E-Transfer Payment"
  }'

A partner-funded rebate:

curl -X POST "$BASE_URL/credit-lines/{creditLineId}/transactions" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "rebate",
    "amount": 560,
    "transactionDate": "2026-07-01",
    "description": "Fuel Rebate"
  }'

Response (all types)

{
  "id": "01a0c2ff-...",
  "creditLineId": "019f8186-...",
  "periodId": "01a0c2f0-...",
  "type": "purchase",
  "amount": 15000,
  "transactionDate": "2026-06-25",
  "description": "PETRO-CANADA 38989",
  "notes": "txn_8f2c31",
  "createdAt": "2026-06-25T18:40:12.000Z"
}

Slate assigns each transaction to the billing period its transactionDate belongs to. You may also see fee transactions on the line — those are created by Slate (e.g. the carried balance fee) and cannot be created through the API.

Common errors: INVALID_STATE — you tried to record a purchase on a suspended line, or a transaction dated in a period that is already closed. Payments dated within a period's grace window are accepted until the grace period ends.

Step 8 — The statement is generated

When a billing period ends, Slate closes it and generates its statement. You receive:

Webhook — credit-line.statement-ready

{
  "event_type": "credit-line.statement-ready",
  "data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
  "timestamp": "2026-06-29T00:05:00Z"
}

data.id is the credit line id. The webhook is a notification only — fetch the statement to get its contents:

curl "$BASE_URL/credit-lines/{creditLineId}/statement" \
  -H "x-api-key: $API_KEY"

Without parameters this returns the most recent statement — which right after the webhook is the one that was just generated.

Response

{
  "creditLineId": "019f8186-...",
  "currency": "CAD",
  "period": {
    "id": "01a0c2f0-...",
    "periodNumber": 0,
    "periodStart": "2026-06-22",
    "periodEnd": "2026-06-28",
    "periodDays": 7,
    "paymentDueDate": "2026-07-01",
    "graceExpiry": "2026-07-01",
    "status": "grace_period"
  },
  "summary": {
    "facilityLimit": 1500000,
    "openingBalance": 0,
    "totalPurchases": 61456,
    "totalPayments": 0,
    "totalRebates": 0,
    "totalFees": 0,
    "statementBalance": 61456,
    "minimumPayment": 3125,
    "amountPaid": 0,
    "carriedBalance": 61456,
    "availableCredit": 938544
  },
  "transactions": [
    {
      "id": "01a0c2ff-...",
      "type": "purchase",
      "amount": 25000,
      "transactionDate": "2026-06-25",
      "description": "PETRO-CANADA 38989"
    }
  ],
  "generatedAt": "2026-06-29T00:05:01.000Z"
}

Show the statement to your customer. The two numbers that matter most:

  • summary.statementBalance — what the customer owes as of the period close. A negative value is a credit in the customer's favor.
  • summary.minimumPayment — what the customer must pay by period.paymentDueDate to keep the line active.

Step 9 — The grace period

After the statement, the customer has until paymentDueDate (3 days after the period ends) to pay at least the minimum. Payments during this window are recorded exactly like any other payment (Step 7) — just make sure transactionDate reflects the real payment date, since that is what determines whether the payment arrived in time.

Two outcomes:

  • Minimum covered by the due date → nothing happens; the line continues normally into the next period. The statement stays frozen; the payment appears on the next period's statement.
  • Minimum not covered → the unpaid balance carries into the next period, a carried balance fee is billed (you'll see it as a fee transaction), and the line is suspended.

Only payment transactions count toward the minimum. Rebates reduce what the customer owes overall, but the minimum itself must be paid by the customer.

Step 10 — Suspension

If the grace period expires without the minimum being covered, you receive:

Webhook — credit-line.suspended

{
  "event_type": "credit-line.suspended",
  "data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
  "timestamp": "2026-07-02T00:05:00Z"
}

What your platform must do: block the card. While suspended:

  • purchase transactions are rejected with INVALID_STATE.
  • payment transactions are still accepted — that's how the customer gets back to active.

You can check the line's status any time with GET /credit-lines/{creditLineId} (status: "suspended").

Step 11 — Reactivation

Reactivation is automatic. Keep recording the customer's payments as usual; once their payments cover the missed minimum payment (partial payments accumulate) — or pay off the outstanding balance entirely — the line reactivates on its own and you receive:

Webhook — credit-line.reinstated

{
  "event_type": "credit-line.reinstated",
  "data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
  "timestamp": "2026-07-03T15:20:00Z"
}

What your platform must do: re-enable the card. The line is active again and purchases are accepted.


Fetching historical statements

Every closed period has its own immutable statement. There are two ways to get a specific one:

Option A — by date (easiest)

Pass any date inside the period you want to GET /statement:

curl "$BASE_URL/credit-lines/{creditLineId}/statement?statementDate=2026-06-25" \
  -H "x-api-key: $API_KEY"

This returns the statement of the period that contains 2026-06-25.

Option B — by period id

List the line's periods, pick the one you want, and request its statement by id:

curl "$BASE_URL/credit-lines/{creditLineId}/periods" \
  -H "x-api-key: $API_KEY"
[
  {
    "id": "01a0c2f0-...",
    "periodNumber": 0,
    "periodStart": "2026-06-22",
    "periodEnd": "2026-06-28",
    "graceExpiry": "2026-07-01",
    "status": "finalized",
    "statementBalance": 61456,
    "minimumPayment": 3125,
    "amountPaid": 0,
    "carriedBalance": 61456,
    "feeCharged": 410,
    "createdAt": "2026-06-22T14:03:11.000Z"
  },
  {
    "id": "01a0c9d1-...",
    "periodNumber": 1,
    "periodStart": "2026-06-29",
    "periodEnd": "2026-07-05",
    "graceExpiry": "2026-07-08",
    "status": "open",
    "statementBalance": null,
    "minimumPayment": null,
    "amountPaid": null,
    "carriedBalance": null,
    "feeCharged": null,
    "createdAt": "2026-06-29T00:05:01.000Z"
  }
]

Then:

curl "$BASE_URL/credit-lines/{creditLineId}/periods/{periodId}/statement" \
  -H "x-api-key: $API_KEY"

periods also supports dateFrom / dateTo (YYYY-MM-DD) query parameters to narrow the range. Statements only exist for periods whose status is not open — an open period has no statement yet.


Monitoring endpoints

Endpoint What it returns
GET /credit-lines Paginated list of your credit lines with their current period dates. Filters: status, search, page, limit
GET /credit-lines/{creditLineId} A single line: status, terms, current period, live outstanding balance and availableCredit
GET /credit-lines/{creditLineId}/transactions Paginated transactions. Filters: periodId, type, status, description, dateFrom, dateTo
GET /credit-lines/{creditLineId}/periods All billing periods with their statement figures
GET /credit-lines/{creditLineId}/statement The most recent statement, or a specific one via statementDate
GET /credit-lines/{creditLineId}/periods/{periodId}/statement The statement of an exact period

Example — the live view of a line:

curl "$BASE_URL/credit-lines/{creditLineId}" \
  -H "x-api-key: $API_KEY"
{
  "id": "019f8186-...",
  "status": "active",
  "facilityLimit": 1500000,
  "currency": "CAD",
  "anchorDate": "2026-06-22",
  "currentPeriod": {
    "periodStart": "2026-06-29",
    "periodEnd": "2026-07-05",
    "graceExpiry": "2026-07-08"
  },
  "balance": {
    "currency": "CAD",
    "balance": 61456,
    "totalOutstanding": 61456
  },
  "availableCredit": 1438544,
  "createdAt": "2026-06-22T14:03:11.000Z",
  "updatedAt": "2026-06-29T00:05:01.000Z"
}

Webhooks summary

Subscribe to these events (see Webhooks for delivery and signature verification). Every payload has the same shape: { event_type, data: { id }, timestamp }.

Event data.id refers to When it fires What you should do
customer.created Customer The customer was created Store the customer id
business.financial-data.attached Financial data record Financial data was ingested Nothing — confirmation only
preapproval.created Pre-approval Slate issued a facility limit offer Present it (Step 4)
preapproval.expired Pre-approval The offer expired unused Remove it from your UI if you built your own banner
preapproval.consumed Pre-approval The offer converted into an application Remove it from your UI if you built your own banner
credit-line.approved Application The application is approved with a maximum facility limit Activate the line (Step 6)
credit-line.created Credit line The line is created and its first period opens Store the credit line id; start recording transactions
credit-line.statement-ready Credit line A billing period closed and its statement was generated GET /statement and present it to your customer
credit-line.suspended Credit line The grace period expired without the minimum covered Block the card; keep recording payments
credit-line.reinstated Credit line Payments covered the missed minimum (or the full balance) Re-enable the card

Good practices

  • Push transactions as they happen. Statements, balances and suspension decisions are computed from what you've recorded — delayed transactions can land in a closed period and be rejected.
  • Always send externalId on transactions. It's stored with the transaction and is your reconciliation key.
  • Use real transaction dates. transactionDate (not the time you call the API) determines which period a transaction belongs to and whether a payment arrived within the grace window.
  • Treat webhooks as notifications, not payloads. They carry only the object id — always fetch the current state from the API afterward.
  • Don't infer amounts client-side. Statement balances, minimum payments and fees are computed by Slate; read them from the statement endpoints.