"When the app isn't sure, the honest move isn't to guess — it's to hand you the choice, clearly labeled."
A failed refresh is not an error to hide
When you ask Keep to refresh a quote and the primary provider fails, there are three tempting-but-wrong moves: silently serve a stale value, silently swap to a fallback, or blow up with a generic 500. Keep does none of them. It returns a 409 — the HTTP status for a conflict that needs resolution — carrying the candidate fallback payload. That 409 is a confirmation challenge: "the primary didn't answer; here's the fallback I could use instead; do you want it?" The refusal to proceed becomes a structured question, not a dead end.
Confirmation as a first-class response
The key design idea is treating "I need you to confirm something" as a legitimate, structured API response rather than an error or a silent default. A FallbackConfirmationRequired maps to 409 with the exact candidate attached, so the frontend can render a real choice — which provider, for which ticker, showing what value — and the human accepts or declines with full information. Compare that to the alternatives: a silent swap gives you no choice; a 500 gives you no information; a stale value gives you a lie. The 409-with-payload gives you agency.
Honest statuses all the way down
The 409 is one instance of a broader rule: Keep's domain errors carry honest HTTP statuses instead of a blanket 500.
FallbackConfirmationRequired→ 409 with the candidate payload- A bad input value → 400
- A missing simulation run → 404
- A provider or runtime failure → 503
Each status tells the client something true about what went wrong and whether retrying, fixing input, or confirming is the right next move. A generic 500 for all of these would erase exactly the information the caller needs. And new provider failures raise a real RuntimeError → 503, never a sentinel value pretending to be data.