Skip to main content
The Ephemeral Proxy is a single endpoint — ANY /v1/proxy — that accepts any HTTP method and forwards the request to the URL you supply in the X-Knox-Proxy-URL header. References of the form {{ token: ... }} in the body or query string are resolved against your tenant’s vaults before the request reaches the upstream. For a conceptual overview see the Ephemeral Proxy guide.
Unlike the Routes endpoints — which manage proxy configuration — this endpoint executes a one-shot proxy call. There is no id to look up, no row to list, and no environment overrides.

Invoke

Forwards the request to the URL in X-Knox-Proxy-URL. Body and query-string {{ token: ... }} references are resolved against your tenant’s vaults before the upstream call is made. The upstream response is streamed back unchanged.

Request Headers

All other headers you send are forwarded to the upstream except for hop-by-hop headers (Connection, Keep-Alive, TE, Trailers, Transfer-Encoding, Upgrade, Proxy-Authorization, Proxy-Authenticate), the Authorization, Host and Content-Length headers, Cookie, and any header beginning with X-Knox-. Three more groups are never forwarded. Each name is matched exactly, in any letter case; apart from X-Knox- there is no prefix rule:
  • The control headers KnoxCall reads itself: X-KnoxCall-Key, X-KnoxCall-Route, X-KnoxCall-Environment, X-KnoxCall-Signature, X-KnoxCall-Agent-Id, X-KnoxCall-Agent-Token, X-KnoxCall-Client-Assertion, X-KnoxCall-Origin and X-KnoxCall-Env.
  • The headers a reverse proxy adds to describe the connection to KnoxCall rather than your request: X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto and X-Forwarded-Server, X-Real-IP, True-Client-IP, X-Client-Cert-Verify, X-Client-Cert-Thumbprint and X-Client-Cert-Subject.
  • The request headers Cloudflare adds: CF-Connecting-IP, CF-Ray, CF-IPCountry and the rest of the exact names in Request headers KnoxCall does not forward, which is the complete list.
A header that only resembles one of these is yours and is forwarded. That includes the CF-Access-Client-Id / CF-Access-Client-Secret pair and the cf-access-token header for an upstream behind Cloudflare Access.
Your KnoxCall credential is never forwarded. Authorization and Cookie are always dropped, and X-Api-Key is dropped whenever it holds a KnoxCall credential (a value beginning tk_, kc_, kr_ or AKE) — that is the key you authenticated to KnoxCall with, and sending it on would put a key granting your whole management API into your upstream’s access logs.An X-Api-Key carrying your upstream’s own key — sk-ant-… for Anthropic, and so on — is forwarded unchanged, so providers that authenticate with this header keep working.

Request Body

Optional. Sent to the upstream after token substitution. The body is scanned recursively for two template forms: Anything else shaped like {{ ... }} is rejected with 400 template_error rather than forwarded to the upstream. See Templates. Maximum 20 distinct token references per request.

Response

The upstream’s response is streamed back unchanged: same status code, same headers (minus hop-by-hop and Content-Length), same body bytes. KnoxCall adds two correlation headers:

Errors


Templates

{{ token: <token-id> }}

Substituted with the raw value of the matching vault token. Tokens are tenant-globally unique — you do not need to specify the vault.
If the token doesn’t exist in any of your tenant’s vaults, the request fails with 400 token_not_found. If the token exists but you lack vault:detokenize permission for the vault that owns it, the request fails with 403 (PolicyDenied). If it exists but has expired, the request fails with 400 token_expired. In every refusal the template is left unresolved and the upstream is not called — a token reference that cannot be opened never travels as itself.

{{ encrypted | json: $.path }}

Reads a value from the decoded X-Knox-Encrypted payload using JSONPath. Useful when you want to keep ad-hoc sensitive values out of your application’s heap.
In v1 the payload is opaque base64-encoded JSON; full JWE with per-tenant frontend keypairs is on the roadmap.

\{{ ... }} — sending literal braces

Any {{ ... }} that is not one of the two forms above is rejected with 400 template_error. It is never forwarded. That is deliberate: silently passing an unrendered {{ token: ... }} through would disclose the token identifier to the third-party upstream and deliver a request with a hole where a credential should be. When the braces are meant for the upstream’s own templating, escape the opening delimiter with a backslash. The escape renders a literal {{, and the text after it is never treated as a template — so a single body can carry both:
The upstream receives card_number substituted with the real value, and email_subject as the literal Hi {{ first_name }}, your receipt. Note the doubled backslash: JSON has no \{ escape, so a literal backslash is written \\ in the source. Only the opening {{ is escapable — a bare }} was never a delimiter and always passes through untouched, so nested JSON such as {"a":{"b":{"c":1}}}} is never rewritten. To bypass templating entirely and forward your bytes verbatim, send X-Knox-Proxy-Mode: transparent instead.

Permissions

Each invocation evaluates two permissions: The vault:detokenize check is enforced per token reference, scoped to the vault that owns the token. A denial throws PolicyDenied and the request fails with 403.

OAuth scope

These evaluate what the credential may do. If you call with an OAuth access token whose client was deliberately narrowed, a separate check evaluates what that token may do, and it requires proxy:invoke (or proxy:*) — on every HTTP method, because one handler serves them all and a bodyless GET spends an escrowed credential exactly as a POST does. proxy:read and proxy:write do not satisfy it: escrowing a credential is not permission to spend it. A refused call returns 403 insufficient_scope, naming the scope in the WWW-Authenticate header and the body. API keys and un-narrowed tokens carry no grant to enforce and are unaffected.

Audit

Every invocation writes:
  • An api_requests row with proxy_mode = 'ephemeral', status code, latency, upstream host, source IP, and token count. The body, headers, and resolved token values are never stored.
  • An audit_logs entry tagged ephemeral_proxy.invoke (Audit Logs).
  • A Fleet Graph signal carrying method, upstream host, status code, and latency. The customer’s path is recorded as /proxy — never the raw upstream path.