Credit Lines

A Slate Credit Line is a revolving spending facility for your customers. 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 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",
    "business": {
      "legalName": "Acme Corp",
      "dba": "Acme",
      "website": "https://acme.com"
    }
  }'

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:

Option A — slate-pre-approval-banner (easiest)

The banner manages the entire lifecycle by itself: it renders only when the customer has an active offer, opens Slate's application flow when clicked, and updates its own state as the application progresses (in progress, submitted, approved, rejected). Drop it in and you're done.

<slate-pre-approval-banner-v2
  env="live"
  user-token="<session-token>"
></slate-pre-approval-banner-v2>

See the Pre Approval 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.