@kashdao/sdk package is the official TypeScript client for the REST API. It’s the recommended way to integrate from any TS/JS runtime: Node 22+, modern browsers, Deno, Bun, Cloudflare Workers or Vercel Edge. It uses native fetch and has one runtime dependency (Zod).
Construct the client
new KashClient() with no arguments reads KASH_API_KEY (and KASH_BASE_URL) from the environment where a process.env exists.
Base URL selection. You normally don’t set baseUrl. It resolves in this order:
- an explicit
baseUrloption; - the
KASH_BASE_URLenvironment variable; - the key prefix:
kash_test_*→https://api-staging.kash.bot/v1(Solana devnet, simulated USDC),kash_live_*→https://api.kash.bot/v1; - otherwise production,
https://api.kash.bot/v1.
PRODUCTION_BASE_URL and STAGING_BASE_URL. The key is validated against kash_(live|test)_ plus 32 alphanumerics at construction, so pasting a whsec_... webhook secret by mistake throws KashConfigurationError immediately.
Other options: retryBaseDelayMs (default 200), retryMaxDelayMs (default 10 000), retryAfterMaxMs (default 60 000: a server Retry-After longer than this surfaces the error instead of sleeping), headers (extra default headers such as traceparent), fetch (custom implementation), hooks, conditionalRequests (default true, see below), conditionalCacheMaxEntries (default 256) and apiVersion.
The client exposes typed sub-clients: kash.markets, kash.quotes, kash.trades, kash.redemptions, kash.portfolio, kash.webhooks, kash.account and kash.traces. kash.healthCheck() checks connectivity and never throws; it returns { ok, latencyMs, status?, version?, requestId? }.
Read markets
status is one of UNSEEDED, ACTIVE, RESOLVED or ABANDONED. Every market carries chainRef (for example solana:mainnet-beta); chainId is present only on EVM markets. Production trading runs on Solana. Use parseChainRef / tryParseChainRef to get a typed view of chainRef.
Get a quote
Quotes are split intobuy and sell so you can’t pass the wrong unit to the wrong side. USDC amounts are atomic, 6 decimals; outcome-token amounts are WAD, 18 decimals. Both accept a string or a bigint.
pAfter, qAfter, reserveAfter) and an embedded market summary.
Place a trade
2026-08-19 (the SDK default), txHash on a Solana trade is the base58 transaction signature. waitForCompletion refuses a trade that is still pending_confirmation with a KashConflictError; confirm it first. Type guards such as isCompletedTrade, isTerminalTrade and isAwaitingConfirmation narrow a TradeResource by status.
Idempotency. Every POST the SDK sends carries an Idempotency-Key. If you don’t pass one, the SDK generates one per call, so its own retries can never duplicate a write. Pass your own key when your code may retry the same logical order across calls.
Redeem a settled position
When a market resolves (or is cancelled), winning tokens stay in your account until you redeem them. One call redeems one(market, outcome) position:
trades:write scope. The request is accepted synchronously and the payout executes asynchronously. Calling it again for a position that already has a request returns that request with idempotent: true. A position that can’t be redeemed throws KashConflictError with code POSITION_NOT_CLAIMABLE.
Portfolio
watchPositions polls with conditional GETs and backs off while nothing changes (minIntervalMs 5 000, maxIntervalMs 60 000, backoffFactor 2 by default). With conditionalRequests on, the client sends If-None-Match on repeat GETs and resolves a 304 Not Modified from its in-memory copy.
Pagination — async iteration
List methods (markets.list, markets.predictions, trades.list, webhooks.list) return a Page<T> that’s both the first page and an AsyncIterable<T>:
Verify a webhook
constructEvent(body, signatureHeader, secret, opts?) checks the X-Kash-Signature HMAC and a 5-minute timestamp tolerance (override with opts.toleranceMs), accepts any matching v1= entry during a secret-rotation overlap, then parses the envelope { id, type, apiVersion, createdAt, data }. verifySignature runs the same check and returns { valid: true } or { valid: false, reason } without parsing. It throws KashConfigurationError for an empty secret and KashValidationError for a body that verifies but isn’t a known event.
Webhook management: kash.webhooks.list({ status, limit, cursor }), kash.webhooks.redeliver(eventId) and kash.webhooks.rotateSecret() (returns { secret, rotatedAt, previousRetainedUntil }, the new secret shown once). These need the webhooks:manage scope.
Typed errors
Every error is a subclass ofKashError:
retryAfterSeconds (on KashRateLimitError, KashServerError and KashMaintenanceError) is undefined when the server sent no Retry-After. KashValidationError and KashConfigurationError expose issues. The SDK retries 429, 5xx, network errors and timeouts on its own, up to maxRetries.
Lifecycle hooks
Tap into every request, response, retry and error for logging or observability. The SDK never logs by itself.onResponse also carries requestId, apiVersion (the server’s X-API-Version), idempotentReplay, notModified and deprecation (parsed Sunset / Deprecation / Link rel="sunset" headers, or null).
Cloning a client
withConfig returns a new client with overridden fields. Useful for switching between API keys or per-scope timeouts:
withConfig re-validates, so an invalid override throws KashConfigurationError. Per-call overrides (timeoutMs, maxRetries, signal, headers) can also be passed as the last argument of any method.
Mock client for tests
@kashdao/sdk/testing exports an in-memory mock client (no network, no retries) with wire-shape-valid default fixtures:
DEFAULT_MARKET, DEFAULT_TRADE, DEFAULT_COMPLETED_TRADE, DEFAULT_QUOTE_BUY, DEFAULT_QUOTE_SELL, DEFAULT_REDEMPTION, DEFAULT_POSITION and DEFAULT_PORTFOLIO_SUMMARY.
API versions
The SDK sendsX-Kash-Api-Version on every request. It defaults to 2026-08-19 (exported as SDK_API_VERSION), and you can override it:
Because production trading is on Solana, keep the SDK default unless you have a specific reason not to. An unsupported value returns
410 API_VERSION_UNSUPPORTED with the accepted list in the body’s supported member (err.extensions?.supported on the thrown error). Responses carry the version that served the request in X-API-Version, plus Sunset / Deprecation headers when a version is winding down; the onResponse hook surfaces both.
The SDK also sends a User-Agent of the form @kashdao/sdk/<version> (<runtime>) (exported as USER_AGENT), with your userAgentSuffix appended.
Versioning and bundle size
The SDK is pre-1.0 (0.x): a minor version may include breaking changes, so pin a minor range in package.json. Each release is held to a size-limit budget, measured excluding Zod: 24 KB for the main entry and 8 KB for @kashdao/sdk/testing.
Source
- npm:
@kashdao/sdk - GitHub:
KashDAO/sdk-typescript(issues: github.com/KashDAO/sdk-typescript/issues) - OpenAPI spec:
api.kash.bot/v1/openapi.json, with an interactive explorer atapi.kash.bot/v1/docs.
Market disputes (
/v1/markets/{id}/disputes) are served by the API but are not wrapped by the SDK. Call those endpoints directly with your X-API-Key.Next
Build a trading bot
End-to-end walkthrough using this SDK.
CLI
kash — the command-line interface. Same primitives, scripting-friendly.Endpoint Reference
The full live spec at
/v1/docs.