openapi: 3.2.0 info: title: KnoxCall Client Management Wrap Credentials API version: '1.0' description: 'The KnoxCall Client Management API provides programmatic access to manage your API gateway resources including routes, secrets, clients, environments, webhooks, and API keys. ## Authentication All requests must include an OAuth 2.1 access token **or** a legacy API key. The recommended path for new integrations is the first-party SDKs, which mint, cache, and refresh OAuth tokens for you (start with `knoxcall login` on a developer machine, or `KNOXCALL_CLIENT_ID` / `KNOXCALL_CLIENT_SECRET` in CI). You can authenticate using any of: - **Authorization header (OAuth 2.1 bearer token — recommended)**: `Authorization: Bearer ` - **Authorization header (legacy API key)**: `Authorization: Bearer tk_xxx_yyy` - **x-api-key header (legacy API key)**: `x-api-key: tk_xxx_yyy` OAuth 2.1 access tokens (`kc_` prefix) are minted at the root-host token endpoint `https://api.knoxcall.com/oauth/token` (not under `/v1`) using the `client_credentials` or `authorization_code` grant. See the `OAuth2` security scheme and the `/oauth/*` endpoints. Some resources that are managed exclusively through OAuth reject API-key auth with a `403 use_oauth` error. Long-lived API keys remain fully supported. They come in three types: - **Standard keys** (`tk_` prefix) are used on the production API (`api.knoxcall.com`). - **Test keys** (`tk_` prefix, type=test) are used on the sandbox API (`sandbox.knoxcall.com`). - **Enterprise Access Keys** (`AKE` prefix) follow the format `AKE[16chars]:[40chars]`. Using a test key on the production API (or vice versa) will return a `403 wrong_key_type` error. ## Request IDs Every response — success or error — includes an `X-Request-Id` response header (a UUID). Its value is identical to the `request_id` field in the response body (`meta.request_id` on success, `error.request_id` on error). Quote it when contacting support. ## API Versioning The API is versioned on two axes: - **Major version — the `/v1` path prefix.** Breaking changes ship under a new major (`/v2`). `/v1` never receives a breaking change. - **Minor version — the `KnoxCall-Version` header (dated, `YYYY-MM-DD`).** Send `KnoxCall-Version: 2026-08-05` to pin the behaviour of a specific dated release. Omit the header to get the newest minor version. Every response echoes the resolved version back in a `KnoxCall-Version` response header. Within a major version, **additive changes are not breaking** and may appear without a new dated version: new endpoints, new optional request fields, new response fields, new `error.type` values, and new enum members. Clients must tolerate unknown response fields and unknown `error.type` values (see the `ErrorResponse` schema). An **unknown or malformed** `KnoxCall-Version` value returns `400` with `error.type: invalid_api_version`. ## Rate Limiting API keys can have optional rate limits configured. When a limit is configured, the following headers are emitted on **every** response (not only `429`s): - `X-RateLimit-Limit` -- maximum requests allowed per window - `X-RateLimit-Remaining` -- remaining requests in the current window - `X-RateLimit-Reset` -- epoch seconds at which the window resets When the limit is exceeded the API returns `429` (`rate_limit_exceeded` / `rate_limited`) and additionally sets `Retry-After` (seconds until the window resets). ## DPoP sender-constrained tokens, and the server nonce A token minted with a `cnf.jkt` binding (DPoP, RFC 9449) requires a `DPoP` proof header on every request, signed over the request''s method and canonical public URL. **Every response to a request that carried a `DPoP` header also carries a `DPoP-Nonce` response header** -- mints, refusals and successful calls alike, on `POST /oauth/token`, `/v1`, the tenant data plane, the self-hosted proxy and the AI gateway. The value is stateless and changes every minute; the server accepts the current and the previous minute''s, so a nonce is good for 60-120 seconds.' contact: name: KnoxCall Support url: https://knoxcall.com email: support@knoxcall.com license: name: Proprietary url: https://knoxcall.com/terms servers: - url: https://api.knoxcall.com/v1 description: Production - url: https://sandbox.knoxcall.com/v1 description: Sandbox (test keys only) security: - OAuth2: [] - BearerAuth: [] - ApiKeyAuth: [] tags: - name: Wrap Credentials description: Escrow a third-party provider key into KnoxCall custody so it leaves your codebase, host-pinned; the ephemeral proxy injects it under X-Knox-Upstream-Auth-Secret. paths: /wrap/credentials: post: operationId: escrowWrapCredential summary: Escrow a provider credential tags: - Wrap Credentials description: 'Move a third-party provider key (e.g. a Stripe `sk_live_…`) into KnoxCall custody so it leaves your codebase. The key is stored envelope-encrypted (BYOK / rotation apply) and bound to `allowed_hosts`. Afterwards the wrapped SDK sends `X-Knox-Upstream-Auth-Secret: ` on `/proxy` instead of the key, and KnoxCall injects it as the upstream `Authorization` header **only** for a request whose host is in `allowed_hosts` — the anti-exfiltration pin. The value is accepted once and is never returned by any endpoint.' parameters: - $ref: '#/components/parameters/IdempotencyKeyParam' requestBody: required: true content: application/json: schema: type: object required: - provider - name - value - hosts properties: provider: type: string description: Free-form provider label ('stripe', 'openai', …). Not a security boundary. example: stripe name: type: string description: Secret name the escrowed key is stored + referenced under. example: wrap-stripe-live value: type: string description: The raw provider credential. Sent once; never returned. hosts: type: array minItems: 1 items: type: string description: The only upstream DNS hostnames this credential may be injected toward (no scheme/port/path/IP literal). example: - api.stripe.com responses: '200': description: The credential was escrowed. The value is never echoed. content: application/json: schema: type: object properties: data: type: object properties: secret_id: type: string format: uuid name: type: string provider: type: string allowed_hosts: type: array items: type: string sandbox: type: boolean meta: $ref: '#/components/schemas/RequestMeta' '400': $ref: '#/components/responses/ValidationError' '401': $ref: '#/components/responses/Unauthorized' '402': $ref: '#/components/responses/PlanLimit' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalError' /wrap/tokens: post: operationId: mintWrapToken summary: Mint a base-URL gateway token tags: - Wrap Credentials description: 'For third-party SDKs that expose ONLY a base-URL override and no `fetch`/HTTP-client hook. Mints a signed **wrap-token** bound to an already-escrowed credential (see POST /wrap/credentials) and one upstream host, and returns a `base_url` to set as the wrapped SDK''s base URL. The SDK then talks to `/`; KnoxCall reconstructs the real destination, injects the escrowed secret (host-pinned), and forwards it through the same proxy controls as `/proxy` (SSRF pin, PAN refusal, plan/rate metering, audit). **Escrow-only** — the SDK''s own key becomes a placeholder; the real key never enters your process. The wrap-token is a bearer credential embedded in `base_url`; treat it as a secret. Each token is individually revocable via `DELETE /wrap/tokens/{id}` (or by deleting the escrowed secret). Only its sha256 is stored server-side. Needs `secret:read` on the credential. A token issued to a person (`knoxcall login`, a device or authorization-code grant) also needs `secret:update` on it: minting a new bearer for a live key is an edit.' parameters: - $ref: '#/components/parameters/IdempotencyKeyParam' requestBody: required: true content: application/json: schema: type: object required: - secret properties: secret: type: string description: The escrowed credential (name or id) to inject. example: wrap-stripe-live host: type: string description: Upstream host to pin. Optional when the credential allows exactly one host; must be one of its allowed_hosts otherwise. example: api.stripe.com ttl_seconds: type: integer minimum: 1 description: Optional token TTL in seconds. Omit for a non-expiring token. label: type: string description: Optional human label for the token list. style: type: string enum: - path - subdomain description: 'Which base_url form to return. `path` (`…/wg//`) is always available. `subdomain` (`