Capital

Slate Capital is a fixed-amount financing product: your customer receives an upfront disbursement and repays it in equal scheduled installments. It is offered proactively, Slate underwrites your customer from the financial data you submit, issues a pre-approval (a non-binding offer), and the customer converts it into a financing agreement by completing an application and signing the contract. Slate handles underwriting, decisioning, the contract, the disbursement, and collection; your platform supplies the customer data and presents the offer.

Depending on who the customer is, the agreement is executed under one of two legal instruments:

Type Instrument Used for
fixed_mca Merchant Cash Advance (MCA) Business customers
fixed_fepa Future Earnings Purchase Agreement (FEPA) Person customers

Both behave identically in the API: the total to repay is fundedAmount × factorRate, split into fixed installments over the agreement's term. The only difference is the underlying contract and which object carries the factorRate (fixedMCA vs fixedFEPA).

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

All amounts in the Capital API are integer cents (e.g. 2500000 = $25,000.00). All dates are YYYY-MM-DD unless noted. All API requests are authenticated with your private API key in the x-api-key header. Webhooks are delivered and signed, see Webhooks for signature verification.

Capital does not use finance requests (those belong to the Early Wage Access product, see Early Wage Access) and has no billing periods or statements (those belong to Credit Lines).


Lifecycle at a glance

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


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

                       you present the offer (embed / own UI)  │

                                            Customer opens the application
                                            webhook: application.created


                                            Customer completes and submits
                                            webhook: application.submitted


                                        Slate underwrites & approves with terms
                                          webhook: application.ready-to-accept


                                   Customer verifies identity & accepts the terms
                              webhooks: application.approved · preapproval.consumed


                                            Agreement is created automatically when 
                                                      terms are accepted
                                                  financing-agreement.created


                                    Disbursement ──► webhook: financing-agreement.disbursed


                              Scheduled repayments ──► webhook: financing-agreement.repayment (each one)

                                              ┌────────────────┴───────────────┐
                                              ▼                                ▼
                                         COMPLETED                        DEFAULTED
                          webhook: financing-agreement.completed   webhook: financing-agreement.defaulted

Key facts:

  • The offer comes from Slate. You don't request financing for a specific amount; you submit financial data, and Slate issues a pre-approval when the customer qualifies.
  • The application, contract and signature are hosted by Slate. Your platform embeds them; it never collects or forwards the application data itself.
  • Repayment is automatic. Slate debits the installments from the customer's verified bank account (PAD). There is no endpoint to record payments, you only monitor them.
  • A customer can be a business or a person. The customer type determines the instrument (fixed_mca vs fixed_fepa); everything else in the flow is the same.

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"
    }
  }'

For a person:

curl -X POST "$BASE_URL/customers" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "PERSON",
    "externalId": "cus_456",
    "person": {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@email.com",
      "phone": "+15551234567"
    }
  }'

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.

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 with an amount, currency and repayment cadence. You receive:

Webhook — preapproval.created

{
  "event_type": "preapproval.created",
  "data": { "id": "019f8186-7021-73ea-a169-b12cd581c38c" },
  "timestamp": "2026-06-20T16: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"
{
  "data": [
    {
      "id": "019f8186-7021-73ea-a169-b12cd581c38c",
      "customerId": "0195b7d2-...",
      "externalId": "cus_123",
      "status": "ACTIVE",
      "amount": 2500000,
      "currency": "CAD",
      "repaymentFrequency": 1,
      "repaymentFrequencyUnit": "month",
      "origin": "slate",
      "createdAt": "2026-06-20T16:32:00.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 1 }
}

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, signature required, 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

Inside the application flow the customer confirms their business or personal details and address, and connects their bank account through Flinks (this is the account Slate disburses to and debits from). Connecting the bank submits the application. Screens already covered by the data you sent when creating the customer are skipped, see The Application Flow for every screen and state in detail. Your platform does nothing here except listen:

Webhook When it fires
application.created The customer opened the application
application.submitted The customer completed and submitted it
application.bank.verified The bank account was verified
application.rejected Slate declined the application — the flow ends here

data.id in every application.* webhook is the application id. Fetch the full picture any time:

curl "$BASE_URL/v2/applications/{applicationId}" \
  -H "x-api-key: $API_KEY"
{
  "application": {
    "id": "01a0c2ff-...",
    "customerId": "0195b7d2-...",
    "externalId": "cus_123",
    "currentStep": "BANK_INFORMATION",
    "status": "PROCESSING",
    "createdAt": "2026-06-21T10:12:00.000Z",
    "updatedAt": "2026-06-21T10:19:42.000Z"
  },
  "preApproval": { "id": "019f8186-...", "status": "ACTIVE", "amount": 2500000, "...": "..." },
  "financeRequest": null,
  "customer": { "id": "0195b7d2-...", "externalId": "cus_123", "type": "BUSINESS", "...": "..." }
}

Application status moves through DRAFT → PROCESSING → APPROVED | REJECTED (REQUIRES_ACTION when the customer must resolve a pending task, CANCELLED if abandoned).

Step 6 — Terms review and acceptance

Slate underwrites the submitted application and makes a decision. If it's approved with terms, the application moves to REQUIRES_ACTION and you receive:

Webhook — application.ready-to-accept

{
  "event_type": "application.ready-to-accept",
  "data": { "id": "01a0c2ff-..." },
  "timestamp": "2026-06-21T14:00:00Z"
}

The customer has two actions left, both inside Slate's flow: complete identity verification (IDV), then review and accept the approved terms; before accepting, they can reduce the amount, but never request more than what was approved. There is nothing for your platform to call; you can surface an "action needed" state in your UI if you build your own banner (Option B).

Step 7 — The financing agreement is created

When the customer accepts the terms, three things happen in sequence and you receive a webhook for each:

  1. application.approved — the application reached its final state.
  2. preapproval.consumed — the offer is spent; the banner stops rendering it.
  3. financing-agreement.created — the executed agreement now exists. data.id is the financing agreement id — store it.

Fetch it:

curl "$BASE_URL/v2/financing-agreements/{id}" \
  -H "x-api-key: $API_KEY"
{
  "id": "01a0d411-...",
  "customerId": "0195b7d2-...",
  "type": "fixed_mca",
  "fixedMCA": { "factorRate": 1.2 },
  "status": "IN_PROGRESS",
  "fundedAmount": 2500000,
  "currency": "CAD",
  "fundedDate": "2026-06-22",
  "firstPaymentDate": "2026-07-22",
  "estimatedLastPaymentDate": "2026-12-22",
  "term": 6,
  "termUnit": "month",
  "repaymentFrequency": 1,
  "repaymentFrequencyUnit": "month",
  "disbursed": false,
  "remainingBalance": 3000000,
  "totalPaid": 0,
  "payments": [],
  "upcomingPayments": [
    { "amount": 500000, "currency": "CAD", "date": "2026-07-22T00:00:00Z" },
    { "amount": 500000, "currency": "CAD", "date": "2026-08-22T00:00:00Z" }
  ],
  "createdAt": "2026-06-22T14:03:11.000Z",
  "updatedAt": "2026-06-22T14:03:11.000Z"
}

Reading the terms: the customer received fundedAmount ($25,000) and repays fundedAmount × factorRate ($30,000) in equal installments every repaymentFrequency repaymentFrequencyUnit over term termUnit, 6 monthly payments of $5,000.

For a person customer the object is identical except "type": "fixed_fepa" and the factor rate lives in "fixedFEPA": { "factorRate": ... }.

Step 8 — Disbursement

Slate transfers the funds to the customer's verified bank account:

Webhook — financing-agreement.disbursed

{
  "event_type": "financing-agreement.disbursed",
  "data": { "id": "01a0d411-..." },
  "timestamp": "2026-06-23T09:00:00Z"
}

The agreement's disbursed field flips to true. The repayment schedule runs from firstPaymentDate.

Step 9 — Repayment and monitoring

Slate collects each installment automatically via PAD from the customer's bank account. Every collected installment fires:

Webhook — financing-agreement.repayment

{
  "event_type": "financing-agreement.repayment",
  "data": { "id": "01a0d411-..." },
  "timestamp": "2026-07-22T12:00:00Z"
}

After each one, refetch the agreement remainingBalance, totalPaid, payments and upcomingPayments are always current:

{
  "payments": [
    { "amount": 500000, "currency": "CAD", "date": "2026-07-22T12:00:00Z", "type": "repayment", "status": "succeed" }
  ],
  "upcomingPayments": [
    { "amount": 500000, "currency": "CAD", "date": "2026-08-22T00:00:00Z" }
  ],
  "remainingBalance": 2500000,
  "totalPaid": 500000
}

Payment status is in_progress, succeed or failed.

Step 10 — Present the agreement to your customer

Once the agreement exists, your customer needs a place to see it. Two ways to build it:

Option A — Embed slate-capital

The capital experience as a single embed for your customer's dashboard. It renders the customer's financing agreements: details, repayment progress, and the payment list; and lets the customer make an early payment: they pick an amount and authorize a PAD debit from their bank account for it, reducing the remaining balance ahead of schedule.

<slate-capital env="live" user-token="<session-token>"></slate-capital>

It also includes the pre-approval banner: whenever the customer has an active offer (their first one, or a new one after completing an agreement), it appears in the same view. Its main purpose, though, is giving the customer the full capital experience around their agreements. See the Capital Page component page for all its properties and caveats.

Because slate-capital already embeds the pre-approval banner, never place it on the same page as slate-pre-approval-banner-v2 — the banner would render twice.

Option B — Build your own screen

Everything the customer needs to see is in GET /v2/financing-agreements/{id} — terms, progress, payment history and the upcoming schedule. Refetch it after every financing-agreement.repayment webhook to keep it current.

Step 11 — Closure

The agreement ends in one of two states:

Webhook Meaning
financing-agreement.completed The full amount was repaid; status becomes COMPLETED
financing-agreement.defaulted Slate marked the agreement as defaulted; status becomes DEFAULTED

A customer with a completed agreement keeps generating financial data (Step 2) keep attaching it, and Slate issues new pre-approvals as they qualify again. The cycle restarts at Step 3.


Monitoring endpoints

Endpoint What it returns
GET /customers Paginated customers. Filters: externalId, type, product=CAPITAL
GET /v2/pre-approvals Paginated pre-approvals. Filters: customerId, externalId, status
GET /v2/applications Paginated applications. Filters: customerId, externalId, status
GET /v2/applications/{id} One application with its pre-approval and customer
GET /v2/financing-agreements Paginated agreements. Filters: externalId, status, type, excludeEwa=true to keep only Capital agreements
GET /v2/financing-agreements/{id} One agreement with payments, upcomingPayments, remainingBalance, totalPaid

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 an 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 agreement Remove it from your UI if you built your own banner
application.created Application The customer opened the application Track engagement
application.submitted Application The application was submitted Show an "under review" state
application.bank.verified Application The bank account was verified Nothing — informational
application.ready-to-accept Application Approved with terms; the customer must verify identity and accept Show an "action needed" state
application.approved Application The customer accepted the terms; final approval Show an "approved" state
application.rejected Application Slate declined the application Show a "declined" state
financing-agreement.created Financing agreement The executed agreement exists Store the agreement id
financing-agreement.disbursed Financing agreement Funds were sent to the customer Reflect it to the customer
financing-agreement.repayment Financing agreement An installment was collected Refetch the agreement
financing-agreement.completed Financing agreement Fully repaid Reflect it; new offers may follow
financing-agreement.defaulted Financing agreement The agreement defaulted Reflect it in your UI

Good practices

  • Keep financial data fresh. Offers are only as good as the data behind them, attach new timeseries on a regular cadence, not just once.
  • Use externalId everywhere. It's the same identifier across customers, pre-approvals, applications and agreements, and every list endpoint filters by it.
  • Treat webhooks as notifications, not payloads. They carry only the object id, always fetch the current state from the API afterward.
  • Mint session tokens server-side, per customer, on demand. Never ship your API key to the browser, and never reuse one customer's token for another.
  • Don't compute balances client-side. remainingBalance, totalPaid and the payment schedule come from Slate's ledger, read them from the agreement endpoint.
  • Filter with excludeEwa=true when listing agreements for a Capital dashboard, so EWA advances (if you also use that product) don't mix in.