Split

Bill an enterprise partner once and have Blitz split the payment across a roster of contractors or vendors on your behalf.

This is endpoint access only, by design — there is no headless UI to embed. An API key can create a Split and revise its roster while it's still pending, but approving, rejecting, and settling margin happens on the two companies' own Blitz accounts — never through this API key — unless the Enterprise has opted your company into auto-accept (see below).

Create a Split

POST/api/commerce/integration/splits

Requires an API key with the split:write scope. Your company is the SME billing the named enterprise.

create-split.sh
curl -X POST https://api.nest.useblitz.co/api/commerce/integration/splits \
  -H "Authorization: Bearer bk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "enterpriseCompanyId": "co_...",
    "apContactBusinessProfileId": "bp_...",
    "clientIdempotencyKey": "unique-per-split",
    "totalAmountCents": "10000",
    "roster": [
      { "amountCents": "6000", "earlyPayoutElected": false, "contractorPaymentProfileId": "pp_..." },
      { "amountCents": "3500", "earlyPayoutElected": true, "autoWithdrawElected": true, "subVendorId": "sv_..." }
    ]
  }'
  • roster — 1 to 50 rows. Each row is exactly one of contractorPaymentProfileId (an existing creator/business), subVendorId (your own vendor roster), or email (onboard someone brand new — see below).
  • earlyPayoutElected (default false) — take an instant advance (fee deducted) once the Split funds, instead of waiting for a manual payout later.
  • autoWithdrawElected (default false) — the moment this row's share settles, auto-forward it to the contractor's own active, verified wallet. If they have none, it simply lands as Blitz balance instead — never an error.
  • businessCoversFee (default false) — your company (not the contractor) absorbs this row's early-payout fee, added on top of what's drawn from your own balance. Only meaningful for a row that actually takes an instant payout.
  • totalAmountCents — optional. Must be at least the roster sum, and the difference (your margin) is capped at 20% of this total. Omit to bill exactly the roster sum (no margin). See Margin below for how it's released.
  • clientIdempotencyKey — required. A repeated call with the same value against the same SME/Enterprise/AP-contact triple returns the existing Split instead of creating a duplicate.

Margin

Billing the Enterprise more than the roster sum (totalAmountCents minus the roster total, capped at 20% of totalAmountCents) declares your company's margin — but it isn't auto-credited. It's held once the Split funds, tracked by marginStatus on the response (NONE → PENDING → HELD → RELEASED), and released either by your own instant election or Blitz's scheduled net-terms auto-release. Both the instant election and the underlying POST :id/settle-margin call are session-only — not exposed to this API key — so subscribe to split.margin.settled (or poll marginStatus) rather than expecting to trigger it yourself.

Response shape

Every Split-returning call below (create, check status, revise roster, list) returns this shape as data — see Responses & Errors for the envelope it's wrapped in:

split-response.json
{
  "id": "spl_...",
  "smeCompanyId": "co_...",
  "smeCompanyName": "Acme Talent Agency",
  "enterpriseCompanyId": "co_...",
  "enterpriseCompanyName": "Big Brand Inc.",
  "enterpriseNetTermsDays": 30,
  "apContactBusinessProfileId": "bp_...",
  "legAPayoutId": "pay_...",
  "totalAmountCents": "10000",
  "marginAmountCents": "500",
  "marginStatus": "PENDING",
  "marginHoldStartedAt": null,
  "marginReleasedAt": null,
  "feeBufferAmountCents": "0",
  "enterpriseCoversFee": false,
  "enterpriseFeeAmountCents": "0",
  "currency": "USD",
  "status": "PENDING_APPROVAL",
  "distributions": [
    {
      "id": "dist_...",
      "contractorPaymentProfileId": "pp_...",
      "subVendorId": null,
      "contractorDisplayName": "Jordan Lee",
      "creatorProfileId": "cr_...",
      "amountCents": "6000",
      "earlyPayoutElected": false,
      "autoWithdrawElected": false,
      "businessCoversFee": false,
      "status": "PENDING",
      "legBTransferPayoutId": null,
      "legBWithdrawalPayoutId": null,
      "feeAmountCents": null,
      "netAmountCents": null,
      "failureReason": null
    }
  ],
  "createdAt": "2026-08-24T18:02:11.482Z",
  "updatedAt": "2026-08-24T18:02:11.482Z"
}
  • status — PENDING_APPROVAL → APPROVED → FUNDED → SETTLING → SETTLED, or REJECTED/CANCELLED. Funding and payout after APPROVEDhappen automatically, server-side — there's no separate "release" call to make.
  • legAPayoutId — the underlying Payoutid for the Enterprise's bill, if you need to cross-reference it elsewhere.
  • Each distributions row's status is "PENDING"until its transfer starts, then the linked payout's own live status (e.g. "COMPLETED"), or "FAILED" if the transfer attempt errored before a payout was even created — treat it as an opaque string, not a fixed enum. feeAmountCents/netAmountCents are set once the row actually transfers.
  • contractorDisplayName and creatorProfileId are resolved for you server-side — useful for rendering a roster row without a separate lookup. creatorProfileId is null for a business-profile contractor or an unresolved row.
A row stuck at a failed transfer isn't yours to retry — there 's no retry endpoint. It's independently retried server-side; surface it to the roster member as "payout failed, contact support" rather than building a retry action around it.

Add a roster row by email

Set email (plus optional fullName) instead of contractorPaymentProfileId or subVendorIdto add a contractor who isn't on your roster yet, with no separate invite step:

roster-row.json
{
  "amountCents": "500",
  "earlyPayoutElected": false,
  "email": "new-contractor@example.com",
  "fullName": "Jordan Lee"
}
  • Already a SubVendor on your own roster for that email? It's reused as-is.
  • Already a real Blitz account for that email? A SubVendor is created and auto-attached to it immediately — no invite/accept step.
  • No existing account at all? A brand-new, passwordless account is provisioned immediately and lands active right away.

Either way, the row resolves to a subVendorId server-side — the response reflects it the same as if you'd passed subVendorIdyourself. A contractor onboarded this way with no PayPal email on file still can't be elected for instant payout until one's added.

Check status

GET/api/commerce/integration/splits/:id

Same API key scope. Only returns Splits your own company created.

Revise a pending roster

PUT/api/commerce/integration/splits/:id/roster

Same API key scope. Only while status is PENDING_APPROVAL — replaces the entire roster.

revise-roster.sh
curl -X PUT https://api.nest.useblitz.co/api/commerce/integration/splits/spl_.../roster \
  -H "Authorization: Bearer bk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "totalAmountCents": "10500",
    "roster": [
      { "amountCents": "6000", "earlyPayoutElected": false, "contractorPaymentProfileId": "pp_..." },
      { "amountCents": "4000", "earlyPayoutElected": true, "subVendorId": "sv_..." }
    ]
  }'

Body shape matches creation: roster (required, same row shape as above — including email) and totalAmountCents (optional, omit to bill exactly the new roster sum, same 20% margin cap as creation). This replaces the roster wholesale, not a partial patch.

List your Splits

GET/api/commerce/integration/splits

Same API key scope. Paginated — page (default 1), limit (default 20, max 100). Scoped to Splits your own company created.

Errors specific to Split

  • 422 — a roster row's contractorPaymentProfileIddoesn't resolve to an active creator's own payment profile, or its subVendorIddoesn't belong to your own company. Also returned if a SubVendor row has no PayPal email on file and was elected for instant payout — add one to that SubVendor first.
  • 400 — totalAmountCents was supplied but is less than the roster sum, the margin it implies exceeds the 20% cap, or the roster sum is zero.
  • 404 (not 403) — GET :id/PUT :id/rosteragainst a Split your API key's company doesn't own. Existence is never leaked across companies.

Skipping manual approval — auto-accept

By default, a Split you create through this API still waits for the named enterprise to approve it manually in their Blitz account, exactly like a Split created in the product. If an enterprise wants to skip that step for your company specifically, they can turn on auto-accept for you from their own Split settings — every Split you then create against them moves straight to approved and pays out automatically. This is entirely their decision; there's nothing for you to configure on your side.

Webhook events

Subscribe to these from your webhook endpoint instead of polling GET :id/GET in a loop. Every payload is intentionally thin — cross-reference the status endpoints above for the full current state (fees, net amounts, status, etc.).

  • split.leg_a.approved — the enterprise approved the Split (manually, or via auto-accept).
  • split.leg_a.rejected — the enterprise rejected the Split.
  • split.leg_a.claimed— your company's balance was credited the roster sum.
  • split.roster_entry.settled — one roster row was transferred to its contractor (instant election or on-demand instant payout).
  • split.roster_entry.failed— one roster row's transfer failed. Not something you retry yourself — it's independently retried server-side.
  • split.margin.settled — your declared margin was released, either by your own instant election or the scheduled net-terms auto-release. See Margin above.

split.leg_a.* payloads carry { splitId, legAPayoutId }:

leg-a-approved.json
{
  "splitId": "spl_...",
  "legAPayoutId": "pay_..."
}

split.roster_entry.* payloads carry { splitId, rosterEntryId }:

roster-entry-settled.json
{
  "splitId": "spl_...",
  "rosterEntryId": "dist_..."
}

split.margin.settled carries only { splitId }— there's no separate margin payout id to cross-reference; read marginAmountCents off GET :id instead:

margin-settled.json
{
  "splitId": "spl_..."
}