Configurator Source
The Stellantis brand "Build & Price" sites (jeep.com, dodge.com, ramtrucks.com,
chrysler.com, fiatusa.com, alfaromeousa.com) expose four public configurator APIs, fetched
per trim (ccode + llp). They are implemented in
projects/ai_stellantis/src/ai_stellantis/sources/configurator.py.
| Endpoint | Purpose |
|---|---|
| CVD | All trims/configurations + base pricing for a model-year code (archival). |
| Equipment Categories | Full ~350-option catalog with descriptions, standard flag, category hierarchy. |
| Catalog Option Details | Per-option pricing, extended descriptions, associations, media. |
| Configure | The standard configuration state of each selectable option — the choice-group half of is_configurable. |
No credentials required; requests use browser-impersonating headers (random_headers()).
Test fixtures
projects/ai_stellantis/tests/data/us/{cvd,equipment_categories,catalog_option,configure}_sample.json
are live captures of all four endpoints for one trim — Jeep Wrangler CUJ202610JLJL74F / 2TF,
a vehicle that also appears in the inventory and vehicle-data fixtures. Refresh the whole set
with uv run python tests/capture.py.
The state field — deciding is_configurable
The configure endpoint
(/hostd/api/configure/EN/states/US/models/{ccode}/lowerLevelPackages/{llp}/configurations/standard)
returns options grouped as options[categoryId][subCategoryId][optionKey]. Each option carries a
state. Established by scanning ~33k options across all 6 brands (MY2023–2027):
state | meaning | selectable? |
|---|---|---|
U | unselected, freely-available add-on | yes |
C | unselected mutually-exclusive alternative (a priced swap for the standard sibling) | yes |
R | unselected alternative constrained by a combinations rule (jeep/dodge/ram only) | yes |
S | standard equipment, selected — the fixed/standard config | no |
G | package-forced selection: on the build only because another selected option pulls it in (always prices.included=true, stateSetBy populated) | no |
Other useful signals: prices.included=true is the exclusive signature of S/G;
locked is unused (always false in current data — do not key on it); content items
(option.content[]) carry no state of their own.
The rule
A single option's flags cannot decide configurability. The standard default of a real choice and
always-on standard equipment are byte-identical (state="S", standard=true, selected=true, onlyOne=true) — they differ only by whether the group offers an alternative. For example, on
the Wrangler the engine group holds ERC (3.6L, state="S") and EC1 (state="C"), so the
standard ERC is configurable; on the Grand Cherokee L the engine group holds only EC7
(state="S"), so it is not.
A top-level option is
is_configurableiff its(categoryId, subCategoryId)group contains a member withstate ∈ {U, C, R}. A group whose members are allS/Gis fixed equipment. Every member of a configurable group — including its standardS/Gdefault — is configurable.
There is intentionally no special-case for powertrain: a trim with a single engine
(state="S", no alternative) is not configurable.
Content items (constituents listed under a package, e.g. the standard Quick Order Package)
have no own state, so they inherit from the parent option: content of a selectable parent
(state ∈ {U, C, R}) is configurable; content that exists only under fixed-standard parents (the
standard QOP) is standard equipment. A content code that is also a configurable top-level option
stays configurable.
The group-state rule keeps the configure feed aligned with code_guides, which treats standard
equipment as non-configurable.
The visibility half — and where the two are combined
A selectable state is necessary but not sufficient. The buyer configurator only renders
subcategories that equipment categories assigns to a view; options in an unassigned subcategory
(e.g. 50 "Other(s)", 113 "Upper Level Packages") are never offered even when a group member has
state=U. Equipment categories owns that map — subcategory_view_class is non-null exactly when
the subcategory belongs to a view.
The two halves stay with the feeds that own them and are combined once, at consolidated:
| Half | Feed | Where |
|---|---|---|
| choice group | configure | extract_features_from_configure — state ∈ {U, C, R} within the group |
subcategory_is_visible | equipment categories | consolidated source-group adapter.fields — subcategory_view_class IS NOT NULL |
is_configurable | — | consolidated derived_fields — is_configurable AND COALESCE(subcategory_is_visible, TRUE) |
subcategory_is_visible is derived in the equipment-categories adapter, from that feed's own rows,
so it never reads a subcategory_view_class another feed picked up through the upstream taxonomy
backfill. COALESCE(..., TRUE) covers keys equipment categories has no row for, which nothing guards.
The AND applies to whichever feed won is_configurable on merge — code_guides at priority 3 where
it covers a key, configure at priority 5 otherwise.
Taxonomy backfill
code_guides (PDF/LLM) and feature_catalog (BigQuery) are the two feature feeds outside the
configurator fan-out, so neither carries the category/subcategory taxonomy. code_guides carries
no feature_prefix either. Both properties are stable per option code, so
StellantisFeatureTaxonomyComponent backfills each feed from a pool of the others, emitting
enriched/code_guides_features and enriched/feature_catalog_features. These are the assets
consolidated actually reads, via tier: enriched on their source groups.
enriched is an optional tier between transformed and consolidated, for a feed that has been
processed by other assets before the merge. A source group is keyed
<oem>/<market>/transformed/<source> unless it sets tier: enriched.
Three tiers are tried in order: (model_code, option_code), (brand, option_code), then
option_code alone, each drawing from the most recent model_year among resolved pool rows. Only
nulls are filled. A handful of codes resolve nowhere at all and are pinned by name in the
consolidated derived_fields.
Doing this per feed rather than on the merged frame keeps each source group arriving with the columns it is expected to contribute, so merge priority means what it says.
Where a manual override goes
derived_fields entries apply in order and may reuse a name, so the guard is followed by a second
is_configurable entry holding every forced-True assertion — the codes on structurally
single-select axes that must stay configurable, and the option mutually exclusive with TBK. Putting them after the
guard is load-bearing: the AND would otherwise undo them.
Forced-False corrections stay with the feed that owns them (code_guides
is_configurable_overrides at the transformed tier), since an AND cannot undo a False.
Scope of the single-select list
An option that goes configurable → standard keeps existing on the car but stops producing an
attribute, because the attribute layer filters on is_configurable. For a structural single-select
axis that is wrong: every car has a transmission and tires whether or not the buyer chose them,
and the cohort silently loses the attribute. JLJL74/B manual reads NO_TRANSMISSION_TYPE, and
WL*P/E reads NO_TIRE_TYPE on two thirds of candidates. This presents downstream as a
candidate-carried demand gap.
The forced-True list is scoped to the MY2026 → MY2027 cohorts that have actually been diagnosed,
not applied by subcategory. The general rule would be that every code in a structurally
single-select subcategory (Tires, Transmissions, Wheels, Engine, Gear Ratios) stays configurable,
since presence on the car never depends on it having been orderable. Applying it wholesale needs
the is_configurable consumers in synthetic_candidates checked first: the generator reads the
same flag to decide what is a choice, so flipping it broadly risks emitting candidates without
tires from the other direction.
The variation_id field — compound pair codes and tiebreaking
Each option in the configure response is identified by a variation_id. For most options this is
a plain three-character code (e.g. PW7 for White Exterior Paint). For options that are defined
as built-in pairings — combinations that the ordering system treats as a single unit — the API
returns a hyphenated compound form: PW7-QW7 (White Paint + Glass Roof), PW7-QX8 (White Paint
- Black Roof), etc.
The three-character form is the operationally significant one for the ordering system. The compound
form is a configure-API artifact: the same PW7 option appears as a standalone code on trims
where the roof is not paired, and as half of a compound code on trims where it is. The choice
between PW7-QW7 and PW7-QX8 is arbitrary from a pipeline perspective — both encode the same
PW7 base option, and the pairing half (roof finish) varies by trim configuration.
Because multiple configure API calls for the same option key may return different compound forms
depending on query order or trim variant, the same logical option can show up as conflicting
variation_id values across raw rows. The pipeline resolves this by tiebreaking on variation_id
with strategy: min (lexicographic ascending), which produces a stable, deterministic choice
without privileging any particular pairing.
invoice and msrp values on configure rows are considered unreliable and are overridden at the
consolidation tier from more authoritative sources.
Content items and missing data via href
Both top-level options and content items carry an href pointing to the catalog-option details
endpoint:
# top-level option
"href": "https://www.jeep.com/hostd/api/catalog-option/EN/details/models/{ccode}/lowerLevelPackages/{llp}/options/{variation}"
# content item
"href": "https://www.jeep.com/hostd/api/catalog-option/EN/details/models/{ccode}/lowerLevelPackages/{llp}/options/{content_variation}"
Querying these hrefs would yield:
- Full descriptions for content items not already captured by the equipment-categories source.
- Prefixed codes — the catalog-option response includes a
media.mackevision.image.codefield that carries the prefixed form (e.g.*NL), making the prefix recoverable for content items without a parent lookup. - Prices for content items whose parent option does not propagate pricing.
- Potentially other fields (associations, extended descriptions) not returned in the configure response itself.
The catalog-option source already fetches details for top-level options via a separate raw asset.