> ## Documentation Index
> Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Invoke Ephemeral Proxy

> One-shot proxy call that resolves vault tokens on the wire and forwards the request to an arbitrary HTTPS upstream.

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](/essentials/proxy/ephemeral-proxy).

<Note>
  Unlike the [Routes](/api-reference/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.
</Note>

***

## Invoke

```text theme={"dark"}
ANY /v1/proxy
```

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

| Header              | Required | Description                                                                                                                                                 |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization`     | Yes      | `Bearer tk_live_...` — your KnoxCall API key.                                                                                                               |
| `X-Knox-Proxy-URL`  | Yes      | The upstream URL. Must be `https://`, must use a DNS hostname (no IP literals), must pass SSRF validation.                                                  |
| `Content-Type`      | No       | Defaults to `application/json` if omitted. Non-JSON bodies are sent as-is for string content.                                                               |
| `X-Knox-Encrypted`  | No       | Base64-encoded JSON payload addressable from templates via `{{ encrypted \| json: $.path }}`. v1 is opaque base64-JSON; full JWE support is on the roadmap. |
| `X-Knox-Timeout-Ms` | No       | Per-request upstream timeout. Positive integer ≤ `30000`. Defaults to `25000`.                                                                              |

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` and `Host` headers, and any header beginning with `X-Knox-`.

### Request Body

Optional. Sent to the upstream after token substitution. The body is scanned recursively for two template forms:

| Template                          | Resolves to                                                                   |
| --------------------------------- | ----------------------------------------------------------------------------- |
| `{{ token: tok_xxx }}`            | The raw value stored under token `tok_xxx` in any of your tenant's vaults.    |
| `{{ encrypted \| json: $.path }}` | The value at JSONPath `$.path` inside the decoded `X-Knox-Encrypted` payload. |

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:

| Response Header             | Description                                                                 |
| --------------------------- | --------------------------------------------------------------------------- |
| `X-Knox-Request-Id`         | The KnoxCall request ID. Use it to look up the audit log entry.             |
| `X-Knox-Destination-Status` | The upstream's status code, echoed in case an intermediate CDN rewrites it. |

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST https://api.knoxcall.com/v1/proxy \
    -H "Authorization: Bearer tk_live_abc123..." \
    -H "X-Knox-Proxy-URL: https://api.stripe.com/v1/charges" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": 5000,
      "currency": "usd",
      "source": "{{ token: tok_01HZX... }}"
    }'
  ```

  ```python Python theme={"dark"}
  import requests

  resp = requests.post(
      "https://api.knoxcall.com/v1/proxy",
      headers={
          "Authorization": "Bearer tk_live_abc123...",
          "X-Knox-Proxy-URL": "https://api.stripe.com/v1/charges",
          "Content-Type": "application/json",
      },
      json={
          "amount": 5000,
          "currency": "usd",
          "source": "{{ token: tok_01HZX... }}",
      },
  )
  print(resp.status_code, resp.headers["X-Knox-Request-Id"])
  print(resp.json())
  ```

  ```javascript Node.js theme={"dark"}
  const resp = await fetch("https://api.knoxcall.com/v1/proxy", {
    method: "POST",
    headers: {
      Authorization: "Bearer tk_live_abc123...",
      "X-Knox-Proxy-URL": "https://api.stripe.com/v1/charges",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: 5000,
      currency: "usd",
      source: "{{ token: tok_01HZX... }}",
    }),
  });

  console.log(resp.status, resp.headers.get("X-Knox-Request-Id"));
  console.log(await resp.json());
  ```
</CodeGroup>

### Errors

| Status | Type               | Description                                                                                                                           |
| ------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `validation_error` | Missing or malformed `X-Knox-Proxy-URL`, invalid `X-Knox-Timeout-Ms`, malformed `X-Knox-Encrypted`, or more than 20 token references. |
| 400    | `token_not_found`  | A `{{ token: ... }}` reference does not exist in any of your tenant's vaults.                                                         |
| 400    | `template_error`   | A template expression failed to render (malformed JSONPath, missing key in encrypted payload, etc.).                                  |
| 402    | `plan_limit`       | Ephemeral Proxy requires the **Starter** plan or above.                                                                               |
| 403    | `forbidden`        | The upstream URL failed SSRF validation (private IP range, metadata endpoint, non-HTTPS, etc.).                                       |
| 403    | `PolicyDenied`     | RBAC denial — either the `ephemeral_proxy:invoke` gate or the per-token `vault:detokenize` gate denied the request.                   |
| 500    | `internal_error`   | A plan-limit lookup or token-resolution step failed internally.                                                                       |
| 502    | `upstream_error`   | The upstream did not respond, returned an unrecoverable transport error, or timed out.                                                |

***

## 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.

```json theme={"dark"}
{
  "card_number": "{{ token: tok_01HZX0M... }}",
  "exp_month": 12
}
```

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`).

### `{{ 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.

```bash theme={"dark"}
# Encode the payload your application would otherwise put directly in the body
PAYLOAD=$(echo -n '{"customer_id":"cus_123","cvc":"492"}' | base64)

curl -X POST https://api.knoxcall.com/v1/proxy \
  -H "Authorization: Bearer tk_live_abc123..." \
  -H "X-Knox-Proxy-URL: https://api.stripe.com/v1/payment_methods" \
  -H "X-Knox-Encrypted: $PAYLOAD" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": "{{ encrypted | json: $.customer_id }}",
    "card": { "cvc": "{{ encrypted | json: $.cvc }}" }
  }'
```

In v1 the payload is opaque base64-encoded JSON; full JWE with per-tenant frontend keypairs is on the roadmap.

***

## Permissions

Each invocation evaluates two permissions:

| Resource          | Action       | When evaluated                                                |
| ----------------- | ------------ | ------------------------------------------------------------- |
| `ephemeral_proxy` | `invoke`     | Once per request, before URL validation.                      |
| `vault`           | `detokenize` | Per token reference, scoped to the vault that owns the token. |

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`.

***

## 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](/api-reference/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.
