External providers (BYOK)
BYOK (bring your own key) routes your requests using your own provider account, not Routeplane’s. You pay the provider directly at their list price — Routeplane takes no rev share, adds no per-token fee, and never holds your keys in plaintext.
BYOK works the same way regardless of deployment:
- Self-hosted / local binary — set provider keys in the environment. Routeplane detects them at startup; no upload or encryption step.
- Routeplane Cloud — keys are encrypted client-side with a sealed box before submission. The node only ever sees ciphertext.
Local mode — env-var auto-detection
Section titled “Local mode — env-var auto-detection”When you self-host or run the binary locally there is no upload step: set provider keys in the environment and you are done. No config file required.
export OPENAI_API_KEY=sk-...export ANTHROPIC_API_KEY=sk-ant-...export GOOGLE_API_KEY=AIza...routeplaneRouteplane detects keys at startup and exposes only the providers whose keys are present. If a provider’s key is missing, attempts to route to that provider return 402 Payment Required with a structured error pointing at the missing variable.
Recognized variables
Section titled “Recognized variables”| Provider | Preferred name | Passthrough fallback |
|---|---|---|
| OpenAI | ROUTEPLANE_OPENAI_API_KEY |
OPENAI_API_KEY |
| Anthropic | ROUTEPLANE_ANTHROPIC_API_KEY |
ANTHROPIC_API_KEY |
ROUTEPLANE_GOOGLE_API_KEY |
GOOGLE_API_KEY, GEMINI_API_KEY |
|
| Custom (registry-listed) | ROUTEPLANE_<PROVIDER_ID>_API_KEY |
— |
The ROUTEPLANE_* variants take precedence over the passthrough names — useful when your shell already has OPENAI_API_KEY set for a different tool and you want Routeplane to use a different account.
For a custom provider registered as id: my-provider in the registry, set ROUTEPLANE_MY_PROVIDER_API_KEY (uppercased, hyphens become underscores).
On Routeplane Cloud
Section titled “On Routeplane Cloud”On cloud.routeplane.app, your provider key is encrypted client-side against the node’s X25519 sealed-box public key before submission. The node never sees plaintext on the write path; ciphertext is decrypted in-memory at request time and never logged.
The flow:
- Fetch the node’s public key.
GET /v1/byok/encryption-pubkeyreturns the current X25519 public key and akek_idfingerprint. Cache by fingerprint and pass it back asIf-None-Matchto short-circuit on304 Not Modified. - Encrypt the plaintext key. Use libsodium
crypto_box_seal(or any sealed-box implementation) against the public key. - Submit the ciphertext. The console does steps 1–2 in-browser when you paste a key — you never need to leave the dashboard. The same submission API is also available for scripted onboarding.
Encryption recipe
Section titled “Encryption recipe”If you’re scripting key submission, here’s the minimum sealed-box step. The output ciphertext is what the submission endpoint expects.
<Tabs items={[‘Node.js’, ‘Python’, ‘Shell’]}>
import sodium from 'libsodium-wrappers';
await sodium.ready;
const meta = await fetch( 'https://cloud.routeplane.app/v1/byok/encryption-pubkey').then(r => r.json());
const ciphertext = sodium.crypto_box_seal( sodium.from_string(process.env.OPENAI_API_KEY), sodium.from_base64(meta.public_key, sodium.base64_variants.ORIGINAL));
const ciphertextB64 = sodium.to_base64(ciphertext, sodium.base64_variants.ORIGINAL);// Submit { provider_name: 'openai', kek_id: meta.kek_id, ciphertext_b64: ciphertextB64, key_prefix: 'sk-...' }import os, base64, requestsfrom nacl.public import PublicKey, SealedBox
meta = requests.get('https://cloud.routeplane.app/v1/byok/encryption-pubkey').json()pubkey = PublicKey(base64.b64decode(meta['public_key']))ciphertext = SealedBox(pubkey).encrypt(os.environ['OPENAI_API_KEY'].encode())
payload = { 'provider_name': 'openai', 'kek_id': meta['kek_id'], 'ciphertext_b64': base64.b64encode(ciphertext).decode(), 'key_prefix': 'sk-...',}# POST `payload` to the BYOK submission endpoint.META=$(curl -s https://cloud.routeplane.app/v1/byok/encryption-pubkey)PUBKEY=$(echo "$META" | jq -r .public_key)KEK_ID=$(echo "$META" | jq -r .kek_id)
CIPHERTEXT=$(printf '%s' "$OPENAI_API_KEY" \ | sodium-seal --pubkey "$PUBKEY" \ | base64)
# Submit { provider_name: "openai", kek_id: "$KEK_ID", ciphertext_b64: "$CIPHERTEXT", key_prefix: "sk-..." }The pubkey endpoint honors If-None-Match for cheap caching — pin the kek_id from a prior response and you’ll get 304 Not Modified while the key is unchanged.
Key scope
Section titled “Key scope”Keys are scoped to your user account — every API key and OAuth token issued under your account can route requests through the keys you have stored. No one can read the ciphertext or the raw key.
Rotation, revocation, and audit
Section titled “Rotation, revocation, and audit”- Rotate by submitting a new ciphertext for the same provider — the previous record is overwritten atomically.
- Revoke from the dashboard. In-flight requests using the prior key complete; new requests get
402 Payment Required. - Audit — every submission is recorded with its time and the
kek_idused. Plaintext is never visible to anyone, including Routeplane operators.
If a node’s kek_id rotates (we re-key every 90 days), the previous key is retained in memory so already-submitted ciphertexts remain decryptable at request time. New submissions must use the current kek_id; re-encrypt only if you explicitly want to migrate to the new key.
Custom providers
Section titled “Custom providers”BYOK works for any provider listed in the registry that declares byok in its manifest’s payment.modes. The provider field in the encryption submission must match the registry id exactly. Local-mode env-var detection follows the ROUTEPLANE_<PROVIDER_ID>_API_KEY convention.
User responsibility
Section titled “User responsibility”Routeplane routes requests through accounts you hold. What each account may be used for is governed by the agreement between you and that provider — not by Routeplane.
Three things to understand before routing:
- API keys are the intended path. Standard pay-per-use API keys (OpenAI, Anthropic, Google, OpenRouter, and registry providers) exist precisely so you can call the API from software of your choosing. Routing these through a local proxy is ordinary usage.
- Subscription products can be different. Some flat-rate subscriptions (for example, coding-assistant or chat subscriptions) carry terms that restrict access to the provider’s own clients, or prohibit automated or third-party access. Routing such a subscription through any proxy — Routeplane included — may violate those terms, and providers can and do suspend accounts for it. Read your provider’s current terms and decide for yourself; we do not make that call for you.
- Enforcement lands on your account. If a provider objects to how its service was accessed, the consequence is applied to your account. Routeplane runs on your machine, never transmits your keys to us, and has no ability to shield an account from its provider.
If you want to stay unambiguously inside every provider’s terms: use API keys via BYOK, or run local models. Treat subscription routing as a convenience you have personally verified against your subscription’s terms.