Outcome Certainty Taxonomy
also called Retryability Classification, Four-State Error Model
Classifying every external response into definitely-failed-retryable, definitely-failed-terminal, definitely-succeeded, or unknown - and giving the fourth state a real resolution path rather than a guess.
When integrating providers whose error reporting is inconsistent — some using HTTP codes, some returning 200 with an error body, some timing out on operations that succeeded — the consuming system needs one question answered: can this be retried, and is the outcome known?
Four categories follow, and the fourth is the one most systems omit.
Why it matters
The unknown category is where duplicates and losses come from. A timeout on a booking, a payment or a dispatch does not mean it failed; it means you cannot tell. Retrying may create a second one; treating it as failed may strand a real one. A system with only success and failure will guess, and it will guess consistently in one direction.
Implementation patterns
- Map every provider response explicitly into one of the four states. This mapping is provider-specific code and it is the real integration work — a provider returning 200 with a failure body and one returning 500 for a validation error both need deliberate handling, and generic error handling gets both wrong.
- Test the mapping against recorded real responses, because provider documentation is frequently incomplete and occasionally wrong about its own error codes.
- Give unknown a resolution path as architecture, not as an error handler: a pending state, a status query against the provider using your own reference, a resolution deadline, and escalation when it expires.
- Send your own idempotency reference in the original request, which is what makes the status query possible. Providers that do not support one force reconciliation by other means, and that limitation belongs on their capability record.
- Never retry a terminal failure, which wastes capacity and can trigger the provider's abuse controls.
- Expose a normalised taxonomy upward with the raw response attached, so consumers get a stable contract and support engineers get the original.
- Reconcile daily against the provider's own record, which is the only mechanism that finds an operation that succeeded after your timeout and exists nowhere in your system.
Industry example
Shipping aggregators such as Shiprocket integrate dozens of carriers with genuinely incompatible error conventions, and card platforms such as Marqeta face the same taxonomy against authorisation networks where an abandoned request may have placed a hold on a cardholder's funds.
In both cases the correct response to unknown is not a retry but a reversal or a status query, and that path is architecture rather than an exception branch.
Failure scenarios
- Timeout classified as failure, producing duplicates on retry.
- Timeout classified as success, recording operations that never happened.
- Generic error handling, mapping a 200-with-error-body to success.
- Pending states with no resolution job, accumulating into a manual backlog.
- No reconciliation, so unresolved ambiguities are found by customers.
- Raw responses discarded, leaving investigations without evidence.
Trade-offs
Four states means a more complex state machine, a resolution service, a reconciliation process, and a user experience that must express uncertainty. Teams under pressure collapse it to two, which works until volume rises.
The complexity is not optional, only deferred — it reappears as an operations team resolving exceptions by hand, which costs more than the code and does not scale.
Interview question
"Your carrier booking call times out. Give me the next five things your system does, what the merchant sees, and what closes the record if the carrier never responds to your status query either."