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
'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.
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, runknoxcall 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 returndata unwrapped; paginated list() methods return the {data, meta} envelope; iterate() walks all pages:
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.
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:
legacy, stripe, github, slack, aws-sns, and custom (pass header_name). All are HMAC-SHA256 with constant-time comparison.
Handle errors
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, honoringRetry-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 viacnf.jkt, and signs a fresh proof per request:
'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-lessKnoxCall::signup(): sdk/knoxcall-php/README.md in the monorepo.