Skip to main content

Quote Flow

The Safeco Auto quote flow is a multi-step sequence. Each step is defined in flow-config.ts and rendered by the Safeco Auto flow router.

Flow Steps

Step IDPathLabelIcon
drivers-confirmdrivers/confirmConfirm Driversusers
excluded-driversdrivers/excludedExcluded Driversusers
vehicles-confirmvehicles/confirmConfirm Vehiclescar
policy-coveragescoverages/policyPolicy Coveragesshield
vehicle-coveragescoverages/vehicleVehicle Coveragesshield-check
discountsdiscountsDiscountstag
summarysummarySummaryclipboard-list
checkoutcheckoutCheckoutshopping-cart
checkout-completecheckout/completeCompletecheck-circle
successsuccessSuccesscheck-circle
knockoutknockoutEligibility Erroralert-circle

Source: apps/fastlane-portal/src/app/pages/carriers/safeco/auto/flow-config.ts

Quote API Endpoints

EndpointMethodWhen Used
POST /ghcms/safeco/initial-quoteInitial quoteFirst load of policy coverages page
POST /ghcms/safeco/update-quoteUpdate quoteAfter coverage/discount changes ("Update Pricing")
POST /ghcms/safeco/rate-with-reportsRate with reportsFinal rate on Summary page before checkout
POST /ghcms/safeco/rateDirect RC2Direct RC2 rate call
POST /ghcms/safeco/payment-plansPayment plansRC3 payment plan retrieval

Initial Quote (CRN Bootstrap)

POST /ghcms/safeco/initial-quote does not call Safeco's API. Instead, it loads the existing quote from the CRN legacy database.

Flow:

  1. getExistingCrnQuote(sessionData, quoteId, state) queries CrnQuoteLookupService
  2. Extracts from CRN: CompanysQuoteNumber, CompanyClientID, carrierUrl, entityRefs, initialPricing, paymentOptions
  3. Creates a CarrierQuoteSession record in Prisma (30-day TTL)
  4. Returns CRN pricing with synthesized payment plans

This avoids an unnecessary API call since DA already rated the quote with Safeco.

Update Quote (Plain RC1)

POST /ghcms/safeco/update-quote runs a single RC1 call — no CC/ADD sub-transactions.

Flow:

  1. Bootstrap session from CRN if missing (bootstrapSessionIfMissing)
  2. Resolve active pricing — prefers latestPricing over initialPricing from session metadata
  3. Map to ACORD XML as plain RC1 (no sub-transaction type)
  4. Single API call to apiClient.rateQuote()
  5. Parse response, persist updated pricing

The previous pricing is sent as "echo-back" so Safeco maintains premium context.

Rate With Reports (Full 4-Step Flow)

POST /ghcms/safeco/rate-with-reports runs the complete Safeco rate flow:

StepTransactionPurposeKey Output
1. CRN BootstrapNone (DB only)Load existing quote, create sessionEntity refs, initial pricing
2. RC1 + CCRateCall1 + com.safeco_ReconcileCCPrior insurance verification (CLUE)CLUE results, updated entity refs
3. RC1 + ADDRateCall1 + com.safeco_ReconcileADDAdditional driver discoveryDriver candidates, updated entity refs
4. RC2RateCall2Final rating with MVRPremiums, coverages, discounts, payment plans

See ACORD XML Protocol for detailed transaction documentation.

Session Management

Carrier Quote Sessions

Quote sessions are stored in carrier_quote_sessions (Prisma), keyed by (quoteId, SAFECO, AUTO).

FieldPurpose
carrierQuoteNumberSafeco's CompanysQuoteNumber
carrierClientIdSafeco's CompanyClientID
carrierUrlSafeco portal URL
metadata.entityRefsDriver/vehicle/location Safeco IDs
metadata.initialPricingCRN pricing (total, PIF, PPM)
metadata.latestPricingMost recent Safeco pricing
metadata.paymentOptionsPayment plan data
metadata.statusactive, completed, or error
expiresAt30-day TTL

Pricing Resolution Cascade

When the service needs pricing, it checks three sources in order:

  1. Session fieldextractCrnPricingFromSession(): looks in final_quotes_data for Safeco quote with premium_total__c, premium_total_pif__c, premium_total_ppm__c
  2. MetadatabuildPricingFromMetadata(): uses initialPricing from session metadata
  3. CRN recoveryrecoverCrnPricing(): re-queries CRN legacy DB as last resort, persists back to session

Payment Plan Synthesis

Safeco doesn't always return usable payment plans. The service has fallback logic:

  1. Safeco plans with real pricing → use as-is
  2. Safeco plans with zero pricing → override with CRN pricing (overridePlansWithCrnPricing)
  3. No Safeco plans → synthesize from CRN pricing:
    • Full plan: total premium from CRN
    • Monthly plan: down payment + installments estimated from total
  4. No CRN pricing, has FullTermAmt → synthesize from Safeco's FullTermAmt (sum of PersVeh[].FullTermAmt.Amt)
  5. Scaling — when Safeco's FullTermAmt differs from CRN total, pricing is proportionally scaled

Frontend Usage

SafecoRateBox

The SafecoRateBox component (components/safeco-rate-box.tsx) displays pricing and handles rate calls:

ModeHookTrigger
'initial'useSafecoRate('initial')Auto-fires on policy coverages page when no premium
'update'useSafecoRate('update')"Update Price" button after coverage changes

Summary Page

The Summary page calls executeRateCall(sessionId) (rate-with-reports) before checkout to get final pricing with MVR results.

Backend Handlers

HandlerService MethodPurpose
safeco-initial-quote.handler.tsgetExistingCrnQuote()Load CRN quote, no API call
safeco-update-quote.handler.tsexecuteUpdateRate()Plain RC1
safeco-rate-with-reports.handler.tsexecuteQuoteWithReports()Full 4-step flow

All handlers in apps/apis/fastlane-api-gateway/src/app/handlers/. They delegate to SafecoV3RateService in libs/apis/carriers/safeco/.

Error Handling

  • ACORD status checkStatusCd !== 0 → throw
  • Message statusMsgStatusCd of Fail, Error, Rejected → throw with ExtendedStatus details
  • Session error marking — on failure, session metadata is updated with status: 'error', error message, and stack trace
  • Knockout detection — rejected quotes or zero premiums trigger navigation to the knockout page (see Knockouts)