Skip to main content
knoxcall/knoxcall-php is the official KnoxCall client for PHP. Requires PHP >= 8.1 with ext-curl + ext-json — no other runtime dependencies.

Install

Not yet published to Packagist — install from the monorepo via a Composer path repository or a git checkout.

Create a client

For the Stripe-style isolated Test environment, pass 'sandbox' => true (uses https://sandbox.knoxcall.com + https://sandbox-{tenant}.knoxcall.com; requires a tk_test_… key). Conflicting explicit options (e.g. api_key + client_id) throw at construction. With no explicit credentials, the SDK resolves in priority order: a pre-acquired token env var (KNOXCALL_ACCESS_TOKEN / KNOXCALL_API_KEY), the credentials file written by knoxcall login (~/.knoxcall/credentials.json), then KNOXCALL_CLIENT_ID + KNOXCALL_CLIENT_SECRET. Legacy API keys — a pre-acquired key or token still works everywhere: new KnoxCall(['api_key' => getenv('KNOXCALL_API_KEY')]) accepts a minted kc_… token or a legacy tk_… / AKE… key. It is sent as-is and never auto-renewed — prefer knoxcall login or client credentials for new integrations.
Token caching under PHP-FPM: typical deployments are per-request processes, so under client_credentials each request mints one token. If that matters, mint out of band, cache it yourself (APCu/Redis), and construct with ['access_token' => $cached]. Long-running workers (CLI, queues, Octane) get in-process caching with refresh-ahead automatically.

Log in once with the CLI

On a developer machine, run knoxcall login once (this package ships it as a Composer bin: composer global require knoxcall/knoxcall-php or vendor/bin/knoxcall — every KnoxCall SDK ships the same command). It signs you in via your browser and stores the credential in ~/.knoxcall/credentials.json — with no explicit credentials, new KnoxCall() picks the file up automatically, including the stored tenant and base URL. Refreshes are cross-process safe (file lock + atomic rotation of the single-use refresh token); if the credential was revoked or expired you get an AuthenticationException telling you to run knoxcall login again. Select a profile with KNOXCALL_PROFILE. Under PHP-FPM the file’s fresh-token fast path is the effective cross-request cache: a request re-reads the file (a local read, no HTTP) while the stored token is fresh and only performs a real refresh-token round trip when it is not — no APCu/Redis store needed for this credential type.

Manage resources

Create a route, then list with pagination — single-object methods return data unwrapped; paginated list() methods return the {data, meta} envelope; iterate() walks all pages:
The same pattern covers every resource: secrets, webhooks, clients, oauthClients, environments, apiKeys, account, auditLogs, agents, crypto, pki, vaults, and dynamicDb.

Call routes through the proxy

call() proxies a request through a KnoxCall route to your upstream and returns the raw response (['status', 'headers', 'body']) — upstream HTTP errors are never thrown; they belong to you. Reference routes by slug (write-once, rename-proof); UUIDs also work.
Legacy (non-kc_) keys automatically travel as the x-knoxcall-key header instead of Authorization: Bearer.

Bound routes

State the route (and optional defaults) once, then use plain HTTP verbs:

Verify webhooks

constructEvent() verifies the delivery AND parses it in one step — use it in your webhook endpoint with the RAW request body:
Supported formats mirror the server exactly: legacy, stripe, github, slack, aws-sns, and custom (pass header_name). All are HMAC-SHA256 with constant-time comparison.

Handle errors

Hierarchy: KnoxCallException (base) → ApiException (with ->statusCode, ->errorCode, ->requestId, ->responseHeaders, ->responseBody) → AuthenticationException (401), PermissionDeniedException (403), NotFoundException (404), ConflictException (409), ValidationException (422, with ->fields), RateLimitException (429, with ->retryAfter), ServerException (5xx), SignupException; plus ConnectionException / ConnectionTimeoutException (transport) and WebhookSignatureVerificationException. Every ApiException carries the server’s request_id — include it when contacting support.

Retries and idempotency

Management requests retry HTTP 408/429/500/502/503/504 (never 409) with half-jitter exponential backoff, honoring Retry-After up to 30s; a 401 purges the cached token and re-auths once. Every mutating request carries a ULID X-Idempotency-Key that stays stable across retries. Data-plane calls never replay a mutation that may have reached the upstream. Configure with retry_max_attempts, retry_base_delay_ms, retry_max_delay_ms, timeout_ms.

DPoP

For higher-security tenants, enable sender-constrained tokens (RFC 9449) — the SDK generates an ES256 keypair, binds the access token via cnf.jkt, and signs a fresh proof per request:
In the default 'auto' mode the SDK starts with plain Bearer tokens and upgrades to DPoP automatically when the OAuth client record requires it (require_dpop): the token endpoint answers invalid_dpop_proof, the SDK generates a keypair, retries once, and operates as DPoP from then on. 'never' opts out — if the server issues a DPoP-bound token anyway, the SDK throws a clear error rather than 401-looping.

Full reference

The package README documents every resource method, the ephemeral proxy, crypto/PKI/vault operations, and credential-less KnoxCall::signup(): sdk/knoxcall-php/README.md in the monorepo.