Summary
When the v2 household API receives an input for a definition_period = MONTH variable keyed to an annual period (e.g., {"2026": ...} instead of {"2026-01": ...}), the value is silently discarded and the variable falls back to its default. The API still returns status: ok with a plausible-looking benefit number, giving partners no signal that their request was malformed.
This contrasts with v1 (api.policyengine.org), which broadcasts the annual value into every month. Whether v1's behavior is "right" is debatable, but at least it surfaces obviously wrong inputs (extreme income → $0 benefit), whereas v2 produces silently wrong outputs (income ignored → max benefit).
Reproductions
Numeric MONTH variable — snap_earned_income
Single 34-year-old, WA, payload sets snap_earned_income: {"2026": 31932} (and equivalent housing_cost).
- v1: broadcasts $31,932/month → way over limit →
snap: $0 (the input was used, even if wildly).
- v2: drops the value, falls back to $0 → looks fully eligible →
snap: $298 (1-person FY2026 max allotment).
Enum MONTH variable — de_ssp_living_arrangement
Aged 70, DE, payload sets de_ssp_living_arrangement: {"2026": "CERTIFIED_RESIDENTIAL_CARE_HOME"}.
- v1 (with version-correct enum name
RESIDENTIAL_CARE): broadcasts → de_ssp: $140, eligible.
- v2: drops the value, falls back to
default_value = NONE → de_ssp: $0, ineligible.
If the same payload is rekeyed monthly ({"2026-01": ...}), both APIs agree.
Why this is the worst-of-three behavior
- Hard reject (preferred): client gets a 400 telling them the period key is wrong; bug never reaches production.
- Broadcast (v1's choice): visibly wrong outputs reveal the bad input quickly during development.
- Silent drop (v2's current behavior): the response looks correct. Partners integrating against v2 have no way to know their request was malformed unless they cross-check externally — which is how this was discovered.
Proposed resolution
Either:
- Reject the request with a 400 and a message like
period key '2026' is invalid for variable 'snap_earned_income' (definition_period = MONTH); use 'YYYY-MM'.
- Broadcast the annual value to every month (matching v1), so at least the input is used.
Pick one and document it on the /us/api docs page. Either is strictly better than today's silent-default behavior.
Optional: validate request period keys against each variable's definition_period at request-parse time, so the error fires before any calculation runs.
Summary
When the v2 household API receives an input for a
definition_period = MONTHvariable keyed to an annual period (e.g.,{"2026": ...}instead of{"2026-01": ...}), the value is silently discarded and the variable falls back to its default. The API still returnsstatus: okwith a plausible-looking benefit number, giving partners no signal that their request was malformed.This contrasts with v1 (
api.policyengine.org), which broadcasts the annual value into every month. Whether v1's behavior is "right" is debatable, but at least it surfaces obviously wrong inputs (extreme income → $0 benefit), whereas v2 produces silently wrong outputs (income ignored → max benefit).Reproductions
Numeric MONTH variable —
snap_earned_incomeSingle 34-year-old, WA, payload sets
snap_earned_income: {"2026": 31932}(and equivalenthousing_cost).snap: $0(the input was used, even if wildly).snap: $298(1-person FY2026 max allotment).Enum MONTH variable —
de_ssp_living_arrangementAged 70, DE, payload sets
de_ssp_living_arrangement: {"2026": "CERTIFIED_RESIDENTIAL_CARE_HOME"}.RESIDENTIAL_CARE): broadcasts →de_ssp: $140, eligible.default_value = NONE→de_ssp: $0, ineligible.If the same payload is rekeyed monthly (
{"2026-01": ...}), both APIs agree.Why this is the worst-of-three behavior
Proposed resolution
Either:
period key '2026' is invalid for variable 'snap_earned_income' (definition_period = MONTH); use 'YYYY-MM'.Pick one and document it on the
/us/apidocs page. Either is strictly better than today's silent-default behavior.Optional: validate request period keys against each variable's
definition_periodat request-parse time, so the error fires before any calculation runs.