Skip to content

Commission and payouts

This page explains how Expedition Insure tracks and pays the commission you earn on attributed sales: how your rate is set, how each sale moves through the commission ledger, and how you actually get paid. It’s for operator finance and engineering teams reconciling payouts.

Expedition Insure is the Merchant of Record (MoR): we collect the insurance premium on our own Stripe account, then pay you commission out of band. For the post-sale signal that drives this, see the policy.issued webhook. For where attribution comes from in the first place, see attribution and tracking.

1. commissionRateBps — your commission rate

Section titled “1. commissionRateBps — your commission rate”

Your commission rate is stored as commissionRateBps, an integer in basis points:

  • 1000 = 10%
  • 2000 = 20%

Basis points are whole integers, so there is no floating-point drift in the math.

Who sets it: Expedition Insure sets your rate. It is configured on your operator record by Expedition Insure during onboarding; there is no operator-facing setting to change it. You can read your current rate from the operator portal (it appears as commissionRateBps in your operator summary), but you cannot edit it. If your rate needs to change, contact us at help@expedition.insure.

No self-service rate edits yet. A per-operator rate is admin-configured today; an operator-facing or admin UI to adjust it is [needs-product-work].

Commission is calculated on the gross attributed premium — the full premium for the policy that was attributed to you — in whole US dollars:

commissionUsd = round(attributedPremiumUsd * commissionRateBps / 10000)

The base (attributedPremiumUsd) is the policy’s premium, not a net-of-discount figure: if a customer applied a promo credit, that reduces what Expedition Insure collects, not what you are owed. Your rate is snapshotted onto the ledger row at the moment the sale is recorded, so a later rate change never retroactively rewrites past commission.

Every attributed sale produces exactly one row in the commission ledger. Each row is keyed to a single quote, so the ledger is idempotent — a sale can never be double-counted. Each row carries:

FieldTypeDescription
operatorIdstringThe operator the sale is attributed to.
quoteIdstringThe quote that produced the sale.
policyNumberstring (optional)Carrier policy number, once known.
attributedPremiumUsdnumberCommission base — the policy premium, whole USD.
commissionRateBpsnumberYour rate, snapshotted at record time (immutable per row).
commissionUsdnumberComputed commission, whole USD.
payoutModelstringseparate_pi or connect (see section 4).
statusstringprovisional, confirmed, paid_out, or voided.
sourcestringHow the row originated (e.g. estimate_at_paid, carrier_confirmation).

All money on the ledger is in whole US dollars. Cents appear only at the Stripe boundary and in the *Cents fields of the policy.issued webhook.

A ledger row moves through up to four states. The first two are automated; the last two are manual today.

(sale recorded) (carrier confirms premium)
┌──────────────────┐ ┌──────────────────┐
│ provisional │ → │ confirmed │
└──────────────────┘ └──────────────────┘
│ │
└───────────┬───────────┘
↓ (manual, out of band)
┌──────────────┐ ┌──────────┐
│ paid_out │ │ voided │
└──────────────┘ └──────────┘

The row is created in provisional the moment an attributed payment succeeds. At this stage the commission is computed from the premium estimate captured at the time of payment (source: estimate_at_paid). Recording the sale also fires the policy.issued webhook to your endpoint.

A row is only created when all of these hold: the quote is attributed to your operator, your commissionRateBps is greater than 0, your payoutModel is separate_pi, and the policy has a positive premium. If any of those is missing, no ledger row is written.

When the carrier confirms the firm premium (which can differ from the estimate at payment time), the row is recomputed against the confirmed premium — using the rate snapshot already on the row, never a re-read rate — and moved to confirmed (source: carrier_confirmation). This refreshes attributedPremiumUsd, commissionUsd, and policyNumber.

Confirmation does not re-fire a webhook. The policy.issued webhook fires once, when the sale is first recorded. Reconfirmation updates the ledger silently — reconcile firm figures from the ledger totals in the operator portal, not from a second webhook.

paid_out marks commission you have actually been paid. Under the current model this is set manually as part of the monthly out-of-band invoice settlement; there is no automated transition into paid_out yet — [needs-product-work].

voided is reserved for clawbacks — commission reversed because a policy was cancelled or refunded. Automated voiding is not implemented today: there is no cancellation or refund trigger that writes voided, and no webhook notifies you when a commission is reversed — [needs-product-work]. Until that lands, any clawback is handled manually and reconciled against the monthly invoice.

Your operator record carries a payoutModel. Two models are defined; one is live.

This is the live model and the default for every new operator. Expedition Insure is the Merchant of Record: the customer’s insurance premium is charged on a separate PaymentIntent on our Stripe account, independent of any trip charge you collect on your own rail. Your commission accrues in the ledger and is paid out of band via a monthly invoice. This is the only model the ledger records — sales for any other model are skipped.

A Stripe Connect model — where commission is netted automatically through a connected Stripe account — is defined but not implemented. The connectAccountId field is reserved on the operator record but unused, and sales for connect operators are skipped by the commission recorder. Connect-based netting is [needs-product-work].

5. Clawbacks, voids, currency, and netting

Section titled “5. Clawbacks, voids, currency, and netting”
  • Clawbacks / voids. Not automated yet (see voided). Refund- or cancellation-driven reversals are handled manually and reflected on the monthly invoice; you are not webhook-notified.
  • Currency. All ledger and commission math is in whole US dollars. There is no multi-currency handling in the ledger; cents appear only at the Stripe boundary and in the webhook *Cents fields.
  • Netting. Under separate_pi, commission is not netted against any charge in real time — it accrues in the ledger and settles on the monthly invoice. Automatic netting is the goal of the deferred connect model.

6. Where to see what you’re owed and paid

Section titled “6. Where to see what you’re owed and paid”

The operator portal surfaces your live commission totals, scoped to your operator only. From your attribution stats you can read:

TotalMeaning
commissionOwedUsdSum of commissionUsd for rows in provisional + confirmed — owed but not yet paid.
commissionPaidOutUsdSum of commissionUsd for rows in paid_out.
attributedSalesCount of ledger rows excluding voided.
salesPremiumUsdTotal attributed premium across non-voided rows.
premiumGmvUsdPremium GMV proxy across all attributed quotes.

Reconcile against policy.issued, not the UX signal. payment.succeeded (the in-iframe Tier-3 checkout event) is a UX-only signal and can be missed; never settle commission against it. The authoritative post-sale signal is the policy.issued webhook, and the ledger totals above are the source of truth for what you’re owed.

If your totals don’t match your records, contact help@expedition.insure before the monthly invoice settles.