Skip to main content
github.com/knoxcall/sdk-go is the official KnoxCall client for Go — stdlib only, context-first, goroutine-safe (the token store uses single-flight refresh).

Install

The package is knoxcall, imported as github.com/knoxcall/sdk-go/knoxcall. Module: github.com/knoxcall/sdk-go on pkg.go.dev. Source: github.com/KnoxCall/sdk-go.

Create a client

Set 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. APIKey and ClientID) is a construction error; explicit options always beat the environment. 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: knoxcall.New(knoxcall.Options{APIKey: os.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.

Log in once with the CLI

On a developer machine, run knoxcall login once (this module ships it: go install github.com/knoxcall/sdk-go/cmd/knoxcall@latest — every KnoxCall SDK ships the same command). It signs you in via your browser and stores the credential in ~/.knoxcall/credentials.json — Go programs using this SDK pick the file up automatically, including the stored tenant and base URL, and handle refresh (cross-process safe, atomic rotation of the single-use refresh token). Select a profile with KNOXCALL_PROFILE (or Credentials: knoxcall.StoredCredentials{Profile: "staging"}).

Manage resources

Create a route, then list with pagination — single-object methods return the unwrapped object; paginated lists return a typed Page[T]; ListAll walks every page:
The same pattern covers every resource: Secrets, Webhooks, Clients, OAuthClients, Environments, APIKeys, Account, AuditLogs, Agents, Crypto, PKI, Vaults, and DynamicDB. All methods take a context.Context first.

Call routes through the proxy

client.Call() proxies a request through a KnoxCall route to your upstream and returns the raw *http.Response — 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.
path is the upstream path. On a KnoxCall cloud tenant host the data plane is served under /api (https://{tenant}.knoxcall.com/api/<path>); the SDK adds that prefix itself whenever the proxy base is a cloud tenant host with no path of its own, and uses any other base verbatim (self-hosted, or an override that already carries a path). So /api/v2/tickets reaches an upstream path that itself begins with /api. For a per-call timeout, pass a context with a deadline (context.WithTimeout).

Bound routes

State the route (and optional defaults) once with client.Route(), then use plain HTTP verbs:
The handle holds no state beyond the defaults — retries, token refresh, and 401 re-mint behave exactly as on Call().

Route-aware interception (preview)

Send an untouched third-party SDK’s traffic through the Route that covers it — and through the ephemeral proxy where no Route does — with no per-SDK wiring. Turn the Route’s Intercept toggle on and it takes effect on the next poll (60 s) or the next refusal, with no code change. The full decision order, what is and is not reached, and the hooks are on Route-aware interception.
Intercept swaps http.DefaultTransport for a route-aware RoundTripper wrapping the previous one; Stop() restores it. Reached: http.DefaultClient and any *http.Client with a nil Transport (most SDK defaults). Not reached: a client built with its own Transport — hand those client.Wrap.RoundTripper(knoxcall.WithRoutes()), which makes the same decisions on an explicit transport (Ready(), Refresh(ctx), Manifest(), Stop()). Per-host options: knoxcall.WithHost("api.resend.com", knoxcall.HostEscrow("resend-key")) (escrow) or knoxcall.HostUnavailableDirect() (transit only); knoxcall.WithRequireContext() limits interception to requests whose context descends from client.Wrap.Routed(ctx). KNOXCALL_INTERCEPT=off is the kill switch.

Verify webhooks

ConstructWebhookEvent (also available as client.Webhooks.ConstructEvent) verifies the delivery’s HMAC-SHA256 signature and parses it into a typed event in one step. Pass the raw body bytes — never re-serialized JSON:
Formats beyond the default legacy header (stripe, github, slack, aws-sns, custom) are selected with &knoxcall.ConstructEventOptions{Format: "stripe"}; the replay window defaults to 300s; custom requires HeaderName.

Handle errors

All API failures are typed and unwrap to *APIError (status, machine-readable type, human message, RequestID for support):
Hierarchy: AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404), ConflictError (409), ValidationError (422), RateLimitError (429), ServerError (5xx), plus SignupError, WebhookSignatureVerificationError, and ConnectionError / 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 and Retry-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. Tune with Options{RetryMaxAttempts, RetryBaseDelay, RetryMaxDelay}.

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 returns a clear error rather than 401-looping.

Full reference

The package README documents every resource method, the ephemeral proxy, field-actions, crypto/PKI/vault operations, and credential-less Signup(): github.com/KnoxCall/sdk-go.