Engineering
Multi-provider failover routing for a reseller API
Multi-provider failover routing for a reseller API tries the cheapest upstream first and falls through on a clear decline, never on an unclear one.
The short answer
Multi-provider failover routing for a reseller API means trying the cheapest upstream provider first and moving to the next only when the first definitely did not act, so the customer is never charged more than the order's starting price. No specification defines 'waterfall routing' as a term; it is industry jargon, and the closest primary sources are Microsoft's Circuit Breaker pattern, Stripe's Orchestration product and OpenRouter's provider routing. Failover is only safe when it is paired with idempotency, because a provider that answers unclearly might already have fulfilled the order. In ResellerHQ's own number ordering, a provider that clearly declines is skipped in favour of the next; a provider that answers unclearly stops the waterfall instead of risking a second purchase.
Multi-provider failover routing for a reseller API is what happens when an order cannot be filled by the cheapest upstream supplier: the request moves to the next supplier that can fill it, in a fixed order, instead of simply failing. Done carelessly, it can buy the same order twice. Done correctly, a customer sees one price and one outcome, and never finds out how many upstream providers were tried behind it.
This matters to anyone building or buying a wholesale API for domains, numbers or licences, because "we support multiple providers" says nothing about what happens at the moment one of them fails. The useful questions are narrower: does a retry count as failover, can the price change partway through, and what happens when a provider's answer is simply unclear.
What is multi-provider failover routing for a reseller API?
It is an ordered list of upstream candidates for one request, tried one at a time, where moving to the next candidate depends on what the previous one said. The common version is least-cost: candidates are sorted cheapest first, and the waterfall tries the cheapest one able to serve the request before it tries a dearer one. OpenRouter documents exactly this shape for routing requests to AI model providers: its default strategy is to "prioritize providers that have not seen significant outages in the last 30 seconds" and then, among the stable ones, to select "weighted by inverse square of the price" (OpenRouter provider routing). A wholesale API for domains, numbers or licences is solving the same shape of problem with a different set of upstreams.
No search turned up an authoritative specification for the term "waterfall routing" itself. It shows up in reseller and telecom conversation as a label for this pattern, not as a defined term in any standards body or vendor's reference documentation. Treat it as industry jargon describing the pattern below, not as a cited definition.
Is a single retry the same as failover?
No, and the distinction is the first thing to get right. Microsoft's Circuit Breaker pattern documentation separates the two directly: "The Circuit Breaker pattern serves a different purpose than the Retry pattern. The Retry pattern enables an application to retry an operation with the expectation that it eventually succeeds. The Circuit Breaker pattern prevents an application from performing an operation that's likely to fail" (Microsoft Learn). A retry calls the same provider again, on the theory that the fault was transient. Failover calls a different provider, because the first one is assumed unlikely to succeed for a while.
The same page describes the breaker as a state machine with three states, which is worth having precisely because the vocabulary recurs in every failover design:
| State | What happens |
|---|---|
| Closed | "The request from the application is routed to the operation." Failures increment a counter; enough failures in a window trips the breaker open. |
| Open | "The request from the application fails immediately and an exception is returned to the application," without calling the provider. |
| Half-Open | "A limited number of requests from the application are allowed to pass through and invoke the operation," to test whether it has recovered. |
Source: Microsoft Learn, Circuit Breaker pattern.
The same document gives a warning that matters more for a reseller than for most readers of it: "Be careful when you use a single circuit breaker for one type of resource if there might be multiple underlying independent providers." Its example is a sharded data store, where merging error responses from the shards "might try to access some shards even when failure is likely," while blocking others "even though it's likely to succeed." A reseller buying from several unrelated upstream suppliers is exactly this situation: one supplier's outage should never close the breaker on a supplier that is working fine. The fix the document implies, and the one that follows from the warning, is one breaker per provider.
How do Stripe and OpenRouter route across providers?
Two products aimed at a different problem, payments and AI inference, document the pattern in public, which makes them the most useful analogues available for a reseller API.
Stripe's Orchestration product lets a merchant "create rules to route payments across your payment processors," and one of its named features is to "retry a payment on a different processor" by configuring "rules to automatically retry failed payment transactions with alternative processors" (Stripe Orchestration). As of this writing Orchestration is in private preview, so this is a description of a feature being built, not evidence of its results at scale. Two details carry over directly to any reseller doing the same thing across registries, carriers or licence distributors:
- The original processor keeps its own terms. "When using Orchestration, your third-party processor continues to act as the processor for payments routed to it. All related activity (including payment processing fees and liability for transaction losses) remain subject to the terms that you agreed to with that processor." Routing through a layer does not change who is accountable for what happened upstream.
- Rules are tested before they go live. Stripe documents a sandbox to "test your rules before creating and activating them in your live account," the same discipline how to test a reseller API integration in a sandbox argues for testing any retry or failover logic before it touches real money.
OpenRouter's provider routing, cited above for its price-weighted default, also documents how routing degrades when something is misconfigured. Its allow_fallbacks setting defaults to true, but "if you have sort or order set in your provider preferences, load balancing will be disabled," and the router instead "will try them one at a time and proceed to other providers if none are operational." The lesson for any waterfall: an explicit priority order and automatic load balancing are different features, and turning one on can silently turn the other off.
| Primary purpose | Default ordering | Status, as cited | |
|---|---|---|---|
| Microsoft Circuit Breaker pattern | A resilience pattern, not a product | Not applicable; it decides when to call a provider, not which one | Published guidance, no version number |
| Stripe Orchestration | Payment processing | Configured by rule | Private preview |
| OpenRouter provider routing | AI model inference | Health over the last 30 seconds, then price-weighted | Documented production behaviour |
Sources: Microsoft Learn, Stripe Orchestration, OpenRouter provider routing.
Why does failover need idempotency to be safe?
Because a waterfall is only safe to advance when the previous attempt is known not to have taken effect. If a call to the first provider times out, three things could be true: it never reached the provider, it reached the provider and was rejected, or it reached the provider, succeeded, and only the response was lost. The first two cases are safe to hand to the next provider. The third is not: trying again can register a second domain, rent a second number or provision a second licence seat, and the customer is billed for an order they only placed once.
This is exactly the problem an idempotency key solves on the way into a single provider, and it does not disappear just because there are several providers behind the API. Idempotency keys and signed webhooks for reseller APIs covers what the pattern guarantees and what Stripe and Adyen publish about their own retention windows; the short version is that a key lets a provider recognise a retry of the same request and return the first outcome instead of repeating the side effect (Stripe idempotent requests, Adyen API idempotency). A waterfall that cannot tell "definitely failed" apart from "unclear" has to treat every unclear answer as if the provider might have acted, which means stopping rather than moving on. That caution is the cost of not having a clean idempotency story with every upstream.
How does ResellerHQ route a number order across providers?
Phone numbers are the one product line in ResellerHQ that currently routes across more than one upstream provider, and the mechanism is a single component, the numbers router, not a general feature of the platform.
For a given order, the router builds a list of candidate providers by intersecting three things: the providers allowed for that reseller's account (a feature gate some accounts have and some do not), the providers whose documented capabilities cover the request (country, product, duration, quantity and so on), and the providers currently considered healthy. The result is sorted cheapest first. Offers behind that list are cached for 60 seconds per provider, country and product, so a burst of price checks does not generate a burst of upstream calls.
Health is tracked per provider, not with one breaker shared across all of them, which matches Microsoft's warning above about mixing independent providers' failures together. A provider is marked unhealthy after 5 consecutive health-relevant failures, and then skipped for 60 seconds before being tried again. Not every kind of failure counts toward that total:
| Counts toward a provider's health failures | Does not count |
|---|---|
| The provider was unavailable, or answered ambiguously | The product is out of stock right now |
| Authentication failed, or the request was rate limited | The request was flatly rejected (invalid input, for instance) |
| The provider reported it has insufficient funds upstream |
An out-of-stock answer or a plain rejection says nothing about whether the provider is working; a provider that is simply sold out today should stay in rotation for a request it can fill tomorrow.
Price is fixed before the waterfall runs, which is the detail that keeps a fallback from costing the reseller more than they expected. The customer's price per unit is set from the cheapest candidate's cost plus the reseller's markup, and only candidates whose raw cost is no higher than that already-marked-up price are tried. A dearer fallback can still fill the order, at a thinner margin for the platform, but it can never push the reseller's cost above the price already shown to the customer. The price displayed does not change depending on which provider ends up fulfilling the order, which the numbers guide states plainly: "The price you see is the price you pay. It does not change with the provider that fills the order."
The waterfall itself ends in one of three ways:
- Fulfilled. A provider accepted the purchase. The order completes against that provider's cost.
- Exhausted. Every candidate gave a definitive decline, such as being out of stock, and none of them could have acted. The order fails outright, nothing is charged, and the hold on the customer's funds is released.
- Ambiguous. A provider answered in a way that does not rule out having fulfilled the order. The waterfall stops immediately rather than risk a second purchase at the next provider. The order is marked needs_reconciliation with the money still held, and a background worker's reconcile sweep settles it once the provider's own state becomes clear, either finding the numbers it delivered or giving up and refunding after a wait.
That third outcome is the direct, practical answer to "why does a waterfall need idempotency to be safe": an unclear answer from one provider is a reason to stop, not a reason to try the next one.
Does the same failover mechanism apply to domains and mailbox licences?
No. Each environment, sandbox or live, of ResellerHQ's domains product and its mailbox licences product is wired to exactly one upstream provider, not a list. There is no waterfall to fall through and no circuit breaker protecting either product line, because there is only one candidate to try in the first place. An outage at that one provider is simply an outage; the order fails and the reservation on the customer's funds is released, with no second supplier behind it to attempt the order instead. The domain reseller API comparison covers what to check across other vendors' domain APIs if a single point of upstream failure is a concern for your business; as of this writing, it is also true of ResellerHQ's own domains and licences lines, and only its numbers line routes across more than one provider.
A checklist for evaluating a reseller API's failover claim
"We support multiple providers" answers almost none of the questions that matter. Before relying on a vendor's failover for revenue you cannot afford to lose twice, ask:
- Is there a defined order candidates are tried in, such as cheapest first, or is it unspecified?
- Is health tracked per provider, or does one provider's outage count against providers that are working fine?
- What kinds of failure mark a provider unhealthy, and does running out of stock get confused with being down?
- Is the customer-facing price fixed before the waterfall runs, or can a fallback provider change what the customer is charged?
- What happens when a provider's answer is unclear rather than a clear decline? Does the system stop, or does it guess?
- Is there a way to see, after the fact, which provider actually fulfilled an order and at what cost?
- Does every product line route across multiple providers, or only some of them?
Where ResellerHQ fits
Per the code behind the numbers router (packages/providers/src/router.ts) and the number ordering service (packages/numbers/src/service.ts), ResellerHQ's number ordering waterfall works as described above: candidates allowed for the account, capable of the request and currently healthy, tried cheapest first; a per-provider breaker that opens after 5 consecutive health-relevant failures for 60 seconds; a price fixed from the cheapest candidate before the waterfall runs; and an ambiguous outcome that stops the waterfall and holds the order for reconciliation rather than risking a second purchase.
Domains and mailbox licences are each wired to a single provider per environment in the code, with no waterfall and no breaker for either. ResellerHQ is in early access: domains and mailbox licences take live orders nowhere yet, and their live routes answer 422 not_available; live phone numbers depend on an upstream provider being enabled for the account. There is no public price list, and sandbox prices are test prices. You can place a number order against the sandbox's built-in test provider, with test funds, after creating an account.
Questions
- It is the practice of trying more than one upstream supplier for the same order, in a fixed order, so that a provider which cannot fulfil the request is not the end of the attempt. The usual order is cheapest first, moving to the next provider only when the previous one gives a clear decline, not when its answer is unclear.
- No. A retry repeats the same call to the same provider, hoping a transient fault clears. Failover moves the request to a different provider after the first one declines. Microsoft's Circuit Breaker documentation treats these as separate patterns with separate purposes: retry assumes the operation will eventually succeed, while a circuit breaker stops calls that are likely to keep failing.
- Microsoft's own guidance says no. Its Circuit Breaker pattern documentation warns to 'be careful when you use a single circuit breaker for one type of resource if there might be multiple underlying independent providers,' because merging their error responses can block a healthy provider along with a failing one. The fix is one breaker per provider.
- Because moving to a second provider is only safe once you know the first one definitely did not act. If an upstream call fails in a way that leaves it unclear whether a purchase went through, trying the next provider risks buying the same order twice. An Idempotency-Key on each provider's own API, and a ledger hold that is captured only once, are what make that judgement possible; see idempotency keys and signed webhooks for reseller APIs for how the key itself works.
- The order's status becomes failed, nothing is charged, and the money held for it is returned to the wallet automatically. This is the exhausted outcome: every candidate provider gave a definitive decline, such as being out of stock, and none of them may have acted.
- No. Each of those two product lines is wired to exactly one provider per environment, so there is no waterfall and no circuit breaker to fail over through. Only phone numbers currently route across more than one upstream provider.
Sources
- Microsoft Learn: Circuit Breaker pattern learn.microsoft.com
- Stripe: Orchestration docs.stripe.com
- OpenRouter: Provider routing openrouter.ai
- Stripe: Idempotent requests docs.stripe.com
- Adyen: API idempotency docs.adyen.com