A reference pattern, not a product.

When your provider goes dark, your app keeps thinking.

Every LLM provider eventually fails you — a revoked key at 2am, a zero-balance card, a regional outage. Universal Fallback wraps each provider so a single missing key or empty wallet automatically reroutes the request through OpenRouter to a mapped equivalent — or better — model. One pattern. Eighty lines. No dependency on any single vendor.

  • Zero added runtime dependencies
  • Health-check on key + balance
  • Per-model OpenRouter mapping
  • DPA / retention parity verified

Interactive failover simulator

Toggle a provider into missing-key or zero-balance and watch live traffic reroute through OpenRouter. Click any request in the log to inspect the decision trace.

Sent0
Direct0
Rerouted0
Failed0
Saved100%

Providers

    Request log

      How it works

      Three deterministic steps. No magic. Every fallback is logged with a structured decision trace.

      1. Health-check the provider

        Before any network call, llm.mjs verifies the provider's API key exists and runs a cheap balance probe. A missing key or zero balance is not an error — it's a routing signal.

        const health = await checkHealth(provider);
        // { healthy: false, reason: 'missing-key' }
      2. Try direct, then fall through

        If healthy, call the provider directly. If the call fails with a retriable error — rate limit, network blip, 5xx — we don't loop; we fall through to OpenRouter. Non-retriable errors propagate as before.

        try { return await providerCall(...); }
        catch (e) { if (!isRetriable(e)) throw e; }
      3. Map & route through OpenRouter

        Each native model has a curated OpenRouter equivalent — same family by default, occasionally a better tier if the native model is deprecated. One call. Same shape in, same shape out.

        const orModel = MAP[provider][model].openrouter;
        return openrouterGen({ model: orModel, ...rest });

      The source — llm.mjs

      Eighty-ish lines of ECMAScript modules. No dependencies. Drop into any Node 20+ project.

      Native → OpenRouter mapping

      Same family when possible. An upgrade tag appears when the curated OpenRouter route is a stronger sibling of a deprecated native model.

      Provider Native model OpenRouter route Tier Parity

      Sandbox results & deploy gate

      Twenty-four scenarios. Every one must pass before the gated deploy unlocks. A single fail blocks the merge.

        Compliance brief

        Adding a fallback route adds a sub-processor. Skipping these checks is how lawyers find you.

        Data Processing Addendum

        OpenRouter's DPA matches or exceeds the strictest native provider in scope: zero retention for prompts and completions on inference; sub-processor list versioned; SCCs for EU traffic.

        Parity verified

        Retention parity

        Native providers in scope retain prompts for 0–30 days depending on opt-out. The fallback route is configured with X-OpenRouter-No-Logs: true to match the strictest tier — zero retention — uniformly.

        Zero-retention enforced

        Sub-processor disclosure

        Customer-facing sub-processor list updated to include OpenRouter, Inc. as a tier-2 inference router. Notice posted to status page; thirty-day objection window opened per DPA.

        Disclosure published

        Gated deploy

        Production rollout is feature-flagged behind llm_fallback_universal, ramped 1% → 10% → 50% → 100% across 72 hours. Auto-rollback on a 5xx parity delta > 0.5%.

        Flag wired