intermediate
troubleshooting
debugging
carrier-outage

Levy Cover Troubleshooting

Common Levy Cover failure modes - opt-in card missing, carrier outage, bind failures, webhook gaps - and how to diagnose them.

Levy Fleets TeamMay 18, 20267 min read

Levy Cover Troubleshooting

This page covers the most common failure modes for Levy Cover and how to diagnose them. Most issues land in one of four buckets: configuration, carrier outage, webhook gaps, or accounting drift.

Opt-in card never appears at unlock

Symptom: Rider unlocks a vehicle, no Levy Cover card appears, the ride proceeds without an offer.

Most likely causes (in order):

  1. Subaccount toggle off. Check /dashboard/insurance/settings. Confirm Levy Cover is enabled for the subaccount.
  2. Jurisdiction not covered. The unlock location resolves to a region marked available: false in the matrix. See Jurisdiction Matrix. Check /api/admin/insurance/jurisdiction-matrix for the current matrix state.
  3. Carrier credentials missing. CoverGeniusClient throws CarrierNotConfiguredError. In production this hides the card entirely. Confirm COVER_GENIUS_API_KEY and COVER_GENIUS_PARTNER_ID are set on the deployed environment.
  4. Carrier returned no tiers. The carrier can decline to offer coverage on specific risk profiles (e.g. a brand-new rider with no history in a high-risk zone). Check the quote response in logs.
  5. Quote timed out. The 2-second budget at unlock elapsed. The ride proceeds without the offer, which is the intended fail-open behavior.
  6. Rider preference set to "always decline." If the rider toggled the persistent preference off, the card is hidden until they re-enable.

Diagnostic queries:

  • SELECT * FROM insurance_offerings WHERE subaccount_id = '<id>' - confirm enabled rows.
  • SELECT * FROM ride_insurance_policies WHERE ride_id = '<ride id>' - confirm whether a quote/bind actually happened.
  • Logs: search for quote.requested and quote.failed events around the unlock timestamp.

Bind returned 200 but no policy in DB

Symptom: Logs show a successful POST /bookings call, but ride_insurance_policies has no row.

Likely causes:

  1. Webhook URL not registered. Some carrier integrations confirm the binding via the inbound booking.confirmed webhook. If that webhook never arrives, Levy logs the request as in-flight indefinitely.
  2. Webhook signature failed. The event arrived but signature_verified = false. Check insurance_webhook_log.
  3. The bind succeeded at the carrier but the response payload was malformed. Rare; usually a sandbox-only issue.

Fix: Run the carrier reconciliation cron manually:

curl -X POST https://fleets.levyelectric.com/api/cron/insurance-carrier-reconciliation

The cron checks bound policies against the carrier and repairs missing ride insurance stamps. If the carrier reports the policy as valid, the row is created with bound_at backdated to the original bind time.

Premium not on the ride receipt

Symptom: The rider opted in, the policy was bound, but the receipt does not show the Levy Cover line item.

Causes:

  1. applyRideInsuranceToPricing was not called. Check that src/lib/rides/process-ride-completion.ts is the path the completion took. The mobile-end, admin force-end, internal ride-end, and auto-end paths all apply this helper - if a new ride-end path was added, it must apply the helper too.
  2. The bind happened but the carrier voided the policy before the ride ended. Check ride_insurance_policies.voided_at. If set, the premium was correctly excluded from the receipt.
  3. Stripe Connect charge failed. The premium is part of the ride total; if the total failed to charge, no line item is generated. Standard payment retry applies.

Diagnostic: SELECT id, total_cost, insurance_premium_amount FROM rides WHERE id = '<ride>'. If insurance_premium_amount is populated but total_cost does not include it, the completion pipeline did not apply the helper.

Claim photos fail to upload

Symptom: Rider tries to upload a photo on the claim screen, the upload errors out.

Causes:

  1. insurance-claims Storage bucket missing. Confirm the bucket exists with rider upload + read policies and service-role management policy. The migration 20270601120100_09_* creates it.
  2. Signed URL expired. Signed URLs for photo upload have a short TTL. If the rider sat on the screen too long, the URL expires. The app should re-fetch automatically; if it does not, restart the claim flow.
  3. File size or type rejected. The bucket policy restricts to image MIME types up to 25 MB per file.

Claim stuck in submitted for days

Symptom: A claim is filed, the operator sees it in the queue, but it never moves to under_review or further.

Causes:

  1. Webhook missed. The carrier moved the claim but the webhook never reached Levy. Check insurance_webhook_log for any failed deliveries around the time of expected status change.
  2. Carrier-side adjuster delay. Cover Genius typically reviews within 48-72 hours. Slice can take longer in some jurisdictions.
  3. Required information missing. Some carriers send a claim.requires_info webhook that asks for additional data. Check the claim detail for a "carrier requesting" note.

Action: Operators can escalate to Levy support, who can chase the carrier through the partner channel. Operators cannot file appeals or status inquiries directly with the carrier.

Webhook signature failures

Symptom: insurance_webhook_log shows signature_verified = false.

Causes:

  1. COVER_GENIUS_WEBHOOK_SECRET mismatch. Most common. Re-fetch the current secret from the Cover Genius portal and update the env var.
  2. The wrong environment's webhook URL is registered. Sandbox events arriving at production endpoint, or vice versa. Re-register the correct URL in the carrier portal.
  3. Replay or out-of-order delivery. Duplicate-key handling on UNIQUE (carrier, event_id) returns { duplicate: true }. Genuine signature failures are different from duplicate handling - check the row's signature_verified field.

Carrier outage

Symptom: Quote calls time out, bind calls return 5xx, the opt-in card stops appearing.

Behavior:

  • Quotes time out -> fail open, ride proceeds without offer, insurance_offered = false.
  • Existing bound policies remain valid. The carrier still honors them when claims are eventually filed.
  • Pending claims sit in their current status until the carrier recovers and processes them.

Action:

  • Confirm the outage on the Cover Genius status page or via the partner channel.
  • Communicate to affected operators if the outage exceeds 1 hour.
  • Do not manually bind policies during the outage. There is no path to do this safely - if the carrier has not actually received the binding, the ride is uninsured.

Premium charged but rider claims they did not opt in

Symptom: Rider support ticket says "I did not opt into insurance but I was charged."

Diagnosis:

  • Check ride_insurance_policies for the ride. If a row exists with a bound_at timestamp, an opt-in event was recorded.
  • Check the policy_wording_version field for the wording the rider was shown.
  • Check the rider's persistent preference - "Remember my choice" set to accept means future rides auto-bind.

Resolution:

  • If the rider genuinely did not opt in (a UI bug or a stale auto-include preference), follow the premium refund path. See Premium Refunds via the Ride.
  • If the rider opted in but forgot - the wording was clear and the binding stamp is intact - explain the receipt line item and the preference toggle. No refund.

Tests are failing

The full test suite is at src/lib/insurance/__tests__/. There are 8 files and 36 tests. To run them:

npx vitest run src/lib/insurance/__tests__/ src/lib/rides/__tests__/durable-completion-jobs.test.ts

Common failures:

  • Env vars set during test runs. If the test environment has COVER_GENIUS_API_KEY set, the registry may pick the real client instead of the mock. Use setCarrierRegistryForTests to pin.
  • Migration drift. If a developer edits the migration files locally, the test fixtures can drift from the schema. Reset the local DB and re-run.

Next

Check FAQ for shorter answers to common questions, or Carrier Credentials Setup for env-var-related issues.


Need help?

If none of the above resolves the issue, contact support@levyelectric.com with the ride ID and any relevant log timestamps.