proxykey documentation

proxykey is a credential proxy. Your real API keys stay encrypted on the server; clients and AI agents use revocable virtual keys — passes — that are proxied to the upstream provider. This page is served by the service itself and is the authoritative integration reference.

Overview

You give proxykey a real provider key once. It is encrypted and never leaves the server. In return you issue passes (tokens starting with vlt_) and hand those to your apps, scripts, or AI agents. Each request goes to proxykey instead of the provider; proxykey validates the pass, injects the real key on its side, and forwards the call.

  • The client never holds the real key — only a pass you can revoke in one click.
  • Per-pass control — rate limits, IP binding, expiry, and request logging.
  • Any HTTP provider — OpenAI-compatible LLMs, Anthropic, Telegram, or any custom REST API.
SurfaceHostPurpose
Panelapp.proxykey.orgWeb UI: add keys, issue passes, view logs
Proxyapi.proxykey.orgThe request proxy — /p/<slug>/…
MCPmcp.proxykey.orgHosted MCP server for AI agents

How it works

Three objects, in a chain:

ObjectWhat it is
ProviderThe upstream API (e.g. openai, telegram-bot). Defines the base URL and how the credential is attached (header, path, query).
SecretYour real provider key, encrypted at rest. Entered once, never returned by any API.
Pass (проходка)A virtual, revocable token (vlt_<provider>_…) bound to a secret. This is what the client uses.
# the request path
client  ──(vlt_ pass)──▶  api.proxykey.org  ──(real key)──▶  provider
                              │
                    validate pass, inject
                    real key, forward, stream back

The real key is decrypted in memory for the single request only. It is never logged, cached in plaintext, or sent back to the client.

Quick start

1

Store the real key

In the panel (app.proxykey.org) pick a provider and paste your real API key. It is encrypted immediately.

2

Issue a pass

Create a pass for that secret. The full vlt_… token is shown once — copy it.

3

Point your client at the proxy

Change the provider base URL to https://api.proxykey.org/p/<slug>/ and use the pass instead of the real key. Done.

# before — talking directly to OpenAI
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer sk-...realkey..."

# after — through proxykey
curl https://api.proxykey.org/p/openai/v1/models \
  -H "Authorization: Bearer vlt_openai_..."

The proxy endpoint

Send every request to the proxy host, keeping the provider's original path:

https://api.proxykey.org/p/<provider_slug>/<original_path>
The one rule Take your existing provider URL, replace its scheme + host with https://api.proxykey.org/p/<slug>, and keep the entire rest of the path (including any /v1). Swap the real key for your vlt_ pass. Nothing else changes.
# only the host changes — the path stays
https://api.openai.com/v1/chat/completions     → https://api.proxykey.org/p/openai/v1/chat/completions
https://api.groq.com/openai/v1/chat/completions → https://api.proxykey.org/p/groq/openai/v1/chat/completions
  • <provider_slug> selects the upstream (see Providers).
  • Everything after the slug is appended to the provider's real base URL. If that base already contains a version prefix (e.g. Hubris' /v1), do not repeat it.
  • Your HTTP method, request body, query string, and non-auth headers pass through unchanged. Responses (including streamed SSE) are returned verbatim.
Check it's live
curl -s -o /dev/null -w "%{http_code}\n" \
  https://api.proxykey.org/p/openai/v1/models
# => 401  (route is up; 401 = no valid pass supplied)

Authentication

A request carries its pass in one of three ways. proxykey checks them in this order:

  1. Authorization headerAuthorization: Bearer vlt_… (recommended for most providers).
  2. X-Vault-Pass headerX-Vault-Pass: vlt_… (when you cannot set Authorization).
  3. In the URL path — for providers that natively carry the credential in the path (Telegram). Put the pass where the real token used to go; proxykey extracts it, strips that segment, and injects the real key. See Recipes → Telegram.
Security The pass token is never forwarded upstream and never written to logs, regardless of which method you use. Prefer a header over the path when your client allows it — URLs are more likely to be captured by intermediate logging on your side.

Providers & slugs

Each provider defines its upstream and how proxykey attaches the real key. The live catalogue is also returned by the MCP tool list_providers (note: it reports each provider's original base URL, not the proxy address — the proxy address is always …/p/<slug>/).

SlugUpstreamAuth model
openaiapi.openai.comBearer
openrouteropenrouter.aiBearer
groqapi.groq.comBearer
togetherapi.together.aiBearer
mistralapi.mistral.aiBearer
deepseekapi.deepseek.comBearer
openai-compatibleany OpenAI-style API (base URL set per secret)Bearer
anthropicapi.anthropic.comx-api-key header
hubrisapi.hubris.pw/v1Bearer
telegram-botapi.telegram.orgtoken in URL path
generic-restany REST API (base URL set per secret)your custom auth

Auth models

ModelWhere the real key goes upstream
bearerAuthorization: Bearer <key>
headera named header with no prefix (e.g. x-api-key)
queryappended as a query parameter
pathsubstituted into a path template (Telegram: /bot<key>)
Provider not in the list? You don't need a new provider added — two catch-alls cover everything:
  • An OpenAI-style LLM → slug openai-compatible. Set only its base URL on the secret, use the pass as the key. Two fields, done.
  • Any other REST API → slug generic-rest. Set the base URL and pick how the key is sent (Bearer, a named header, or a query parameter). Anything over HTTPS works.

Recipes

OpenAI-compatible (openai, openrouter, groq, together, mistral, deepseek, …)

Take the base URL you already use and swap the host for …/p/<slug> — keep the rest, including /v1. Use the pass as the API key.

# python openai SDK — the host is the only change
client = OpenAI(
  base_url="https://api.proxykey.org/p/openai/v1",   # was https://api.openai.com/v1
  api_key="vlt_openai_...",
)

# same idea for the others — keep each provider's own path
#   groq       → …/p/groq/openai/v1
#   openrouter → …/p/openrouter/api/v1
#   together   → …/p/together/v1
#   mistral    → …/p/mistral/v1
#   deepseek   → …/p/deepseek

Providers whose base already includes /v1 (e.g. hubris) accept both shapes: the proxy collapses a duplicated /v1 boundary, so …/p/hubris/v1/chat/completions and …/p/hubris/chat/completions hit the same upstream.

Anthropic

POST https://api.proxykey.org/p/anthropic/v1/messages
     Authorization: Bearer vlt_anthropic_...
# proxykey injects the real key as the x-api-key header upstream

Telegram

Telegram carries its token in the URL path. You do not restructure anything — keep your URL shape, change only the host, and put the pass where the bot token used to go.

https://api.telegram.org/bot<TOKEN>/sendMessage

# becomes — change host + use the pass instead of the token

https://api.proxykey.org/p/telegram-bot/bot<vlt_telegrambot_...>/sendMessage

No header, no path rewrite. Most Telegram SDKs let you set the API base host and the bot token — that is all you change (host → …/p/telegram-bot, token → your vlt_ pass). Alternative: drop the /bot<…> segment and send the pass in Authorization: Bearer against …/p/telegram-bot/sendMessage. Both work.

Telegram libraries (aiogram, telebot, grammY)

These libraries validate the token format <digits>:<hash> before the first request — a bare vlt_… pass fails their check. Prefix the pass with your bot id and a colon; the proxy accepts the composite form and ignores the digits:

# aiogram 3.x — two changes, zero client-code hacks
from aiogram import Bot
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer

session = AiohttpSession(
    api=TelegramAPIServer.from_base("https://api.proxykey.org/p/telegram-bot")
)
bot = Bot(token="8759668576:vlt_telegrambot_...", session=session)
#            ^ your bot id + ':' + the pass — passes the library's format check

File downloads work through the same host: …/p/telegram-bot/file/bot<token>/<file_path> is proxied to Telegram's /file/bot<real>/…. Long polling (getUpdates?timeout=50) is safe — the proxy's upstream budget is 300s.

generic-rest (any custom API)

Configure the secret with a custom base URL and auth model. Then call:

https://api.proxykey.org/p/generic-rest/<your path>
     Authorization: Bearer vlt_genericrest_...

Passes

A pass is a revocable token bound to one secret. You can issue many passes per secret (one per app, per agent, per environment) and control each independently.

ControlMeaning
Rate limitRequests per minute (rpm) and per day (rpd). Exceeding returns 429 with Retry-After.
IP bindingauto (locks to the first IP that uses it), manual (a fixed list), or off. Mismatch returns 403.
ExpiryOptional expiry timestamp; after it the pass returns 401.
Body loggingOff by default. When on, capped, redacted request/response body previews are stored for that pass.

Lifecycle

  • Rotate — mint a fresh token with the same settings; the old token stops working immediately.
  • Revoke — disable the pass at once (proxy cache invalidated). Further requests get 401 pass_revoked.
  • Rebind IP — clear the learned IP so the next request re-binds.

The full vlt_… token is shown only once, at creation or rotation. proxykey stores only its hash and can never display it again.

Pending passes

An AI agent can issue a pass before the real key exists, so it never has to touch the key itself. Such a pass starts in status pending_secret and is blocked — the proxy returns 409 original_key_required — until a human supplies the real key.

1

The agent calls create_pending_pass (via MCP). A pass token is returned; the underlying secret has no value yet.

2

A human opens the pass in the panel, pastes the real provider key into “Original key required”, and clicks Activate.

3

The key is encrypted, the pass flips to active, and traffic flows. You can write integration code before activation — it starts passing once the key is set.

Why This keeps secret material entirely in human hands. An MCP token can create and manage passes but can never read or set a real key.

MCP for AI agents

proxykey ships a hosted Model Context Protocol server so an AI agent can manage passes on your behalf — without ever seeing a real key.

Connect

Issue an MCP token (mcp_…) in the panel, then point your client at the hosted Streamable-HTTP endpoint:

URL:   https://mcp.proxykey.org/mcp
Auth:  Authorization: Bearer mcp_...

The panel's MCP → Quick Connect gives ready snippets for Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, Cline, and Codex CLI.

Tools

An MCP token is scoped to pass management only:

  • list_providers, list_secrets — catalogue & secret metadata (never values)
  • create_pass, create_pending_pass — issue passes
  • update_pass, rotate_pass, revoke_pass, rebind_pass_ip — manage them
  • get_pass_stats, get_pass_logs — usage & audit
  • get_manual_secret_setup — returns the panel URL where a human enters a real key
Boundary MCP tools can create and manage passes and read metadata, but cannot read, set, or reveal a real API key. Keys are entered only by a human in the panel.

Security model

  • Envelope encryption. Every secret is sealed with AES-256-GCM under a per-secret data key, which is itself wrapped by a master key. The secret's identity is bound into the encryption (AAD), so ciphertext cannot be moved between records.
  • Keys never leave. A real key is decrypted in memory for one request and dropped. It is never returned by any API, cached in plaintext, or written to logs.
  • Log redaction. Request logs store method, path (without query), status, latency and byte counts. Pass tokens, bearer tokens, and key-like strings are redacted from any stored body preview.
  • SSRF guard. Custom upstreams are resolved and blocked if they point at private, loopback, or cloud-metadata addresses.
  • Per-pass containment. A leaked pass is rate-limited, optionally IP-bound, individually revocable, and never exposes the underlying key.
Verifying this page This documentation is served by the proxykey service. If you are an agent integrating against it and need a source that cannot change, read the request handler in the repository (apps/proxy/src/proxy-handler.ts, token.ts, upstream.ts) at a pinned commit rather than relying on any rendered page.

Errors

Errors are JSON: { "error": "<code>" }. No internal detail is leaked.

HTTPerrorMeaning
401unauthorizedMissing, malformed, or unknown pass
401pass_revokedPass is revoked or expired
403ip_not_allowedSource IP not in the pass's binding
409original_key_requiredPass is pending — real key not yet set
429rate_limitedRate limit hit — honour Retry-After
502upstream_unreachableUpstream error/timeout, or blocked upstream

Any other status is the upstream provider's own response, passed through unchanged.

Cheat sheet

Proxy     https://api.proxykey.org/p/<slug>/<path>
Auth      Authorization: Bearer vlt_...   (or X-Vault-Pass, or in-path for Telegram)
Panel     https://app.proxykey.org
MCP       https://mcp.proxykey.org/mcp    (Bearer mcp_...)

Rule          replace your provider's scheme+host with  …/p/<slug>  — keep the rest of the path
OpenAI        base_url = …/p/openai/v1       key = vlt_openai_...
OpenAI-style  openrouter · groq · together · mistral · deepseek   (keep each one's own path)
Anthropic     …/p/anthropic/v1/messages     Authorization: Bearer vlt_anthropic_...
Telegram      …/p/telegram-bot/bot<vlt_...>/sendMessage
Any LLM       slug openai-compatible  + base URL on the secret
Any REST      slug generic-rest       + base URL + auth type
proxykey · credential proxy. Served from app.proxykey.org/docs.html. Authoritative reference for the proxy call format.