Excluding vendors from a capability, and testing the chain first

routing/get shows the live failover chain, routing/set excludes vendors you can't use, routing/test probes each one. What is customizable and what isn't.

Infrai routes each capability through a failover chain of vendors, and you can see and constrain that chain per account. GET /v1/account/routing/get returns the effective chain, PUT /v1/account/routing/set excludes vendors you aren’t allowed to use, and POST /v1/account/routing/test probes every vendor in the chain and reports which are up — before you send real traffic.

The last one is the reason this is a short article rather than a support thread. You can check a routing change works without discovering it in production.

See the chain you’re actually on

curl -sS "https://api.infrai.cc/v1/account/routing/get" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"
{
  "ok": true,
  "data": {
    "preferences": {},
    "effective_chains": {
      "ai.chat": [
        {"vendor": "alibaba_intl", "model": "qwen3.7-plus"},
        {"vendor": "tokenhub_intl", "model": "hy3"},
        {"vendor": "azure_foundry", "model": "gpt-5.4"},
        {"vendor": "openaisub", "model": "gpt-5.5"}
      ]
    },
    "no_china_route": false
  }
}

That’s the real failover order: first choice, then the next three if it’s unavailable. preferences is empty until you set something, and no_china_route is the account-level switch for excluding China-region vendors wholesale — one field rather than an exclusion list you have to maintain as vendors change.

Probe before you commit

curl -sS -X POST "https://api.infrai.cc/v1/account/routing/test" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"capability": "ai.chat"}'
{
  "ok": true,
  "data": {
    "capability": "ai.chat",
    "customizable": true,
    "chain": ["alibaba_intl", "tokenhub_intl", "azure_foundry", "openaisub"],
    "results": [
      {"vendor": "alibaba_intl", "model": "qwen3.7-plus", "up": true},
      {"vendor": "tokenhub_intl", "model": "hy3", "up": true},
      {"vendor": "azure_foundry", "model": "gpt-5.4", "up": true},
      {"vendor": "openaisub", "model": "gpt-5.5", "up": true}
    ],
    "ok": true
  }
}

Four vendors, each probed, each up. Two fields to read carefully: customizable tells you whether this capability accepts a preference at all, and ok is the summary — false means nothing in the chain answered, which is a different problem from a preference being wrong.

Run it after every routing change. It’s free and it takes a moment.

Exclude what you can’t use

curl -sS -X PUT "https://api.infrai.cc/v1/account/routing/set" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"capability": "ai.chat", "exclude": ["openaisub"]}'

The shape is an exclusion list, not a preference order, and that’s a deliberate design: you name the vendors you must not use, and the platform keeps choosing among the rest. A contract that forbids a particular processor is expressible; “always use my favourite and never fail over” is not, because it would turn a vendor outage into your outage.

import os

import requests

API = "https://api.infrai.cc"
KEY = os.environ["INFRAI_API_KEY"]
SESSION = requests.Session()
SESSION.headers.update({"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})


def constrain(capability: str, exclude: list[str]) -> dict:
    """Set an exclusion, then prove the remaining chain still has somewhere to go.
    An exclusion that empties the chain is a self-inflicted outage, so check."""
    probe_before = SESSION.post(f"{API}/v1/account/routing/test",
                               json={"capability": capability}, timeout=30).json()["data"]
    if not probe_before.get("customizable"):
        raise RuntimeError(f"{capability} does not accept routing preferences")

    SESSION.put(f"{API}/v1/account/routing/set",
                json={"capability": capability, "exclude": exclude}, timeout=25).raise_for_status()

    probe_after = SESSION.post(f"{API}/v1/account/routing/test",
                              json={"capability": capability}, timeout=30).json()["data"]
    remaining = [r for r in probe_after.get("results", []) if r.get("up")]
    return {
        "chain_before": probe_before.get("chain"),
        "chain_after": probe_after.get("chain"),
        "vendors_up_after": len(remaining),
        "safe": bool(remaining),
    }


if __name__ == "__main__":
    print(constrain("ai.chat", ["openaisub"]))

If vendors_up_after is zero, put the exclusion back. An empty chain doesn’t fail gracefully — there’s nowhere for a request to go.

What is and isn’t customizable

Capability shapeCustomizableWhy
Stateless inference (ai.chat, ai.embed)yesany vendor can serve any request
Stateful resources (a stored object, a provisioned room)nothe resource lives at one vendor
Self-hosted capabilitiesnothere is no vendor to choose
Single-vendor capabilitiesno meaningful choicethe chain has one entry

That second row is the honest limitation and it isn’t a gap that can be closed. Once a resource exists at a vendor — an object in a bucket, a room, a provisioned database — subsequent calls have to reach the same vendor, so routing preferences apply to stateless work and not to anything sticky. customizable: false on the test response is the platform telling you which side of that line a capability sits on.

Why the chain is the interesting part

A single-vendor API gives you exactly that vendor’s availability, and your mitigation is a second integration you write yourself, maintain forever, and exercise for the first time during the incident it was meant to cover — which is the part that never actually works, because the fallback path has no traffic on it until the day it has all of it.

Here failover is the default and the chain is inspectable.

That also makes the cost story legible: GET /v1/account/usage breaks spend down by capability across whichever vendor served it, so you’re not reconciling four invoices to learn what inference cost — one key, one bill, and the vendor choice becomes an implementation detail you can constrain rather than a procurement decision you’re locked into. Switching is a model string or an exclusion list, not a re-integration.

All three routing routes report billing_class: free in discovery, so inspecting and probing costs nothing. The rates behind each vendor are live in GET /v1/discovery and your spend in GET /v1/account/usage (verified 2026-09-21); those rates move downward as contracts improve, which is exactly why the chain, and not a fixed vendor, is the thing worth owning.

References

Browse more account developer guides