knoxcall is the official KnoxCall client for Ruby — stdlib only, Ruby >= 3.1. The client is Mutex-safe: share a single instance across Puma / Sidekiq threads.
Install
Not yet published to RubyGems — install from the monorepo path or via a git checkout.
Create a client
sandbox: true to target the isolated Test data plane (sandbox.knoxcall.com management host, sandbox-{tenant}.knoxcall.com proxy host) with a tk_test_ key. Passing conflicting credential options (e.g. api_key: and client_id:) raises ArgumentError 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.
Log in once with the CLI
On a developer machine, runknoxcall login once (this gem ships it: gem install knoxcall puts knoxcall on your PATH — every KnoxCall SDK ships the same command). It signs you in via your browser and stores the credential in ~/.knoxcall/credentials.json — this SDK picks the file up automatically, including the stored tenant and base URL, and handles refresh (cross-process safe, atomic rotation of the single-use refresh token). If the stored credential is revoked or expired, the SDK raises AuthenticationError telling you to run knoxcall login again. Select a profile with KNOXCALL_PROFILE (or bootstrap: KnoxCall::StoredCredentials.new(profile: "staging")).
Manage resources
Create a route, then list with pagination — single-object methods return the unwrapped Hash; paginated lists return the{data, meta} envelope; each walks every page transparently (block form or lazy Enumerator):
each_* twins: routes.each_log(id), webhooks.each_log(id), vaults.each_token(name). The same pattern covers every resource: secrets, webhooks, clients, oauth_clients, environments, api_keys, account, audit_logs, agents, crypto, pki, vaults, and dynamic_db.
Call routes through the proxy
client.call proxies a request through a KnoxCall route to your upstream and returns the raw Net::HTTPResponse — the upstream’s status belongs to you; the SDK never turns it into an error. Reference routes by slug (write-once, rename-proof); UUIDs also work.
timeout: overrides the client default.
Bound routes
State the route (and optional defaults) once withclient.route, then use plain HTTP verbs:
call.
Verify webhooks
KnoxCall::Client.construct_event (also available as client.construct_event and client.webhooks.construct_event) verifies the delivery’s HMAC-SHA256 signature and parses it into an event Hash in one step. Pass the raw body string — never re-serialized JSON:
legacy header (stripe, github, slack, aws-sns, custom) are selected with format:; the replay window defaults to 300s (tolerance_seconds: nil disables it); custom requires header_name:. All comparisons are constant-time.
Handle errors
All API failures are typed subclasses ofKnoxCall::Error:
AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404), RateLimitError (429), ServerError (5xx), all under APIError (with status_code / headers / body); plus SignupError (with status_code / error_type / request_id), WebhookSignatureVerificationError, TokenError, and NetworkError / ConnectionTimeoutError for transport failures.
Retries and idempotency
Management requests retry automatically on transport errors and HTTP 408/429/500/502/503/504 (never 409), with exponential half-jitter backoff andRetry-After honored up to 30s. Every mutating request carries a ULID X-Idempotency-Key that stays stable across retries. A 401 purges the cached token and retries once with fresh credentials. Data-plane calls never replay a mutating request after it may have reached the wire. Tune with retry_max_attempts:, retry_base_delay:, retry_max_delay:.
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. dpop: "never" opts out — if the server issues a DPoP-bound token anyway, the SDK raises KnoxCall::TokenError 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-ruby/README.md in the monorepo.