+

The plan and rate data engine for ACA quotingFormerly Vericred

Inbound integration with IDEON (rebranded from Vericred) — Plansight's primary source for ACA / individual market plan data, member rates, benefit summaries, and rating areas. Powers the small-group community-rated quoting workflow.

Direction: Inbound Auth: API key in header Status: Production · Working with notes Docs: docs.ideonapi.com ↗
What it is

External rate engine for ACA market quoting

When Plansight quotes an ACA / community-rated plan, IDEON is where the rates come from, where the plan attributes are sourced, and where the benefit summaries are pulled. The integration imports carriers, syncs rates by rating area, and parses ~65 plan attributes into Plansight's standardized quote schema.

v8
REST API version (Accept-Version header)
~65
Plan attributes mapped per quote
Retry attempts with exponential backoff (5s → 40s)
S3
For carrier issuers, state metadata, benefit summary PDFs
📋

Plan + benefit data

Detailed plan attributes (deductible, copays, coinsurance, OOP max, drug tiers, network info) keyed by HIOS ID. Benefit summary PDFs downloaded, virus-scanned, stored in S3.

💰

Rates by rating area

Member rates queried by ZIP + FIPS county + rating area. Returns per-member premiums (age-banded) plus 4-tier composite premiums (Employee, ES, EC, Family).

🏢

Carrier issuer data

Issuers list pulled from IDEON's S3 bucket (not API), filtered by state/year/quarter. Each new carrier creates a Carrier record + system user in Plansight.

🗺️

Geo / rating area mapping

ZIP-to-FIPS county lookup via GET /zip_counties. Cached in-memory per request. ACA rating areas keyed by FIPS + ZIP composite.

Naming Note

The Vericred → IDEON rename

Vericred rebranded to IDEON a few years back. The Plansight codebase still uses the old name throughout — service classes, models, config keys.

What's still called Vericred in code

External-facing docs and conversations should use IDEON. Code references can stay as-is until a coordinated rename pass — these aren't broken, just dated.

Data Flows

How data moves between IDEON and Plansight

Five distinct flows. Carriers and rating areas refresh on a schedule; rates and benefit summaries fetch on demand during quoting.

1

Carrier import

Pull issuers JSON from IDEON's S3 bucket (vericred-emr-workers) by state/year/quarter. Upsert to Carrier table keyed by HIOS ID. Create a plansightSystem user per new carrier.

2

Rate fetch

For an ACA quote, Plansight calls GET /rates with ZIP + FIPS + rating area. Paginates through all per-plan rates. Stores results keyed by HIOS ID + plan option type.

3

Member rates

For each rate ID, call GET /rates/{rateId}/member_rates. Correlate with census IDs. Store age-banded premiums per member and 4-tier composite premiums per coverage tier.

4

Benefit summary PDF

Plansight downloads the PDF, validates via ClamAV virus scan, uploads clean PDFs to Plansight's S3 at acaBenefitSummary/{hiosId}.pdf.

5

ZIP / FIPS / rating area

Map ZIP to FIPS county via GET /zip_counties. Cache the mapping in a static in-memory cache. ACA rating areas join FIPS county to a regulatory area code.

Wiring

Authentication & connection

Simple API key in header for the REST API; separate AWS credentials for S3 fetches.

SettingValue / Source
Auth headerVericred-Api-Key: {apiKey}
API base URLhttps://api.vericred.com/
API version headerAccept-Version: v8
API key configconfig/vericred.apiKey
HTTP clientGuzzle
Connection timeout15 seconds
Socket timeout60 seconds
Retry strategy4 attempts with exponential backoff (5s → 10s → 20s → 40s)
Retry triggersConnectException · HTTP 429
S3 bucketvericred-emr-workers (separate AWS creds)
Codebase

Where it lives in the Plansight repo

FilePurpose
app/Helpers/Vericred.php:32–233Main request dispatcher — pagination, retry, rate-limit handling
app/Helpers/Vericred.php:235–263ZIP-to-FIPS mapping with in-memory cache
app/Helpers/Vericred.php:291–439Member rate retrieval; age-banded + composite tier mapping
app/Helpers/Vericred.php:441–551Carrier issuers import from S3 JSON
app/Helpers/Vericred.php:617–710State data sync from S3
app/Services/VericredParseService.php:425–469Field map dispatcher — ~65 plan attributes
app/Services/VericredParseService.php:855–1184Medical deductible / coverage string parser (regex-based)
app/Services/VericredParseService.php:1532–1634Benefit summary PDF download, ClamAV scan, S3 upload
app/Models/VericredRateId.phpComposite key model (rfpId + hiosIdPlanOptionType)
app/Models/ACARate.php · ACARateRatingArea.php · ACARatingArea.phpACA rate / rating-area models

Notable design choices

Exponential backoff

Lines 97–136 of Vericred.php implement 4-attempt retry with 5/10/20/40s delays for connection errors and HTTP 429. Sensible for transient timeouts and rate limits during heavy quote bursts.

Massive field map

VericredParseService uses a ~65-field map (lines 21–393) with pluggable parser functions. Covers benefit structure, tiers, cost-share, special cases, and ACA metadata.

Regex parsing of free-text

Lines 985–1084 use regex to extract cost-share from human-readable strings like "In-Network: $250 / Out-of-Network: 50% after deductible." Fragile but necessary for unstructured benefit summaries.

Virus scanning before S3 upload

PDFs validated via ClamAV (lines 1587–1617) before storage. Prevents serving infected PDFs back to brokers / clients in proposals.

S3 as data source for issuers

Carrier issuers and state data fetched from IDEON's S3 bucket, not from the API. Decouples the rate engine from API throughput.

In-memory ZIP-to-FIPS cache

Static $zipToFipsCache speeds repeated lookups within a request. Not shared across requests.

Parsing logic

How we parse IDEON plans into Plansight Quotes

A walkthrough of the field-map dispatcher (~61 attributes) and the regex-based cost-share parser that handles free-text strings like "In-Network: $25 / Out-of-Network: 50% after deductible". The parser favors robustness over elegance — it preserves the raw IDEON string for every field as an audit trail and falls back to type Other rather than dropping data.

Pipeline

1

IDEON API response

Vericred.php lines 32–233: request() retrieves plan JSON with pagination, retries (4 attempts, exponential backoff), and rate-limit handling.

2

Field map dispatcher

VericredParseService.php lines 425–469: parse() iterates the $fieldMap array, dispatching each of ~61 IDEON attributes to a specialized parser (medicalDeductible, medicalRx, tfToYn, etc.). Fields prefixed aca bypass the vericredData tracking object.

3

Per-field parser functions

Most cost-share fields call parseMedicalDeductibleCoverageString() (lines 964–1184), the core regex engine. Simple fields use one-liners like tfToYn (boolean → "Yes"/"No") or textStraight (pass-through).

4

Benefit summary PDF handling

benefitSummaryDownload() lines 1532–1634: downloads the PDF, validates with ClamAV, uploads to S3 at acaBenefitSummary/{hiosId}.pdf. Failures return empty string silently — see "Things to verify" below.

5

Result assembly

Each parser returns an array of {field, value} pairs. The dispatcher accumulates them and populates the Quote model. Every parsed field also writes a sibling aca{FieldName} with the raw IDEON string for audit.

Field-map dispatcher (~61 attributes)

Grouped by category. The 28 service-cost-share fields share a single parser (medicalDeductible) — listed once with the full set, since the mapping shape is identical.

IDEON fieldPlansight field(s)ParserTransformation
Plan metadata (10 fields)
display_namenametextStraightPass-through
actuarial_valueinNetworkActuarialValueactuarialValueIn-network only; out-of-network defaults to 0
levelacaLeveltextStraightBronze / Silver / Gold / Platinum
network_nameinNetworkNetworktextStraightPass-through
hios_issuer_idhiosCarrierIdtextStraightPass-through
typevericredTypetextStraightPlan type from Vericred enum
plan_typemedicalPlanTypemedicalPlanTypeMaps to config enum; defaults to "No Selection" if unrecognized
effective_dateacaEffectiveDate + rateGuaranteeDateRangeFromtextStraightDuplicated to both fields
expiration_dateacaExpirationDate + rateGuaranteeDateRangeTotextStraightDuplicated to both fields
benefits_summary_urlacaBenefitSummaryFilebenefitSummaryDownloadDownload → ClamAV scan → S3 upload; empty string on failure
Deductibles & OOP max (8 fields)
individual_medical_deductible{in,out}NetworkDeductibleIndividual*medicalDeductibleMulti-network parse: value, type ($/%/Other), and deductible flag (AD/ND)
family_medical_deductible{in,out}NetworkDeductibleFamily*medicalDeductibleSame shape as individual
individual_medical_moop{in,out}NetworkOOPIndividual*medicalDeductibleMulti-network parse
family_medical_moop{in,out}NetworkOOPFamily*medicalDeductibleMulti-network parse
individual_drug_deductible{in,out}NetworkRxIndividualDeductiblerxMoneyDeductibleIncludes deductible-required flag (Yes / No / Major Medical / Not Covered)
family_drug_deductible{in,out}NetworkRxFamilyDeductiblerxMoneyDeductibleFamily flag intentionally suppressed (line 1322)
embedded_deductible{in,out}NetworkDeductibleTypeembeddedAggregate"embedded" → Embedded · "non_embedded" → Aggregate · else blank
plan_calendar{in,out}NetworkDeductibleOopAccumulationcalendarPlanYear"calendar_year" → Calendar Year · else → Plan Year
Service cost-share (29 fields, all use medicalDeductible except ER)
All twenty-eight standard service-cost-share IDEON fields route through medicalDeductible and produce the same 4-sibling shape per network: value, Type ($/%/Not Covered/N/A/Range/Unlimited/Other), Ded (AD/ND), and Other (free-text fallback).
primary_care_physician · specialist · preventative_care · diagnostic_test · imaging · outpatient_facility · outpatient_physician · ambulance · urgent_care · inpatient_facility · inpatient_physician · outpatient_mental_health · inpatient_mental_health · outpatient_substance · inpatient_substance · prenatal_care · inpatient_birth_physician · inpatient_birth · home_health_care · rehabilitation_services · habilitation_services · skilled_nursing · durable_medical_equipment · hospice_service · child_dental · child_eyewear · child_eye_exam · chiropractic_services · coinsurance
emergency_room {in,out}NetworkEmergencyRoom*erDeductible (special)Detects "plus" for copay+coinsurance and splits into separate copay and coinsurance fields; sets copay type to $ when % coinsurance is present
Pharmacy / Rx (9 fields)
generic_drugs{in,out}NetworkRxTier1*medicalRxTier 1 — label "Generic"
nonpreferred_generic_drug_share{in,out}NetworkRxTier2*medicalRxTier 2 — label "NP Gen."
preferred_brand_drugs{in,out}NetworkRxTier3*medicalRxTier 3 — label "Brand"
non_preferred_brand_drugs{in,out}NetworkRxTier4*medicalRxTier 4 — label "NP Brand"
specialty_drugs{in,out}NetworkRxTier5*medicalRxTier 5 — label "Spec."
nonpreferred_specialty_drug_share{in,out}NetworkRxTier6*medicalRxTier 6 — label "NP Spec." (Plansight enforces a 6-tier ceiling)
mail_order_rx{in,out}NetworkRxMailOrderinOutTextStraightSame value to both networks
formulary_name{in,out}NetworkRxBenefitFormularyinOutTextStraightSame value to both networks
drug_formulary_url{in,out}NetworkRxBenefitFormularyURLinOutURLValidates with filter_var(); empty if invalid
Network behavior & flags (3 fields)
gated{in,out}NetworkSpecialistReferraltfToInOutReqBoolean → Yes / No (true = referral required)
telemedicine{in,out}NetworkTelehealth*telehealthEmpty → "Not Covered"; non-empty → "Included"
hsa_eligiblehsatfToYnBoolean → Yes / No

Field suffixes — * denotes the parser returns multiple sibling fields per attribute: the value, Type, Ded (AD/ND), and (when applicable) Other for free-form fallback or To for ranges. The raw IDEON string is also written to a sibling aca{FieldName} as an audit trail.

Parsing — deep dive

The regex-based cost-share parser

parseMedicalDeductibleCoverageString() at lines 964–1184 is the heart of the integration. It takes IDEON's free-text cost-share strings and extracts numeric values, types, deductible flags, and ranges. This is where most parsing bugs live and where most "Other"-type fallbacks come from.

Section-based parsing

The parser splits the input string by network section using three labels:

$sections = [
    'inNetwork'   => 'In-Network:',
    'in2Network'  => 'In-Network-Tier-2:',
    'outNetwork'  => 'Out-of-Network:',
];

For each section, regex preg_match('/' . preg_quote($label, '/') . '\s*([^\/]*)/', $ideonStr, $matches) at line 975 captures everything from the label to the next /. The captured chunk is then run through type detection, deductible-flag detection, and special-pattern handlers.

Type-detection order (first match wins)

TypeDetectionNotes
$ Rangeregex /\$(\d[\d,]*)\s*-\s*\$(\d[\d,]*)/iCaptures both ends of a dollar range. Stores value + valueTo.
$ or %Both symbols present → first by strpos() winsBrittle when IDEON writes "$25 or 50%" — $ wins regardless of intent
Not Covered / N/ASubstring check on cleaned valueForces Ded = ND
UnlimitedSubstring checkUsed for preventive-style "no cost" services
OtherFallback when nothing else matchesPreserves the original string for manual review

Special pattern handlers

Worked examples

Input stringParsed result
In-Network: $30 / Out-of-Network: 50%in: type=$, value=30, ded=ND
out: type=%, value=50, ded=ND
In-Network: $50 after deductible / Out-of-Network: Not Coveredin: type=$, value=50, ded=AD
out: type=Not Covered, ded=ND
In-Network: $500 - $2000 / Out-of-Network: $3000 - $4000in: type=$ Range, value=500, valueTo=2000
out: type=$ Range, value=3000, valueTo=4000
In-Network: first 2 visit(s) $30 then 20% / Out-of-Network: 40%in: type=Other, value="(2)$30 then 20%"
out: type=%, value=40, ded=ND
In-Network: $25 plus 10% coinsurance / Out-of-Network: 30%in: type=Other, value="$25+10% coinsurance"
out: type=%, value=30, ded=ND
In-Network: Unlimited / Out-of-Network: Unlimitedin: type=Unlimited, ded=ND
out: type=Unlimited, ded=ND

Known fragility

The parser philosophy

This is a data integration layer, not a clean room. The parser favors preservation over precision — it errs toward Other when uncertain and keeps the raw IDEON string in aca{FieldName} sibling fields so a human can sanity-check downstream. That's the right call for an inbound feed where IDEON's format may evolve, but it does mean the proliferation of Other-typed values in production reports is a signal that some IDEON formats remain outside the regex coverage. Worth a quarterly grep against the live data to surface unrecognized patterns.

Missing fields

Fields IDEON returns that Plansight doesn't read

A scan of IDEON's documented medical-plan response against our parser, with corrections from the team applied. Every row below is a field IDEON exposes that Plansight genuinely doesn't read today (or doesn't read in this form). Twelve fields, grouped by category. The phasing block at the bottom shows where to start.

IDEON fieldWhat it isWhy we'd want itPriority
Plan identification (3 fields)
idIDEON's stable internal plan IDSurvives across renewals; the right key for caching, delta-sync, and reconciliationHigh
plan_idCarrier's internal plan codeWhat the carrier uses on their side — broker-facing for support tickets and "this isn't the same plan" conversationsMedium
marketing_name / plan_marketing_nameConsumer-facing display nameThe legal name (which we read as display_name) and the marketing name diverge for many carriers. Brokers want the marketing name on proposalsMedium
Documents & URLs (2 fields)
provider_directory_urlDirect link to the carrier's provider searchMassive UX win on customer-facing proposals — brokers love being able to drop "search for your doctor" linksHigh
formulary_urlDirect link to the drug formulary PDFWe read drug_formulary_url (a legacy alias). Should also pull the canonical formulary_url for consistencyMedium
Marketplace & subsidies (3 fields)
on_marketplaceBoolean — plan is available on Healthcare.gov / state exchangeCritical for PTC subsidy eligibility logic. Today we infer this from other signals (carrier + plan_type) — fragileHigh
off_marketplaceBoolean — plan available outside the exchange onlyCounterpart to on_marketplace. Some plans are dual-listed; we should surface thatHigh
csr_variant_idCost Sharing Reduction variant (73 / 87 / 94 silver)Without this, we can't surface the right CSR plan for subsidy-eligible families. CSR plans are different SKUs of the same Silver plan with reduced deductibles / OOP — easy to missHigh
Network identifier (1 field)
network_id / network_idsIdentifier(s) for the plan's provider networkCurrently we read network_name (string). The ID is the stable handle for cross-referencing with bulk endpoints. Required for the bulk-load architecture (Gap #4)Medium
Pre-tax account flags (2 fields)
hra_eligibleBoolean — Health Reimbursement Account compatibleSibling to hsa_eligible we already read. Cheap to addMedium
fsa_compatibleBoolean — Flexible Spending Account compatibleSibling to hsa_eligible. Cheap to addLow
Member contact (1 field)
customer_service_phoneMember services phone numberStandard on plans. Useful in proposal footers and "what to do next" leave-behindsLow

Already covered, no action needed

Confirmed by the team — these IDEON fields look like gaps on paper but are already handled today, either directly or via an equivalent flow:

Explicitly skipped

Recommended phasing

Phase 1 (highest leverage, shortest paths): id, on_marketplace + off_marketplace, csr_variant_id, provider_directory_url. Five fields, all single-line additions to the field map. Unblocks subsidy-correctness and gives brokers a new URL to surface in proposals.

Phase 2 (architecture-aligned): network_id. Directly enables the bulk-load architecture in Gap #4 — without it, even if we cache plans we can't reliably resolve them by network.

Phase 3 (polish): marketing_name, plan_id, formulary_url, hra_eligible, fsa_compatible, customer_service_phone. Cheap to add once Phase 1 is done; not worth a separate sprint.

Gap analysis

What we're not using yet — and what we'd gain

Three specific gaps from comparing our integration to the IDEON docs. Each is the cause of a real production issue, and each is a fixable change rather than a structural rewrite.

1

Level-funded plans only come through the Quote API

Blocking UHC level-funded

Problem. Level-funded plans are deliberately excluded from /bulk/plans and /bulk/pricings because they're risk-rated per-group based on census. They only come through the Quote API: POST /quotes → poll GET /quotes/{id} until status == "complete"GET /quotes/{id}/rates. Our current architecture (bulk catalog + per-call /rates) bypasses this path entirely, so level-funded is structurally invisible to us. From the docs: "The Quoting API is asynchronous for Level Funded Quotes. You can use the status field to poll until it is complete."

Fix. Add a parallel Quote API path used only when the broker requests level-funded. Keep the existing /rates path for ACA / community-rated. The level-funded rate response includes claims_surplus_fraction, stop_loss_amount, claims_fund (monthly claims reserve), and fixed_costs (admin/stop-loss fees). Persist the Quote ID + payload against the RFP so subsequent broker views don't re-quote.

Cursor prompt

In app/Helpers/Vericred.php, add a getLevelFundedQuote($groupId, $effectiveDate, $productLine) method that POSTs to /quotes, polls GET /quotes/{id} until status == 'complete' (timeout 60s, 2s interval with backoff), then GETs /quotes/{id}/rates. Store the response keyed by RFP ID. Add a feature flag RFP_FETCH_LEVEL_FUNDED to gate it. Reference: docs.ideonapi.com → Group Quoting → Level Funded.

2

Sub-group quoting trips carrier minimums

Causing UHC rejections

Problem. When a broker uploads a 7-person census and assigns members to current plans (e.g. 5 on Plan A, 1 on Plan B, 1 on Plan C), Plansight submits each plan-option as a separate sub-group quote. UHC's enrolled-lives minimum kicks in for the 1-person Plan B and Plan C sub-groups, returning no rate even though the actual group is 7 people and clears the minimum easily.

Fix. Stop fragmenting the census before quoting. Submit the full 7-person census to IDEON for every plan we want priced. Compute per-plan "real-world enrollment" cost locally by summing GET /rates/{rate_id}/member_rates for just the members the broker assigned to that plan. From the docs: "Rates are broken down by Member and Dependent, so that you can show the final cost in different scenarios where an employer might cover a different percentage." The intended pattern is one quote per plan, then enrollment-scenario math locally.

Cursor prompt

Find every place a sub-group census is submitted to Vericred::getRates(). Refactor to submit the full census once per plan. Compute per-plan enrollment cost locally from GET /rates/{rate_id}/member_rates by summing the rates of members assigned to that plan in the RFP. Verify on a known UHC group that today returns no rate due to sub-group minimums.

3

We don't read /limiting_factors — silent rejections

Visibility gap

Problem. When IDEON rejects a plan due to a carrier business rule (minimum enrolled lives, participation, geography, age constraints, etc.), Plansight doesn't surface why. The broker just sees a missing rate with no explanation, which makes Gap #2 (above) particularly painful to debug. From the docs: "information... when there is a business rule that prevents one or more plan from being quoted."

Fix. After each quote, call GET /quotes/{id}/limiting_factors and surface per-plan rejection reasons in the quote view (carrier, plan ID, the rule that fired). Combined with Gap #2, this turns "no rate" into "Plan B unavailable: UHC requires 5 enrolled lives" or similar.

Cursor prompt

Add Vericred::getLimitingFactors($quoteId) calling GET /quotes/{id}/limiting_factors. Parse the response into per-plan rejection reasons (carrier, plan ID, business rule). Surface in the quote view as a tooltip or inline footnote on plans that didn't return rates.

4

Bulk-load plans + rates from flat files instead of per-quote API calls

Architecture opportunity

Problem. Today, every ACA / community-rated quote calls GET /rates, paginated, per quote. That's a per-broker, per-RFP cost — both in latency (long page-throughs of rate data) and in carrier-imposed rate-limit risk. We chose this architecture early because UnitedHealthcare requires per-call quoting (see Gap #1) — but UHC is the exception, not the rule. For the other carriers, the data we need lives in IDEON's bulk endpoints already.

The opportunity. IDEON's bulk endpoints (/bulk/plans, /bulk/pricings, /bulk/rating-areas, /bulk/zip-counties, /bulk-exports) collectively give us everything needed to determine rating area + quarter + plans + rates from flat files alone for non-level-funded, non-UHC plans. With those cached locally (refreshed quarterly when IDEON publishes new bulk data), Plansight could let a broker drop a new plan into a customer's profile without hitting IDEON's API at all.

What "loading a plan as a new plan" looks like under this model. Broker chooses a customer, types/searches for a plan (Plansight surfaces options from the local bulk cache), Plansight resolves rating area from the customer's ZIP+FIPS, picks the appropriate quarter's rates, and instantiates the plan + member rates locally. Zero API calls, instant response. Bulk refresh is a quarterly ETL pipeline against POST /bulk-exports → poll GET /bulk-exports/{id} → ingest the resulting payload into Plansight tables.

What still requires the API. UHC level-funded (Gap #1 — Quote API). Anything where we need real-time per-group risk-adjusted pricing. Bulk gives us the catalog; the API is for the subset of pricing that's group-specific.

Cursor prompt

Scope a BulkSync job that runs quarterly: calls POST /bulk-exports/plans-in-bulk, polls GET /bulk-exports/{id}, downloads the resulting bulk payload, and ingests into local tables (BulkPlan, BulkRate, BulkRatingArea, BulkZipCounty). Then add VericredBulkService::loadPlanFromCache($hiosId, $zip, $effectiveDate) that resolves the plan + rates from local cache without an API call. Gate behind a BULK_PLAN_LOADING feature flag. Validate parity against a known-good API-fetched plan as a regression check.

The bigger question

If level-funded forces us into the Quote API anyway, should we move all rate fetching to the Quote API? Trade-offs: stay on /rates = bulk catalog still works, no async polling for ACA, two code paths. Move to /quotes = all product lines unified, composite tiers come server-side (delete local code), CSR/CHIP/PTC subsidies come for free, limiting-factors endpoint becomes available everywhere.

Recommendation: add the Quote API path for level-funded now, leave /rates for ACA/community-rated for the moment, and plan a consolidation pass later. That avoids a big-bang rewrite while unblocking UHC level-funded.

Things to verify

Items worth a developer's review

Static-read findings. The integration works in production; these are areas where edge cases or maintenance attention would pay off.

🧩

Regex fragility in benefit parsing

Complex regex patterns make many assumptions about format ("first X visit(s)", "plus", "per day"). If IDEON varies format, parsing may silently fail or misinterpret values. Worth fixture-based unit tests.

Verify
🔀

In-Network-Tier-2 merging logic

Lines 1098–1150 merge in2Network and inNetwork results. Logic is convoluted — checks for "%", conditionally adds symbol, checks for "up to". Hard to audit for edge cases.

Verify
📄

Soft failure on PDF download/scan

Lines 1575–1616 catch exceptions during PDF download and virus scan, returning empty field instead of failing hard. Quote may be created with missing benefit summary without alerting the broker.

Silent fallback
🆔

No validation of HIOS ID format

HIOS ID is used as primary key / filename with no validation (line 1549). Unusual IDs could cause filesystem issues. Defensive check worth adding.

Note
⏱️

Static cache not cleared

$zipToFipsCache is never cleared. Long-running batch processes could serve stale data if a ZIP's FIPS mapping changes mid-run.

Note

60s socket timeout may be short

For large rate payloads or slow network, 60s socket timeout (Vericred.php:34) could be exceeded. The 4-attempt retry mitigates but the timeout is worth reviewing.

Note