Premium Refunds via the Ride
The ride is the source of truth for all financial accounting. A Levy Cover premium refund is a ride correction first. Any wallet credit is downstream.
Never credit the wallet directly for a premium refund
Refunding a Levy Cover premium by inserting a wallet_transactions row, or by calling creditWalletForRefund outside the ride refund path, corrupts net_deposited, partner payouts, and tax remittance. There is no scenario where this is correct. If you see code or scripts that do this, stop and route through the ride refund API instead.
Why this rule exists
The premium is a line item on the ride. It contributes to rides.insurance_premium_amount, to the ride total, to net_deposited (after the carrier's net premium is deducted), and to partner payout math (Levy commission and operator commission).
If the wallet is credited without the ride being adjusted:
- The ride's accounting still thinks the premium was kept.
net_depositedis overstated.- The partner payout cron pays the operator their share of a premium that was actually refunded.
- The carrier's settlement file still includes the premium.
- Tax remittance is misreported.
When the refund flows through the ride, all of those downstream effects are correctly recalculated by the existing accounting pipeline.
When a premium refund is valid
A Levy Cover premium refund is only valid in these cases:
- The ride never started. The motor never engaged within 5 minutes of unlock. The opt-in card was accepted but the rider walked away.
- The bind call failed retroactively. Levy got a 200 response from the carrier, but the carrier later reports "no policy on file" for that ride. The nightly carrier reconciliation cron flags these for ops.
- The carrier voided the policy. Rare. The carrier issues a
booking.voidedwebhook; Levy receives it, flags the ride for premium refund. - A genuine billing error. The rider was charged a premium they did not opt into - typically a UI bug or a stale preference. These are diagnosed before any refund.
Premium refunds are not triggered by:
- A claim payout. The premium was correctly collected; the carrier is paying out on a valid policy. See Carrier Payouts and the Wallet.
- A rider asking nicely. We do not goodwill-refund premiums.
- A claim being denied. Denial means the carrier did not pay out; it does not mean the policy was invalid.
The mechanics
Premium refunds delegate to the existing ride refund infrastructure. The flow:
- Ops or the reconciliation cron identifies a premium that needs refunding.
- Levy calls (or stages a call to)
/api/admin/insurance/policies/[id]/premium-refund. - That endpoint:
- Voids the carrier policy via the
CarrierClientinterface (if the carrier hasn't already voided it). - Returns a request body the caller must POST to the standard ride refund API:
/api/rides/[ride_id]/refundswith{ destination, mode: "partial", amount: <premium_amount>, reason: "Levy Cover premium refund - <reason>" }.
- Voids the carrier policy via the
- The ride refund API:
- Records a
ride_refundsrow. - Calls the
update_ride_net_depositedRPC sonet_depositedis correct. - Issues the wallet credit (if
destination = "wallet") via the standard refund helper that requires a realride_refundsrow. - Recomputes partner payouts.
- Records a
The premium refund helper src/lib/insurance/premium-refund.ts does not call creditWalletForRefund directly. It returns the refund request body. The actual wallet credit happens inside the ride refund API, with full auditability.
What the operator sees
A premium refund appears on the ride detail as:
- A
ride_refundsrow with reason"Levy Cover premium refund - <specific reason>". - An updated
net_depositedthat excludes the premium. - A wallet credit (if the refund destination was the wallet) tied to the
ride_refundsrow, not to aninsurance_claimsrow.
Importantly, the ride's insurance_premium_amount is not zeroed out. The original premium is still recorded for audit purposes; the refund is recorded as a separate adjustment. This mirrors how partial fare adjustments work elsewhere in the platform.
Carrier-voided policies
When the carrier voids a policy, Levy receives a booking.voided webhook. The webhook handler:
- Sets
ride_insurance_policies.voided_at = now(). - Logs the void reason.
- Creates a
premium_refund_requiredtask for ops. - Does not automatically refund the premium. A human reviews the void and routes the refund through the ride refund API.
The reason for the human in the loop: carrier voids can be triggered by fraud signals, rider KYC failures, or policy errors. Some of those scenarios warrant a refund; some do not. Defaulting to automatic refund risks paying out on fraudulent voids.
What you absolutely cannot do
The following are direct violations of CLAUDE.md and break the accounting model:
- Direct UPDATE on
customers.wallet_balanceto add the premium amount back. - Direct INSERT into
wallet_transactionswith the premium amount as a manual credit. - Calling
creditWalletForRefundwith noride_refund_idand noinsurance_claim_idand no enclosing ride correction. - Inserting an
insurance_claimsrow as a workaround to create aninsurance_claim_payoutwallet credit for what is really a premium refund. These are categorically different events and the schema enforces the distinction.
If you are tempted to do any of these because the API is unreachable, the right answer is to wait or escalate, not to bypass.
Why this guardrail is non-negotiable
This is the same rule that protects every other refund flow on the platform: ride fare corrections, automatic refunds, Rider Score rewards, damage charges. The pattern is consistent across the codebase. Insurance premiums are not special - they are a line item on the ride and they refund like a line item on the ride.
The full guardrail is documented in the project's CLAUDE.md under "Refunds & Fare Adjustments". This page is the insurance-specific application of that rule.
Next
Read Carrier Payouts and the Wallet for the opposite case - when the carrier pays the rider, the accounting is entirely different.
Need help?
Questions on premium refunds, contact support@levyelectric.com.