# ACTIVE_KEY_LIMIT_REACHED Source: https://docs.kash.bot/developer-docs/api-errors/ACTIVE_KEY_LIMIT_REACHED HTTP 409 — Active key limit reached **HTTP status:** 409 · **Title:** "Active key limit reached" ## When it fires The user has hit `MAX_ACTIVE_KEYS_PER_USER` (5) and `POST /api/account/api-keys` in the webapp (or `kash-admin api-keys issue`) was called to issue another. The cap is enforced both at the application layer and via the `trg_api_keys_enforce_max_active_per_user` BEFORE INSERT trigger (which closes a TOCTOU window that the app-level check alone leaves open). ## Why it happens * A genuine ceiling — most users only need 2-3 keys (one per environment, maybe a CI key). The cap exists to prevent runaway issuance from compromising audit hygiene. * A bot that issues a fresh key per run instead of reusing one (anti-pattern). ## How to fix * Revoke an existing key first — Settings → API Keys → Revoke (or `kash-admin api-keys revoke `). * Reuse keys across environments where appropriate: one `kash_live_…` for production code, one `kash_test_…` for everything else. * For mm/enterprise tiers, the cap is higher — contact support if your usage genuinely needs more. ## Related codes * [`API_KEY_REVOKED`](/developer-docs/api-errors/API_KEY_REVOKED) — what happens after revocation if you keep using the old key # AMOUNT_TOO_LARGE Source: https://docs.kash.bot/developer-docs/api-errors/AMOUNT_TOO_LARGE HTTP 400 — Amount too large **HTTP status:** 400 · **Title:** "Amount too large" ## When it fires `amount` exceeds `MAX_USDC_PER_TRADE`. This is a defence-in-depth check distinct from your key's per-trade and daily spending limits — it catches obvious mistakes regardless of your tier. ## Why it happens The most common cause is a **decimal-place mistake**: passing `1000000000000000000` ("1 USDC in 18-decimal atomic units") instead of `1` ("1 USDC in human units"). The public API takes amounts in **human units** (the same string a person would type), not 18-decimal atomic units. ## How to fix * Pass `amount` as a string of the human-unit USDC value: `"100"`, `"0.50"`, `"2500"`. * If you have an atomic value, divide by 10^6 (USDC is 6 decimals on-chain, but our API normalises to "USDC the user thinks about"). * For sustained high-value flows above `MAX_USDC_PER_TRADE`, request a tier upgrade — your key's `per_trade_limit_usdc` can be raised, but the absolute cap is a platform-level safety net. ## Related codes * [`SPENDING_LIMIT_EXCEEDED`](/developer-docs/api-errors/SPENDING_LIMIT_EXCEEDED) — your key's per-trade or daily limit, separate from the platform cap * [`VALIDATION_FAILED`](/developer-docs/api-errors/VALIDATION_FAILED) — generic validation failure # API_KEY_EXPIRED Source: https://docs.kash.bot/developer-docs/api-errors/API_KEY_EXPIRED HTTP 401 — API key expired **HTTP status:** 401 · **Title:** "API key expired" ## When it fires The key has a non-null `expires_at` and the current time is past it. ## Why it happens * You requested a time-limited key at issuance (recommended for CI keys, contractor access, demos). * An operator set an expiry on a long-lived key during a security review. * The key is unchanged; only the wall clock moved. ## How to fix * Issue a new key via Settings → API Keys (self-service, free/developer tiers) or `kash-admin api-keys issue` (mm/enterprise tiers). * For automated rotation, schedule a rotation job that issues + activates the new key 24 h before the old one expires. * The new key will have a different `id` and prefix; update every consumer. ## Related codes * [`API_KEY_REVOKED`](/developer-docs/api-errors/API_KEY_REVOKED) — explicit operator action, distinct from time-based expiry # API_KEY_INVALID Source: https://docs.kash.bot/developer-docs/api-errors/API_KEY_INVALID HTTP 401 — Authentication required **HTTP status:** 401 · **Title:** "Authentication required" ## When it fires The `X-API-Key` header is well-formed but no row in the API key store matches its prefix or HMAC. ## Why it happens * The key was deleted (not just revoked — see [`API_KEY_REVOKED`](/developer-docs/api-errors/API_KEY_REVOKED) for that distinct case). * A character was modified in transit or storage (e.g., copy-paste truncation that still happened to land on the right length). * Wrong environment — a `kash_test_…` key sent to production, or vice versa. Production never accepts test keys. * The HMAC pepper has been rotated and the rotation grace period has elapsed for an old issuance. ## How to fix * Compare the stored value to what the webapp Settings → API Keys page shows for the prefix (`kash_live_xxxx…`). * If the prefix doesn't match anything in your account, the key was never yours or has been deleted — issue a new one. * If the prefix matches but auth still fails, contact support with the prefix and your `X-Request-ID` from a recent failed call. ## Related codes * [`API_KEY_MISSING`](/developer-docs/api-errors/API_KEY_MISSING), [`API_KEY_MALFORMED`](/developer-docs/api-errors/API_KEY_MALFORMED) — earlier failure points * [`API_KEY_REVOKED`](/developer-docs/api-errors/API_KEY_REVOKED) — key existed but was explicitly revoked * [`API_KEY_EXPIRED`](/developer-docs/api-errors/API_KEY_EXPIRED) — key existed but is past `expires_at` # API_KEY_MALFORMED Source: https://docs.kash.bot/developer-docs/api-errors/API_KEY_MALFORMED HTTP 401 — Authentication required **HTTP status:** 401 · **Title:** "Authentication required" ## When it fires The `X-API-Key` header is present but does not match the expected `kash_(live|test)_<32 alphanumeric>` shape. ## Why it happens * A leading or trailing whitespace character (e.g., from a copy-paste with a trailing newline). * The wrong env var was substituted (e.g., a Privy token or a bare UUID instead of a Kash API key). * Truncation by a logging or proxy layer that capped header values. ## How to fix * The format is `kash__<32 alphanumeric>` (base62 — `A-Z`, `a-z`, `0-9`, no dashes or underscores after the prefix). The `` segment is `live` or `test`. * `live` keys hit production; `test` keys hit staging. Don't mix. * If you stored the key in an env var, double-check the substitution: `echo $KASH_API_KEY | head -c 16` should print `kash_live_…` or `kash_test_…`. ## Related codes * [`API_KEY_MISSING`](/developer-docs/api-errors/API_KEY_MISSING) — no header at all * [`API_KEY_INVALID`](/developer-docs/api-errors/API_KEY_INVALID) — well-formed but unknown # API_KEY_MISSING Source: https://docs.kash.bot/developer-docs/api-errors/API_KEY_MISSING HTTP 401 — Authentication required **HTTP status:** 401 · **Title:** "Authentication required" ## When it fires A route that requires authentication received a request with no `X-API-Key` header. ## Why it happens * The request was sent without the header (most common — first integration). * A reverse proxy or HTTP client stripped the header. * The wrong base URL — the documented endpoints live under `/v1/*`; some other route may not require auth. ## How to fix ```http theme={null} GET /v1/portfolio HTTP/1.1 Host: api.kash.bot X-API-Key: kash_live_ ``` * Pass the key on every authenticated request. Keys are issued via the webapp Settings → API Keys page or `kash-admin api-keys issue`. * The plaintext is shown once at issuance. If you've lost it, revoke and re-issue. ## Related codes * [`API_KEY_MALFORMED`](/developer-docs/api-errors/API_KEY_MALFORMED) — header present but doesn't match the `kash_(live|test)_*` shape * [`API_KEY_INVALID`](/developer-docs/api-errors/API_KEY_INVALID) — header well-formed but no row matches # API_KEY_REVOKED Source: https://docs.kash.bot/developer-docs/api-errors/API_KEY_REVOKED HTTP 401 — API key revoked **HTTP status:** 401 · **Title:** "API key revoked" ## When it fires The key is well-formed and recognised, but its `revoked_at` column is set. The auth middleware fails closed. ## Why it happens * A user (you or a teammate) explicitly revoked the key via Settings → API Keys → Revoke. * An operator revoked the key via `kash-admin api-keys revoke ` (e.g., suspected leak). * A security automation revoked it (e.g., per-key velocity anomaly tripped a kill switch). ## How to fix * If revocation was unintentional, ask the operator who ran it (the audit log records who/when/why). * If revocation was intentional and you need to keep working, **issue a new key**. Revoked keys cannot be un-revoked — that's the security property. * Update every place the old key was stored (env vars, secret stores, CI configs) before the new key goes live. ## Related codes * [`API_KEY_INVALID`](/developer-docs/api-errors/API_KEY_INVALID) — key was deleted, not revoked * [`API_KEY_EXPIRED`](/developer-docs/api-errors/API_KEY_EXPIRED) — key reached its `expires_at` (no operator action) # API_TRADE_PROCESSING_HALTED Source: https://docs.kash.bot/developer-docs/api-errors/API_TRADE_PROCESSING_HALTED HTTP 503 — Service unavailable **HTTP status:** 503 · **Title:** "Service unavailable" · **Retry-After:** 300s ## When it fires The coarse `API_TRADE_PROCESSING` kill switch is active. Ops uses this to halt all trade-write traffic platform-wide during incidents (e.g., RPC outage, oracle failure, security investigation). Read endpoints (markets, portfolio, trade lookup) stay available throughout — only the write path is blocked. ## Why it happens * Active incident. Check the status page (`status.kash.bot`) for the current banner. * Pre-deploy lockdown for a high-risk migration. * A security-driven freeze in response to anomaly detection. ## How to fix * Honour `Retry-After: 300` and back off. Do NOT poll faster — the kill switch is binary, polling won't unblock you sooner. * Subscribe to the status page or our incident webhook to know when service resumes. * In the meantime, your read paths still work — surface a "trading temporarily paused" banner in your UI rather than failing silently. ## Related codes * [`ROUTE_DISABLED`](/developer-docs/api-errors/ROUTE_DISABLED) — single-route kill switch (more granular than the coarse halt) * [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) — specific dependency rather than platform-wide # API_VERSION_UNSUPPORTED Source: https://docs.kash.bot/developer-docs/api-errors/API_VERSION_UNSUPPORTED HTTP 410 — API version unsupported **HTTP status:** 410 · **Title:** "API version unsupported" ## When it fires Your request sent an `X-Kash-Api-Version` header whose value is not in `SUPPORTED_PUBLIC_API_VERSIONS`. The runtime accepts only date-string values that the API has actively published as a supported contract version. Unrecognized values — typos, future-dated guesses, versions long past their sunset window — return this error. ## Why 410? `410 Gone` is the right semantic: the version was either once accepted (an old date past its 12-month deprecation window) or never has been (a typo, a future date). Either way, sending the request again with the same value will not succeed. `400` would imply the client could fix syntax and retry without changing the value — which is not the case here. `406 Not Acceptable` is closer but is reserved for content-type negotiation. ## Why it happens * **Typo in the pin** — sent `2026-04-30` when the canonical version is `2026-04-29`. * **Pinned to a sunsetted version** — the date you pinned against passed its 12-month `Sunset` window and was removed from the supported set. * **Pinned to a future version** — your client was upgraded with a version pin that the production API hasn't shipped yet (canary drift between client release and server deploy). * **Multi-valued header confusion** — a load balancer or proxy appended a second `X-Kash-Api-Version` value (`,`-joined) and the runtime correctly refuses to silently pick one. ## How to recover The response body's `metadata.supported` field carries the full list of accepted versions, in canonical-default-first order: ```json theme={null} { "type": "https://docs.kash.bot/developer-docs/api-errors/API_VERSION_UNSUPPORTED", "title": "API version unsupported", "status": 410, "code": "API_VERSION_UNSUPPORTED", "detail": "Requested API version '2099-01-01' is not supported. Send no header to use the current default (2026-04-29), or pick a value from the 'supported' list.", "instance": "/v1/markets", "requestId": "01HQGY8K2N3X4Z5C6D7E8F9G0H", "requested": "2099-01-01", "supported": ["2026-04-29"], "current": "2026-04-29" } ``` Pick a value from `supported` and resend the request. The simplest recovery is to **drop the header** entirely and let the runtime default to `current` — that's the recommended consumer mode unless you have a specific reason to pin. ## Migration policy When the API ships a new version, the previous version stays in `SUPPORTED_PUBLIC_API_VERSIONS` for **12 months** (per AD-15 / RFC 8594\). During that window, every response from the deprecated version carries `Sunset: ` and `Deprecation: true` headers so your monitoring can flag the impending expiry well before it hits. If you're seeing this error on a date that is older than 12 months since its `Sunset` was advertised, that's working as intended — it's time to update the pin. ## Best practice Don't pin if you don't have to. The default contract (no header) runs against the current `PUBLIC_API_VERSION`, and we promise additive, non-breaking changes within a date version. Pin only when you're locking down a release for compliance or contract-test reasons. If you do pin, follow the registry: read `https://api.kash.bot/v1/openapi.json` (or the live OpenAPI route) and let your client library auto-select the highest version it recognises. ## See also * `apps/public-api/src/plugins/api-version.ts` — the negotiation plugin source. * `packages/constants/src/public-api-version.ts` — the canonical registry. * `apps/public-api/openapi/index.json` — the snapshot index, listing every published version. * `docs/runbooks/hmac-algorithm-rotation.md` — example of a version bump driven by a security event. # CHAIN_NOT_SUPPORTED Source: https://docs.kash.bot/developer-docs/api-errors/CHAIN_NOT_SUPPORTED HTTP 400 — Chain not supported **HTTP status:** 400 · **Title:** "Chain not supported" ## When it fires A route that performs an on-chain read (`POST /v1/redemptions`, `GET /v1/markets/{id}/quote`) resolved a market on a chain the API does not support for on-chain reads. ## Why it happens * The market is on a chain this environment does not support. Your request is well-formed — this is a chain-support condition, not a client input error, so it is a permanent `400` (retrying will not help). (A *supported* chain missing a contract address is a server-config fault and returns `500 INTERNAL_ERROR`, not this code.) ## How to fix * Confirm the market's chain is one of the supported chains (the `detail` lists them). * If you believe the chain should be supported, contact support — it is a server configuration gap, not something the caller can correct. ## Related codes * [`MARKET_NOT_FOUND`](/developer-docs/api-errors/MARKET_NOT_FOUND), [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) # CLIENT_REQUEST_ID_CONFLICT Source: https://docs.kash.bot/developer-docs/api-errors/CLIENT_REQUEST_ID_CONFLICT HTTP 409 — Client request id conflict **HTTP status:** 409 · **Title:** "Client request id conflict" ## When it fires The same `(user_id, clientRequestId)` body pair was used for an earlier trade with a **different body**. The trade row is unique on `(user_id, clientRequestId)` once you opt in. ## Why it happens * Power-user mistake: `clientRequestId` is a body field with stricter conflict semantics than the HTTP `Idempotency-Key` header. Most callers should use the header; opt into `clientRequestId` only if you specifically want trade-row-scoped dedup. * A bug regenerated the body but kept the `clientRequestId` constant. ## How to fix * For most use cases, drop `clientRequestId` and use only `Idempotency-Key`. See `apps/public-api/README.md` § Idempotency. * If you need `clientRequestId`'s row-scoped dedup, generate a fresh value per logical operation. * The conflicting trade row is referenced in the `detail` field — fetch it via `GET /v1/trades/:id` to see what was previously submitted. ## Related codes * [`IDEMPOTENCY_KEY_CONFLICT`](/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT) — same idea, header-level # CONFIRMATION_EXPIRED Source: https://docs.kash.bot/developer-docs/api-errors/CONFIRMATION_EXPIRED HTTP 409 — Confirmation expired **HTTP status:** 409 · **Title:** "Confirmation expired" ## When it fires The confirmation token is past its `confirmation_expires_at`. High-value trades have a short confirmation window (60 seconds by default; configurable per-tier). ## Why it happens * A human-in-the-loop confirmation flow took longer than the window allows. * A queue/worker latency between `POST /v1/trades` returning the token and the consumer calling confirm. ## How to fix * Tighten the consumer's confirm call — ideally fire it immediately after receiving the token. * For human-in-the-loop flows that need longer, request a per-key window extension (mm tier customers who pipeline trades through human review get longer windows). * The trade is automatically cancelled when the window expires — re-create it to retry. ## Related codes * [`TRADE_NOT_AWAITING_CONFIRMATION`](/developer-docs/api-errors/TRADE_NOT_AWAITING_CONFIRMATION), [`CONFIRMATION_TOKEN_INVALID`](/developer-docs/api-errors/CONFIRMATION_TOKEN_INVALID) # CONFIRMATION_TOKEN_INVALID Source: https://docs.kash.bot/developer-docs/api-errors/CONFIRMATION_TOKEN_INVALID HTTP 409 — Confirmation token invalid **HTTP status:** 409 · **Title:** "Confirmation token invalid" ## When it fires The token you sent in `POST /v1/trades/:id/confirm` doesn't match the stored hash for that trade. ## Why it happens * Wrong trade id — the token is one-time and tied to a specific trade. * Token was truncated or modified in transit/storage. * Replay against a different trade than the one that issued the token. The token plaintext is **never persisted server-side** — only the HMAC. We compare your candidate via `timingSafeEqual` to prevent per-byte timing attacks. ## How to fix * Re-confirm that the trade id in the URL matches the trade id from the create response. * Capture the token verbatim from `confirmation.token` in the create response — the SDK does this automatically; raw HTTP callers must preserve case and length (43 chars, base64url). * If you've lost the token (only shown once in the create response), the trade can't be confirmed — wait for it to auto-expire and re-create. ## Related codes * [`CONFIRMATION_EXPIRED`](/developer-docs/api-errors/CONFIRMATION_EXPIRED), [`CONFIRMATION_TOKEN_USED`](/developer-docs/api-errors/CONFIRMATION_TOKEN_USED), [`TRADE_NOT_AWAITING_CONFIRMATION`](/developer-docs/api-errors/TRADE_NOT_AWAITING_CONFIRMATION) # CONFIRMATION_TOKEN_USED Source: https://docs.kash.bot/developer-docs/api-errors/CONFIRMATION_TOKEN_USED HTTP 409 — Confirmation token used **HTTP status:** 409 · **Title:** "Confirmation token used" ## When it fires The token has already been redeemed. Confirmation tokens are **one-time use** — the first successful confirm clears the stored hash so a replay can't re-fire the trade. ## Why it happens * A retry layer that doesn't check the trade's status before re-confirming (most common). * Two parallel confirmation attempts from the same consumer (race condition in the caller). * A double-click in a human-in-the-loop UI. ## How to fix * Check the trade's status via `GET /v1/trades/:id` — if it's anything past `pending_confirmation`, the confirm already succeeded. * Make your retry loop idempotent: poll trade status first, only confirm if `status === 'pending_confirmation'`. * For UI flows: disable the confirm button immediately on click to prevent double-submission. ## Related codes * [`TRADE_NOT_AWAITING_CONFIRMATION`](/developer-docs/api-errors/TRADE_NOT_AWAITING_CONFIRMATION) — same outcome via different state-check path # DEPENDENCY_UNAVAILABLE Source: https://docs.kash.bot/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE HTTP 503 — Service unavailable **HTTP status:** 503 · **Title:** "Service unavailable" · **Retry-After:** 30s ## When it fires A required external dependency (Postgres, Secrets Manager, Redis, blockchain RPC) is unreachable, and the route can't degrade further. Distinct from [`INTERNAL_ERROR`](/developer-docs/api-errors/INTERNAL_ERROR) (which is "we don't know what happened") and [`ROUTE_DISABLED`](/developer-docs/api-errors/ROUTE_DISABLED) (which is "ops disabled this on purpose"). ## Why it happens * Transient infrastructure blip (most common — clears in seconds). * Sustained dependency outage (visible on `status.kash.bot`). * A rare cascade where one dependency degraded and another can't compensate. ## How to fix * Honour `Retry-After: 30` and back off with jitter. Most occurrences resolve within one retry. * If retries keep failing past 5 minutes, check the status page. * The TS SDK handles this automatically (`KashServerError`, retried with backoff up to its retry budget). ## Related codes * [`API_TRADE_PROCESSING_HALTED`](/developer-docs/api-errors/API_TRADE_PROCESSING_HALTED) — ops-driven, not infra-driven * [`REQUEST_TIMEOUT`](/developer-docs/api-errors/REQUEST_TIMEOUT), [`INTERNAL_ERROR`](/developer-docs/api-errors/INTERNAL_ERROR) # IDEMPOTENCY_KEY_CONFLICT Source: https://docs.kash.bot/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT HTTP 409 — Idempotency key conflict **HTTP status:** 409 · **Title:** "Idempotency key conflict" ## When it fires The same `Idempotency-Key` was used for two requests with **different bodies**. The server refuses to either re-execute (would violate idempotency) or replay the cached response (would mislead the caller about which request "won"). ## Why it happens * Most common: a retry layer regenerated the body on each attempt instead of capturing it once and replaying. * A bug in the caller that mutates the body in-place between the request being prepared and the request being sent. * Two distinct logical operations accidentally sharing a key (e.g., a global key generator that didn't account for concurrency). ## How to fix * Pin the request body before generating the idempotency key. Either: * Capture body + key together in your retry wrapper and re-send the exact pair on each attempt. * Generate a fresh key per logical operation (one trade = one key). * For genuinely retryable bodies that legitimately change between attempts (e.g., updated timestamps), generate a fresh key per attempt — that's the contract. ## Related codes * [`CLIENT_REQUEST_ID_CONFLICT`](/developer-docs/api-errors/CLIENT_REQUEST_ID_CONFLICT) — analogous conflict, but on the body-level `clientRequestId` field rather than the HTTP header * [`IDEMPOTENCY_KEY_EXPIRED`](/developer-docs/api-errors/IDEMPOTENCY_KEY_EXPIRED) — key valid earlier, now past TTL # IDEMPOTENCY_KEY_EXPIRED Source: https://docs.kash.bot/developer-docs/api-errors/IDEMPOTENCY_KEY_EXPIRED HTTP 410 — Idempotency key expired **HTTP status:** 410 · **Title:** "Idempotency key expired" ## When it fires The `Idempotency-Key` you sent matches a row in the idempotency store but that row is past its 24-hour TTL. ## Why it happens * A retry that took longer than 24 hours to fire (e.g., a job that stalled in a queue and retried days later). * An offline-first client that buffered the request locally and only got online after the TTL. The 410 (rather than re-executing or returning the cached response) is intentional: by 24 h, the original response is gone and the system gives you a clear "this key is no longer valid" signal so you can decide whether the operation is still desired. ## How to fix * Generate a fresh `Idempotency-Key` for the retry. * Before retrying, check whether the original operation actually completed — query `GET /v1/trades/:id` (using a `clientRequestId` you stored, if applicable) so you don't double-execute. * For long-tail retries, prefer the body-level `clientRequestId` field over the header — it has the same conflict semantics but no TTL. ## Related codes * [`IDEMPOTENCY_KEY_CONFLICT`](/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT), [`CLIENT_REQUEST_ID_CONFLICT`](/developer-docs/api-errors/CLIENT_REQUEST_ID_CONFLICT) # IDEMPOTENCY_KEY_FORMAT_INVALID Source: https://docs.kash.bot/developer-docs/api-errors/IDEMPOTENCY_KEY_FORMAT_INVALID HTTP 400 — Idempotency key format invalid **HTTP status:** 400 · **Title:** "Idempotency key format invalid" ## When it fires `Idempotency-Key` header contains a character outside the allowed set: `[A-Za-z0-9_\-:.]`. ## Why it happens * The key contained whitespace, a slash, or a non-ASCII character. * Control characters (newlines, NULs) sneaked in from a copy-paste. * An emoji or Unicode separator accidentally landed in the key. The strict allowlist exists because the value flows through structured logs, Redis keys, and PG rows — non-printable / control characters could poison log shippers or downstream systems that key on the value. ## How to fix * Stick to UUIDs (`crypto.randomUUID()` in Node, `uuidgen` in shell) — they're always conforming. * If you must derive the key from external input, sanitise: `key.replace(/[^A-Za-z0-9_\-:.]/g, '')` then check it's still unique. ## Related codes * [`IDEMPOTENCY_KEY_TOO_LONG`](/developer-docs/api-errors/IDEMPOTENCY_KEY_TOO_LONG), [`IDEMPOTENCY_KEY_CONFLICT`](/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT) # IDEMPOTENCY_KEY_TOO_LONG Source: https://docs.kash.bot/developer-docs/api-errors/IDEMPOTENCY_KEY_TOO_LONG HTTP 400 — Idempotency key too long **HTTP status:** 400 · **Title:** "Idempotency key too long" ## When it fires `Idempotency-Key` header value exceeds the 255-character cap. ## Why it happens * You concatenated several identifiers into the key (user id + market id + timestamp + nonce) and the result blew past 255 chars. * A library generated an unusually long token (e.g., a JWT instead of a UUID). ## How to fix * Use a UUID v4 (`uuidgen`, `crypto.randomUUID()`) or a ULID — both are 26–36 chars and unique enough, well under the 255 cap. * If you need to embed semantic information, hash it: `sha256(your-blob).slice(0, 32)` instead of the raw concatenation. * Acceptable character set: `[A-Za-z0-9_\-:.]` — see [`IDEMPOTENCY_KEY_FORMAT_INVALID`](/developer-docs/api-errors/IDEMPOTENCY_KEY_FORMAT_INVALID). ## Related codes * [`IDEMPOTENCY_KEY_FORMAT_INVALID`](/developer-docs/api-errors/IDEMPOTENCY_KEY_FORMAT_INVALID) — disallowed characters * [`IDEMPOTENCY_KEY_CONFLICT`](/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT) — same key, different body # INSUFFICIENT_BALANCE Source: https://docs.kash.bot/developer-docs/api-errors/INSUFFICIENT_BALANCE HTTP 409 — Insufficient balance **HTTP status:** 409 · **Title:** "Insufficient balance" ## When it fires The actor lacks the balance the operation needs — the exact meaning depends on the endpoint: * **`POST /v1/trades`** — the smart account's USDC balance is below the requested `amount` (the rest of this page). * **`POST /v1/redemptions`** — the smart account holds **zero** of the outcome token you are trying to claim, so there is nothing to redeem (e.g. the tokens were already moved or redeemed). For redemptions, ignore the USDC-funding advice below — check the account actually holds the outcome token via `GET /v1/portfolio/positions`. ## Why it happens * You haven't funded the account yet. * A previous trade consumed more than expected (gas + slippage) and left a sub-threshold balance. * The auto-funding pipeline is backed up — common during very high-throughput windows. * Org account: trade is being charged to the user but the org policy expected the org to pay (or vice versa). Check `feePayerType`. ## How to fix * Check the current balance via `GET /v1/portfolio` — `usdcBalance` field. * For users: fund via the webapp's onramp flow (or transfer USDC into the smart account directly). * For orgs: the org admin tops up the org wallet. * For staging/sandbox: use the testnet faucet flow. ## Related codes * [`SMART_ACCOUNT_NOT_PROVISIONED`](/developer-docs/api-errors/SMART_ACCOUNT_NOT_PROVISIONED), [`SPENDING_LIMIT_EXCEEDED`](/developer-docs/api-errors/SPENDING_LIMIT_EXCEEDED) # INSUFFICIENT_SCOPE Source: https://docs.kash.bot/developer-docs/api-errors/INSUFFICIENT_SCOPE HTTP 403 — Insufficient scope **HTTP status:** 403 · **Title:** "Insufficient scope" ## When it fires The key authenticated successfully but lacks one of the scopes the route requires. ## Why it happens * The key was issued with a narrower scope set than the route needs (e.g., a `markets:read` key calling `POST /v1/trades` which requires `trades:write`). * A new endpoint was added that requires a scope your existing key doesn't carry. ## How to fix * Look up the route's required scopes in `apps/public-api/README.md` → Authentication → Scopes table. * Issue a new key with the needed scopes (or revoke + re-issue with a broader scope set). * Principle of least privilege: don't add scopes you don't actually need — narrow keys reduce blast radius if leaked. | Scope | Routes | | ----------------- | ----------------------------------------------------- | | `markets:read` | `GET /v1/markets*`, `GET /v1/markets/:id/predictions` | | `markets:quote` | `GET /v1/markets/:id/quote` | | `trades:read` | `GET /v1/trades(/:id)` | | `trades:write` | `POST /v1/trades`, `POST /v1/trades/:id/confirm` | | `portfolio:read` | `GET /v1/portfolio*` | | `webhooks:manage` | webhook URL/secret rotation, replay endpoint | | `auth:manage` | self-service key CRUD | ## Related codes * [`IP_NOT_ALLOWED`](/developer-docs/api-errors/IP_NOT_ALLOWED) — also 403, but driven by IP allowlist rather than scope # INTERNAL_ERROR Source: https://docs.kash.bot/developer-docs/api-errors/INTERNAL_ERROR HTTP 500 — Internal Server Error **HTTP status:** 500 · **Title:** "Internal Server Error" ## When it fires The error handler caught something it didn't recognise. The actual cause is logged + Sentry-captured server-side; nothing leaks to the caller. This is the catch-all — any other code in this catalogue is a structured, anticipated failure. `INTERNAL_ERROR` means we hit something we hadn't categorised. ## Why it happens * A genuinely unexpected condition (the most common reason this fires). * A new code path that hasn't been instrumented with a typed error yet — please report it so we can add a proper code. ## How to fix * **Always include `requestId` in any support ticket.** It's our handle into the trace, the logs, and the Sentry capture. * Retry once — if it was a transient bug, you may get past it. * If it's reproducible from a specific request, tell us the exact request shape (with secrets redacted) and we'll trace it. * The TS SDK exposes `requestId` on `KashServerError`; raw HTTP callers should pull it from the `requestId` field on the problem response. ## Related codes Every other code in this catalogue is preferable — if you see `INTERNAL_ERROR` consistently for one operation, the right fix is for us to add a new typed code, not for you to work around it. # IP_NOT_ALLOWED Source: https://docs.kash.bot/developer-docs/api-errors/IP_NOT_ALLOWED HTTP 403 — IP not allowed **HTTP status:** 403 · **Title:** "IP not allowed" ## When it fires The key has a non-empty `ip_allowlist` configured AND the request's source IP isn't in it. Empty allowlist (the default) means any IP is allowed — this code only fires when an allowlist is configured AND the caller's IP doesn't match. ## Why it happens * The key is locked to a specific egress IP (common for MM and enterprise tier keys) and the call originated from elsewhere — your laptop, a CI runner, a proxy with a different egress. * The egress IP changed (cloud provider rotated NAT gateway, your home ISP's dynamic IP rolled). ## How to fix * Find the request's IP in the `requestId`'s Loki logs — search `requestId=""`, the structured log carries the source IP. * If the new IP is legitimate, add it to the key's allowlist via Settings → API Keys → Edit (or `kash-admin api-keys` ops command). * If it's not, this code is a successful detection of an exfiltration attempt — investigate. ## Related codes * [`INSUFFICIENT_SCOPE`](/developer-docs/api-errors/INSUFFICIENT_SCOPE) — also 403, scope rather than IP # MARKET_NOT_FOUND Source: https://docs.kash.bot/developer-docs/api-errors/MARKET_NOT_FOUND HTTP 404 — Market not found **HTTP status:** 404 · **Title:** "Market not found" ## When it fires The `marketId` (UUID) you passed doesn't match any market. ## Why it happens * Typo in the market id. * Cross-environment confusion: a staging market id sent to production, or vice versa. Markets do not exist across environments. * The market was never created — you may have a placeholder id from documentation rather than a real one. * **Redemptions only:** the market exists but has no on-chain id yet (it hasn't been created on-chain), so it cannot be redeemed against. `POST /v1/redemptions` reports this as `MARKET_NOT_FOUND` — wait until the market is live on-chain. ## How to fix * Look up real market ids via `GET /v1/markets?status=active` (filterable by status). * Cache the id once you've found it; markets are immutable in their identity. * If you suspect the market should exist (e.g., you saw it in the webapp), confirm you're hitting the same environment. ## Related codes * [`MARKET_NOT_TRADEABLE`](/developer-docs/api-errors/MARKET_NOT_TRADEABLE) — market exists but is FROZEN/RESOLVED # MARKET_NOT_TRADEABLE Source: https://docs.kash.bot/developer-docs/api-errors/MARKET_NOT_TRADEABLE HTTP 409 — Market not tradeable **HTTP status:** 409 · **Title:** "Market not tradeable" ## When it fires The market exists but is in a state that doesn't accept trades — typically `FROZEN` (admin paused trading) or `RESOLVED` (outcome decided, only redemptions allowed). ## Why it happens * Market reached its `resolve_time` and froze automatically. * An admin froze the market manually (e.g., pending an oracle dispute). * The market was resolved between your quote call and your trade call. ## How to fix * Check the market's current `status` via `GET /v1/markets/:id` before retrying. * If the market was just resolved, your existing positions are still redeemable (handled automatically by the payout pipeline) — you don't need to do anything. * For freeze-then-resume scenarios, retry once the market reopens; subscribe to the `market.resumed` webhook (when streaming lands) to know exactly when. ## Related codes * [`MARKET_NOT_FOUND`](/developer-docs/api-errors/MARKET_NOT_FOUND), [`OUTCOME_INDEX_INVALID`](/developer-docs/api-errors/OUTCOME_INDEX_INVALID) # OUTCOME_INDEX_INVALID Source: https://docs.kash.bot/developer-docs/api-errors/OUTCOME_INDEX_INVALID HTTP 400 — Outcome index invalid **HTTP status:** 400 · **Title:** "Outcome index invalid" ## When it fires `outcomeIndex` is greater than or equal to the market's outcome count, or negative. ## Why it happens * You hard-coded `outcomeIndex: 1` (assuming a binary market) and pointed at a multi-outcome market. * Off-by-one: the field is **zero-indexed** — first outcome is `0`, not `1`. * A bug computed the index from a label without bounds-checking. ## How to fix * Look up the market via `GET /v1/markets/:id` and check `outcomes.length` — valid range is `[0, length - 1]`. * Map outcome labels to indices once when you fetch the market, then use the index throughout your code. ## Related codes * [`MARKET_NOT_FOUND`](/developer-docs/api-errors/MARKET_NOT_FOUND), [`MARKET_NOT_TRADEABLE`](/developer-docs/api-errors/MARKET_NOT_TRADEABLE), [`VALIDATION_FAILED`](/developer-docs/api-errors/VALIDATION_FAILED) # POSITION_NOT_CLAIMABLE Source: https://docs.kash.bot/developer-docs/api-errors/POSITION_NOT_CLAIMABLE HTTP 409 — Position not claimable **HTTP status:** 409 · **Title:** "Position not claimable" ## When it fires `POST /v1/redemptions` was called for a `(marketId, outcomeIndex)` that is not currently claimable for your key's user. Claimability is decided by the same authority the webapp uses, so an accepted claim is one the payout pipeline can always fulfil. ## Why it happens * **The outcome did not win.** On a resolved market, only the winning outcome(s) pay out; losing-outcome tokens keep a non-zero on-chain balance but are worthless. * **No refund for that outcome.** On a cancelled market, only outcomes with a non-zero cancel price are refundable. * **The market is not in self-service mode.** It resolved before self-service redemption was enabled and was paid out automatically. * **Already redeemed.** The position has already been paid out (on-chain) or has an in-flight payout. * **A previous claim failed.** An earlier claim for this exact position ended in a failed state. It still occupies the one request slot, so a new claim cannot be created through the API until an operator clears it — the `detail` says so. ## How to fix * Redeem the **winning** outcome index. On a cancelled market, redeem an outcome you actually hold that carries a refund. * If the market was auto-paid, your USDC is already settled — check `GET /v1/portfolio`. * If you have an **in-flight** (non-failed) claim, a repeat returns `200` with that original request rather than this error. * If a previous claim **failed**, contact support — the stuck request must be cleared before a new one can be made. ## Related codes * [`INSUFFICIENT_BALANCE`](/developer-docs/api-errors/INSUFFICIENT_BALANCE), [`REDEMPTIONS_NOT_ENABLED`](/developer-docs/api-errors/REDEMPTIONS_NOT_ENABLED) # RATE_LIMIT_EXCEEDED Source: https://docs.kash.bot/developer-docs/api-errors/RATE_LIMIT_EXCEEDED HTTP 429 — Too Many Requests **HTTP status:** 429 · **Title:** "Too Many Requests" ## When it fires Per-user (authenticated) or per-IP (anonymous reads) rate limit was exceeded. Limits are enforced as a sliding window in Redis. For authenticated requests, the limit is **tier-differentiated** — 60 req/min on `free`, 300 req/min on `developer`, custom (admin-set) on `enterprise` and `mm`. See [Rate limits](/developer-docs/rest-api/rate-limits) for the full matrix. ## Response headers Every 429 response carries: * `Retry-After: ` — wait at least this long before the next request * `X-RateLimit-Limit: ` — the limit that applied * `X-RateLimit-Remaining: 0` — confirms you're capped * `X-RateLimit-Reset: ` — when the window resets ## Why it happens * Burst traffic spike (e.g., a polling loop without backoff). * A retry storm — every consumer retried at the same instant after a transient failure. * Genuine sustained traffic above your tier's quota. ## How to fix * Honour `Retry-After`. The TS SDK does this automatically (`KashRateLimitError.retryAfterSeconds`). * Add jitter to retry timing so multiple consumers don't synchronise. * For polling, switch to webhooks — every poll is a wasted request. * If you consistently bump against the limit during normal operation, upgrade your tier — see [Rate limits](/developer-docs/rest-api/rate-limits) for the upgrade paths. The 60 → 300 req/min jump from `free` → `developer` is 5×, and `enterprise`/`mm` have no application-level cap. ## Related codes * [`RATE_LIMIT_UNAVAILABLE`](/developer-docs/api-errors/RATE_LIMIT_UNAVAILABLE) — 503; the rate-limit subsystem itself is temporarily unavailable. Distinct from `EXCEEDED`: `UNAVAILABLE` means we couldn't check your quota; `EXCEEDED` means we checked and you're over. * [`WEBHOOK_REPLAY_LIMIT_REACHED`](/developer-docs/api-errors/WEBHOOK_REPLAY_LIMIT_REACHED) — also 429, but specific to the webhook replay endpoint's amplification cap # RATE_LIMIT_UNAVAILABLE Source: https://docs.kash.bot/developer-docs/api-errors/RATE_LIMIT_UNAVAILABLE HTTP 503 — Rate limit subsystem unavailable **HTTP status:** 503 · **Title:** "Rate limit subsystem unavailable" · **Retry-After:** 1s ## When it fires The rate-limit subsystem (Redis-backed token bucket / sliding window) is temporarily unavailable, and the per-task circuit breaker is still in its CLOSED state — i.e., this is a transient Redis blip, not a sustained outage. The API fails CLOSED here because your rate limit IS your per-tier quota (a paid product feature). Serving traffic without enforcing the cap during a blip would silently leak quota to free-tier callers. The safe-for-business default is to refuse the request, surface a typed retryable error, and let your client retry. This is the **transient blip** counterpart to the sustained-outage [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) — same family, different policy: a transient blip in the limiter is fail-CLOSED (we know the limit must apply but can't check), while a sustained outage causes the circuit breaker to open and the API to fail-OPEN (serve traffic without quota enforcement, paged to ops). ## Why it happens * A momentary ElastiCache Redis network blip — typically clears in `<100ms`. * Pool acquisition contention under burst — a Lua call queued for a free connection past the per-call timeout. * A momentary failover during a Redis cluster maintenance window. If Redis stays unhealthy past the circuit breaker's failure threshold (10 consecutive failures), the breaker OPENs and requests start passing through fail-OPEN instead of returning 503. Ops gets paged on `kash_public_api_rate_limit_redis_circuit_state == 2`. ## How to fix * **Honour `Retry-After: 1`.** The TS SDK's default retry policy already does this — it auto-retries 5xx responses with exponential backoff, so most blips are invisible to your application code. If you've disabled SDK retries (`maxRetries: 0`), wire your own backoff loop. * **Don't treat this as quota exhaustion.** Distinct from [`RATE_LIMIT_EXCEEDED`](/developer-docs/api-errors/RATE_LIMIT_EXCEEDED) (429): `EXCEEDED` is "you're over your tier's cap"; `UNAVAILABLE` is "we couldn't check, so we won't risk leaking your cap." Your dashboards / billing UI should NOT surface `RATE_LIMIT_UNAVAILABLE` as a quota event. * If you see a sustained rate of these in your client logs (>1/min over several minutes), check `status.kash.bot` — that's the threshold where ops should already be paged. ## Response headers * `Retry-After: 1` — Stripe-style short retry; SDK respects it automatically. * `X-API-Version`, `X-Request-Id` — standard cross-cutting headers. * The `X-RateLimit-*` family is NOT emitted on a 503 (we don't have a quota answer to attach). ## Related codes * [`RATE_LIMIT_EXCEEDED`](/developer-docs/api-errors/RATE_LIMIT_EXCEEDED) — 429, you went over your quota. * [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) — 503, infrastructure-level dependency outage outside the rate-limit path. # REDEMPTIONS_NOT_ENABLED Source: https://docs.kash.bot/developer-docs/api-errors/REDEMPTIONS_NOT_ENABLED HTTP 404 — Redemptions not enabled **HTTP status:** 404 · **Title:** "Redemptions not enabled" ## When it fires `POST /v1/redemptions` is called while self-service redemption is not live on the platform. Resolved markets are paid out automatically (bulk fan-out) rather than by user-initiated claim, so there is nothing for the endpoint to do. ## Why it happens * Self-service redemption has not been rolled out to this environment yet (the default). Markets settle to automated payout and are paid without a claim. * The feature was rolled back platform-wide. This is distinct from a per-route **kill switch** (a temporary `503` operational lever): `REDEMPTIONS_NOT_ENABLED` means the capability is not enabled at all. ## How to fix * Nothing to do as an integrator — your winnings are still paid out automatically once the market resolves. Poll `GET /v1/portfolio` for the settled balance. * If you believe self-service redemption should be enabled for your environment, contact support. ## Related codes * [`POSITION_NOT_CLAIMABLE`](/developer-docs/api-errors/POSITION_NOT_CLAIMABLE) # REQUEST_SIGNATURE_INVALID Source: https://docs.kash.bot/developer-docs/api-errors/REQUEST_SIGNATURE_INVALID HTTP 401 — Request signature invalid **HTTP status:** 401 · **Title:** "Request signature invalid" ## When it fires `X-Kash-Signature` was present but did not verify against the API key plaintext over the canonical signing input `${ts}.${method}.${path}.${body}`. Signing is **opt-in**: requests without `X-Kash-Signature` skip this check entirely. Once present, it MUST verify. ## Why it happens * The body was modified in transit (a transparent proxy that re-encoded JSON, an HTTP client that renormalised whitespace). * The timestamp is outside the ±5 min tolerance (clock skew). * The signature was computed over the wrong input — common mistake: signing the URL instead of the path-only, or omitting the leading slash on `path`. * The wrong secret was used (e.g., the `webhook_secret` instead of the API key plaintext). ## How to fix * Canonical input format: `${unixMillis}.${UPPERCASE_METHOD}.${pathWithLeadingSlash}.${rawBody}` (raw body bytes, not re-serialised JSON). * Key the HMAC with the API key plaintext (the same value as `X-API-Key`). * Header format: `X-Kash-Signature: t=,v1=`. * Sync your clock — NTP. If you're consistently 6+ minutes off, fix the host clock. * Check the `signatureReason` extension field on the problem response — it pinpoints which check failed (`stale`, `mismatch`, `malformed`). See `apps/public-api/README.md` § Optional request body signing for the full reference. ## Related codes * [`API_KEY_MISSING`](/developer-docs/api-errors/API_KEY_MISSING) / [`API_KEY_INVALID`](/developer-docs/api-errors/API_KEY_INVALID) — earlier failure points # REQUEST_TIMEOUT Source: https://docs.kash.bot/developer-docs/api-errors/REQUEST_TIMEOUT HTTP 504 — Gateway timeout **HTTP status:** 504 · **Title:** "Gateway timeout" · **Retry-After:** 5s ## When it fires The per-request server timeout fired (default 30 s). Bounded by `bodyLimit` + Fastify timeout config to keep slow upstreams from blocking healthy traffic. ## Why it happens * An unusually slow downstream call (Postgres lock contention, RPC latency spike, etc.). * A request that does too much work in one call — most reads complete in `<100 ms`. * Network path degradation between you and our edge (rare but possible). ## How to fix * Retry once with backoff. Most timeouts are transient. * If you're consistently timing out on the same endpoint, narrow the request — pass `limit` parameters, fetch one resource at a time. * For long-running operations, use webhooks instead of waiting on the response. ## Related codes * [`DEPENDENCY_UNAVAILABLE`](./DEPENDENCY_UNAVAILABLE.md), [`INTERNAL_ERROR`](./INTERNAL_ERROR.md) # RESOURCE_NOT_FOUND Source: https://docs.kash.bot/developer-docs/api-errors/RESOURCE_NOT_FOUND HTTP 404 — Not Found **HTTP status:** 404 · **Title:** "Not Found" ## When it fires A request for a specific resource (trade, key, webhook event, etc.) didn't find a matching row that the caller is authorised to see. This is the generic catch-all 404. Specific resource types may have their own dedicated codes (e.g., [`MARKET_NOT_FOUND`](/developer-docs/api-errors/MARKET_NOT_FOUND)) where there's value in distinguishing the resource type to the caller. ## Why it happens * The id is wrong (typo, cross-environment confusion). * The resource exists but belongs to a different user — we return 404 (not 403) deliberately, so attackers can't probe for the existence of resources they don't own. * The resource was deleted. ## How to fix * Re-check the id (capitalisation, hyphens, environment). * If the resource should exist but you can't see it, confirm you're authenticated as the owner. * For webhook events: events are retained for 90 days; older events return 404. ## Related codes * [`MARKET_NOT_FOUND`](/developer-docs/api-errors/MARKET_NOT_FOUND) — specific case for markets # ROUTE_DISABLED Source: https://docs.kash.bot/developer-docs/api-errors/ROUTE_DISABLED HTTP 503 — Route disabled **HTTP status:** 503 · **Title:** "Route disabled" · **Retry-After:** 60s ## When it fires A single route is disabled by ops via a per-route kill switch. Distinct from the coarse [`API_TRADE_PROCESSING_HALTED`](/developer-docs/api-errors/API_TRADE_PROCESSING_HALTED) so SDK consumers can branch on "my entire trading capability is offline" vs "just one endpoint." The Problem detail's `extensions.flag` field carries the kill-switch name (e.g., `"api-quotes-read"`) so you can tell exactly which route is gated. ## Why it happens * Targeted rollback after a route-specific bug shipped. * Pre-deploy lockdown of one endpoint while the rest of the API stays live. * Capacity-driven shedding (rare — usually we'd raise rate limits first). ## How to fix * Honour `Retry-After`. The flag is binary; polling won't help. * Read the `extensions.flag` field — if it's an endpoint you don't actually need, ignore it; other endpoints are fine. * For mission-critical endpoints, contact support to understand the ETA. ## Related codes * [`API_TRADE_PROCESSING_HALTED`](/developer-docs/api-errors/API_TRADE_PROCESSING_HALTED) — coarser kill switch covering all trade writes * [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) — distinct: this is "ops chose to disable", that's "infra is broken" # SMART_ACCOUNT_NOT_PROVISIONED Source: https://docs.kash.bot/developer-docs/api-errors/SMART_ACCOUNT_NOT_PROVISIONED HTTP 409 — Smart account not provisioned **HTTP status:** 409 · **Title:** "Smart account not provisioned" ## When it fires The actor (user or organization) doesn't have a smart account row yet, so trades can't execute on-chain. ## Why it happens * New account that hasn't completed the wallet-creation step in the webapp. * An organization that's enabled the API but hasn't provisioned its own org-level smart account. * The smart-account worker is backlogged — provisioning is async. ## How to fix * For users: complete the wallet setup flow in the webapp (Settings → Wallet). Smart account provisioning is async — typically a few seconds. * For orgs: an admin needs to provision the org smart account via the org settings page. * If you provisioned recently and still see this error, retry after 30 s. If it persists past a minute, contact support with your `requestId`. ## Related codes * [`WALLET_DELEGATION_NOT_ENABLED`](/developer-docs/api-errors/WALLET_DELEGATION_NOT_ENABLED) — smart account exists but Privy delegation is off * [`INSUFFICIENT_BALANCE`](/developer-docs/api-errors/INSUFFICIENT_BALANCE) — smart account provisioned but no USDC # SPENDING_LIMIT_EXCEEDED Source: https://docs.kash.bot/developer-docs/api-errors/SPENDING_LIMIT_EXCEEDED HTTP 409 — Spending limit exceeded **HTTP status:** 409 · **Title:** "Spending limit exceeded" ## When it fires The trade would exceed one of the API key's spending caps: * `per_trade_limit_usdc` — single-trade maximum * `daily_spend_limit_usdc` — rolling 24-hour cumulative maximum across all trades The `detail` field specifies which cap fired and by how much. ## Why it happens * A bot iteration that computed a larger order than expected (e.g., signal scaling factor was off). * Cumulative drift over a busy day finally pushed a moderate trade over the daily cap. * The cap was lowered (by you or an admin) since you last designed your sizing logic. ## How to fix * Inspect your key's caps in Settings → API Keys → Edit. * For per-trade overruns: split the trade into smaller chunks under the cap. * For daily overruns: wait for the rolling window to clear, or upgrade your tier (see "How to raise the cap" below). * Programmatically: every key's caps are returned in the issuance response — cache them so your client knows the headroom. ## How to raise the cap The cap is set by your API key's tier. See [Rate limits](/developer-docs/rest-api/rate-limits) for the full tier matrix and rationale, then: * **`free` → `developer`** (10× headroom — $1k → $10k daily, $500 → $2.5k per-trade): email [support@kash.bot](mailto:support@kash.bot) for early access. Self-serve upgrades are planned. * **`developer` → `enterprise`** ($100k daily, $25k per-trade, custom rate limit): [contact sales](mailto:sales@kash.bot) — pricing is contractual. * **MM-tier counterparty terms** ($1M daily, $50k per-trade): [contact partnerships](mailto:partnerships@kash.bot) — KYB and ToS attestation required; this is a relationship, not a price tier. The caps are not punitive — they exist for blast-radius and AMM-stability reasons. When you outgrow your tier, the cap is the signal to upgrade, not a problem to work around. ## Related codes * [`AMOUNT_TOO_LARGE`](/developer-docs/api-errors/AMOUNT_TOO_LARGE) — platform-wide cap, separate from your key's caps * [`INSUFFICIENT_BALANCE`](/developer-docs/api-errors/INSUFFICIENT_BALANCE) — wallet balance too low (different failure surface) # TRADE_NOT_AWAITING_CONFIRMATION Source: https://docs.kash.bot/developer-docs/api-errors/TRADE_NOT_AWAITING_CONFIRMATION HTTP 409 — Trade not awaiting confirmation **HTTP status:** 409 · **Title:** "Trade not awaiting confirmation" ## When it fires `POST /v1/trades/:id/confirm` was called for a trade that's not in `pending_confirmation` status. ## Why it happens * The trade was already confirmed (you'll see this if you accidentally call confirm twice) — see also [`CONFIRMATION_TOKEN_USED`](/developer-docs/api-errors/CONFIRMATION_TOKEN_USED). * The trade was below your tier's high-value threshold and never required confirmation in the first place. * The trade has progressed past confirmation into `pending` / `executing` / a terminal state. * The confirmation window expired and the trade was auto-cancelled — see [`CONFIRMATION_EXPIRED`](/developer-docs/api-errors/CONFIRMATION_EXPIRED). ## How to fix * Check the trade's current `status` via `GET /v1/trades/:id`. The lifecycle is: `pending_confirmation` → `pending` → `executing` → `completed`/`failed`. * If the trade is `completed`, there's nothing more to do. * If the trade is `cancelled`, re-create it and confirm within the new window. ## Related codes * [`CONFIRMATION_EXPIRED`](/developer-docs/api-errors/CONFIRMATION_EXPIRED), [`CONFIRMATION_TOKEN_INVALID`](/developer-docs/api-errors/CONFIRMATION_TOKEN_INVALID), [`CONFIRMATION_TOKEN_USED`](/developer-docs/api-errors/CONFIRMATION_TOKEN_USED) # VALIDATION_FAILED Source: https://docs.kash.bot/developer-docs/api-errors/VALIDATION_FAILED HTTP 400 — Validation failed **HTTP status:** 400 · **Title:** "Validation failed" ## When it fires The request body failed Zod schema parse. One or more fields are missing, malformed, or out of range. ## Why it happens * Required field is missing or `null`. * Field has the wrong type (e.g., `amount` sent as a number instead of a string — we use string for arbitrary-precision USDC amounts). * Field violates a constraint (UUID format, enum value, min/max length). * An unknown extra field was sent — we accept extras (`.passthrough()`) so this is rarely the cause; if it is, you'll see it called out in `detail`. ## How to fix * The `detail` field carries the path of the first offending field (e.g., `body.outcomeIndex must be a non-negative integer`). * Cross-reference with the OpenAPI spec at `https://api.kash.bot/v1/openapi.json` — every endpoint's request body is fully typed there. * If you're using the TS SDK, you'd have caught this client-side via `KashValidationError` before the request went out. ## Example ```json theme={null} { "type": "https://docs.kash.bot/developer-docs/api-errors/VALIDATION_FAILED", "title": "Validation failed", "status": 400, "code": "VALIDATION_FAILED", "detail": "body.amount must be a string matching ^[0-9]+(\\.[0-9]+)?$", "instance": "/v1/trades", "requestId": "01HX-..." } ``` ## Related codes * [`AMOUNT_TOO_LARGE`](/developer-docs/api-errors/AMOUNT_TOO_LARGE) — specific case for `amount` exceeding the per-trade cap * [`OUTCOME_INDEX_INVALID`](/developer-docs/api-errors/OUTCOME_INDEX_INVALID) — specific case for invalid `outcomeIndex` # WALLET_DELEGATION_NOT_ENABLED Source: https://docs.kash.bot/developer-docs/api-errors/WALLET_DELEGATION_NOT_ENABLED HTTP 409 — Wallet delegation not enabled **HTTP status:** 409 · **Title:** "Wallet delegation not enabled" ## When it fires A smart account exists for the actor, but Privy server-side delegation is disabled. The platform can't sign user-operations on behalf of the user, so trades can't execute. We surface this at the API layer as a preflight check rather than failing at execution — better UX for the consumer. ## Why it happens * The user revoked delegation in Privy after onboarding. * A Privy app config change disabled delegation for a class of users. * An incomplete onboarding flow that skipped the delegation grant step. ## How to fix * The user must re-grant delegation via the webapp (Settings → Wallet → Re-enable). One-click; uses the existing Privy session. * For orgs, the org admin re-grants on behalf of the org wallet. * If the issue is platform-wide (multiple users), check the Privy app config or the status page. ## Related codes * [`SMART_ACCOUNT_NOT_PROVISIONED`](/developer-docs/api-errors/SMART_ACCOUNT_NOT_PROVISIONED) — earlier failure point # WEBHOOK_REPLAY_LIMIT_REACHED Source: https://docs.kash.bot/developer-docs/api-errors/WEBHOOK_REPLAY_LIMIT_REACHED HTTP 429 — Replay limit reached **HTTP status:** 429 · **Title:** "Replay limit reached" ## When it fires `POST /v1/webhooks/events/{id}/redeliver` was called for an event that has already been replayed up to its cap (default 5). ## Why it happens * A bug in the consumer kept calling the redeliver endpoint in a loop instead of fixing the underlying delivery problem. * An automation re-replayed without checking past attempts. * A genuinely persistent need to replay an event many times — uncommon and usually signals an upstream issue. The cap exists to stop a compromised key from looping the replay endpoint to flood its own customer endpoint or our delivery worker. ## How to fix * Stop calling the redeliver endpoint for this event id. The cap is per-event, so other events still replay normally. * Fix the underlying delivery failure first — check the event's delivery status via `GET /v1/trades/:id` (the `webhookDelivery` field) or via `kash-admin webhooks trace --event-id ` (operator command). * If you legitimately need more replays for one event (e.g., recovering from a long endpoint outage), contact support — we can clear the counter manually. ## Related codes * [`RATE_LIMIT_EXCEEDED`](/developer-docs/api-errors/RATE_LIMIT_EXCEEDED) — general per-key rate limit # WEBHOOK_SECRET_ROTATION_COOLDOWN Source: https://docs.kash.bot/developer-docs/api-errors/WEBHOOK_SECRET_ROTATION_COOLDOWN HTTP 429 — Rotation cooldown active **HTTP status:** 429 · **Title:** "Rotation cooldown active" ## When it fires `POST /v1/auth/api-keys/me/webhook-secret/rotate` was called within the 60-second cooldown that follows a previous successful rotation. ## Why the cooldown exists This is the most important error to read carefully — the cooldown is not a rate-limit, it is a **rollback-safety** guard. Every successful rotation moves the previous secret into `webhook_secret_previous` so operations can roll back within 7 days if the rotation breaks the customer's verifier. If you rotate twice in quick succession, the second rotation overwrites that rollback slot with the FIRST rotation's brand-new secret — a secret you may have never actually received in your response (e.g., the first POST timed out, your HTTP client retried, and the new plaintext from the first attempt was lost in flight). Rolling back later would restore a secret no verifier was ever configured for. The cooldown forces this dangerous case into a visible 429 instead of silently corrupting the rollback guarantee. ## How to fix * **If you successfully captured the new secret from the previous rotation:** wait for the `Retry-After` window to expire and call again. The cooldown is per-key, so other keys are unaffected. * **If you did NOT capture the new secret from the previous rotation** (network timeout, lost response, dropped connection): **do not retry**. Contact support so we can rotate via an operator path that preserves the rollback chain. Re-rotating yourself would replace `webhook_secret_previous` with the secret you never received, breaking the only recovery path. ## Response headers * `Retry-After: ` — how long to wait before re-attempting (computed from the prior rotation's timestamp). ## Related codes * [`RATE_LIMIT_EXCEEDED`](/developer-docs/api-errors/RATE_LIMIT_EXCEEDED) — generic per-user/per-IP rate limit (volume-based, not safety-based) * [`DEPENDENCY_UNAVAILABLE`](/developer-docs/api-errors/DEPENDENCY_UNAVAILABLE) — Postgres or downstream temporarily unreachable; safe to retry # Error catalogue Source: https://docs.kash.bot/developer-docs/api-errors/overview Stable, RFC 7807-shaped problem responses. Every code has its own page; the `type` URL on every response points back here. The Kash REST API returns errors as [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) Problem Details JSON. Every response carries a stable `code` and a `type` URL that resolves to the error's documentation page on this site: ```json theme={null} { "type": "https://docs.kash.bot/developer-docs/api-errors/MARKET_NOT_TRADEABLE", "title": "Market not tradeable", "status": 409, "code": "MARKET_NOT_TRADEABLE", "detail": "Market 0xabc... is FROZEN; trades are rejected until the resolution period closes.", "instance": "/v1/trades", "requestId": "01HQGY8K2N3X4Z5C6D7E8F9G0H" } ``` The `code` field is **the** stable identifier — branch on it from your client. The `type` URL is for humans (and AI agents) following the link. The `detail` string is a human-readable explanation that may evolve over time; **don't** parse it. ## How to read this catalogue Each per-code page is the canonical reference for what triggers the error, why it happens, and how to recover. Pages are short by design — when something fires, you should be able to read the relevant page in under a minute and know your next move. The catalogue is **single-sourced** from the public-API service's markdown at `apps/public-api/docs/api-errors/.md` in the Kash monorepo. Mintlify pages here are auto-generated from those source files at every release; the contract test `tests/unit/errors/documentation-url.test.ts` enforces that every runtime `ErrorCode` has a corresponding source-of-truth page. ## Stability promise The `code` field is part of the public-API contract. We follow these rules: * **Adding a new `code`** is non-breaking — your client should fall through to a generic error handler for any code it doesn't recognize. * **Renaming a `code`** is breaking and requires a version bump. * **Repurposing a `code`** (changing what conditions cause it to fire) is breaking and requires a version bump. * **Changing the HTTP status** for an existing `code` is breaking. The `title` field is also stable — it appears in customer-facing logs and dashboards. The `detail` string is **not** stable; it may be reworded for clarity in any release. The `requestId` and `instance` fields are present on every error response and are the fastest path to support — quote them when filing an issue. ## Branching on errors ```ts theme={null} import { KashAuthenticationError, KashRateLimitError, KashConflictError, KashError, } from '@kashdao/sdk'; try { await kash.trades.create({ ... }); } catch (err) { if (err instanceof KashAuthenticationError) { // Auth-tier failures: API_KEY_MISSING, API_KEY_INVALID, API_KEY_EXPIRED, ... return refreshKey(); } if (err instanceof KashRateLimitError) { // RATE_LIMIT_EXCEEDED — back off until err.retryAfterSeconds return scheduleRetry(err.retryAfterSeconds); } if (err instanceof KashConflictError && err.code === 'CONFIRMATION_EXPIRED') { // Specific conflict; recreate the trade return retryTradeCreation(); } throw err; // pass through everything else } ``` The TS SDK maps each catalogue code to a typed error class; the Python SDK does the same with the protocol-level error hierarchy. See the per-SDK pages for the exact mapping. # account Source: https://docs.kash.bot/developer-docs/cli/account Read-only account surfaces (usage telemetry, etc.). Read-only account surfaces (usage telemetry, etc.). > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash account --help` — this page is the structured reference. ## Usage ```bash theme={null} kash account [options] ``` ## Subcommands ### `kash account usage` Show per-key telemetry summary (24h / 7d / 30d windows). # auth Source: https://docs.kash.bot/developer-docs/cli/auth Manage local API credentials. Manage local API credentials. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash auth --help` — this page is the structured reference. ## Usage ```bash theme={null} kash auth [options] ``` ## Subcommands ### `kash auth set-key` Store an API key in \~/.kash/config.json (mode 0600). **Arguments** * `key` (optional) — API key starting with "kash\_" (omit to read from stdin or prompt) **Options** | Flag | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--from-stdin` | read the key from stdin (preferred for secret-store integration) | | `--activate` | also switch the active profile to the one being written (default: leave the active profile unchanged — `kash auth set-key --profile staging` updates 'staging' but doesn't make it active; pass --activate or run `kash config use staging` separately) | ### `kash auth status` Show locally-configured credentials. Does not call the API. ### `kash auth logout` Remove the stored API key from \~/.kash/config.json. # completion Source: https://docs.kash.bot/developer-docs/cli/completion Shell tab-completion utilities. Shell tab-completion utilities. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash completion --help` — this page is the structured reference. ## Usage ```bash theme={null} kash completion [options] ``` ## Subcommands ### `kash completion install` Install bash/zsh/fish completion to your shell config. ### `kash completion uninstall` Remove kash completions from your shell config. # config Source: https://docs.kash.bot/developer-docs/cli/config Inspect and edit ~/.kash/config.json (multi-profile). Inspect and edit \~/.kash/config.json (multi-profile). > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash config --help` — this page is the structured reference. ## Usage ```bash theme={null} kash config [options] ``` ## Subcommands ### `kash config show` Print the resolved CLI configuration. ### `kash config set` Set a single config field. **Arguments** * `key` — top-level key OR `customChain.` / `customChain.smartAccount.` dot-path * `value` — value to set ### `kash config profiles` List configured profiles in the config file. ### `kash config use` Switch the active profile (writes currentProfile to the config file). **Arguments** * `profile` — profile name **Options** | Flag | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `--allow-new` | permit switching to a profile that does not yet exist (you must populate it via `kash auth set-key --profile ` afterwards) | ### `kash config remove` Delete a named profile from the config file. **Arguments** * `profile` — profile name to remove ### `kash config reset` Delete \~/.kash/config.json. **Options** | Flag | Description | | ----------- | --------------------------------- | | `-y, --yes` | skip the interactive confirmation | ### `kash config export` Dump the entire multi-profile config (API keys redacted by default). **Options** | Flag | Description | | ------------------- | ---------------------------------------------------- | | `-o, --out ` | write to a file instead of stdout (mode 0600) | | `--include-secrets` | include raw API keys in the export (round-trippable) | ### `kash config import` Merge a `kash config export` bundle into the local config (mode 0600). **Arguments** * `file` (optional) — path to a JSON bundle (omit to read from stdin) **Options** | Flag | Description | | ---------------- | ----------------------------------------------- | | `--no-overwrite` | skip profiles whose name already exists locally | | `--dry-run` | preview the merge without writing to disk | # docs Source: https://docs.kash.bot/developer-docs/cli/docs Emit the full command tree as JSON (or summary in human mode). Emit the full command tree as JSON (or summary in human mode). > Generated from `kash docs --json`. Run `kash docs --help` for full usage and examples. ## Usage ```bash theme={null} kash docs [options] ``` # eoa Source: https://docs.kash.bot/developer-docs/cli/eoa Self-orchestrated direct-to-chain operations using a vanilla EOA (no smart account). Like every Kash entry point, this path is non-custodial. Self-orchestrated direct-to-chain operations using a vanilla EOA (no smart account). Like every Kash entry point, this path is non-custodial. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash eoa --help` — this page is the structured reference. ## Usage ```bash theme={null} kash eoa [options] ``` ## Subcommands ### `kash eoa balance` Read on-chain USDC + native gas balances for the EOA (defaults to signer's address). **Arguments** * `account` (optional) — address to read (default: signer's ownerAddress) ### `kash eoa market` Read on-chain market state (reserves, supplies, weights, derived probabilities). **Arguments** * `address` — market contract address (0x-prefixed) ### `kash eoa quote` On-chain price quote for buying or selling an outcome. **Arguments** * `address` — market contract address **Options** | Flag | Description | | ------------------------ | -------------------------------------- | | `-s, --side ` | buy \| sell | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC for buy / outcome tokens for sell | ### `kash eoa position` Read on-chain outcome-token holdings (ERC-1155) for a market. **Arguments** * `market` — market contract address (0x-prefixed) * `account` (optional) — address whose holdings to read (default: signer's) ### `kash eoa allowance` Read the on-chain USDC allowance from `account` to `spender`. **Arguments** * `spender` — spender contract address * `account` (optional) — owner address (default: signer's) ### `kash eoa fees` EIP-1559 fee estimate for the configured chain. **Options** | Flag | Description | | --------------------------- | --------------------------------------------------------- | | `--blocks ` | number of recent blocks to sample (default 4) | | `--percentile ` | priority-fee percentile per block (1-99, default 50) | | `--base-multiplier ` | multiplier on predicted next-block base fee (default 2.0) | | `--priority-floor-gwei ` | floor for maxPriorityFeePerGas in gwei (default 1) | ### `kash eoa trade` Direct-mode trade execution (vanilla EIP-1559 tx, signed locally). ### `kash eoa trade buy` One-shot BUY (EIP-1559 tx, signed locally and submitted to chain). **Arguments** * `market` — market contract address **Options** | Flag | Description | | ----------------------- | ------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC to spend (decimal) | | `--slippage-bps ` | slippage tolerance in bps (default 50) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit | | `--no-simulate` | skip eth\_call preflight | | `--no-wait` | fire-and-forget: return tx hash without waiting for inclusion | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | ### `kash eoa trade sell` One-shot SELL. **Arguments** * `market` — market contract address **Options** | Flag | Description | | ----------------------- | -------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-t, --tokens ` | outcome tokens to sell (WAD decimal) | | `--slippage-bps ` | slippage tolerance in bps (default 50) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit | | `--no-simulate` | skip eth\_call preflight | | `--no-wait` | fire-and-forget | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | ### `kash eoa trade close` Sell the entire EOA balance for an outcome. **Arguments** * `market` — market contract address **Options** | Flag | Description | | ----------------------- | -------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `--slippage-bps ` | slippage tolerance in bps (default 50) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit | | `--no-simulate` | skip eth\_call preflight | | `--no-wait` | fire-and-forget | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | ### `kash eoa trade approve` USDC approval — required once before the first BUY. **Arguments** * `spender` — spender contract address (typically a Market) **Options** | Flag | Description | | ----------------------- | -------------------------------------------- | | `-a, --amount ` | atomic-USDC amount (default unlimited) | | `--dry-run` | prepare + simulate but DO NOT sign or submit | | `--no-simulate` | skip eth\_call preflight | | `--no-wait` | fire-and-forget | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | # explain Source: https://docs.kash.bot/developer-docs/cli/explain Look up one or more error codes and their recommended recovery steps. Look up one or more error codes and their recommended recovery steps. > Generated from `kash docs --json`. Run `kash explain --help` for full usage and examples. ## Usage ```bash theme={null} kash explain [codes] [options] ``` **Arguments** * `codes` (optional) (variadic) — error codes to explain (omit to list every known code) # health Source: https://docs.kash.bot/developer-docs/cli/health Check connectivity to the Kash API. Exits 1 when not ok. Honors --timeout-ms (default 5000). Check connectivity to the Kash API. Exits 1 when not ok. Honors --timeout-ms (default 5000). > Generated from `kash docs --json`. Run `kash health --help` for full usage and examples. ## Usage ```bash theme={null} kash health [options] ``` # markets Source: https://docs.kash.bot/developer-docs/cli/markets List and inspect prediction markets. List and inspect prediction markets. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash markets --help` — this page is the structured reference. ## Usage ```bash theme={null} kash markets [options] ``` ## Subcommands ### `kash markets list` List markets. **Options** | Flag | Description | | ----------------------- | ----------------------------------------------------------------------------- | | `-s, --status ` | filter by status (UNSEEDED \| ACTIVE \| RESOLVED) | | `-l, --limit ` | page size (1-100) | | `-c, --cursor ` | pagination cursor returned by a previous call | | `-a, --all` | walk every page (use with --json for export) | | `--ndjson` | stream results as newline-delimited JSON (one record per line); implies --all | ### `kash markets get` Fetch a single market by id. **Arguments** * `id` — market UUID **Options** | Flag | Description | | ----------------- | -------------------------------------------------------------------------------------------------------------------------- | | `--fields ` | comma-separated dot-paths to project on JSON output (e.g. id,title,outcomes.label) — applied only with `--json`/`--ndjson` | ```bash theme={null} kash markets get 9f0b... --fields id,status,resolvedOutcomeIndex --json --quiet ``` ### `kash markets predictions` Recent trades against a market (cursor-paginated, newest first). **Arguments** * `marketId` — market UUID **Options** | Flag | Description | | ----------------------- | ----------------------------------------------------------------------------- | | `-s, --side ` | filter to a single side: buy \| sell | | `-o, --outcome ` | filter to a single outcome index (0-based) | | `-l, --limit ` | page size (1-100) | | `-c, --cursor ` | pagination cursor returned by a previous call | | `-a, --all` | walk every page (use with --json for export) | | `--ndjson` | stream results as newline-delimited JSON (one record per line); implies --all | # CLI Source: https://docs.kash.bot/developer-docs/cli/overview kash — the command-line interface. Same primitives as the SDK, designed for scripting. The `@kashdao/cli` package ships the `kash` binary — a scripting-friendly command-line client over the REST API. Use it for one-off queries, shell scripts, CI jobs, agent integrations, and exploratory debugging. ```bash theme={null} npm install -g @kashdao/cli # or: pnpm add -g @kashdao/cli # or: brew install kashdao/tap/kash # (Homebrew tap, coming soon) ``` Verify install: ```bash theme={null} kash --version kash --help ``` ## Configure ```bash theme={null} # Interactive setup — walks you through API key, base URL, default chain kash setup # Or set explicitly kash auth set-key kash_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx kash config set baseUrl https://api.kash.bot/v1 # Read it back kash config show ``` Config lives at `~/.kash/config.json` (written mode 0600). For CI use the env vars instead: ```bash theme={null} export KASH_API_KEY="kash_live_..." export KASH_BASE_URL="https://api.kash.bot/v1" ``` Env vars override on-disk config. ## Common commands ```bash theme={null} # Markets kash markets list --status ACTIVE --limit 10 kash markets get # Quotes (top-level command; requires the markets:quote scope) kash quote buy --outcome 0 --amount 100 kash quote sell --outcome 0 --amount 50 # Trades kash trade buy --outcome 0 --amount 100 --wait kash trade sell --outcome 0 --amount 50 kash trade confirm kash trade list --status completed --limit 25 kash trade status kash trade status --poll # poll until terminal state # Portfolio kash portfolio show kash portfolio positions --limit 50 # Account telemetry kash account usage # Webhooks kash webhooks list --status failed --limit 20 kash webhooks redeliver kash webhooks rotate-secret kash webhooks verify --signature "t=…,v1=…" --body-file payload.json --secret $SECRET kash webhooks replay payload.json --target http://localhost:3000/webhooks/kash --secret $SECRET # Tracing (top-level command) kash trace # get it from: kash trade status --json | jq -r .correlationId # Errors kash explain INSUFFICIENT_SCOPE # local lookup; works offline kash explain RATE_LIMIT_EXCEEDED --json ``` Every command supports `--help` for full usage. ## Output modes Two output shapes: * **Human (default)** — colored, tabular, designed for interactive reading. * **JSON (`--json`)** — stable envelope, designed for piping into `jq`, scripts, or agent contexts. ```bash theme={null} kash account usage --json | jq '.trades["24h"].successRate' # 0.9166666666666667 ``` The JSON envelope is SemVer-stable. Schema: ```json theme={null} { "ok": true, "data": { ... } } ``` On error: ```json theme={null} { "ok": false, "error": { "code": "INSUFFICIENT_SCOPE", "message": "...", "recoverable": false, "suggestion": "Re-issue the key with the trades:write scope.", "docsUrl": "https://docs.kash.bot/developer-docs/api-errors/INSUFFICIENT_SCOPE", "requestId": "req_...", "actions": [ { "type": "open_url", "url": "https://kash.bot/settings/api-keys", "description": "..." } ] } } ``` `actions` is the agent-friendly recovery path. Each `type` is a stable enum (`run_command`, `set_env`, `wait_and_retry`, `open_url`, `check_input`) — agents branch on it without parsing prose. ## Exit codes | Code | Meaning | | ---- | -------------------------------------------------- | | `0` | Success. | | `1` | Generic error (validation, server, network, etc.). | | `2` | Auth failure (missing/invalid key, missing scope). | Rate-limit errors exit `1` (recoverable, but the script should fail loudly). The `recoverable` boolean and `retryAfterMs` field tell agents how to handle it. ## Quiet mode Suppress the human-readable preamble when scripting: ```bash theme={null} kash account usage --json --quiet | jq '...' ``` Without `--quiet`, the JSON output is preceded by a one-line summary suitable for terminal output. With `--quiet`, you get only the JSON envelope. ## Tab completion ```bash theme={null} kash completion bash > /etc/bash_completion.d/kash kash completion zsh > /usr/local/share/zsh/site-functions/_kash kash completion fish > ~/.config/fish/completions/kash.fish ``` ## Config import / export Useful for moving between machines or sharing CI templates: ```bash theme={null} # Export current config (omits the API key by default — opt in with --include-secrets) kash config export > ~/kash-config.json # Import on another machine kash config import ~/kash-config.json ``` ## Retry behaviour The CLI retries transient failures (network errors, 429, 5xx) with exponential backoff and jitter — same as the SDK. Use the global `--max-retries ` flag (0–10) to override the retry budget for a single invocation — `--max-retries 0` disables retries entirely. ## Direct (self-orchestrated) mode Some commands have a direct mode that bypasses the REST API and signs transactions directly with a local key, your own RPC, and your own bundler. Both modes are non-custodial — Kash never holds keys or funds either way; this one just removes the platform-side orchestration so the entire execution stack runs on your infra. See `kash protocol --help`. ## Source & changelog * npm: [`@kashdao/cli`](https://www.npmjs.com/package/@kashdao/cli) * GitHub (public mirror): [`KashDAO/cli`](https://github.com/KashDAO/cli) * Changelog: [`CHANGELOG.md`](https://github.com/KashDAO/cli/blob/main/CHANGELOG.md) * Issues / discussions: [github.com/KashDAO/cli/issues](https://github.com/KashDAO/cli/issues) ## Next The library version of the same primitives, for in-process integration. The full live spec at `/v1/docs`. # portfolio Source: https://docs.kash.bot/developer-docs/cli/portfolio View your portfolio. View your portfolio. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash portfolio --help` — this page is the structured reference. ## Usage ```bash theme={null} kash portfolio [options] ``` ## Subcommands ### `kash portfolio show` Show the aggregate portfolio summary. ### `kash portfolio positions` List active positions. **Options** | Flag | Description | | ------------------- | ------------------- | | `-m, --market ` | filter by market id | # protocol Source: https://docs.kash.bot/developer-docs/cli/protocol Self-orchestrated direct-to-chain operations (uses @kashdao/protocol-sdk). Like the default Kash-orchestrated mode, this path is non-custodial — the difference is who builds and submits trades. Self-orchestrated direct-to-chain operations (uses @kashdao/protocol-sdk). Like the default Kash-orchestrated mode, this path is non-custodial — the difference is who builds and submits trades. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash protocol --help` — this page is the structured reference. ## Usage ```bash theme={null} kash protocol [options] ``` ## Subcommands ### `kash protocol balance` Read on-chain USDC + gas balance for a smart account (defaults to the profile's). **Arguments** * `account` (optional) — smart-account address (defaults to the active profile's smartAccount) ### `kash protocol market` Read on-chain market state (reserves, supplies, weights, derived probabilities). **Arguments** * `address` — market contract address (0x-prefixed) ### `kash protocol quote` On-chain price quote for buying or selling an outcome. **Arguments** * `address` — market contract address (0x-prefixed) **Options** | Flag | Description | | ------------------------ | ------------------------------------------------------------------------------------- | | `-s, --side ` | trade side: buy or sell | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | amount: USDC decimal for buy (e.g. "10"), outcome-token decimal for sell (e.g. "1.5") | ### `kash protocol position` Read on-chain outcome-token holdings (ERC-1155) for a market. **Arguments** * `market` — market contract address (0x-prefixed) * `account` (optional) — EOA or smart-account address whose holdings to read (default: profile's smartAccount) ### `kash protocol allowance` Read the on-chain USDC allowance from `account` to `spender`. **Arguments** * `spender` — spender contract address (e.g. a market) — 0x-prefixed * `account` (optional) — owner address (default: profile's smartAccount) ### `kash protocol smart-account` Smart-account address derivation and deployment-status checks. ### `kash protocol smart-account compute` Derive the deterministic smart-account address for an EOA owner. **Options** | Flag | Description | | ----------------------- | --------------------------------------------------- | | `-o, --owner
` | EOA owner address (0x-prefixed) | | `-s, --salt ` | optional salt as a non-negative integer (default 0) | ### `kash protocol smart-account is-deployed` Check whether a smart account has bytecode (i.e. has been deployed on-chain). **Arguments** * `address` (optional) — smart account address (default: profile's smartAccount) ### `kash protocol fees` EIP-1559 fee estimate for the configured chain (uses eth\_feeHistory). **Options** | Flag | Description | | --------------------------- | --------------------------------------------------------------------------------------------- | | `--blocks ` | number of recent blocks to sample (default 4) | | `--percentile ` | priority-fee percentile to take from each sampled block (1-99, default 50) | | `--base-multiplier ` | multiplier applied to predicted next-block base fee (default 2.0; raise for congested chains) | | `--priority-floor-gwei ` | floor for maxPriorityFeePerGas in gwei (default 1) | ### `kash protocol token-id` Compute the ERC-1155 token id for a (marketId, outcomeIndex) pair (offline; no RPC). **Options** | Flag | Description | | -------------------------- | ------------------------------------------------------- | | `-m, --market-id ` | on-chain numeric market id (decimal or 0x-prefixed hex) | | `-o, --outcome ` | outcome index (0-255) | ### `kash protocol decode-revert` Decode raw revert data into (name, args) using the Market + EntryPoint ABIs. **Arguments** * `data` — raw revert data (0x-prefixed hex) ### `kash protocol trade` Direct-mode trade execution (UserOp signed locally, submitted via bundler). ### `kash protocol trade buy` One-shot BUY: prepare → simulate → sign → submit (and wait by default). **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ------------------------ | ---------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC to spend (decimal — e.g. "10" or "12.50") | | `--slippage-bps ` | slippage tolerance in basis points (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit; print the UserOp | | `--no-simulate` | skip the eth\_call preflight (faster, riskier) | | `--no-wait` | fire-and-forget: return userOpHash without waiting for inclusion | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | | `--wait-interval-ms ` | receipt poll interval (default 1500) | ### `kash protocol trade sell` One-shot SELL: prepare → simulate → sign → submit (and wait by default). **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ------------------------ | ---------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-t, --tokens ` | outcome tokens to sell (decimal — WAD precision) | | `--slippage-bps ` | slippage tolerance in basis points (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit; print the UserOp | | `--no-simulate` | skip the eth\_call preflight (faster, riskier) | | `--no-wait` | fire-and-forget: return userOpHash without waiting for inclusion | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | | `--wait-interval-ms ` | receipt poll interval (default 1500) | ### `kash protocol trade close` Sell the entire SA balance for an outcome (one-shot prepare → submit). **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ------------------------ | ---------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `--slippage-bps ` | slippage tolerance in basis points (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--dry-run` | prepare + simulate but DO NOT sign or submit; print the UserOp | | `--no-simulate` | skip the eth\_call preflight (faster, riskier) | | `--no-wait` | fire-and-forget: return userOpHash without waiting for inclusion | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | | `--wait-interval-ms ` | receipt poll interval (default 1500) | ### `kash protocol trade approve` USDC approval — required once before the first BUY. **Arguments** * `spender` — spender contract address (typically a Market) — 0x-prefixed **Options** | Flag | Description | | ------------------------ | ---------------------------------------------------------------------------- | | `-a, --amount ` | atomic-USDC amount to approve (decimal); default is unlimited (MAX\_UINT256) | | `--dry-run` | prepare + simulate but DO NOT sign or submit; print the UserOp | | `--no-simulate` | skip the eth\_call preflight (faster, riskier) | | `--no-wait` | fire-and-forget: return userOpHash without waiting for inclusion | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | | `--wait-interval-ms ` | receipt poll interval (default 1500) | ### `kash protocol userop` Granular UserOp lifecycle: build, simulate, submit, hash, receipt, wait. ### `kash protocol userop build` Build (prepare) a fully-populated unsigned UserOp ready for offline signing. ### `kash protocol userop build buy` Build a fully-populated unsigned BUY UserOp ready for offline signing. **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ----------------------- | ---------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC to spend (decimal) | | `--slippage-bps ` | slippage tolerance in bps (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--out ` | write the UserOp envelope to a file (default stdout) | | `--no-simulate` | skip eth\_call preflight in `prepare` | ### `kash protocol userop build sell` Build a fully-populated unsigned SELL UserOp ready for offline signing. **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ----------------------- | ---------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-t, --tokens ` | outcome tokens to sell (WAD decimal) | | `--slippage-bps ` | slippage tolerance in bps (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--out ` | write the UserOp envelope to a file (default stdout) | | `--no-simulate` | skip eth\_call preflight in `prepare` | ### `kash protocol userop build close` Build a fully-populated unsigned UserOp that closes a position (full balance). **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ----------------------- | ---------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `--slippage-bps ` | slippage tolerance in bps (default 50 = 0.5%) | | `--deadline-sec ` | unix-seconds deadline (default now + 5min) | | `--out ` | write the UserOp envelope to a file (default stdout) | | `--no-simulate` | skip eth\_call preflight in `prepare` | ### `kash protocol userop build approve` Build a fully-populated unsigned approve UserOp ready for offline signing. **Arguments** * `spender` — spender contract address (typically a Market) — 0x-prefixed **Options** | Flag | Description | | --------------------- | -------------------------------------------------------------- | | `-a, --amount ` | atomic-USDC amount (decimal); default unlimited (MAX\_UINT256) | | `--out ` | write the UserOp envelope to a file (default stdout) | | `--no-simulate` | skip eth\_call preflight in `prepare` | ### `kash protocol userop simulate` Simulate (eth\_call) an UnsignedUserOp from a file or stdin. **Arguments** * `file` (optional) — path to a UserOp JSON file (omit or pass "-" for stdin) ### `kash protocol userop submit` Submit a SignedUserOp from a file or stdin to the configured bundler. **Arguments** * `file` (optional) — path to a SignedUserOp JSON file (omit or pass "-" for stdin) **Options** | Flag | Description | | ------------------------ | ----------------------------------------------------------- | | `--skip-staleness-check` | bypass the EIP-191 staleness check (for typed-data signers) | | `--wait` | wait for receipt after submitting | | `--wait-timeout-ms ` | cap on the receipt wait (default 60000) | | `--wait-interval-ms ` | receipt poll interval (default 1500) | ### `kash protocol userop hash` Recompute the canonical EIP-4337 v0.7 hash for a UnsignedUserOp. **Arguments** * `file` (optional) — path to a UserOp JSON file (omit or pass "-" for stdin) ### `kash protocol userop receipt` Fetch the bundler receipt for a UserOp hash (null if not yet included). **Arguments** * `hash` — UserOp hash (0x-prefixed, 32 bytes) ### `kash protocol userop wait` Wait for a UserOp to be included; polls with exponential backoff. **Arguments** * `hash` — UserOp hash (0x-prefixed, 32 bytes) **Options** | Flag | Description | | ------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `--wait-timeout-ms, --timeout-ms ` | total time budget across polls (default 60000) — distinct from the global --timeout-ms (per-HTTP-request) | | `--interval-ms ` | poll interval (default 1500) | ### `kash protocol watch` Subscribe to on-chain trade events for a market (NDJSON stream). **Arguments** * `market` — market contract address (0x-prefixed) **Options** | Flag | Description | | ------------------ | ---------------------------------------------- | | `--max-events ` | exit cleanly after observing N events | | `--timeout-ms ` | exit cleanly after this wall-clock budget (ms) | # quote Source: https://docs.kash.bot/developer-docs/cli/quote Get on-chain AMM price quotes (requires `markets:quote` scope). Get on-chain AMM price quotes (requires `markets:quote` scope). > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash quote --help` — this page is the structured reference. ## Usage ```bash theme={null} kash quote [options] ``` ## Subcommands ### `kash quote buy` Quote a buy of USDC into an outcome. **Arguments** * `marketId` — market UUID **Options** | Flag | Description | | ----------------------- | --------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC to spend (decimal, e.g. "10" or "12.50") | ### `kash quote sell` Quote a sell of outcome tokens back into USDC. **Arguments** * `marketId` — market UUID **Options** | Flag | Description | | ----------------------- | -------------------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-t, --tokens ` | outcome tokens to surrender (decimal, e.g. "1.5") — quote-side input shape | # schema Source: https://docs.kash.bot/developer-docs/cli/schema Emit JSON Schema for the SDK request/response shapes. Emit JSON Schema for the SDK request/response shapes. > Generated from `kash docs --json`. Run `kash schema --help` for full usage and examples. ## Usage ```bash theme={null} kash schema [name] [options] ``` **Arguments** * `name` (optional) — optional schema name; omit to list every available schema (one of: CliConfigEnvelope, CliErrorAction, CliErrorEnvelope, ConfirmTradeBody, ConfirmTradeResponse, CreateTradeAcceptedResponse, CreateTradeBody, GetMarketResponse, GetTraceResponse, GetTradeResponse, HealthResult, ListMarketsResponse, ListTradesResponse, ListWebhookEventsResponse, MarketResource, Pagination, PortfolioSummary, PositionResource, PositionsResponse, ProblemDetails, ProtocolBalanceEnvelope, ProtocolMarketEnvelope, ProtocolQuoteEnvelope, QuoteBuyDetail, QuoteMarketSummary, QuoteResponse, QuoteSellDetail, RedeliverWebhookEvent, RedeliverWebhookResponse, RotateWebhookSecretResponse, TraceEvent, TraceEventData, TraceResource, TradeDryRunEnvelope, TradeResource, VersionManifest, WebhookEventResource) ## Options | Flag | Description | | -------- | -------------------------------------------------------------------------------------------------------------------- | | `--list` | emit only the schema names (cheap probe: skips the zod-to-json-schema conversion that the full --json catalog needs) | # setup Source: https://docs.kash.bot/developer-docs/cli/setup Interactive first-run wizard: configure an API key, verify, install completion. Interactive first-run wizard: configure an API key, verify, install completion. > Generated from `kash docs --json`. Run `kash setup --help` for full usage and examples. ## Usage ```bash theme={null} kash setup [options] ``` ## Options | Flag | Description | | ----------------- | --------------------------------------------------------------- | | `-y, --yes` | accept all defaults and skip optional prompts (non-interactive) | | `--api-key ` | pass the API key non-interactively (skips the prompt) | # trade Source: https://docs.kash.bot/developer-docs/cli/trade Place trades and inspect their status. Place trades and inspect their status. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash trade --help` — this page is the structured reference. ## Usage ```bash theme={null} kash trade [options] ``` ## Subcommands ### `kash trade buy` Buy outcome tokens for a market. **Arguments** * `marketId` — market UUID **Options** | Flag | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | USDC amount as a decimal (max 6 fractional digits) | | `--wait` | block until the trade reaches a terminal state | | `--wait-timeout-ms, --timeout ` | wait timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request) | | `--poll-interval-ms, --poll-interval ` | wait poll interval in milliseconds (default 2000) | | `--idempotency-key ` | sets the Idempotency-Key HTTP header | | `--auto-idempotency-key` | auto-generate an Idempotency-Key (UUID v4) and surface it in the response | | `--client-request-id ` | sets the body-level clientRequestId for replay safety | | `--dry-run` | preview the request without sending — emits the would-be body and resolved headers | ### `kash trade sell` Sell outcome tokens back to the market (Kash-orchestrated / hosted-API flow). **Arguments** * `marketId` — market UUID **Options** | Flag | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-o, --outcome ` | outcome index (0-based) | | `-a, --amount ` | target USDC to receive (decimal, max 6 fractional digits) — NOT tokens-in. Use `kash quote sell --tokens ` if you have a tokens-in figure and want a USDC quote first. | | `--wait` | block until the trade reaches a terminal state | | `--wait-timeout-ms, --timeout ` | wait timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request) | | `--poll-interval-ms, --poll-interval ` | wait poll interval in milliseconds (default 2000) | | `--idempotency-key ` | sets the Idempotency-Key HTTP header | | `--auto-idempotency-key` | auto-generate an Idempotency-Key (UUID v4) and surface it in the response | | `--client-request-id ` | sets the body-level clientRequestId for replay safety | | `--dry-run` | preview the request without sending — emits the would-be body and resolved headers | ### `kash trade status` Show the status of a trade. Use --poll to block until terminal. **Arguments** * `id` — trade UUID **Options** | Flag | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- | | `--poll` | poll until the trade reaches a terminal state | | `--wait-timeout-ms, --timeout ` | poll timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request) | | `--poll-interval-ms, --poll-interval ` | poll interval in milliseconds (default 2000) | ### `kash trade list` List your trades. **Options** | Flag | Description | | -------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `-s, --status ` | status filter; repeat or comma-separate (e.g. --status pending --status executing \| --status pending,executing) | | `-m, --market ` | filter by market id | | `-l, --limit ` | page size (1-100) | | `-c, --cursor ` | pagination cursor | | `-a, --all` | walk every page (use with --json for export) | | `--ndjson` | stream results as newline-delimited JSON (one record per line); implies --all | ### `kash trade confirm` Confirm a high-value trade using its one-time token. **Arguments** * `id` — trade UUID * `token` (optional) — confirmation token (omit to read from stdin or prompt) **Options** | Flag | Description | | --------------- | -------------------------------------- | | `--token-stdin` | read the confirmation token from stdin | # version Source: https://docs.kash.bot/developer-docs/cli/version Show CLI version and runtime environment. Show CLI version and runtime environment. > Generated from `kash docs --json`. Run `kash version --help` for full usage and examples. ## Usage ```bash theme={null} kash version [options] ``` ## Options | Flag | Description | | --------- | ----------------------------------------------------------- | | `--check` | probe npm for a newer @kashdao/cli release (cached for 24h) | # webhooks Source: https://docs.kash.bot/developer-docs/cli/webhooks Manage webhook delivery, signing secrets, and signature verification. Manage webhook delivery, signing secrets, and signature verification. > Generated from `kash docs --json`. Each command's behaviour is documented in-binary via `kash webhooks --help` — this page is the structured reference. ## Usage ```bash theme={null} kash webhooks [options] ``` ## Subcommands ### `kash webhooks list` List recent webhook delivery events for the calling key. **Options** | Flag | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------- | | `-l, --limit ` | page size (1-100) | | `-c, --cursor ` | opaque cursor from a previous response | | `-a, --all` | walk every page (subject to --limit per page) | | `--ndjson` | stream results as newline-delimited JSON (one record per line); implies --all | | `--status ` | filter by derived status (one of: none, pending, delivered, retrying, failed); repeat or comma-separate | ### `kash webhooks rotate-secret` Rotate the webhook signing secret for the current API key. **Options** | Flag | Description | | ----------- | --------------------------------------------------------------- | | `-y, --yes` | skip the interactive confirmation (required for --json --quiet) | ### `kash webhooks redeliver` Re-queue a webhook event for delivery. **Arguments** * `eventId` — webhook event UUID ### `kash webhooks verify` Verify a captured X-Kash-Signature against the raw body and secret. **Options** | Flag | Description | | -------------------------- | -------------------------------------------------------------------- | | `-s, --signature
` | the X-Kash-Signature header value (e.g. "t=…,v1=…") | | `--body ` | raw request body as a string (use --body-file for binary-safe input) | | `--body-file ` | path to a file containing the raw request body | | `--secret ` | webhook signing secret (overrides KASH\_WEBHOOK\_SECRET) | | `--secret-file ` | read secret from a file (preferred over --secret on shared shells) | | `--tolerance-ms ` | replay-window tolerance in milliseconds (default 300000 = 5 minutes) | ### `kash webhooks replay` Re-sign a captured webhook payload and POST it to a target URL. **Arguments** * `body` (optional) — path to a JSON body file (omit or pass "-" for stdin) **Options** | Flag | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------------- | | `-t, --target ` | destination URL (e.g. ngrok tunnel or localhost endpoint) | | `-s, --secret ` | webhook signing secret (overrides KASH\_WEBHOOK\_SECRET) | | `--secret-file ` | read the signing secret from a file (preferred — keeps the value out of argv and env) | | `--secret-env ` | read the signing secret from this environment variable (default: KASH\_WEBHOOK\_SECRET) | | `--timestamp-ms ` | unix-ms timestamp for the signature header (default: current time) | | `--signature-header ` | override the signature header name (default: X-Kash-Signature) | | `--timeout-ms ` | fetch timeout (default 10000) | | `--dry-run` | compute the signature header and inspect the would-be POST without sending it | | `--refuse-private-addresses` | hard-fail (instead of warning) when --target is a loopback / private / link-local address — recommended for CI | # with-retry Source: https://docs.kash.bot/developer-docs/cli/with-retry Re-run a kash command on recoverable failures (rate limits, transient errors). Re-run a kash command on recoverable failures (rate limits, transient errors). > Generated from `kash docs --json`. Run `kash with-retry --help` for full usage and examples. ## Usage ```bash theme={null} kash with-retry [options] ``` **Arguments** * `command` (variadic) — kash command + args; pass after `--` to disambiguate ## Options | Flag | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--max-attempts ` | maximum total attempts including the first (default 5) | | `--initial-delay-ms ` | starting backoff when the envelope has no retryAfterMs (default 1000) | | `--max-delay-ms ` | cap on the per-attempt wait (default 30000) | | `--retry-without-json` | retry even when the inner command did not emit --json (no envelope to discriminate terminal from transient — opt in if you trust the inner command failed transiently) | # Developer Documentation Source: https://docs.kash.bot/developer-docs/introduction Build with Kash prediction markets Welcome to the Kash developer documentation. Build trading bots and agents against the protocol, sign transactions directly on-chain, embed markets in your website, and access real-time market data. **Kash is non-custodial across every surface.** User funds always sit in ERC-4337 smart accounts controlled by the user's own Privy embedded wallet — Kash never holds keys or funds, on any path. The split below is about **who orchestrates execution**, not custody. ## Pick your surface Kash-orchestrated programmatic access at `api.kash.bot`. Place trades, manage portfolios, receive HMAC-signed webhooks. API-key authenticated; trades are signed by the user's own Privy smart account via scoped delegation. Kash backend builds the userop, sponsors gas, and routes bridging. Right surface for bots, agents, and market makers who want managed execution without running infra. Self-orchestrated access. Plug in your own signer (EOA, smart account, HSM, Fireblocks), your own RPC, your own bundler — transactions never touch Kash servers. TypeScript and Python implementations with cross-language parity. `kash` — the same primitives as the SDK, packaged as a scripting-friendly command-line client. Ideal for one-offs, CI jobs, shell scripts, and agent integrations. Read-only public REST surface at `app.kash.bot/api`. Drop iframes and market data into websites and articles. No authentication required. **Which one do I want?** * Want Kash to orchestrate execution (build trades, sponsor gas, bridge funds) on top of the user's Privy smart account? → **REST API**. * Want to sign and submit transactions entirely with your own infrastructure? → **Protocol SDK**. * Want to script flows from a terminal or CI? → **CLI**. * Embedding markets in a website for humans? → **Public Embed API**. ## Quick links Generate a key, place your first trade, receive your first webhook — under 5 minutes. Sign your first on-chain trade from an EOA in under 10 minutes. Embed your first prediction market in 5 minutes. Typed REST client. Trades, markets, portfolios, webhooks, retries. Native Python implementation of the Protocol SDK, plus a Hummingbot connector. Every error code, what it means, and how to recover. ## What can you build? ### Trading bots & agents Use the [REST API](/developer-docs/rest-api/overview) to place trades, monitor portfolios, and react to market events via webhooks. Supports idempotent retries, high-value confirmation gates, and per-key rate limiting. ### Self-orchestrated trading & market-making Use the [Protocol SDK](/developer-docs/protocol-sdk/overview) (TypeScript or [Python](/developer-docs/protocol-sdk-python/overview)) to sign and submit transactions yourself against the on-chain AMM with your own signer, RPC, and bundler. Combine with the REST API's quote endpoint for live pricing, or quote on-chain directly. (Both paths are non-custodial — this one just keeps the entire execution stack in your infra.) ### Scripting & ops Use the [CLI](/developer-docs/cli/overview) for shell-driven workflows: list markets, place trades, rotate webhook secrets, redeliver events, decode error codes — all with a stable JSON envelope for piping into `jq` or agent contexts. ### Embed prediction markets Use the [Public Embed API](/developer-docs/public-embed-api/overview) to drop iframes and read-only market data into news sites, financial platforms, or community forums. Embeds are read-only — users are directed to app.kash.bot to trade. ## Support Need help? We're here for you: Email [dev@kash.bot](mailto:dev@kash.bot) for technical questions Contact [partnerships@kash.bot](mailto:partnerships@kash.bot) for whitelabeling or revenue sharing # Hummingbot integration Source: https://docs.kash.bot/developer-docs/protocol-sdk-python/hummingbot Build a Hummingbot strategy that trades on Kash. Reference cross-venue arbitrage strategy included. [Hummingbot](https://hummingbot.org) is the canonical open-source market-making framework for Python. The Python protocol SDK is the intended integration path — you import it inside a `StrategyV2Base` subclass and drive trades through it on every tick. Kash never sees a private key; the strategy holds the signer. ## Architectural shape ``` ┌────────────────────────────────────────────────────────────┐ │ Hummingbot process │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ KashAmmArbStrategy (StrategyV2Base) │ │ │ │ │ │ │ │ on_tick() → safe_ensure_future(_tick_async()) │ │ │ │ │ │ │ │ _tick_async(): │ │ │ │ 1. Read Kash quote (kashdao-protocol-sdk) │ │ │ │ 2. Read other-venue price (your code) │ │ │ │ 3. Decide: profitable spread? │ │ │ │ 4. Trade Kash leg (kashdao-protocol-sdk) │ │ │ │ 5. (optionally) hedge on the other venue │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ EoaClient (created once at strategy init) │ │ │ │ • httpx.AsyncClient (HTTP RPC) │ │ │ │ • web3.AsyncWeb3 (read primitives) │ │ │ │ • LocalEoaSigner (eth_account.Account) │ │ │ └─────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────┘ ▲ │ HTTPS (Base Sepolia / mainnet) ▼ ┌──────────────────────┐ │ Your RPC endpoint │ └──────────────────────┘ ▲ │ chain consensus ▼ ┌──────────────────────┐ │ Kash protocol on-chain│ │ (Market.sol, etc.) │ └──────────────────────┘ ``` Kash is on neither side of the wire. Your strategy holds the EOA signer; your RPC sees the read traffic and the signed transaction. ## Reference strategy The package ships `examples/hummingbot/amm_arb_kash_uniswap.py` — a working `StrategyV2Base` that demonstrates the canonical SDK integration pattern. Skeleton runs against Base Sepolia in EOA mode; the Uniswap leg + sizing model are stubbed for you to wire to your real venue. ```python theme={null} from kashdao_protocol_sdk import ( BuildApproveParams, BuildBuyParams, MAX_UINT256, QuoteParams, create_eoa_client, usdc, viem_account_eoa_signer, ) from hummingbot.strategy.strategy_v2_base import StrategyV2Base class KashAmmArbStrategy(StrategyV2Base): markets: ClassVar[dict[str, set[str]]] = {} # no Hummingbot connectors def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) account = Account.from_key(os.environ["KASH_TRADER_PK"]) self._kash = create_eoa_client( chain_id=84532, rpc=os.environ["KASH_BASE_SEPOLIA_RPC"], signer=viem_account_eoa_signer(account), ) self._owner = account.address self._approved = False def on_tick(self) -> None: from hummingbot.core.utils.async_utils import safe_ensure_future safe_ensure_future(self._tick_async()) async def on_stop(self) -> None: await self._kash.aclose() async def _tick_async(self) -> None: await self._ensure_approval() kash_quote = await self._kash.markets.quote( os.environ["KASH_MARKET_ADDRESS"], QuoteParams(side="BUY", outcome=0, amount=usdc(10)), ) uni_quote = await self._get_uniswap_price() if self._is_profitable(kash_quote, uni_quote): await self._kash.trades.send.buy( os.environ["KASH_MARKET_ADDRESS"], BuildBuyParams( # `account` is the canonical field name in both # modes; in EOA mode pass the EOA. (`smart_account=` # still works but is deprecated.) account=self._owner, outcome=0, amount_usdc=usdc(10), max_slippage_bps=50, ), ) ``` The full file is at [`examples/hummingbot/amm_arb_kash_uniswap.py`](https://github.com/KashDAO/protocol-sdk-python/blob/main/examples/hummingbot/amm_arb_kash_uniswap.py) on the public mirror. ## Running the strategy in Hummingbot ```bash theme={null} # 1. Install Hummingbot per their docs. # 2. Drop the Kash strategy into your Hummingbot scripts/ folder. # 3. From a Hummingbot REPL: hummingbot start > import_script_file kash_amm_arb examples/hummingbot/amm_arb_kash_uniswap.py > start --script kash_amm_arb ``` ## Resource requirements Per active strategy: | Resource | Footprint | | -------------- | --------------------------------------------------------------------------------------------------------------- | | RAM | \~20 MB resident for the SDK + httpx pool + web3.py + the WS subscription if used | | Sockets | 1 HTTP keep-alive pool to the chain RPC; 1 WSS connection per `markets.watch` | | Concurrency | All async; no thread pools (unless your signer's CPU-bound — then `LocalSigner` runs sign in `run_in_executor`) | | Network egress | One `eth_call` per tick (quote) + one `eth_sendRawTransaction` per executed trade | A typical 1-second tick interval generates \~2 RPS to the chain RPC and \~0.05 RPS of trade submits (assuming 5% of ticks find a profitable spread). Both well under any commercial RPC's free tier. ## Latency profile | Stage | p50 | p99 | | ------------------------------------- | ------- | ------ | | `markets.quote(...)` → return | 30 ms | 250 ms | | `trades.build_buy(...)` → return | `<1 ms` | 5 ms | | `trades.prepare_buy(...)` → return | 50 ms | 400 ms | | `trades.signer.sign_transaction(...)` | `<1 ms` | 5 ms | | `trades.submit(...)` → tx hash | 100 ms | 800 ms | | Mempool inclusion (chain-side) | 2 s | 10 s | Numbers are anecdotal — they depend on your RPC, your signer (local vs remote), and the chain's mempool state. Use them as order-of-magnitude estimates, not SLA targets. ## Observability Every public method accepts an optional `signal: asyncio.Event` for cancellation. Long-running strategies should propagate Hummingbot's shutdown signal into every SDK call so a `ctrl-c` cleanly aborts in-flight ops. Lifecycle hooks (`KashProtocolHooks`) are six fire-and-forget callbacks for telemetry / structured logging — all optional, passed on the client config: * `on_bundler_request(BundlerRequestEvent)` — before every bundler JSON-RPC call (`method`, `url`) * `on_bundler_response(BundlerResponseEvent)` — on a successful bundler response (`method`, `url`, `duration_ms`, `status`) * `on_bundler_error(BundlerErrorEvent)` — on a bundler JSON-RPC error (`method`, `url`, `duration_ms`, `error`) * `on_signer_request(SignerRequestEvent)` — before a signer adapter call (`owner_address`, `kind`: `userop-hash` / `typed-data` / `transaction`) * `on_signer_error(SignerErrorEvent)` — on a signer adapter failure * `on_wait_orphaned(WaitOrphanedEvent)` — a `send.*` call gave up waiting for inclusion (consumer aborted, or the bundler poll timed out) Hooks are invoked via `safe_fire` — exceptions never propagate into the request path, and async callbacks are scheduled but never awaited. Wire these into your existing logging stack (Hummingbot's structured log, OpenTelemetry, or whatever) without polluting the trade-path code with logging side effects. Never use hooks for control flow. ## Cancellation + clean shutdown Always close the client. Hummingbot's `on_stop` is the hook: ```python theme={null} async def on_stop(self) -> None: await self._kash.aclose() ``` This drains the httpx pool, closes the WebSocket subscription if any, and is idempotent — calling it twice is a no-op. The `async with` form does this for you in standalone scripts. ## Troubleshooting * **`KashSimulationRevertedError` (code `SIMULATION_REVERTED`)** — the prepared trade will revert on-chain. The decoded revert reason lives in `context["revert_reason"]` (and `context["decoded_error"]`) — a common cause is a `max_slippage_bps` tighter than the market could meet. The pre-flight `eth_call` caught the revert before you paid for any signing-infra round-trips. * **`KashSignerError` (code `STALE_SIGNED_TX`)** — you signed a tx and then mutated its gas/fees/nonce before submit. The submit-side staleness guard recovered the signing address from the bytes, saw a mismatch, and refused to broadcast. * **`KashChainError` (e.g. codes `QUOTE_FAILED`, `MARKET_READ_FAILED`, `TX_SEND_FAILED`)** — a chain RPC call failed; often your RPC is down or rate-limiting. Back off and retry; the stable `code` string tells you which read/write path failed. ## See also The EOA-mode Python integration the strategy above uses. For AA-stack Python integrations. Same encoding bytes as the TypeScript SDK. # Python Protocol SDK — overview Source: https://docs.kash.bot/developer-docs/protocol-sdk-python/overview kashdao-protocol-sdk — non-custodial Python SDK for the Kash on-chain protocol. The canonical Hummingbot integration path. `kashdao-protocol-sdk` is the **direct-to-chain** Python SDK for Kash. Same trust model, same on-chain protocol, same encoding bytes as the TypeScript [`@kashdao/protocol-sdk`](/developer-docs/protocol-sdk/overview) — a position opened via either SDK can be closed via the other. If you want a Python integration with Kash, this is the package. Hummingbot strategies, Python market makers, AI agents written in Python, Jupyter / data-science workflows — all sit on this SDK. ## Trust model — zero custody, zero Kash dependency The package never holds keys, never signs UserOps or transactions, never sponsors gas, and never reaches Kash-hosted infrastructure. You bring: * your own RPC URL, * your own private key OR signer abstraction (`eth_account.Account`, web3signer over RPC, hardware wallet, Fireblocks, AWS-KMS — anything exposing the `EoaSignerAdapter` Protocol), * (SA mode only) ERC-4337 v0.7 bundler URL. Kash sees only what hits the public chain. The library's job is to construct calldata + the unsigned tx (or UserOp), then hand signed artifacts to your RPC or your bundler. ## Two trading modes — pick one Same modes, same factory shapes as the TS SDK: | Mode | Factory | Best for | | --------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------- | | **EOA** (vanilla EIP-1559) | `create_eoa_client` | Hummingbot strategies, Python market makers, anyone with their own EIP-1559 signer | | **Smart Account** (ERC-4337 v0.7) | `create_smart_account_client` | Python AA-stack integrations, paymaster sponsorship, batched approve+trade flows | EOA mode is the recommended Hummingbot integration path — it's what `examples/hummingbot/amm_arb_kash_uniswap.py` demonstrates. ## Supported chains | Chain | Chain ID | Status | | ------------ | -------- | ----------------------------------------------------------------- | | Base mainnet | 8453 | ⏳ Pre-deploy — `KashConfigError(CHAIN_NOT_DEPLOYED)` until launch | | Base Sepolia | 84532 | ✅ Live | | Custom chain | any | ✅ Via `custom_chain=CustomChain(...)` (Anvil / forks / dev) | ## Install ```bash theme={null} pip install kashdao-protocol-sdk ``` Requires Python ≥ 3.10. End-user installs touch only the public PyPI registry — no private repos, no auth tokens. ## Differences vs the TypeScript SDK The packages are byte-equal at the encoding level (see [**Cross-language parity**](/developer-docs/protocol-sdk/cross-language-parity)), but the Python ergonomics naturally differ: | Aspect | TypeScript | Python | | ----------------- | --------------------------------- | ------------------------------------------ | | Async runtime | Native promises | `asyncio` — every public method is `async` | | Config validation | Zod | Pydantic v2 | | Naming | `camelCase` | `snake_case` | | Numeric type | `bigint` | `int` | | Hex addresses | viem's `0x${string}` literal type | `eth_typing.Hex` (string with hex prefix) | | Lifecycle | `await client.aclose()` | `await client.aclose()` or `async with` | Same protocol, same trust model, same trade lifecycle, same error hierarchy. Just Pythonic ergonomics on the surface. ## Public surface (\~210 exports) * **Factories**: `create_eoa_client`, `create_smart_account_client` * **Trade lifecycle**: `client.trades.{build_*, prepare_*, simulate, hash_of, submit, send.*}` * **Market reads**: `client.markets.{get, state, quote, watch}` * **Account reads**: `client.account.{usdc_balance, position, gas_balance, usdc_allowance}` (plus `compute_address`, `is_deployed` in smart-account mode only) * **Signers**: `LocalEoaSigner`, `JsonRpcEoaSigner` (EOA); `LocalSigner`, `JsonRpcSigner` (SA) * **Bundlers** (SA): `create_generic_bundler_client` plus presets for Pimlico / Alchemy / Flashbots * **Unit conversion**: `usdc`, `tokens`, `format_usdc`, `format_tokens`, `MAX_UINT256` * **Fee estimation**: `estimate_chain_fees` with `EstimateFeesOptions` * **Custom-chain support**: `CustomChain`, `resolve_custom_chain` * **Error hierarchy**: `KashProtocolError` and 6 subclasses ## Where to next Vanilla EIP-1559 from a plain wallet. Canonical Hummingbot path. ERC-4337 v0.7 with a bundler. Full strategy walkthrough. Same protocol, TypeScript instead of Python. # Quickstart — EOA mode (Python) Source: https://docs.kash.bot/developer-docs/protocol-sdk-python/quickstart-eoa Vanilla EIP-1559 trading from a plain EOA. Canonical Hummingbot integration path. EOA mode is the recommended path for Hummingbot strategies, Python market makers, and any integration with its own EIP-1559 signer. The signer IS the trading address — no SimpleAccount indirection, no bundler relay, no ERC-4337 verification overhead. ## Construct the client ```python theme={null} import asyncio import os from eth_account import Account from kashdao_protocol_sdk import ( BuildBuyParams, create_eoa_client, usdc, viem_account_eoa_signer, ) async def main() -> None: account = Account.from_key(os.environ["KASH_PRIVATE_KEY"]) signer = viem_account_eoa_signer(account) async with create_eoa_client( chain_id=84532, # Base Sepolia rpc=os.environ["BASE_SEPOLIA_RPC"], signer=signer, ) as client: # Read minimal market state. minimal = await client.markets.get(os.environ["KASH_MARKET"]) print(f"market has {minimal.num_outcomes} outcomes; status={minimal.status}") # All-in-one trade. # `BuildBuyParams.account` is the canonical field name used # by both EOA and SA modes — in EOA mode pass the EOA # address itself (the trader IS the signer). The legacy # `smart_account=` alias is still accepted but deprecated. result = await client.trades.send.buy( os.environ["KASH_MARKET"], BuildBuyParams( account=client.signer.owner_address, outcome=0, amount_usdc=usdc(10), # 10 USDC max_slippage_bps=50, # 0.5% ), ) print(result.transaction_hash, result.success, result.gas_used) asyncio.run(main()) ``` `async with` (or `await client.aclose()`) is mandatory for clean shutdown — the underlying `httpx.AsyncClient` and `web3.py` WebSocket sessions need explicit teardown to avoid socket leaks in long-running Hummingbot strategies. ## First trade — approve USDC ```python theme={null} from kashdao_protocol_sdk import BuildApproveParams, MAX_UINT256 await client.trades.send.approve( BuildApproveParams( account=client.signer.owner_address, spender=KASH_MARKET, amount=MAX_UINT256, ), ) ``` After the first approve, every subsequent `send.buy` / `send.sell` proceeds without an extra approve hop. ## Power users — explicit lifecycle Same three-layer model as the TypeScript SDK: | Layer | When you'd use it | | ----------------------------------------------------- | -------------------------------------------------------------- | | `client.trades.send.(...)` | Default. Hummingbot, AI agents, dashboards. | | `client.trades.prepare_(...)` + manual submit | Explicit control of signing — e.g. log + audit before signing. | | `client.trades.build_(...)` + `hash_of(...)` | Construct the unsigned tx, hash it, route to a remote signer. | Submit-time staleness guard (`KashSignerError(STALE_SIGNED_TX)`) matches the TS-SDK behaviour. Bypass via `EoaSubmitOptions(skip_staleness_check=True)` if intentional. ## Run the bundled examples The `examples/eoa/` folder ships with three runnable scripts: ```bash theme={null} git clone https://github.com/KashDAO/protocol-sdk-python cd protocol-sdk-python python -m venv .venv && source .venv/bin/activate pip install -e ".[dev]" # Set required env first; see each file's header for the env var list. python examples/eoa/01_quickstart.py # read-side only, safe to run python examples/eoa/02_one_line_trade.py --confirm # broadcasts a real tx, gated python examples/eoa/03_error_handling.py # exercises the typed error hierarchy ``` ## What's next ERC-4337 v0.7 with a bundler. Full strategy walkthrough with cross-venue arbitrage. How this SDK stays byte-equal with the TS SDK. # Quickstart — Smart-account mode (Python) Source: https://docs.kash.bot/developer-docs/protocol-sdk-python/quickstart-smart-account ERC-4337 v0.7 trading via SimpleAccount + bundler from Python. Same surface as the TS SDK. Smart-account mode in Python mirrors the TypeScript surface exactly. Use it for Python AA-stack integrations, paymaster sponsorship, batched approve+trade flows, or any case where the trading address should be a SimpleAccount derived from an external signer. ## Construct the client ```python theme={null} import asyncio import os from eth_account import Account from kashdao_protocol_sdk import ( BundlerOptions, create_smart_account_client, viem_account_signer, ) async def main() -> None: account = Account.from_key(os.environ["KASH_PRIVATE_KEY"]) async with create_smart_account_client( chain_id=84532, rpc=os.environ["BASE_SEPOLIA_RPC"], signer=viem_account_signer(account), bundler=BundlerOptions( provider="pimlico", url=os.environ["KASH_BUNDLER_URL"], ), ) as client: sa = await client.account.compute_address(client.signer.owner_address) print(f"SmartAccount: {sa}") health = await client.bundler.health() print(f"bundler health: {health}") asyncio.run(main()) ``` `BundlerOptions(provider=..., url=..., api_key=...)` accepts `alchemy`, `pimlico`, `flashbots`, or `generic`. The provider value is informational; the actual vendor preset is chosen by the consumer when wiring the client. ## Place a trade ```python theme={null} from kashdao_protocol_sdk import BuildBuyParams, usdc sa = await client.account.compute_address(client.signer.owner_address) result = await client.trades.send.buy( os.environ["KASH_MARKET"], BuildBuyParams( account=sa, outcome=0, amount_usdc=usdc(10), max_slippage_bps=50, ), ) print( result.user_op_hash, result.receipt.receipt.get("transactionHash"), result.receipt.success, ) ``` The `user_op_hash` is what the signer signed — useful for log correlation across the bundler RPC and the EntryPoint contract. The smart-account `send.buy` result carries only `user_op_hash` and the bundler-returned `receipt`; the on-chain transaction hash is nested at `result.receipt.receipt["transactionHash"]` and success at `result.receipt.success`. External monitoring typically wants both views. ## First-trade-with-deployment Same auto-deploy story as the TypeScript SDK: the first UserOp from a fresh SmartAccount carries `factory` + `factory_data` so the EntryPoint deploys the account in the same op. The Python SDK auto-detects the undeployed state and populates these fields; you don't pass anything special. Verification gas is \~5x higher on the deployment op, and the bundler's gas estimate accounts for this automatically. ## Run the bundled examples ```bash theme={null} # Read-only by default: derives the SA via CREATE2, prints deploy/balance state and a quote python examples/smart_account/buy_with_simple_account.py # --confirm opts in to broadcasting: submits a real SA BUY UserOp on Base Sepolia python examples/smart_account/quote_and_buy.py --confirm ``` ## What's next Vanilla EIP-1559, no bundler. Full strategy walkthrough. # Cross-language parity Source: https://docs.kash.bot/developer-docs/protocol-sdk/cross-language-parity How @kashdao/protocol-sdk and kashdao-protocol-sdk (Python) stay byte-equal. A position opened via either can be closed via the other. Kash ships **two** non-custodial protocol SDKs: * `@kashdao/protocol-sdk` — TypeScript, the canonical implementation. * `kashdao-protocol-sdk` — Python, a peer mirror. Both are first-class. Both have full coverage of EOA mode and smart-account mode. Both target the same on-chain protocol. **Most importantly, both encode bytes identically** — so a UserOp signed in TypeScript and submitted via Python's bundler client validates correctly under the same EntryPoint, and vice versa. This page documents how that guarantee is enforced. ## What "parity" means Two encoding/hashing functions matter for cross-language safety: 1. **EIP-1559 transaction serialization + hash** — `keccak256(serializeTransaction(tx))` produces the digest the chain's signature recovery validates against. If TypeScript and Python disagree on the serialized bytes for the same logical `UnsignedTransaction`, an EOA trade prepared in one language can't be signed and submitted via the other. 2. **EIP-4337 v0.7 UserOp hash** — `getUserOperationHash(...)` produces the digest the EntryPoint contract validates during `validateUserOp`. If the two SDKs disagree, a SmartAccount UserOp prepared in Python can't be validated under a TypeScript-derived signature (or vice versa) — surfacing as `AA24 signature error` at submit time. These functions are deceptively simple. Both delegate to widely-used primitives (`viem` in TS, `eth_abi` + `keccak` in Python). But the interfaces differ in subtle ways: optional fields default differently, RLP-encoding edge cases for empty calldata, packed-uint128 encoding for v0.7's `gasFees` field. A real correctness bug **was** caught by this test infrastructure — see the [`@kashdao/protocol-sdk` 0.1.x changelog](https://github.com/KashDAO/protocol-sdk-typescript/blob/main/CHANGELOG.md). ## How the parity tests work The TypeScript SDK is the source of truth. A generation script (`scripts/generate-parity-fixtures.ts`) constructs deterministic, hand-crafted shapes covering edge cases: * Canonical buy / zero-data / high-nonce / non-zero-value EOA txs. * Minimal SA UserOp / first-trade-with-deployment (factory + factoryData) / paymaster-sponsored / Base mainnet high nonce. For each fixture, the script computes the canonical hash and the serialized bytes, and writes both as recorded `expected` values to `tests/parity/fixtures/{eoa,smart-account}.json`. ``` packages/protocol-sdk/scripts/generate-parity-fixtures.ts │ │ pnpm parity:fixtures ▼ packages/protocol-sdk/tests/parity/fixtures/eoa.json packages/protocol-sdk/tests/parity/fixtures/smart-account.json │ │ scripts/sync-parity.py (Python side) ▼ packages/protocol-sdk-python/tests/parity/fixtures/eoa.json packages/protocol-sdk-python/tests/parity/fixtures/smart-account.json ``` Both SDKs run a parity test that consumes the same JSON fixtures and asserts the local re-encoding produces identical bytes: * TypeScript: `pnpm test:parity` → `tests/parity/eoa-parity.test.ts` + `userop-parity.test.ts` * Python: `pytest -m parity` → `tests/parity/test_eoa_parity.py` + `test_userop_parity.py` Drift is rejected at PR time. The Python sync script's `--check` mode acts as a release gate. ## Error class parity Beyond byte-level encoding, the public error hierarchies match class-for-class: | TypeScript | Python | | ----------------------------- | ----------------------------- | | `KashProtocolError` | `KashProtocolError` | | `KashConfigError` | `KashConfigError` | | `KashChainError` | `KashChainError` | | `KashBundlerError` | `KashBundlerError` | | `KashSignerError` | `KashSignerError` | | `KashSimulationRevertedError` | `KashSimulationRevertedError` | | `KashAbortedError` | `KashAbortedError` | The string-valued `ErrorCode` constants are identical across languages. A cross-language log aggregator can correlate by `error.code` without translation. ## Naming conventions | TypeScript | Python | | ------------------------------------ | -------------------------------------- | | `createSmartAccountClient(...)` | `create_smart_account_client(...)` | | `client.trades.send.buy(...)` | `client.trades.send.buy(...)` | | `client.markets.quote(...)` | `client.markets.quote(...)` | | `BuildBuyParams { amountUsdc, ... }` | `BuildBuyParams(amount_usdc=..., ...)` | | `usdc(10)` → bigint | `usdc(10)` → int | | `MAX_UINT256` (bigint) | `MAX_UINT256` (int) | The shape and behaviour are the same. Snake\_case is the only syntactic delta. ## What's deliberately NOT in parity * **Bundler presets.** The TS SDK ships dedicated subpath imports for Pimlico/Alchemy/Flashbots; the Python SDK accepts the same vendors but exposes them as `BundlerOptions(provider="pimlico", ...)` data classes. Functionally equivalent. * **Sync transports.** The TS SDK uses viem's HTTP/WebSocket transports; the Python SDK uses `httpx` (sync HTTP) + `websockets`. This is a low-level transport choice — the bytes the SDK sends out are the same. * **Type system idioms.** TS uses Zod for runtime config validation * viem's `0x${string}` template-literal types; Python uses Pydantic v2. Both reject malformed inputs at construction time with `KashConfigError`. The error shape is the same. ## See also The Python sibling — same protocol, same trade lifecycle, same error hierarchy. Open-source TS-side parity test suite. Open-source Python-side parity test suite. # Error handling Source: https://docs.kash.bot/developer-docs/protocol-sdk/error-handling Typed error hierarchy, retry semantics, decoded revert hints. Same shape across both modes and the Python SDK. Every error in `@kashdao/protocol-sdk` inherits from `KashProtocolError` and carries a stable `code`, structured `context`, plus `isRetryable` / `isOperational` flags. The hierarchy mirrors the Python SDK exactly — same class names, same string-valued `ErrorCode` constants — so a cross-language deployment can branch on the error class without parsing strings. ## The 6 named subclasses | Class | When it fires | Retryable? | | ----------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------- | | `KashConfigError` | Construction-time validation failure (invalid chain id, bad RPC URL, malformed signer) | **No** — never retry config errors | | `KashChainError` | Chain or RPC-side failure (RPC unreachable, reorg-suspected receipt loss, gas estimate failed) | Sometimes — check `isRetryable` | | `KashBundlerError` | Bundler RPC-side failure (Pimlico/Alchemy/Flashbots returned an error) | Sometimes — check `isRetryable` | | `KashSignerError` | Signer-side failure (signature rejected, MFA timeout, hardware-wallet disconnect, stale tx/UserOp) | **Always no** — re-prompting MFA is a footgun | | `KashSimulationRevertedError` | Pre-flight `eth_call` would revert (slippage exceeded, insufficient allowance, market state changed) | **No** — input issue, not retryable | | `KashAbortedError` | Caller's `AbortSignal` fired | **No** — abort was deliberate | ## Canonical exception-handling shape ```ts theme={null} import { KashConfigError, KashChainError, KashBundlerError, KashSignerError, KashSimulationRevertedError, KashAbortedError, } from '@kashdao/protocol-sdk'; try { await client.trades.send.buy(MARKET, params); } catch (err) { if (err instanceof KashSimulationRevertedError) { // `context.revertReason` holds the decoded custom error name when known. // `actionableRevertHint(err)` turns it into a user-facing suggestion. console.error('would revert:', err.context?.revertReason ?? err.message); // Update slippage / amount / market and try again. return; } if (err instanceof KashSignerError) { // ALWAYS non-retryable. Surface to the user as a sign-required UI step. console.error('signer failed:', err.code, err.message); return; } if (err instanceof KashChainError && err.isRetryable) { // Retry with backoff; cap attempts. return retryWithBackoff(() => client.trades.send.buy(MARKET, params)); } if (err instanceof KashConfigError) { // Never retryable. Fix the code. throw err; } if (err instanceof KashAbortedError) { // Caller's AbortSignal fired. Don't auto-retry. return; } throw err; } ``` ## Decoded revert hints `KashSimulationRevertedError` exposes the decoded reason on `err.context?.revertReason`. The exported helpers `decodeMarketRevert(...)` and `actionableRevertHint(err)` turn Market/EntryPoint custom errors into readable strings. Today's catalogue includes: * `SlippageExceeded(actualOut, minOut)` — your `maxSlippageBps` was tighter than the market could meet. * `InsufficientAllowance(spender, owner, current, required)` — you haven't approved enough USDC for the spender; call `client.trades.send.approve` first. * `MarketFrozen` — the market is frozen pending resolution; trades rejected. * `OutcomeOutOfRange(supplied, max)` — your `outcome` index is ≥ the market's outcome count. Unknown custom errors fall through to `.message` with the raw 4-byte selector for debugging. ## `isOperational` vs `isRetryable` `isOperational: true` means the error is a known, expected failure mode — the consumer's input or the chain's state is the cause, and the SDK is not internally broken. `isOperational: false` would indicate an SDK bug; you should never see this — file an issue. `isRetryable: true` means a fresh attempt MIGHT succeed without fixing inputs (e.g., the RPC was overloaded). `isRetryable: false` means a retry will deterministically fail. These flags are independent. A `KashSimulationRevertedError` is operational (input issue) but not retryable (slippage won't spontaneously recover). ## Cross-realm identification Every Kash error carries a cross-realm brand (`Symbol.for('@kashdao/protocol-sdk/error')`). When you have an SDK loaded in two realms (e.g., a Web Worker + main thread) the `instanceof` check still works because it falls back to brand detection. This is how the SDK survives bundler de-duplication across module graphs. ## See also Error class names and codes match the Python SDK exactly. The full `ErrorCode` enum on the public mirror. # Protocol SDK — overview Source: https://docs.kash.bot/developer-docs/protocol-sdk/overview @kashdao/protocol-sdk — non-custodial TypeScript SDK for the on-chain Kash protocol. BYO RPC, signer, bundler. `@kashdao/protocol-sdk` is the **direct-to-chain** SDK for Kash. It talks to the Kash protocol contracts on Base — no Kash backend, no Kash REST API in the trade path, no Kash-controlled keys, no Kash-sponsored gas. Trade execution is end-to-end self-custodied. If you'd like a hosted, API-key-authed integration (typical retail flow, fastest time to first trade), use [`@kashdao/sdk`](/developer-docs/rest-api/sdks/typescript) instead. This page is for builders who run their own signing infrastructure (market makers, AI agents, on-chain UIs). ## Trust model — zero custody, zero Kash dependency The package never holds keys, never signs transactions, never sponsors gas, and never reaches Kash-hosted infrastructure. Everything required to use it lives on the consumer's side: their RPC URL, their signer, (optionally) their bundler URL, their funded address. Kash sees only what hits the public chain. This is by design — the SDK is a strict library, not a service. Code review for an audit only needs to verify what `@kashdao/protocol-sdk` imports (today: `viem`, `zod`) and what it sends out (`eth_sendRawTransaction` to your RPC; `eth_sendUserOperation` to your bundler). No phone-home. No telemetry. ## Two trading modes — pick one The SDK supports both wallet models the protocol allows. | Mode | Factory | Best for | | --------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | **EOA** (vanilla EIP-1559) | `createEoaClient` | Market makers with existing tx-signing infra (web3signer, Fireblocks-direct, AWS-KMS, ethers/viem wallet). No bundler. Lowest per-trade overhead. | | **Smart Account** (ERC-4337 v0.7) | `createSmartAccountClient` | Self-custody UIs, AI-agent runners, retail-adjacent integrators that benefit from gasless onboarding via paymaster, social recovery, or session keys. | Both modes are first-class peers. Both share the same on-chain read surface. Both preserve the zero-custody trust story. You can use one or both side-by-side. ## Supported chains | Chain | Chain ID | Status | | ------------ | -------- | ---------------------------------------------------------------- | | Base mainnet | 8453 | ⏳ Pre-deploy — `KashChainError(CHAIN_NOT_DEPLOYED)` until launch | | Base Sepolia | 84532 | ✅ Live | | Custom chain | any | ✅ Via `customChain: CustomChain(...)` (Anvil / forks / dev) | Custom chains are an explicit escape hatch — supply your own chain id, RPC URL, and contract address bundle. The static registry is bypassed; Anvil deployments with non-canonical EntryPoint addresses keep working. ## Install ```bash theme={null} pnpm add @kashdao/protocol-sdk viem # or: npm install @kashdao/protocol-sdk viem ``` `viem` is a **peer dependency** — bring your own. The SDK accepts any `viem@^2.45.0` you have in your tree (so it doesn't drag a duplicate `viem` into your bundler). ## Where to next Vanilla EIP-1559 from a plain wallet. The canonical path for market makers. ERC-4337 v0.7 with a bundler. The canonical path for AA stacks. Typed error hierarchy, retry policies, decoded revert hints. How this SDK stays byte-equal with `kashdao-protocol-sdk` (Python). A position opened via either SDK can be closed via the other. Same protocol, same trust model, Python instead of TypeScript. The canonical Hummingbot integration path. # Quickstart — EOA mode Source: https://docs.kash.bot/developer-docs/protocol-sdk/quickstart-eoa Vanilla EIP-1559 trading from a plain EOA. No bundler. No SimpleAccount. Lowest per-trade overhead. EOA mode is the canonical path for market makers, AI agents, and any consumer with their own EIP-1559 signing infrastructure. The signer IS the trading address — no SimpleAccount indirection, no bundler relay, no ERC-4337 verification overhead. ## Construct the client ```ts theme={null} import { createEoaClient, viemAccountEoaSigner } from '@kashdao/protocol-sdk'; import { privateKeyToAccount } from 'viem/accounts'; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const client = createEoaClient({ chainId: 84532, // Base Sepolia rpc: process.env.KASH_BASE_RPC_URL ?? 'https://sepolia.base.org', signer: viemAccountEoaSigner(account), }); ``` `viemAccountEoaSigner` is one of several reference signer adapters. Others are documented under "Signers" — covers AWS KMS, Fireblocks, JSON-RPC remote signers (web3signer / clef), Privy embedded wallets, and Coinbase Smart Wallet. Or implement `EoaSignerAdapter` yourself; the protocol is two methods. ## Read market state ```ts theme={null} const minimal = await client.markets.get('0xMARKET_ADDRESS'); // → { marketAddress, numOutcomes, status: 'unseeded' | 'active' | 'frozen' | 'resolved' } const quote = await client.markets.quote('0xMARKET_ADDRESS', { side: 'BUY', outcome: 0, amount: usdc(10), // 10 USDC }); // → { side, outcomeIndex, amountIn, amountOut, reserveAfterWad, pricesAfterWad } ``` The read surface is **mode-agnostic** — same shapes whether you constructed an EOA client or a smart-account client. ## Place a trade — all-in-one `client.trades.send.buy(...)` is the highest-level entry. It builds, prepares (gas + fees + nonce), simulates, signs, submits, and (by default) waits for inclusion. Returns the on-chain receipt. ```ts theme={null} import { BuildBuyParams, usdc } from '@kashdao/protocol-sdk'; const result = await client.trades.send.buy( '0xMARKET_ADDRESS', { account: client.signer.ownerAddress, outcome: 0, amountUsdc: usdc(10), maxSlippageBps: 50, // 0.5% }, ); console.log(result.transactionHash, result.success, result.gasUsed); ``` The `account` field is checked at build time against `client.signer.ownerAddress` — passing a different address raises `KashSignerError(BUY_ACCOUNT_MISMATCH)` synchronously, before any RPC call. This catches the most common footgun (stale account from a profile switch) before it costs you a gas-paid revert. ## First trade — approve USDC The market contract needs an allowance to pull USDC for trades. Once per market per signer: ```ts theme={null} import { BuildApproveParams, MAX_UINT256 } from '@kashdao/protocol-sdk'; await client.trades.send.approve({ account: client.signer.ownerAddress, spender: MARKET_ADDRESS, amount: MAX_UINT256, // approve max; refresh once and you're done }); ``` After the first approve, every subsequent `send.buy` / `send.sell` proceeds without an extra approve hop. ## Power users — explicit lifecycle The trade lifecycle has three layers; pick the highest one that fits your control needs: | Layer | When you'd use it | | ---------------------------------------------------- | ---------------------------------------------------------------------- | | `client.trades.send.(...)` | Default. Hummingbot, AI agents, dashboards. | | `client.trades.prepare(...)` + manual submit | You want explicit control over signing — log + audit before signing. | | `client.trades.build(...)` + `hashOf(...)` | Construct the unsigned tx, hash it, route to a remote signer yourself. | The submit-side **staleness guard** kicks in automatically: if you hand-edit a built transaction's gas / nonce / chainId after signing, the SDK recovers the signing address from the signed bytes, sees a mismatch, and refuses to submit with `KashSignerError(STALE_SIGNED_TX)`. Bypass via `submit({ skipStalenessCheck: true })` only when you know the override is intentional. ## What's next Same surface, ERC-4337 v0.7 instead of EIP-1559. Typed error hierarchy, retry policies. Hummingbot strategies and Python market makers. # Quickstart — Smart-account mode Source: https://docs.kash.bot/developer-docs/protocol-sdk/quickstart-smart-account ERC-4337 v0.7 trading via SimpleAccount + bundler. Gasless onboarding via paymaster, batched ops, session keys. Smart-account mode is the canonical path for AA stacks (Privy embedded wallets, Coinbase Smart Wallet, Pimlico, Alchemy AA), self-custody UIs, and any flow that benefits from gasless onboarding via paymaster, batched approve+trade, or session keys. The trader is the **SimpleAccount v0.7** address — derived deterministically from the signer's owner address + a salt. The signer signs UserOps; the bundler relays them to the EntryPoint; the EntryPoint validates and executes. Kash is on neither side of the wire. ## Construct the client ```ts theme={null} import { createSmartAccountClient, viemAccountSigner } from '@kashdao/protocol-sdk'; import { privateKeyToAccount } from 'viem/accounts'; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const client = createSmartAccountClient({ chainId: 84532, // Base Sepolia rpc: process.env.KASH_BASE_RPC_URL ?? 'https://sepolia.base.org', signer: viemAccountSigner(account), bundler: process.env.KASH_BUNDLER_URL!, // e.g., https://api.pimlico.io/v2/84532/rpc?apikey=... }); ``` The bundler is **optional at construction**. Read-only consumers (`markets.*`, `account.*`) can omit it. The first call into `trades.*` will throw `KashConfigError(BUNDLER_REQUIRED)` if the bundler wasn't provided — explicit failure rather than silent. ## Bundler presets — pick yours Each preset is a vendor-tuned wrapper around the generic v0.7 bundler client. Subpath imports let your bundler keep its vendor-specific bytes out of consumers using a different vendor. ```ts theme={null} // Pimlico import { createPimlicoBundlerClient } from '@kashdao/protocol-sdk/bundler/pimlico'; // Alchemy import { createAlchemyBundlerClient } from '@kashdao/protocol-sdk/bundler/alchemy'; // Flashbots Protect (private RPC; ETH mainnet patterns extended to Base) import { createFlashbotsBundlerClient } from '@kashdao/protocol-sdk/bundler/flashbots'; // Generic — any bundler that speaks ERC-4337 v0.7 JSON-RPC import { createGenericBundlerClient } from '@kashdao/protocol-sdk/bundler/generic'; ``` Pass the resulting client directly as `bundler`: ```ts theme={null} const bundler = createPimlicoBundlerClient({ rpcUrl: process.env.PIMLICO_URL! }); const client = createSmartAccountClient({ chainId: 84532, rpc, signer, bundler }); ``` ## Derive the SmartAccount address ```ts theme={null} const sa = await client.account.computeAddress(client.signer.ownerAddress); console.log('SmartAccount address:', sa); ``` The address is deterministic — same owner + same salt → same address. The account doesn't need to be deployed for this to work; it's pure CREATE2 derivation. You can hand the address to a faucet, fund it, and trade in the next call. The `factory` UserOp fields auto-populate on the first trade so the deployment goes in the same UserOp as the actual transaction. After that they stay empty. ## Place a trade — all-in-one ```ts theme={null} import { BuildBuyParams, usdc } from '@kashdao/protocol-sdk'; const result = await client.trades.send.buy( '0xMARKET_ADDRESS', { // `account` is the shared `BuildBuyParams` field — in SA mode // pass the SmartAccount address (derived deterministically from // your signer's owner address). account: await client.account.computeAddress(client.signer.ownerAddress), outcome: 0, amountUsdc: usdc(10), maxSlippageBps: 50, }, ); console.log( result.userOpHash, // EntryPoint hash — what the signer signed result.transactionHash, // bundler-reported on-chain tx hash result.success, result.gasUsed, ); ``` The bundler health check runs automatically before the first trade in a session — saves a confusing `eth_estimateUserOperationGas` failure when the bundler URL is wrong. You can also explicitly probe via `await client.bundler.health()`. ## First-trade-with-deployment The first UserOp from a fresh SmartAccount carries `factory` + `factoryData` so the EntryPoint deploys the account in the same op. The SDK auto-detects this — you don't pass anything special. Subsequent UserOps elide the factory fields. Verification gas is \~5x higher on the deployment op (\~600k vs \~100k); the SDK's gas estimate accounts for this automatically via the bundler's `pm_estimateUserOperationGas` (or the generic equivalent). ## Power users — explicit UserOp lifecycle ```ts theme={null} const built = await client.trades.buildBuy(MARKET, params); // unsigned UserOp const ready = await client.trades.prepareBuy(MARKET, params); // built + populated gas + fees const signature = await client.signer.signUserOpHash( // your signer signs the userOpHash client.trades.hashOf(ready), ); const result = await client.trades.submit({ ...ready, signature }); // hands to bundler ``` `client.trades.hashOf(userOp)` is the canonical hash you sign over. Recompute it after populating gas + fees and BEFORE signing — otherwise the build-time hash is stale and the bundler will reject the UserOp with `AA24 signature error`. The auto-prepare path handles this; manual lifecycle users must remember the recompute. ## What's next Vanilla EIP-1559, no bundler, lower per-trade overhead. Typed error hierarchy. UserOp v0.7 hashes are byte-equal across the TS and Python SDKs by construction. # GET /api/markets/:id Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/market-detail Get detailed information for a specific prediction market Fetch detailed information for a specific prediction market, including optional price history and recent trades. *** ## Endpoint ``` GET https://app.kash.bot/api/markets/{id} ``` ## Path Parameters The unique market identifier ## Query Parameters Include price history snapshots (up to 100 data points) Include recent trades Number of recent trades to return (max: 100). Only applies when `trades=true` ## Response Complete market details Unique market identifier Market question Market description Current YES probability (0-1) Current YES share price Current NO share price Total trading volume in cents Number of trades Market status: `OPEN`, `FROZEN`, `RESOLVED`, `CANCELLED` Market end time (Unix timestamp in milliseconds) Market category ISO 8601 timestamp of market creation URL of the original market tweet Price history data (only if `history=true`) Price history snapshots (only included if `history=true`) ISO 8601 timestamp Price at this timestamp (0-1) Probability at this timestamp (0-1) Cumulative volume at this timestamp Recent trades (only included if `trades=true`) Trade ID Trade side: `YES` or `NO` Trade amount Execution price Trade timestamp ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/markets/abc123" ``` ```bash cURL (with history) theme={null} curl "https://app.kash.bot/api/markets/abc123?history=true&trades=true&trades_limit=10" ``` ```javascript JavaScript theme={null} const response = await fetch('https://app.kash.bot/api/markets/abc123'); const data = await response.json(); console.log(`Market: ${data.market.question}`); console.log(`Probability: ${(data.market.probability * 100).toFixed(1)}%`); console.log(`Volume: $${(data.market.totalVolume / 100).toLocaleString()}`); ``` ## Example Response ```json 200 - Success theme={null} { "market": { "id": "abc123", "question": "Will BTC hit $100k by end of 2025?", "description": "Resolves YES if Bitcoin reaches $100,000 USD on any major exchange before December 31, 2025", "probability": 0.72, "yesPrice": 0.72, "noPrice": 0.28, "totalVolume": 1250000, "tradeCount": 450, "status": "OPEN", "endTime": 1735689600000, "category": "crypto", "createdAt": "2024-01-01T00:00:00Z", "sourceTweetUrl": "https://x.com/kash_bot/status/123456789" }, "_meta": { "requestedAt": "2025-01-15T12:00:00Z", "includes": { "history": false, "trades": false, "tradesCount": 0 } } } ``` ```json 200 - With History & Trades theme={null} { "market": { "id": "abc123", "question": "Will BTC hit $100k by end of 2025?", "probability": 0.72, "priceHistory": [ { "timestamp": 1705320000000, "probability": 0.72 }, { "timestamp": 1705233600000, "probability": 0.68 }, { "timestamp": 1705147200000, "probability": 0.65 } ] }, "priceHistory": [ { "timestamp": "2025-01-15T12:00:00Z", "price": 0.72, "probability": 0.72, "volume": 1250000 }, { "timestamp": "2025-01-14T12:00:00Z", "price": 0.68, "probability": 0.68, "volume": 1100000 } ], "recentTrades": [ { "id": "trade-1", "side": "YES", "amount": 5000, "price": 0.72, "timestamp": "2025-01-15T11:30:00Z" }, { "id": "trade-2", "side": "NO", "amount": 2500, "price": 0.28, "timestamp": "2025-01-15T11:15:00Z" } ], "_meta": { "requestedAt": "2025-01-15T12:00:00Z", "includes": { "history": true, "trades": true, "tradesCount": 2 } } } ``` ```json 404 - Market Not Found theme={null} { "error": "Market not found" } ``` *** ## Use Cases ### Display Market Details ```javascript theme={null} async function displayMarket(marketId) { const res = await fetch(`https://app.kash.bot/api/markets/${marketId}`); const { market } = await res.json(); return { title: market.question, probability: `${(market.probability * 100).toFixed(0)}% YES`, volume: `$${(market.totalVolume / 100).toLocaleString()}`, status: market.status }; } ``` ### Build a Price Chart ```javascript theme={null} async function getChartData(marketId) { const res = await fetch(`https://app.kash.bot/api/markets/${marketId}?history=true`); const { priceHistory } = await res.json(); return priceHistory.map(point => ({ x: new Date(point.timestamp), y: point.probability * 100 })); } ``` # GET /api/markets/:id/quote Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/market-quote Get an on-chain AMM quote for buying or selling outcome tokens on a market Fetch a live buy-or-sell price quote for any outcome of a prediction market. The endpoint calls the AMM's own on-chain view functions — `quoteBuyExactAssetsIn` and `quoteSellExactTokensIn` on the `Market` contract — and returns the full protocol response, so integrators have access to the same numbers the UI uses. No authentication is required. *** ## Endpoint ``` GET https://app.kash.bot/api/markets/{id}/quote ``` ## Path Parameters The market's unique identifier (UUID). Use [`GET /api/markets`](/developer-docs/public-embed-api/endpoints/markets) to discover market IDs. ## Query Parameters Which outcome to price. Zero-indexed. For binary markets: `0` = YES, `1` = NO. Must be between `0` and `numOutcomes - 1`. `buy` quotes how many outcome tokens you receive for a given USDC input. `sell` quotes how much USDC you receive for a given outcome-token input (already net of the AMM's sell fee). Input size as a positive **integer in atomic units**, passed as a string to preserve precision. Units depend on `action`: * `buy` → **USDC atomic-6**. `1_000_000` = 1 USDC. * `sell` → **token WAD-18**. `1_000_000_000_000_000_000` = 1.0 tokens. *** ## Units All bigint values in the response are serialised as **decimal strings** so that callers can parse them losslessly with `BigInt(...)`. | Field family | Unit | Example | Notes | | --------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------- | ----------------------------------------- | | USDC (`amountIn`, `usdcOut`) | atomic-6 | `"1000000"` = 1 USDC | 6 decimals, matches USDC on Base. | | Tokens, prices, capital shares (`tokensOut`, `tokensIn`, `grossRelease`, `reserveAfter`, `c`, `pAfter[]`, `qAfter[]`) | WAD-18 | `"1000000000000000000"` = 1.0 | Protocol-internal 18-decimal fixed point. | The response always includes a `units` object reflecting the above so you never have to hardcode the convention on the caller side. ```json theme={null} "units": { "usdc": "atomic-6", "token": "wad-18" } ``` *** ## Response — Buy Echoes the requested outcome index. Always `"buy"` for this variant. USDC you would send in, atomic-6 (echoed from `amount`). Outcome tokens you would receive, WAD-18. The core value to show the user. AMM USDC-equivalent reserve after the hypothetical trade, WAD-18. Useful for modelling how large your trade is relative to pool depth. The Pythagorean coefficient (`c`) of the market's quadratic bonding curve after the trade, WAD-18. `c` captures the market's price curvature; it is preserved on buys (buy fee = 0) and decreases on sells. Marginal outcome prices after the trade, WAD-18, one per outcome. Values sum to 1e18 (i.e. a proper probability distribution). `pAfter[outcomeIndex]` is the post-trade probability the market is assigning to your chosen outcome. Capital shares after the trade, WAD-18, one per outcome. These tell you what fraction of the reserve is backing each outcome. Values sum to 1e18. Convenience number: `Number(amountIn) / Number(tokensOut)` — the average USDC-per-token price for this particular trade. Approximate (JS `number` precision); authoritative values are the string bigints above. Convenience number: `pAfter[outcomeIndex] / 1e18`. Handy for display; null if the market has no outcomes at the requested index. Summary of the market the quote was computed for: `id`, `contractAddress` (the on-chain `Market` contract), `chainId` (8453 = Base mainnet), `outcomes` (labels and DB-side probabilities), and `status`. `{ "usdc": "atomic-6", "token": "wad-18" }` — spelling out the units so you don't have to hardcode them. ## Response — Sell The sell response has the same shape as the buy response, with the following differences: Always `"sell"` for this variant. Outcome tokens you would burn, WAD-18 (echoed from `amount`). USDC you would receive, atomic-6. **This is already net of the AMM's sell fee** (`sellFeeBps`) — it is what the user actually gets. The pre-fee reserve release in WAD-18. The difference between `grossRelease` (converted to atomic USDC) and `usdcOut` is the fee the protocol retains. Exposed for integrators who want to display the fee transparently. All other fields (`reserveAfter`, `c`, `pAfter`, `qAfter`, `effectivePrice`, `impliedProbability`) behave identically to the buy response. *** ## Rate Limit **100 requests per minute** per IP address. Quotes are cached for 10 seconds on the server side; repeated requests for the same `(id, outcomeIndex, action, amount)` within that window are essentially free. ## Example Requests ```bash cURL (buy 1 USDC of YES) theme={null} curl "https://app.kash.bot/api/markets/abc123/quote?outcomeIndex=0&action=buy&amount=1000000" ``` ```bash cURL (sell 0.5 YES tokens) theme={null} curl "https://app.kash.bot/api/markets/abc123/quote?outcomeIndex=0&action=sell&amount=500000000000000000" ``` ```javascript JavaScript theme={null} // Buy 1 USDC of YES on a binary market const res = await fetch( 'https://app.kash.bot/api/markets/abc123/quote' + '?outcomeIndex=0&action=buy&amount=1000000' ); const { quote } = await res.json(); const tokensOut = Number(BigInt(quote.tokensOut)) / 1e18; console.log(`You would receive ${tokensOut.toFixed(4)} YES tokens`); console.log(`Implied probability after trade: ${(quote.impliedProbability * 100).toFixed(1)}%`); ``` ## Example Responses ```json 200 - Buy quote theme={null} { "quote": { "outcomeIndex": 0, "action": "buy", "amountIn": "1000000", "tokensOut": "1923076923076923076", "reserveAfter": "11000000000000000000", "c": "1000000000000000000", "pAfter": ["600000000000000000", "400000000000000000"], "qAfter": ["600000000000000000", "400000000000000000"], "effectivePrice": 5.2e-13, "impliedProbability": 0.6 }, "market": { "id": "abc123", "contractAddress": "0xc7785fca5b1e7fd2ebd3cc1b20e0e73bca3f1fed", "chainId": 8453, "outcomes": [ { "outcomeIndex": 0, "label": "YES", "probability": 0.5 }, { "outcomeIndex": 1, "label": "NO", "probability": 0.5 } ], "status": "ACTIVE" }, "units": { "usdc": "atomic-6", "token": "wad-18" } } ``` ```json 200 - Sell quote theme={null} { "quote": { "outcomeIndex": 0, "action": "sell", "tokensIn": "500000000000000000", "usdcOut": "237500", "grossRelease": "250000000000000000", "reserveAfter": "9750000000000000000", "c": "990000000000000000", "pAfter": ["498000000000000000", "502000000000000000"], "qAfter": ["498000000000000000", "502000000000000000"], "effectivePrice": 4.75e-13, "impliedProbability": 0.498 }, "market": { "id": "abc123", "contractAddress": "0x...", "chainId": 8453, "outcomes": [], "status": "ACTIVE" }, "units": { "usdc": "atomic-6", "token": "wad-18" } } ``` ```json 400 - Validation error theme={null} { "error": "ValidationError", "message": "Amount must be greater than zero" } ``` ```json 404 - Market not found theme={null} { "error": "NotFoundError", "message": "Market not found" } ``` *** ## Worked Examples Integrators rarely want the raw tuple — they want to answer real user questions. Here are the four most common ones, each mapped to one field. ### "If I put in \$100, how many YES tokens do I get?" ```javascript theme={null} // $100 in USDC atomic-6 = 100 * 1_000_000 const res = await fetch( 'https://app.kash.bot/api/markets/abc123/quote' + '?outcomeIndex=0&action=buy&amount=100000000' ); const { quote } = await res.json(); const tokens = Number(BigInt(quote.tokensOut)) / 1e18; console.log(`You receive ${tokens.toFixed(4)} YES tokens`); // → "You receive 177.6534 YES tokens" ``` ### "What's the average price I'd pay per token?" ```javascript theme={null} // amountIn is in USDC atomic-6, tokensOut in WAD-18 // To get $ / token: (amountIn / 1e6) / (tokensOut / 1e18) const usdcIn = Number(BigInt(quote.amountIn)) / 1e6; const tokens = Number(BigInt(quote.tokensOut)) / 1e18; const pricePerToken = usdcIn / tokens; console.log(`Average price: $${pricePerToken.toFixed(4)} / token`); // → "Average price: $0.5629 / token" ``` ### "What's the market probability after my trade?" ```javascript theme={null} // impliedProbability is already a 0..1 decimal — just multiply by 100 console.log( `After your $100 buy, YES is priced at ${(quote.impliedProbability * 100).toFixed(1)}%` ); // → "After your $100 buy, YES is priced at 56.3%" ``` For all outcomes at once, read `pAfter[]`: ```javascript theme={null} // pAfter values are WAD-18; divide by 1e18 to get probabilities quote.pAfter.forEach((p, i) => { const prob = Number(BigInt(p)) / 1e18; console.log(` Outcome ${i}: ${(prob * 100).toFixed(1)}%`); }); ``` ### "How much USDC do I get if I sell 1 YES token?" ```javascript theme={null} // 1 token in WAD-18 = 1_000000000000000000 const res = await fetch( 'https://app.kash.bot/api/markets/abc123/quote' + '?outcomeIndex=0&action=sell&amount=1000000000000000000' ); const { quote } = await res.json(); const usdc = Number(BigInt(quote.usdcOut)) / 1e6; // post-fee const gross = Number(BigInt(quote.grossRelease)) / 1e18; // pre-fee in USDC const fee = gross - usdc; console.log(`You receive $${usdc.toFixed(2)} (fee: $${fee.toFixed(4)})`); // → "You receive $0.28 (fee: $0.0035)" ``` ### Advanced: "How much would it cost to move the market to 70%?" There's no single-call answer — walk `amount` up and watch `pAfter[outcomeIndex]` move. Quotes are cached 10s server-side, so a client-side binary search is cheap: ```javascript theme={null} async function costToReach(marketId, outcomeIndex, targetProb) { let lo = 1_000_000n; // $1 let hi = 1_000_000_000_000n; // $1M for (let i = 0; i < 20; i++) { const mid = (lo + hi) / 2n; const r = await fetch( `https://app.kash.bot/api/markets/${marketId}/quote` + `?outcomeIndex=${outcomeIndex}&action=buy&amount=${mid}` ); const { quote } = await r.json(); if (quote.impliedProbability < targetProb) lo = mid; else hi = mid; } return Number(lo) / 1e6; // USDC } ``` *** ## Notes & Gotchas * **Quotes are view calls — nothing on-chain changes.** You can poll at will within the rate limit. * **`usdcOut` on sells is post-fee.** If you need to display the fee, compute `grossRelease / 1e18 * 1e6 - Number(usdcOut)` (or do it in bigint math). * **Buy fee is zero** on the current AMM; `tokensOut` reflects the full reserve increase. * **`pAfter` and `qAfter` always sum to `1e18`.** The last element may absorb up to 1 wei of rounding dust. * **`effectivePrice` is a JS-number convenience field** and can lose precision on tiny trades. For any authoritative display or accounting, derive values from the string bigint fields with `BigInt(...)`. * **Finding market IDs.** Use [`GET /api/markets`](/developer-docs/public-embed-api/endpoints/markets) for pagination/filtering or [`GET /api/markets/:id`](/developer-docs/public-embed-api/endpoints/market-detail) for a single market. Both are public and CORS-enabled. # GET /api/markets/:id/twitter-link Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/market-twitter-link Generate a Twitter prediction URL for a single market Generate a Twitter intent URL for a single market prediction. This enables you to build custom UIs that open Twitter with pre-filled prediction tweets. ## How It Works When users click your custom prediction buttons: 1. Twitter opens with a pre-composed prediction tweet 2. User posts the tweet 3. Our Twitter bot (@kash\_bot) detects and processes the prediction 4. User is automatically onboarded if they don't have an account **No wallet connection or login required!** Everything happens via Twitter. *** ## Endpoint ``` GET https://app.kash.bot/api/markets/{id}/twitter-link ``` ## Path Parameters The market ID ## Query Parameters Prediction side: `yes` or `no` Prediction amount in USD (1-10000) ## Response Full Twitter intent URL ready to open The pre-filled tweet content Market information Market ID Market question Original market tweet URL (for quote tweets) ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/markets/abc123/twitter-link?side=yes&amount=25" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/markets/abc123/twitter-link?side=yes&amount=25' ); const data = await response.json(); // Open Twitter with the prediction window.open(data.twitterUrl, '_blank'); ``` ## Example Response ```json 200 - Success theme={null} { "twitterUrl": "https://twitter.com/intent/tweet?text=%40kash_bot%20%2425%20on%20Yes%20https%3A%2F%2Ftwitter.com%2Fkash_bot%2Fstatus%2F123...", "tweetContent": "@kash_bot $25 on Yes https://twitter.com/kash_bot/status/123...", "market": { "id": "abc123", "question": "Will BTC hit $100k by end of 2025?", "sourceTweetUrl": "https://twitter.com/kash_bot/status/123..." } } ``` ```json 400 - Invalid Parameters theme={null} { "error": "Invalid or missing side parameter", "message": "side must be 'yes' or 'no'", "code": "INVALID_SIDE" } ``` ```json 404 - Market Not Found theme={null} { "error": "Market not found", "message": "No market found with id: xyz789", "code": "MARKET_NOT_FOUND" } ``` *** ## Custom UI Example Build your own prediction buttons: ```html theme={null} ``` # GET /api/markets Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/markets List all available prediction markets with filtering and pagination Fetches a paginated list of all available prediction markets with optional filtering. ## Endpoint ``` GET https://app.kash.bot/api/markets ``` ## Query Parameters Filter by market status: `active`, `resolved`, `cancelled`, `paused`, `refunded`, `all`. Defaults to `active`, so resolved markets are excluded unless you request them. Use `all` to fetch the full active + resolved set in one pass. Convenience filter that overrides `status` when present: `active=true` returns active markets, `active=false` returns resolved markets. Omit to keep the `status` default. Epoch-second lower bound on resolution time. Applies when the result set includes resolved markets (`active=false`, `status=resolved`, or `status=all`) — use it to slice a large resolved archive by date. Epoch-second upper bound on resolution time. Applies when the result set includes resolved markets (`active=false`, `status=resolved`, or `status=all`). Filter by category (e.g., `politics`, `crypto`, `sports`) Filter to only markets in a specific thread (UUID). Also accepts `thread_id` for backward compatibility. Filter to markets belonging to a specific organization (UUID) Filter to markets belonging to a specific partner (UUID) Filter to markets in a specific chapter Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by: `created_at`, `total_volume`, `current_probability`, `title`, `end_time` Sort direction: `asc`, `desc` ## Response Array of market objects Unique market identifier Market question Market description (optional) Current YES probability (0-1) Current YES share price Current NO share price Total trading volume in cents Number of trades Market status: `OPEN`, `FROZEN`, `RESOLVED`, `CANCELLED` Market end time (Unix timestamp in milliseconds) Market category ISO 8601 timestamp of market creation URL of the original market tweet (for quote tweets) Pagination metadata Current offset Results per page Total number of matching markets Whether more results are available ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/markets?status=active&limit=10&sortBy=total_volume&sortOrder=desc" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/markets?status=active&limit=10' ); const data = await response.json(); console.log(`Found ${data.markets.length} active markets`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/markets', params={'status': 'active', 'limit': 10} ) data = response.json() print(f"Found {len(data['markets'])} active markets") ``` ## Example Response ```json 200 - Success theme={null} { "markets": [ { "id": "market-abc123", "question": "Will BTC hit $100k by end of 2025?", "description": "Resolves YES if Bitcoin reaches $100,000 USD on any major exchange", "probability": 0.72, "yesPrice": 0.72, "noPrice": 0.28, "totalVolume": 1250000, "tradeCount": 450, "status": "OPEN", "endTime": 1735689600000, "category": "crypto", "createdAt": "2024-01-01T00:00:00Z", "sourceTweetUrl": "https://twitter.com/kash_bot/status/123..." } ], "pagination": { "offset": 0, "limit": 10, "total": 156, "hasMore": true } } ``` *** # GET /api/markets/trending Get trending markets based on recent activity and momentum. ## Endpoint ``` GET https://app.kash.bot/api/markets/trending ``` ## Query Parameters Trending timeframe: `1h`, `24h`, `7d` Maximum results (max: 200) Filter by category ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/markets/trending?timeframe=24h&limit=5" ``` ## Example Response ```json 200 - Success theme={null} { "markets": [ { "id": "market-xyz789", "question": "Will ETH flip BTC this year?", "probability": 0.15, "totalVolume": 890000, "tradeCount": 234, "trendingScore": 95.4, "trendingRank": 1, "recentMetrics": { "tradeCount": 45, "volume": 125000, "uniqueTraders": 28 } } ], "timeframe": { "trending": "24h" } } ``` *** # GET /api/markets/featured Get featured high-volume markets curated by Kash. ## Endpoint ``` GET https://app.kash.bot/api/markets/featured ``` ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/markets/featured" ``` ## Example Response ```json 200 - Success theme={null} { "markets": [ { "id": "market-featured-1", "question": "Who will win the 2024 Presidential Election?", "probability": 0.52, "totalVolume": 5600000, "tradeCount": 1250, "featured": true } ], "_meta": { "count": 6 } } ``` *** ## Common Use Cases ### Fetch high-volume markets ```bash theme={null} curl "https://app.kash.bot/api/markets?sortBy=total_volume&sortOrder=desc&limit=10" ``` ### Filter by category ```bash theme={null} curl "https://app.kash.bot/api/markets?category=crypto&status=active" ``` ### Fetch the full set (active + resolved) in one pass The endpoint returns only active markets by default. Pass `status=all` to include resolved markets, then page through with `offset`/`limit` (`pagination.total` and `pagination.hasMore` tell you when to stop). ```bash theme={null} curl "https://app.kash.bot/api/markets?status=all&limit=500" ``` ### Fetch only resolved markets ```bash theme={null} # active=false is equivalent to status=resolved curl "https://app.kash.bot/api/markets?active=false&limit=100" ``` ### Slice the resolved archive by date `resolvedAfter`/`resolvedBefore` are epoch seconds, applied to each market's resolution time — handy for analysing a large resolved set without pulling it all at once. ```bash theme={null} curl "https://app.kash.bot/api/markets?active=false&resolvedAfter=1782345600&resolvedBefore=1782432000" ``` ### Get markets from a specific thread ```bash theme={null} curl "https://app.kash.bot/api/markets?threadId=abc123-def456" ``` ### Get markets for an organization ```bash theme={null} curl "https://app.kash.bot/api/markets?organizationId=550e8400-e29b-41d4-a716-446655440000" ``` To show **only your organization's markets** on your own site, you can also use the dedicated [`GET /api/organizations/:id/markets`](/developer-docs/public-embed-api/endpoints/organization-markets) endpoint, or drop in the [organization iframe](/developer-docs/public-embed-api/guides/organization-iframe) for a ready-made branded grid. ### Get markets for a partner ```bash theme={null} curl "https://app.kash.bot/api/markets?partnerId=6ba7b810-9dad-11d1-80b4-00c04fd430c8" ``` ### Get markets in a specific chapter ```bash theme={null} curl "https://app.kash.bot/api/markets?chapterId=ch-group-stage" ``` ### Build a dynamic market feed ```javascript theme={null} async function fetchMarketFeed() { // Get trending markets const trendingRes = await fetch('https://app.kash.bot/api/markets/trending?limit=5'); const trending = await trendingRes.json(); // Get featured markets const featuredRes = await fetch('https://app.kash.bot/api/markets/featured'); const featured = await featuredRes.json(); // Combine and dedupe const allMarkets = [...featured.markets, ...trending.markets]; const uniqueMarkets = allMarkets.filter((m, i, arr) => arr.findIndex(x => x.id === m.id) === i ); return uniqueMarkets; } ``` *** ## Related Endpoints List a single organization's markets (scoped sub-resource) Drop your markets onto your site with one iframe Get organization metadata for filtering Get partner metadata for filtering List chapters to filter markets by List all threads # GET /api/organizations/:id Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/organization-detail Get public metadata for a specific organization Fetches public-safe metadata for an organization by its UUID. Sensitive data (credentials, billing) is never returned. ## Endpoint ``` GET https://app.kash.bot/api/organizations/:id ``` ## Path Parameters The unique organization identifier (UUID format) ## Response Organization metadata (public-safe fields only) Unique organization identifier Organization name URL-friendly slug Display name for UI rendering Short tagline or description Brand color in hex format (e.g., `#FBD109`) Logo image URL (nullable) Organization status (nullable) Request metadata ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000' ); const data = await response.json(); console.log(`Organization: ${data.organization.displayName}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000' ) data = response.json() print(f"Organization: {data['organization']['displayName']}") ``` ## Example Response ```json 200 - Success theme={null} { "organization": { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "acme-markets", "slug": "acme-markets", "displayName": "Acme Markets", "tagline": "Predict the future with Acme", "colorPrimary": "#4FC4FF", "logoUrl": "https://cdn.kash.bot/orgs/acme-logo.png", "status": "active" }, "_meta": { "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Not Found theme={null} { "error": "Organization not found", "message": "No organization exists with ID: 550e8400-e29b-41d4-a716-446655440000", "code": "NOT_FOUND" } ``` ```json 429 - Rate Limit Exceeded theme={null} { "error": "Too many requests", "message": "Rate limit of 100 requests per minute exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45 } ``` ## Response Headers ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1709553660000 Access-Control-Allow-Origin: * ``` ## Common Use Cases ### Fetch branding for a white-label embed ```javascript theme={null} async function applyOrgBranding(orgId) { const { organization } = await fetch( `https://app.kash.bot/api/organizations/${orgId}` ).then(r => r.json()); document.documentElement.style.setProperty( '--brand-color', organization.colorPrimary ); if (organization.logoUrl) { document.getElementById('logo').src = organization.logoUrl; } } ``` ## Related Endpoints List partners under this organization List threads under this organization Filter markets by organizationId # GET /api/organizations/:id/markets Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/organization-markets List an organization's markets with pagination, filtering, and sorting Fetches a paginated list of markets belonging to a specific organization. This is a convenience sub-resource — it returns exactly the same results as [`GET /api/markets?organizationId=:id`](/developer-docs/public-embed-api/endpoints/markets), scoped to a single organization so you only ever get back that organization's markets. ## Endpoint ``` GET https://app.kash.bot/api/organizations/:id/markets ``` ## Path Parameters The unique organization identifier (UUID format) ## Query Parameters Filter by market status: `active`, `resolved`, `cancelled`, `paused`, `refunded`, `all` Filter by category (e.g., `politics`, `crypto`, `sports`) Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by: `created_at`, `total_volume`, `current_probability`, `title`, `end_time` Sort direction: `asc`, `desc` ## Response Array of market objects (same shape as [`GET /api/markets`](/developer-docs/public-embed-api/endpoints/markets)) Unique market identifier Market question Current YES probability (0-1) Total trading volume in cents Market status Market end time (Unix timestamp in milliseconds) Market category ISO 8601 timestamp of market creation Pagination metadata Current offset Results per page Total number of matching markets Whether more results are available Request metadata The organization ID that was queried ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/markets?status=active&sortBy=total_volume&sortOrder=desc&limit=10" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/markets?' + new URLSearchParams({ status: 'active', sortBy: 'total_volume', sortOrder: 'desc', limit: 10 }) ); const data = await response.json(); console.log(`Found ${data.markets.length} markets`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/markets', params={ 'status': 'active', 'sortBy': 'total_volume', 'sortOrder': 'desc', 'limit': 10 } ) data = response.json() print(f"Found {len(data['markets'])} markets") ``` ## Example Response ```json 200 - Success theme={null} { "markets": [ { "id": "market-abc123", "question": "Will BTC hit $100k by end of 2025?", "probability": 0.72, "totalVolume": 1250000, "status": "OPEN", "endTime": 1735689600000, "category": "crypto", "createdAt": "2025-01-01T00:00:00Z" } ], "pagination": { "offset": 0, "limit": 10, "total": 24, "hasMore": true }, "_meta": { "organizationId": "550e8400-e29b-41d4-a716-446655440000", "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Organization Not Found theme={null} { "error": "Organization not found", "message": "No organization exists with ID: 550e8400-e29b-41d4-a716-446655440000", "code": "NOT_FOUND" } ``` ## Showing only your organization's markets Because the organization is fixed by the path, every market returned belongs to that organization — there is no way for another organization's markets to appear in the response. This is the recommended endpoint for powering an "our markets" feed on your own website. If you prefer the flat endpoint, the equivalent call is: ```bash theme={null} curl "https://app.kash.bot/api/markets?organizationId=550e8400-e29b-41d4-a716-446655440000" ``` ## Related Endpoints Drop your markets onto your site with a single iframe Get organization metadata (name, logo, branding) List all markets with the equivalent organizationId filter Generate a Twitter deep link to post & trade a market # GET /api/organizations/:id/partners Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/organization-partners List partners under an organization with pagination and filtering Fetches a paginated list of partners belonging to a specific organization. ## Endpoint ``` GET https://app.kash.bot/api/organizations/:id/partners ``` ## Path Parameters The unique organization identifier (UUID format) ## Query Parameters Filter by partner status: `active`, `inactive`, `all` Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages ## Response Array of partner objects Unique partner identifier Parent organization ID Partner name URL-friendly slug Partner description (nullable) Display name for UI rendering (nullable) Short tagline (nullable) Brand color in hex format (nullable) Logo image URL (nullable) Partner website URL (nullable) Partner status: `active`, `inactive` Pagination metadata Current offset Results per page Total number of matching partners Whether more results are available Request metadata The organization ID that was queried ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/partners?status=active&limit=10" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/partners?' + new URLSearchParams({ status: 'active', limit: 10 }) ); const data = await response.json(); console.log(`Found ${data.partners.length} partners`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/partners', params={'status': 'active', 'limit': 10} ) data = response.json() print(f"Found {len(data['partners'])} partners") ``` ## Example Response ```json 200 - Success theme={null} { "partners": [ { "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "organizationId": "550e8400-e29b-41d4-a716-446655440000", "name": "sports-daily", "slug": "sports-daily", "description": "Leading sports prediction marketplace", "displayName": "Sports Daily", "tagline": "Predict every game", "colorPrimary": "#3EF47E", "logoUrl": "https://cdn.kash.bot/partners/sports-daily-logo.png", "websiteUrl": "https://sportsdaily.example.com", "status": "active" } ], "pagination": { "offset": 0, "limit": 10, "total": 3, "hasMore": false }, "_meta": { "organizationId": "550e8400-e29b-41d4-a716-446655440000", "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Organization Not Found theme={null} { "error": "Organization not found", "message": "No organization exists with ID: 550e8400-e29b-41d4-a716-446655440000", "code": "NOT_FOUND" } ``` ## Common Use Cases ### Build a partner directory ```javascript theme={null} async function renderPartnerDirectory(orgId) { const { partners } = await fetch( `https://app.kash.bot/api/organizations/${orgId}/partners?status=active` ).then(r => r.json()); return partners.map(partner => ({ name: partner.displayName || partner.name, logo: partner.logoUrl, url: partner.websiteUrl, color: partner.colorPrimary, })); } ``` ### Fetch partners then drill into their threads ```javascript theme={null} async function getPartnerWithThreads(orgId) { const { partners } = await fetch( `https://app.kash.bot/api/organizations/${orgId}/partners` ).then(r => r.json()); // Fetch threads for each partner const results = await Promise.all( partners.map(async (partner) => { const { threads } = await fetch( `https://app.kash.bot/api/partners/${partner.id}/threads` ).then(r => r.json()); return { partner, threads }; }) ); return results; } ``` ## Related Endpoints Get organization metadata Get individual partner details List threads for a partner # GET /api/organizations/:id/threads Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/organization-threads List threads under an organization with pagination, filtering, and sorting Fetches a paginated list of threads belonging to a specific organization. Supports status filtering and sorting. ## Endpoint ``` GET https://app.kash.bot/api/organizations/:id/threads ``` ## Path Parameters The unique organization identifier (UUID format) ## Query Parameters Filter by thread status: `active`, `upcoming`, `closed`, `all` Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by: `created_at`, `start_time`, `total_volume` Sort direction: `asc`, `desc` ## Response Array of thread objects Unique thread identifier Thread title (nullable) Thread description (nullable) Cover image URL (nullable) Thread status (nullable) ISO 8601 start time (nullable) ISO 8601 end time (nullable) Number of markets in this thread (nullable) Total trading volume as a numeric string (nullable) Number of unique participants (nullable) ISO 8601 timestamp of thread creation Pagination metadata Current offset Results per page Total number of matching threads Whether more results are available Request metadata The organization ID that was queried ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/threads?status=active&sortBy=total_volume&sortOrder=desc&limit=10" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/threads?' + new URLSearchParams({ status: 'active', sortBy: 'total_volume', sortOrder: 'desc', limit: 10 }) ); const data = await response.json(); console.log(`Found ${data.threads.length} threads`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/organizations/550e8400-e29b-41d4-a716-446655440000/threads', params={ 'status': 'active', 'sortBy': 'total_volume', 'sortOrder': 'desc', 'limit': 10 } ) data = response.json() print(f"Found {len(data['threads'])} threads") ``` ## Example Response ```json 200 - Success theme={null} { "threads": [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "title": "2026 World Cup Predictions", "description": "Predict match outcomes for the 2026 FIFA World Cup", "image": "https://cdn.kash.bot/threads/world-cup-2026.jpg", "status": "active", "startTime": "2026-06-11T00:00:00Z", "endTime": "2026-07-19T00:00:00Z", "marketCount": 48, "totalVolume": "5600000", "participantCount": 1250, "createdAt": "2026-01-15T00:00:00Z" } ], "pagination": { "offset": 0, "limit": 10, "total": 5, "hasMore": false }, "_meta": { "organizationId": "550e8400-e29b-41d4-a716-446655440000", "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Organization Not Found theme={null} { "error": "Organization not found", "message": "No organization exists with ID: 550e8400-e29b-41d4-a716-446655440000", "code": "NOT_FOUND" } ``` ## Common Use Cases ### Build an organization landing page ```javascript theme={null} async function buildOrgPage(orgId) { const [orgRes, threadsRes] = await Promise.all([ fetch(`https://app.kash.bot/api/organizations/${orgId}`), fetch(`https://app.kash.bot/api/organizations/${orgId}/threads?status=active&sortBy=total_volume&sortOrder=desc`), ]); const { organization } = await orgRes.json(); const { threads } = await threadsRes.json(); return { organization, threads }; } ``` ## Related Endpoints Get organization metadata List partners under this organization Get details for a specific thread # GET /api/partners/:id Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/partner-detail Get public metadata for a specific partner Fetches public metadata for a partner by its UUID, including branding information. ## Endpoint ``` GET https://app.kash.bot/api/partners/:id ``` ## Path Parameters The unique partner identifier (UUID format) ## Response Partner metadata Unique partner identifier Parent organization ID Partner name URL-friendly slug Partner description (nullable) Display name for UI rendering (nullable) Short tagline (nullable) Brand color in hex format (nullable) Logo image URL (nullable) Partner website URL (nullable) Partner status: `active`, `inactive` Request metadata ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8' ); const data = await response.json(); console.log(`Partner: ${data.partner.displayName}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8' ) data = response.json() print(f"Partner: {data['partner']['displayName']}") ``` ## Example Response ```json 200 - Success theme={null} { "partner": { "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "organizationId": "550e8400-e29b-41d4-a716-446655440000", "name": "sports-daily", "slug": "sports-daily", "description": "Leading sports prediction marketplace", "displayName": "Sports Daily", "tagline": "Predict every game", "colorPrimary": "#3EF47E", "logoUrl": "https://cdn.kash.bot/partners/sports-daily-logo.png", "websiteUrl": "https://sportsdaily.example.com", "status": "active" }, "_meta": { "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Not Found theme={null} { "error": "Partner not found", "message": "No partner exists with ID: 6ba7b810-9dad-11d1-80b4-00c04fd430c8", "code": "NOT_FOUND" } ``` ```json 429 - Rate Limit Exceeded theme={null} { "error": "Too many requests", "message": "Rate limit of 100 requests per minute exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45 } ``` ## Common Use Cases ### Apply partner branding to an embed ```javascript theme={null} async function applyPartnerBranding(partnerId) { const { partner } = await fetch( `https://app.kash.bot/api/partners/${partnerId}` ).then(r => r.json()); return { name: partner.displayName || partner.name, color: partner.colorPrimary, logo: partner.logoUrl, tagline: partner.tagline, }; } ``` ### Navigate from partner to its organization ```javascript theme={null} async function getPartnerOrg(partnerId) { const { partner } = await fetch( `https://app.kash.bot/api/partners/${partnerId}` ).then(r => r.json()); const { organization } = await fetch( `https://app.kash.bot/api/organizations/${partner.organizationId}` ).then(r => r.json()); return { partner, organization }; } ``` ## Related Endpoints List threads for this partner Get the parent organization Filter markets by partnerId # GET /api/partners/:id/threads Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/partner-threads List threads under a partner with pagination, filtering, and sorting Fetches a paginated list of threads belonging to a specific partner. Supports status filtering and sorting. ## Endpoint ``` GET https://app.kash.bot/api/partners/:id/threads ``` ## Path Parameters The unique partner identifier (UUID format) ## Query Parameters Filter by thread status: `active`, `upcoming`, `closed`, `all` Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by: `created_at`, `start_time`, `total_volume` Sort direction: `asc`, `desc` ## Response Array of thread objects Unique thread identifier Thread title (nullable) Thread description (nullable) Cover image URL (nullable) Thread status (nullable) ISO 8601 start time (nullable) ISO 8601 end time (nullable) Number of markets in this thread (nullable) Total trading volume as a numeric string (nullable) Number of unique participants (nullable) ISO 8601 timestamp of thread creation Pagination metadata Current offset Results per page Total number of matching threads Whether more results are available Request metadata The partner ID that was queried ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8/threads?status=active&limit=10" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8/threads?' + new URLSearchParams({ status: 'active', limit: 10 }) ); const data = await response.json(); console.log(`Found ${data.threads.length} threads`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/partners/6ba7b810-9dad-11d1-80b4-00c04fd430c8/threads', params={'status': 'active', 'limit': 10} ) data = response.json() print(f"Found {len(data['threads'])} threads") ``` ## Example Response ```json 200 - Success theme={null} { "threads": [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "title": "NBA Playoffs 2026", "description": "Predict the outcomes of every NBA playoff series", "image": "https://cdn.kash.bot/threads/nba-playoffs.jpg", "status": "active", "startTime": "2026-04-15T00:00:00Z", "endTime": "2026-06-20T00:00:00Z", "marketCount": 30, "totalVolume": "3200000", "participantCount": 890, "createdAt": "2026-03-01T00:00:00Z" } ], "pagination": { "offset": 0, "limit": 10, "total": 2, "hasMore": false }, "_meta": { "partnerId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Partner Not Found theme={null} { "error": "Partner not found", "message": "No partner exists with ID: 6ba7b810-9dad-11d1-80b4-00c04fd430c8", "code": "NOT_FOUND" } ``` ## Common Use Cases ### Build a partner page with threads and markets ```javascript theme={null} async function buildPartnerPage(partnerId) { // Fetch partner branding + threads in parallel const [partnerRes, threadsRes] = await Promise.all([ fetch(`https://app.kash.bot/api/partners/${partnerId}`), fetch(`https://app.kash.bot/api/partners/${partnerId}/threads?status=active`), ]); const { partner } = await partnerRes.json(); const { threads } = await threadsRes.json(); // Fetch markets for each thread const threadsWithMarkets = await Promise.all( threads.map(async (thread) => { const { markets } = await fetch( `https://app.kash.bot/api/threads/${thread.id}/markets?limit=5` ).then(r => r.json()); return { ...thread, markets }; }) ); return { partner, threads: threadsWithMarkets }; } ``` ## Related Endpoints Get partner metadata Get thread details Get markets in a thread # GET /api/threads/:id/chapters Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/thread-chapters List chapters within a thread, derived from market groupings Fetches all chapters within a thread. Chapters are logical groupings of markets derived from denormalized fields on the markets table, ordered by position. ## Endpoint ``` GET https://app.kash.bot/api/threads/:id/chapters ``` ## Path Parameters The unique thread identifier (UUID format) ## Response Array of chapter summary objects, ordered by position Unique chapter identifier Chapter title (nullable) Chapter position within the thread (nullable, used for ordering) Total number of chapters in the thread (nullable) Number of markets in this chapter Request metadata The thread ID that was queried ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/threads/7c9e6679-7425-40de-944b-e07fc1f90ae7/chapters" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/threads/7c9e6679-7425-40de-944b-e07fc1f90ae7/chapters' ); const data = await response.json(); console.log(`Found ${data.chapters.length} chapters`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/threads/7c9e6679-7425-40de-944b-e07fc1f90ae7/chapters' ) data = response.json() print(f"Found {len(data['chapters'])} chapters") ``` ## Example Response ```json 200 - Success theme={null} { "chapters": [ { "chapterId": "ch-group-stage", "title": "Group Stage", "position": 1, "total": 4, "marketCount": 24 }, { "chapterId": "ch-round-of-16", "title": "Round of 16", "position": 2, "total": 4, "marketCount": 8 }, { "chapterId": "ch-quarter-finals", "title": "Quarter Finals", "position": 3, "total": 4, "marketCount": 4 }, { "chapterId": "ch-finals", "title": "Semi Finals & Final", "position": 4, "total": 4, "marketCount": 3 } ], "_meta": { "threadId": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "requestedAt": "2026-03-04T12:00:00Z" } } ``` ```json 404 - Thread Not Found theme={null} { "error": "Thread not found", "message": "No thread exists with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7", "code": "NOT_FOUND" } ``` ## Common Use Cases ### Build a chapter navigation sidebar ```javascript theme={null} async function buildChapterNav(threadId) { const { chapters } = await fetch( `https://app.kash.bot/api/threads/${threadId}/chapters` ).then(r => r.json()); return chapters.map(ch => ({ id: ch.chapterId, label: ch.title || `Chapter ${ch.position}`, marketCount: ch.marketCount, active: false, })); } ``` ### Fetch markets for a specific chapter Use the `chapterId` from this endpoint to filter markets: ```javascript theme={null} async function getChapterMarkets(threadId, chapterId) { // Get chapter info const { chapters } = await fetch( `https://app.kash.bot/api/threads/${threadId}/chapters` ).then(r => r.json()); const chapter = chapters.find(ch => ch.chapterId === chapterId); // Get markets filtered by chapterId const { markets } = await fetch( `https://app.kash.bot/api/markets?` + new URLSearchParams({ chapterId, status: 'active' }) ).then(r => r.json()); return { chapter, markets }; } ``` ### Build a complete thread page with chapters ```javascript theme={null} async function buildThreadWithChapters(threadId) { // Fetch thread details and chapters in parallel const [threadRes, chaptersRes] = await Promise.all([ fetch(`https://app.kash.bot/api/threads/${threadId}`), fetch(`https://app.kash.bot/api/threads/${threadId}/chapters`), ]); const { thread } = await threadRes.json(); const { chapters } = await chaptersRes.json(); // Fetch markets for each chapter const chaptersWithMarkets = await Promise.all( chapters.map(async (chapter) => { const { markets } = await fetch( `https://app.kash.bot/api/markets?chapterId=${chapter.chapterId}` ).then(r => r.json()); return { ...chapter, markets }; }) ); return { thread, chapters: chaptersWithMarkets }; } ``` ## Related Endpoints Get thread details Get all markets in a thread Filter markets by chapterId # GET /api/threads/:id Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/thread-detail Get details for a specific prediction market thread Fetches detailed information for a single prediction market thread by its ID. ## Endpoint ``` GET https://app.kash.bot/api/threads/:id ``` ## Path Parameters The unique thread identifier (UUID format) ## Query Parameters When `true`, the response also includes the thread's markets alongside the thread details. Defaults to `false` (thread details only). ## Response Thread details object Unique thread identifier Thread title Thread description (may include markdown) Thread cover image URL (IPFS or CDN) Background image URL for enhanced displays Total trading volume across all markets (in cents) Number of markets in this thread Number of unique participants Thread start time (Unix timestamp in seconds, optional) Thread end time (Unix timestamp in seconds, optional) Current status (derived from start/end times): `active`, `upcoming`, `closed` Thread category (e.g., `politics`, `crypto`, `sports`) Array of tag strings for filtering and discovery ISO 8601 timestamp of thread creation ISO 8601 timestamp of last update Request metadata Whether the request was authenticated ISO 8601 timestamp of request processing ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/threads/abc123-def456" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/threads/abc123-def456' ); const data = await response.json(); console.log(`Thread: ${data.thread.title}`); console.log(`Markets: ${data.thread.marketCount}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/threads/abc123-def456' ) data = response.json() print(f"Thread: {data['thread']['title']}") print(f"Markets: {data['thread']['marketCount']}") ``` ## Example Response ```json 200 - Success theme={null} { "thread": { "id": "abc123-def456", "title": "2024 Election Predictions", "description": "Make predictions on the 2024 US Presidential Election and related political events.", "image": "https://ipfs.io/ipfs/QmXyz...", "backgroundImage": "https://ipfs.io/ipfs/QmAbc...", "totalVolume": 1250000, "marketCount": 12, "participantCount": 342, "startTime": 1704067200, "endTime": 1735689600, "status": "active", "category": "politics", "tags": ["election", "politics", "2024", "usa"], "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T12:30:00Z" }, "_meta": { "authenticated": false, "requestedAt": "2024-01-16T12:00:00Z" } } ``` ```json 404 - Not Found theme={null} { "error": "Thread not found", "message": "No thread exists with ID: abc123-def456", "code": "THREAD_NOT_FOUND" } ``` ```json 429 - Rate Limit Exceeded theme={null} { "error": "Too many requests", "message": "Rate limit of 100 requests per minute exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45 } ``` ## Response Headers All responses include rate limit headers: ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1704067260000 Access-Control-Allow-Origin: * ``` ## Common Use Cases ### Fetch thread before embedding Before embedding a thread iframe, fetch its details to display metadata: ```javascript theme={null} async function embedThread(threadId) { // Fetch thread details const response = await fetch(`https://app.kash.bot/api/threads/${threadId}`); const { thread } = await response.json(); // Display thread info above iframe document.getElementById('thread-title').textContent = thread.title; document.getElementById('thread-stats').textContent = `${thread.marketCount} markets • ${thread.participantCount} participants`; // Embed iframe const iframe = document.createElement('iframe'); iframe.src = `https://app.kash.bot/threads/${threadId}/iframe`; iframe.width = '100%'; iframe.height = '900'; document.getElementById('embed-container').appendChild(iframe); } ``` ### Check thread status before displaying Avoid embedding inactive threads: ```javascript theme={null} const { thread } = await fetch(`https://app.kash.bot/api/threads/${threadId}`) .then(r => r.json()); if (thread.status !== 'active') { console.log('Thread is not active, showing fallback content'); return; } // Proceed with embedding ``` ### Build custom thread cards Create custom UI cards with thread data: ```jsx React Component theme={null} function ThreadCard({ threadId }) { const [thread, setThread] = useState(null); useEffect(() => { fetch(`https://app.kash.bot/api/threads/${threadId}`) .then(r => r.json()) .then(data => setThread(data.thread)); }, [threadId]); if (!thread) return
Loading...
; return (
{thread.title}

{thread.title}

{thread.description}

{thread.marketCount} markets ${(thread.totalVolume / 100).toLocaleString()} volume
); } ``` ## Related Endpoints List all available threads with filtering Get all markets within this thread # GET /api/threads/:id/markets Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/thread-markets Get all prediction markets within a specific thread Fetches all prediction markets that belong to a specific thread, with filtering, sorting, and pagination. ## Endpoint ``` GET https://app.kash.bot/api/threads/:id/markets ``` ## Path Parameters The unique thread identifier (UUID format) ## Query Parameters Filter by market status. Options: `active`, `resolved`, `cancelled`, `paused` Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by. Options: `created_at`, `total_volume`, `current_probability`, `title` Sort direction. Options: `asc`, `desc` ## Response Array of market objects in this thread Unique market identifier The prediction market question Market description with resolution criteria Current market probability (0-1, e.g., 0.72 = 72%) Current price for YES shares (0-1) Current price for NO shares (0-1) Total trading volume for this market (in cents) Number of trades executed Market status: `active`, `resolved`, `cancelled`, `paused` Market end time (Unix timestamp in milliseconds) Market category Array of tag strings ISO 8601 timestamp of market creation Pagination metadata Current offset Results per page Total number of matching markets Whether more results are available ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/threads/abc123/markets?status=active&limit=10&sortBy=current_probability&sortOrder=desc" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/threads/abc123/markets?' + new URLSearchParams({ status: 'active', limit: 10, sortBy: 'current_probability', sortOrder: 'desc' }) ); const data = await response.json(); console.log(`Found ${data.markets.length} markets`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/threads/abc123/markets', params={ 'status': 'active', 'limit': 10, 'sortBy': 'current_probability', 'sortOrder': 'desc' } ) data = response.json() print(f"Found {len(data['markets'])} markets") ``` ## Example Response ```json 200 - Success theme={null} { "markets": [ { "id": "market-1", "question": "Will Biden win the Democratic nomination?", "description": "This market resolves YES if Joe Biden is officially nominated as the Democratic Party candidate for the 2024 Presidential Election.", "currentProbability": 0.72, "yesPrice": 0.72, "noPrice": 0.28, "totalVolume": 125000, "tradeCount": 450, "status": "active", "endTime": 1735689600000, "category": "politics", "tags": ["biden", "nomination", "2024"], "createdAt": "2024-01-01T00:00:00Z" }, { "id": "market-2", "question": "Will Trump run as independent candidate?", "description": "Resolves YES if Donald Trump announces an independent presidential campaign for 2024.", "currentProbability": 0.15, "yesPrice": 0.15, "noPrice": 0.85, "totalVolume": 89000, "tradeCount": 320, "status": "active", "endTime": 1735689600000, "category": "politics", "tags": ["trump", "independent", "2024"], "createdAt": "2024-01-02T00:00:00Z" } ], "pagination": { "offset": 0, "limit": 10, "total": 12, "hasMore": true }, "_meta": { "authenticated": false, "requestedAt": "2024-01-16T12:00:00Z" } } ``` ```json 404 - Thread Not Found theme={null} { "error": "Thread not found", "message": "No thread exists with ID: abc123", "code": "THREAD_NOT_FOUND" } ``` ```json 429 - Rate Limit Exceeded theme={null} { "error": "Too many requests", "message": "Rate limit of 100 requests per minute exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45 } ``` ## Common Use Cases ### Build a custom thread page Fetch all markets to build a custom thread display: ```javascript theme={null} async function buildThreadPage(threadId) { // Fetch thread details const threadResponse = await fetch( `https://app.kash.bot/api/threads/${threadId}` ); const { thread } = await threadResponse.json(); // Fetch all markets in thread const marketsResponse = await fetch( `https://app.kash.bot/api/threads/${threadId}/markets?limit=100` ); const { markets } = await marketsResponse.json(); return { thread, markets }; } ``` ### Show top markets by volume Display the most actively traded markets in a thread: ```javascript theme={null} const response = await fetch( `https://app.kash.bot/api/threads/${threadId}/markets?` + new URLSearchParams({ status: 'active', sortBy: 'total_volume', sortOrder: 'desc', limit: 5 }) ); const { markets } = await response.json(); console.log('Top 5 markets by volume:', markets); ``` ### Paginate through all markets Fetch all markets in batches: ```javascript theme={null} async function* fetchAllMarkets(threadId) { let offset = 0; let hasMore = true; while (hasMore) { const response = await fetch( `https://app.kash.bot/api/threads/${threadId}/markets?` + new URLSearchParams({ limit: 100, offset }) ); const data = await response.json(); for (const market of data.markets) { yield market; } hasMore = data.pagination.hasMore; offset += 100; } } // Usage for await (const market of fetchAllMarkets('abc123')) { console.log(market.question); } ``` ### Filter active markets only Show only currently active markets for trading: ```javascript theme={null} const response = await fetch( `https://app.kash.bot/api/threads/${threadId}/markets?status=active` ); const { markets } = await response.json(); // All markets are active and ready for trading const activeMarkets = markets.filter(m => m.status === 'active'); ``` ### Build a probability leaderboard Show markets ranked by probability: ```jsx React Component theme={null} function MarketLeaderboard({ threadId }) { const [markets, setMarkets] = useState([]); useEffect(() => { fetch( `https://app.kash.bot/api/threads/${threadId}/markets?` + new URLSearchParams({ status: 'active', sortBy: 'current_probability', sortOrder: 'desc', limit: 10 }) ) .then(r => r.json()) .then(data => setMarkets(data.markets)); }, [threadId]); return (

Most Likely Outcomes

{markets.map(market => (
{market.question} {(market.currentProbability * 100).toFixed(0)}%
))}
); } ``` ## Related Endpoints List all available threads Get thread details # GET /api/threads/:id/twitter-links Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/thread-twitter-links Generate Twitter prediction URLs for all markets in a thread Generate Twitter intent URLs for all active markets in a thread. Returns both YES and NO links for each market, making it easy to build custom thread UIs. *** ## Endpoint ``` GET https://app.kash.bot/api/threads/{id}/twitter-links ``` ## Path Parameters The thread ID ## Query Parameters Default prediction amount in USD for all markets (1-10000) ## Response Thread information Thread ID Thread title Thread description Array of markets with Twitter links Market ID Market question Original market tweet URL Twitter URL for YES prediction Twitter URL for NO prediction Tweet content for YES prediction Tweet content for NO prediction ## Rate Limit **100 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/threads/thread123/twitter-links?amount=25" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/threads/thread123/twitter-links?amount=25' ); const data = await response.json(); // Render buttons for each market data.markets.forEach(market => { console.log(`${market.question}`); console.log(` Yes: ${market.yesLink}`); console.log(` No: ${market.noLink}`); }); ``` ## Example Response ```json 200 - Success theme={null} { "thread": { "id": "thread123", "title": "2024 Election Predictions", "description": "Markets for the 2024 US Presidential Election" }, "markets": [ { "marketId": "market-1", "question": "Will Biden win the nomination?", "sourceTweetUrl": "https://twitter.com/kash_bot/status/123...", "yesLink": "https://twitter.com/intent/tweet?text=...", "noLink": "https://twitter.com/intent/tweet?text=...", "yesTweetContent": "@kash_bot $25 on Yes https://twitter.com/...", "noTweetContent": "@kash_bot $25 on No https://twitter.com/..." }, { "marketId": "market-2", "question": "Will Trump be the Republican nominee?", "sourceTweetUrl": "https://twitter.com/kash_bot/status/456...", "yesLink": "https://twitter.com/intent/tweet?text=...", "noLink": "https://twitter.com/intent/tweet?text=...", "yesTweetContent": "@kash_bot $25 on Yes https://twitter.com/...", "noTweetContent": "@kash_bot $25 on No https://twitter.com/..." } ], "_meta": { "requestedAt": "2025-01-15T12:00:00Z", "amount": 25, "totalMarkets": 2 } } ``` # GET /api/threads Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/threads List all available prediction market threads Fetches a paginated list of all available prediction market threads. ## Endpoint ``` GET https://app.kash.bot/api/threads ``` ## Query Parameters Filter by thread status (derived from the thread's start/end times). Options: `active`, `upcoming`, `closed`, `all` Filter by category (e.g., `politics`, `crypto`, `sports`) Comma-separated list of tags to filter by (e.g., `election,2024`) Maximum number of results per page (max: 100) Pagination offset for fetching subsequent pages Field to sort by. Options: `created_at`, `start_time`, `total_volume` Sort direction. Options: `asc`, `desc` ## Response Array of thread objects Unique thread identifier Thread title Thread description (optional) Total trading volume across all markets in cents Number of markets in this thread Number of unique participants Thread start time (Unix timestamp in seconds) Thread end time (Unix timestamp in seconds) Current status (derived from start/end times): `active`, `upcoming`, `closed` Thread category Array of tag strings ISO 8601 timestamp of thread creation ISO 8601 timestamp of last update Pagination metadata Current offset Results per page Total number of matching threads Whether more results are available ## Rate Limit **50 requests per minute** per IP address ## Example Request ```bash cURL theme={null} curl "https://app.kash.bot/api/threads?status=active&limit=10&sortBy=total_volume&sortOrder=desc" ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://app.kash.bot/api/threads?status=active&limit=10' ); const data = await response.json(); console.log(`Found ${data.threads.length} active threads`); ``` ```python Python theme={null} import requests response = requests.get( 'https://app.kash.bot/api/threads', params={'status': 'active', 'limit': 10} ) data = response.json() print(f"Found {len(data['threads'])} active threads") ``` ## Example Response ```json 200 - Success theme={null} { "threads": [ { "id": "abc123-def456", "title": "2024 Election Predictions", "description": "Markets for the 2024 US Presidential Election", "totalVolume": 1250000, "marketCount": 12, "participantCount": 342, "startTime": 1704067200, "endTime": 1735689600, "status": "active", "category": "politics", "tags": ["election", "politics", "2024"], "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T12:30:00Z" }, { "id": "xyz789-abc123", "title": "Crypto Price Predictions 2024", "description": "Will Bitcoin reach $100k?", "totalVolume": 980000, "marketCount": 8, "participantCount": 256, "startTime": 1704067200, "endTime": 1735689600, "status": "active", "category": "crypto", "tags": ["bitcoin", "crypto", "2024"], "createdAt": "2024-01-05T00:00:00Z", "updatedAt": "2024-01-16T08:15:00Z" } ], "pagination": { "offset": 0, "limit": 10, "total": 25, "hasMore": true }, "_meta": { "authenticated": false, "requestedAt": "2024-01-16T12:00:00Z" } } ``` ```json 400 - Bad Request theme={null} { "error": "Invalid query parameters", "message": "limit must be between 1 and 100", "code": "INVALID_REQUEST" } ``` ```json 429 - Rate Limit Exceeded theme={null} { "error": "Too many requests", "message": "Rate limit of 50 requests per minute exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45 } ``` ## Response Headers All responses include rate limit headers: ``` X-RateLimit-Limit: 50 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 1704067260000 Access-Control-Allow-Origin: * ``` ## Common Use Cases ### Fetch active threads with high volume ```bash theme={null} curl "https://app.kash.bot/api/threads?status=active&sortBy=total_volume&sortOrder=desc&limit=5" ``` ### Filter by category and tags ```bash theme={null} curl "https://app.kash.bot/api/threads?category=politics&tags=election,2024" ``` ### Paginate through all threads ```javascript theme={null} async function fetchAllThreads() { let allThreads = []; let offset = 0; let hasMore = true; while (hasMore) { const response = await fetch( `https://app.kash.bot/api/threads?limit=100&offset=${offset}` ); const data = await response.json(); allThreads = allThreads.concat(data.threads); hasMore = data.pagination.hasMore; offset += 100; } return allThreads; } ``` ## Related Endpoints Get details for a specific thread Get all markets in a thread List chapters within a thread List threads for an organization # POST /api/twitter-links Source: https://docs.kash.bot/developer-docs/public-embed-api/endpoints/twitter-links-batch Generate Twitter prediction URLs for multiple markets in batch Generate Twitter intent URLs for multiple markets in a single request. Useful for building custom UIs with multiple prediction buttons. *** ## Endpoint ``` POST https://app.kash.bot/api/twitter-links ``` ## Request Body ```json theme={null} { "markets": [ { "marketId": "abc123", "side": "yes", "amount": 25 }, { "marketId": "def456", "side": "no", "amount": 50 } ] } ``` Array of market prediction requests (max 50) Market ID Prediction side: `yes` or `no` Prediction amount in USD (1-10000) ## Response Successfully generated links Market ID Market question Original market tweet URL Twitter intent URL Tweet content Prediction side Prediction amount Failed requests with error messages Market ID that failed Error message ## Rate Limit **100 requests per minute** per IP address (max 50 markets per request) ## Example Request ```bash cURL theme={null} curl -X POST "https://app.kash.bot/api/twitter-links" \ -H "Content-Type: application/json" \ -d '{ "markets": [ { "marketId": "abc123", "side": "yes", "amount": 25 }, { "marketId": "def456", "side": "no", "amount": 50 } ] }' ``` ```javascript JavaScript theme={null} const response = await fetch('https://app.kash.bot/api/twitter-links', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ markets: [ { marketId: 'abc123', side: 'yes', amount: 25 }, { marketId: 'def456', side: 'no', amount: 50 } ] }) }); const data = await response.json(); console.log(`Generated ${data.links.length} links, ${data.errors.length} errors`); ``` ## Example Response ```json 200 - Success theme={null} { "links": [ { "marketId": "abc123", "question": "Will BTC hit $100k?", "sourceTweetUrl": "https://twitter.com/...", "twitterUrl": "https://twitter.com/intent/tweet?text=...", "tweetContent": "@kash_bot $25 on Yes https://twitter.com/...", "side": "yes", "amount": 25 } ], "errors": [ { "marketId": "def456", "error": "Market not found" } ], "_meta": { "requestedAt": "2025-01-15T12:00:00Z", "totalRequested": 2, "totalGenerated": 1, "totalErrors": 1 } } ``` *** ## Dynamic Market Discovery Since markets can expire after 24 hours, use discovery endpoints to always show active markets: ```javascript theme={null} async function loadTrendingPredictions() { // 1. Get trending markets const marketsRes = await fetch('https://app.kash.bot/api/markets/trending?limit=5'); const { markets } = await marketsRes.json(); // 2. Generate Twitter links for each market const linksRes = await fetch('https://app.kash.bot/api/twitter-links', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ markets: markets.map(m => ({ marketId: m.id, side: 'yes', amount: 25 })) }) }); const { links } = await linksRes.json(); // 3. Render your custom UI with the links renderMarkets(markets, links); } // Refresh every hour to get fresh markets setInterval(loadTrendingPredictions, 60 * 60 * 1000); loadTrendingPredictions(); ``` # HTML/JavaScript Examples Source: https://docs.kash.bot/developer-docs/public-embed-api/examples/html Complete vanilla HTML and JavaScript examples for embedding Kash markets Complete, production-ready examples for embedding Kash prediction markets using vanilla HTML and JavaScript. Copy and adapt these examples for your website. *** ## Basic Embed Simple thread embed with no JavaScript: ```html theme={null} 2024 Election Markets

2024 Presidential Election Markets

``` *** ## Embed with Event Listeners Listen for events from the embed: ```html theme={null} Kash Markets - With Events

Live Prediction Markets

Loading markets...
``` *** ## Responsive Sidebar Widget Compact widget perfect for sidebars: ```html theme={null} Market Widget ``` *** ## Multi-Thread Dashboard Display multiple thread embeds on one page: ```html theme={null} Market Dashboard

📈 Market Dashboard

Live prediction markets across multiple categories

``` *** ## Loading States and Error Handling Robust error handling and fallbacks: ```html theme={null} Kash Markets - Error Handling

Prediction Markets

Loading markets...
``` *** ## Complete Production Example Full production-ready example with all best practices: ```html theme={null} Live Prediction Markets

🎲 Prediction Markets

Live probabilities on politics, sports, and current events

Loading markets...
``` *** ## Next Steps React component examples Complete iframe embedding guide # JavaScript Advanced Patterns Source: https://docs.kash.bot/developer-docs/public-embed-api/examples/javascript Advanced JavaScript patterns, utilities, and best practices for Kash embeds Production-ready JavaScript patterns, utilities, and advanced techniques for building robust Kash embed integrations. *** ## Embed Manager Class Reusable class for managing embed lifecycle: ```javascript theme={null} // EmbedManager.js class KashEmbedManager { constructor(config) { this.config = { threadId: config.threadId, containerId: config.containerId, source: config.source || window.location.hostname, height: config.height || 900, onReady: config.onReady || null, onMarketClick: config.onMarketClick || null, onError: config.onError || null, }; this.iframe = null; this.isReady = false; this.messageHandler = this.handleMessage.bind(this); } // Initialize embed init() { const container = document.getElementById(this.config.containerId); if (!container) { console.error(`Container ${this.config.containerId} not found`); return; } // Create iframe this.iframe = document.createElement('iframe'); this.iframe.src = this.buildEmbedUrl(); this.iframe.width = '100%'; this.iframe.height = this.config.height; this.iframe.frameBorder = '0'; this.iframe.setAttribute('loading', 'lazy'); this.iframe.setAttribute('allow', 'clipboard-write'); this.iframe.setAttribute('title', 'Kash prediction markets'); container.appendChild(this.iframe); // Listen for messages window.addEventListener('message', this.messageHandler); } // Build embed URL buildEmbedUrl() { const url = new URL( `https://app.kash.bot/threads/${this.config.threadId}/iframe` ); url.searchParams.set('source', this.config.source); return url.toString(); } // Handle messages from iframe handleMessage(event) { if (event.origin !== 'https://app.kash.bot') return; const { type, data } = event.data; switch (type) { case 'kash-iframe-ready': this.isReady = true; this.config.onReady?.(data); break; case 'kash-market-clicked': this.config.onMarketClick?.(data); break; } } // Clean up destroy() { window.removeEventListener('message', this.messageHandler); if (this.iframe && this.iframe.parentNode) { this.iframe.parentNode.removeChild(this.iframe); } this.iframe = null; this.isReady = false; } } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = KashEmbedManager; } ``` **Usage:** ```javascript theme={null} const embed = new KashEmbedManager({ threadId: 'abc123', containerId: 'kash-embed-container', source: 'my-website', height: 900, onReady: (data) => { console.log(`Loaded ${data.marketCount} markets`); }, onMarketClick: (data) => { console.log(`Market clicked: ${data.marketId}`); }, }); embed.init(); // Clean up when done embed.destroy(); ``` *** ## Retry Logic Utility Robust retry mechanism with exponential backoff for API calls: ```javascript theme={null} // RetryUtil.js async function fetchWithRetry(url, options = {}, retryConfig = {}) { const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000, backoffFactor = 2, retryableStatuses = [408, 429, 500, 502, 503, 504], } = retryConfig; let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const response = await fetch(url, options); // Success if (response.ok) { return response; } // Don't retry non-retryable status codes if (!retryableStatuses.includes(response.status)) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // Rate limit - use Retry-After header if (response.status === 429) { const retryAfter = parseInt(response.headers.get('Retry-After'), 10) || baseDelay / 1000; const delay = Math.min(retryAfter * 1000, maxDelay); console.warn(`Rate limited. Retrying after ${delay}ms`); await sleep(delay); continue; } // For other errors, use exponential backoff lastError = new Error(`HTTP ${response.status}: ${response.statusText}`); } catch (error) { lastError = error; } // Don't delay after last attempt if (attempt < maxRetries) { const delay = Math.min(baseDelay * Math.pow(backoffFactor, attempt), maxDelay); console.warn(`Attempt ${attempt + 1}/${maxRetries} failed. Retrying after ${delay}ms`); await sleep(delay); } } throw new Error(`Failed after ${maxRetries} retries: ${lastError.message}`); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } ``` **Usage:** ```javascript theme={null} try { const response = await fetchWithRetry( 'https://app.kash.bot/api/threads?status=active', { method: 'GET', headers: { 'Content-Type': 'application/json' }, }, { maxRetries: 3, baseDelay: 1000, backoffFactor: 2, } ); const data = await response.json(); console.log('Threads:', data.threads); } catch (error) { console.error('Failed to fetch threads:', error); } ``` *** ## Lazy Load Utility Load embeds only when visible using IntersectionObserver: ```javascript theme={null} // LazyLoadEmbeds.js class LazyEmbedLoader { constructor(config = {}) { this.config = { root: config.root || null, rootMargin: config.rootMargin || '50px', threshold: config.threshold || 0.1, }; this.observer = new IntersectionObserver( this.handleIntersection.bind(this), this.config ); this.embeds = new Map(); } // Register embed for lazy loading register(element, embedConfig) { this.embeds.set(element, embedConfig); this.observer.observe(element); } // Handle intersection handleIntersection(entries) { entries.forEach((entry) => { if (entry.isIntersecting) { const element = entry.target; const config = this.embeds.get(element); if (config) { this.loadEmbed(element, config); this.observer.unobserve(element); this.embeds.delete(element); } } }); } // Load embed loadEmbed(container, config) { const iframe = document.createElement('iframe'); const url = new URL(`https://app.kash.bot/threads/${config.threadId}/iframe`); if (config.source) url.searchParams.set('source', config.source); iframe.src = url.toString(); iframe.width = '100%'; iframe.height = config.height || 900; iframe.frameBorder = '0'; iframe.setAttribute('title', 'Kash prediction markets'); iframe.setAttribute('allow', 'clipboard-write'); container.innerHTML = ''; container.appendChild(iframe); console.log(`Lazy loaded embed: ${config.threadId}`); } // Destroy observer destroy() { this.observer.disconnect(); this.embeds.clear(); } } ``` **Usage:** ```html theme={null}
``` *** ## Event Aggregator Centralized event handling for multiple embeds: ```javascript theme={null} // EventAggregator.js class EmbedEventAggregator { constructor() { this.handlers = new Map(); this.embedMap = new Map(); // iframe -> embedId mapping window.addEventListener('message', this.handleMessage.bind(this)); } // Register embed registerEmbed(iframe, embedId) { this.embedMap.set(iframe, embedId); } // Register event handler on(embedId, eventType, handler) { const key = `${embedId}:${eventType}`; if (!this.handlers.has(key)) { this.handlers.set(key, []); } this.handlers.get(key).push(handler); } // Remove event handler off(embedId, eventType, handler) { const key = `${embedId}:${eventType}`; const handlers = this.handlers.get(key); if (handlers) { const index = handlers.indexOf(handler); if (index !== -1) { handlers.splice(index, 1); } } } // Handle message handleMessage(event) { if (event.origin !== 'https://app.kash.bot') return; const { type, data } = event.data; // Find which embed sent this message let embedId = null; for (const [iframe, id] of this.embedMap.entries()) { if (iframe.contentWindow === event.source) { embedId = id; break; } } if (!embedId) return; // Trigger handlers const key = `${embedId}:${type}`; const handlers = this.handlers.get(key); if (handlers) { handlers.forEach((handler) => { try { handler(data); } catch (error) { console.error('Handler error:', error); } }); } // Trigger wildcard handlers const wildcardKey = `${embedId}:*`; const wildcardHandlers = this.handlers.get(wildcardKey); if (wildcardHandlers) { wildcardHandlers.forEach((handler) => { try { handler(type, data); } catch (error) { console.error('Wildcard handler error:', error); } }); } } // Unregister embed unregisterEmbed(iframe) { this.embedMap.delete(iframe); } } ``` **Usage:** ```javascript theme={null} const aggregator = new EmbedEventAggregator(); // Setup embeds const iframe1 = document.getElementById('embed-1'); const iframe2 = document.getElementById('embed-2'); aggregator.registerEmbed(iframe1, 'embed-1'); aggregator.registerEmbed(iframe2, 'embed-2'); // Listen to specific embed events aggregator.on('embed-1', 'kash-iframe-ready', (data) => { console.log('Embed 1 ready:', data.marketCount, 'markets'); }); aggregator.on('embed-2', 'kash-market-clicked', (data) => { console.log('Embed 2 market click:', data.marketId); }); // Listen to all events from embed aggregator.on('embed-1', '*', (eventType, data) => { console.log(`Embed 1 event: ${eventType}`, data); }); ``` *** ## Performance Monitor Track embed performance metrics: ```javascript theme={null} // PerformanceMonitor.js class EmbedPerformanceMonitor { constructor(embedId) { this.embedId = embedId; this.metrics = { loadTime: null, interactionCount: 0, errors: [], }; this.startTime = performance.now(); } // Mark embed as loaded markLoaded() { this.metrics.loadTime = performance.now() - this.startTime; console.log(`Embed ${this.embedId} loaded in ${this.metrics.loadTime.toFixed(2)}ms`); } // Track interaction trackInteraction() { this.metrics.interactionCount++; } // Track error trackError(error) { this.metrics.errors.push({ message: error.message, timestamp: Date.now(), }); } // Get metrics summary getSummary() { return { embedId: this.embedId, loadTime: this.metrics.loadTime, interactionCount: this.metrics.interactionCount, errorCount: this.metrics.errors.length, }; } } ``` **Usage:** ```javascript theme={null} const monitor = new EmbedPerformanceMonitor('embed-123'); window.addEventListener('message', (event) => { if (event.origin !== 'https://app.kash.bot') return; switch (event.data.type) { case 'kash-iframe-ready': monitor.markLoaded(); break; case 'kash-market-clicked': monitor.trackInteraction(); break; } }); // Log metrics on page unload window.addEventListener('beforeunload', () => { console.log('Embed metrics:', monitor.getSummary()); }); ``` *** ## State Manager Manage embed state across page lifecycle: ```javascript theme={null} // StateManager.js class EmbedStateManager { constructor(storageKey = 'kash_embed_state') { this.storageKey = storageKey; this.state = this.loadState(); } // Load state from localStorage loadState() { try { const stored = localStorage.getItem(this.storageKey); return stored ? JSON.parse(stored) : {}; } catch (error) { console.error('Failed to load state:', error); return {}; } } // Save state to localStorage saveState() { try { localStorage.setItem(this.storageKey, JSON.stringify(this.state)); } catch (error) { console.error('Failed to save state:', error); } } // Set embed state setEmbedState(embedId, key, value) { if (!this.state[embedId]) { this.state[embedId] = {}; } this.state[embedId][key] = value; this.saveState(); } // Get embed state getEmbedState(embedId, key, defaultValue = null) { return this.state[embedId]?.[key] ?? defaultValue; } // Clear embed state clearEmbedState(embedId) { delete this.state[embedId]; this.saveState(); } // Clear all state clearAllState() { this.state = {}; this.saveState(); } } ``` **Usage:** ```javascript theme={null} const stateManager = new EmbedStateManager(); // Save visited markets stateManager.setEmbedState('embed-1', 'visitedMarkets', ['market-1', 'market-2']); // Restore on page load const visitedMarkets = stateManager.getEmbedState('embed-1', 'visitedMarkets', []); console.log('Previously visited:', visitedMarkets); ``` *** ## Next Steps Vanilla JavaScript implementations React components and hooks # React Examples Source: https://docs.kash.bot/developer-docs/public-embed-api/examples/react Production-ready React components for embedding Kash prediction markets Complete React component examples for embedding Kash prediction markets with TypeScript support and hooks. *** ## Basic Embed Component Simple reusable thread embed component: ```typescript theme={null} // components/KashEmbed.tsx import { useEffect, useRef, useState } from 'react'; interface KashEmbedProps { threadId: string; source?: string; height?: number; className?: string; onReady?: (data: { threadId: string; marketCount: number; totalVolume: number }) => void; onMarketClick?: (data: { threadId: string; marketId: string }) => void; } export function KashEmbed({ threadId, source = 'my-site', height = 900, className = '', onReady, onMarketClick, }: KashEmbedProps) { const iframeRef = useRef(null); useEffect(() => { function handleMessage(event: MessageEvent) { if (event.origin !== 'https://app.kash.bot') return; const { type, data } = event.data; switch (type) { case 'kash-iframe-ready': onReady?.(data); break; case 'kash-market-clicked': onMarketClick?.(data); break; } } window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); }, [onReady, onMarketClick]); const embedUrl = `https://app.kash.bot/threads/${threadId}/iframe?source=${encodeURIComponent(source)}`; return ( ``` Replace `your-org` with your organization's **slug** or **UUID** (both work). The widget automatically shows every active market that belongs to your organization — and nothing else. *** ## The organization identifier The `{org}` segment in `https://app.kash.bot/o/{org}/iframe` accepts either form: Your organization's slug — friendly and human-readable, e.g. `acme`. Recommended for embeds you hand-write. Your organization's UUID, e.g. `550e8400-e29b-41d4-a716-446655440000`. Matches the `organizationId` used by the REST API. You can find both values via [`GET /api/organizations/:id`](/developer-docs/public-embed-api/endpoints/organization-detail) (the response includes `id` and `slug`). Only your organization's markets are ever returned — the organization is fixed by the URL, so another organization's markets can never appear in your embed. *** ## Iframe Attributes ### Required Attributes The organization iframe URL: `https://app.kash.bot/o/{org}/iframe` ### Recommended Attributes Iframe width - use `100%` for responsive design Iframe height in pixels. The grid scrolls internally, so taller is better for showing more markets without scrolling. Border width (set to `0` for seamless embed) Use `eager` for above-fold embeds, `lazy` for below-fold Accessibility: describe the iframe content for screen readers *** ## Query Parameters Source label for embed analytics (e.g., your domain name). **Recommended** so you can see where embed traffic comes from. *** ## Complete Example ```html theme={null} ``` **What this does:** * Embeds every active market belonging to the `acme` organization * Renders a branded grid with your organization's logo and name * Labels embed traffic as `acme.com` in analytics (revenue share itself is earned on trades in markets your organization creates) * Each card deep-links to Twitter so visitors can post & trade (no login required in the iframe) *** ## Responsive Design Wrap the iframe in a responsive container: ```html theme={null}
``` The market grid is responsive on its own (3 columns on wide screens, down to 1 on narrow ones) and scrolls inside the iframe, so you only need to size the iframe's outer height. *** ## Security Considerations ### Sandbox Attribute ```html theme={null} ``` **Sandbox permissions explained:** * `allow-scripts` - Required for embed functionality * `allow-same-origin` - Allows API calls to Kash backend * `allow-popups` - Enables opening Twitter to post & trade * `allow-forms` - Allows trading form submissions **Do not use `allow-top-navigation`** - This prevents the iframe from redirecting your parent page. ### Content Security Policy (CSP) If your site uses CSP headers, add Kash to allowed sources: ``` Content-Security-Policy: frame-src https://app.kash.bot; script-src 'self' https://app.kash.bot; ``` *** ## Performance Best Practices ### Preconnect to Kash Domain ```html theme={null} ``` *** ## Common Issues ### Widget shows no markets **Problem:** Embed loads but the grid is empty **Solutions:** 1. Confirm your organization has active markets via [`GET /api/organizations/:id/markets`](/developer-docs/public-embed-api/endpoints/organization-markets) 2. Verify the slug or UUID in the URL is correct 3. If you only have resolved markets, they won't show by default — the widget shows active markets ### "Organization not found" **Problem:** The widget renders a not-found message **Solutions:** 1. Double-check the slug or UUID — a typo can't be matched to an organization 2. Fetch your organization to confirm its identifiers: [`GET /api/organizations/:id`](/developer-docs/public-embed-api/endpoints/organization-detail) *** ## Next Steps Fetch your markets programmatically instead of embedding Embed a single thread's markets instead # Embedding Thread Iframes Source: https://docs.kash.bot/developer-docs/public-embed-api/guides/thread-iframe Learn how to embed Kash prediction market threads on your website Embed Kash prediction market threads on your website using HTML iframes. This guide covers the basics of thread embeds, iframe parameters, and best practices. *** ## Quick Start The simplest way to embed a thread is using an iframe with the thread iframe URL: ```html theme={null} ``` Replace `abc123` with your thread ID (obtained from the [GET /api/threads](/developer-docs/public-embed-api/endpoints/threads) endpoint). *** ## Iframe Attributes ### Required Attributes The thread iframe URL: `https://app.kash.bot/threads/{threadId}/iframe` ### Recommended Attributes Iframe width - use `100%` for responsive design Iframe height in pixels (minimum 400px recommended) Border width (set to `0` for seamless embed) Allow fullscreen mode for market charts Use `eager` for above-fold embeds, `lazy` for below-fold Accessibility: describe the iframe content for screen readers *** ## Query Parameters Analytics label for embed attribution (e.g., your domain name). **Recommended** — it lets you (and Kash) segment embed traffic and interactions by placement. Note: `source` does not affect revenue share, which accrues per trade to the organization or partner that created the market. *** ## Complete Example Here's a fully configured iframe embed: ```html theme={null} ``` **What this does:** * Embeds thread `abc123` with the 20-tile market heatmap * Labels embed traffic as `example.com` for analytics attribution (organizations and partners earn revenue share on trades in markets they create — independent of `source`) * Displays thread title, description, and stats * Users can click any tile to view market details in a new tab * Predictions are made via Twitter (no login required in the iframe) *** ## Responsive Design ### Using CSS for Responsiveness Wrap the iframe in a responsive container: ```html theme={null}
``` ### Mobile-Responsive Height Adjust iframe height for different screen sizes using CSS media queries: ```html theme={null} ``` *** ## Security Considerations ### Sandbox Attribute Use the `sandbox` attribute to restrict iframe capabilities: ```html theme={null} ``` **Sandbox permissions explained:** * `allow-scripts` - Required for embed functionality * `allow-same-origin` - Allows API calls to Kash backend * `allow-popups` - Enables market detail modals * `allow-forms` - Allows trading form submissions **Do not use `allow-top-navigation`** - This prevents the iframe from redirecting your parent page. ### Content Security Policy (CSP) If your site uses CSP headers, add Kash to allowed sources: ``` Content-Security-Policy: frame-src https://app.kash.bot; script-src 'self' https://app.kash.bot; ``` *** ## Finding Thread IDs Use the [GET /api/threads](/developer-docs/public-embed-api/endpoints/threads) endpoint to discover thread IDs: ```javascript theme={null} async function findThreads() { const response = await fetch( 'https://app.kash.bot/api/threads?status=active&limit=10' ); const data = await response.json(); data.threads.forEach((thread) => { console.log(`Thread: ${thread.title}`); console.log(`ID: ${thread.id}`); console.log(`Embed URL: https://app.kash.bot/threads/${thread.id}/iframe?source=your-site\n`); }); } findThreads(); ``` *** ## Performance Best Practices ### 1. Lazy Loading Load below-fold iframes only when visible: ```html theme={null} ``` ### 2. Preconnect to Kash Domain Speed up iframe loading with DNS preconnect: ```html theme={null} ``` *** ## Accessibility ### Provide Descriptive Title Help screen reader users understand iframe content: ```html theme={null} ``` ### Keyboard Navigation Kash embeds support full keyboard navigation: * **Tab** - Navigate between markets * **Enter** - Open market details * **Escape** - Close modals ### ARIA Labels The iframe automatically includes ARIA labels for interactive elements within the embed. *** ## Common Issues ### Iframe Not Displaying **Problem:** Blank iframe or "Refused to display" error **Solutions:** 1. Verify thread ID is correct 2. Ensure URL uses the `/iframe` route: `https://app.kash.bot/threads/{id}/iframe` 3. Check browser console for CSP errors 4. Confirm parent page is served over HTTPS (required) ### Height Not Correct **Problem:** Iframe shows scrollbars or cuts off content **Solutions:** 1. Set appropriate height: `height="900"` for threads, `height="500"` for markets 2. Use CSS media queries to adjust height for different screen sizes 3. Test on multiple devices and screen sizes ### Markets Not Loading **Problem:** Embed loads but shows no markets **Solutions:** 1. Check thread has active markets using [GET /api/threads/:id/markets](/developer-docs/public-embed-api/endpoints/thread-markets) 2. Verify the thread ID is correct and the thread is active *** ## Next Steps View complete implementation examples Explore REST API endpoints # Public Embed API Overview Source: https://docs.kash.bot/developer-docs/public-embed-api/overview Embed prediction markets on your website with the Kash Public Embed API The Kash Public Embed API enables anyone to embed prediction markets and threads on their website. All embeds are read-only and anonymous — users are directed to app.kash.bot to trade (or to X, via optional tweet deep links). ## Key Features Embed complete threads with a 20-tile market heatmap grid or single market cards Generate Twitter URLs that let users place predictions via tweet (requires the user's opt-in trading bot authorization) Public API endpoints to fetch threads, markets, and real-time data No authentication required - just copy the code and start embedding *** ## API Endpoint Groups ### Thread Endpoints Fetch and display prediction market threads (collections of related markets): * **GET /api/threads** - List all available threads * **GET /api/threads/:id** - Get details for a specific thread * **GET /api/threads/:id/markets** - Get all markets within a thread ### Market Endpoints Discover and filter individual prediction markets: * **GET /api/markets** - List markets with filtering and pagination * **GET /api/markets/:id** - Get detailed info for a specific market * **GET /api/markets/trending** - Get trending markets by recent activity * **GET /api/markets/featured** - Get curated high-volume markets ### Twitter Deep Link Endpoints Generate Twitter intent URLs for predictions (no iframe needed): * **GET /api/markets/:id/twitter-link** - Generate link for a single market * **GET /api/threads/:id/twitter-links** - Generate links for all markets in a thread * **POST /api/twitter-links** - Batch generate links for multiple markets *** ## Quick Start Get started in under 5 minutes: ```html HTML theme={null} ``` Replace `{threadId}` with an actual thread ID from our [thread list API](/developer-docs/public-embed-api/endpoints/threads). ## Use Cases ### News & Media Sites Embed live prediction markets alongside news articles to drive engagement and provide readers with real-time sentiment data. ### Financial Platforms Integrate market probabilities into your financial dashboards and analysis tools. ### Community Forums Add prediction markets to discussion threads for interactive forecasting. ### Educational Platforms Use markets as teaching tools for economics, statistics, and decision-making courses. ## Authentication The Public Embed API is **completely public** and requires **no authentication**. All endpoints are rate-limited by IP address to ensure fair usage. ## Rate Limits | Endpoint Type | Limit | | ------------------------------- | ------------------ | | GET /api/threads | 50 req/min per IP | | GET /api/threads/ | 100 req/min per IP | | GET /api/threads//markets | 100 req/min per IP | | GET /api/threads//twitter-links | 100 req/min per IP | | GET /api/markets | 100 req/min per IP | | GET /api/markets/ | 100 req/min per IP | | GET /api/markets//twitter-link | 100 req/min per IP | | GET /api/markets/trending | 100 req/min per IP | | GET /api/markets/featured | 100 req/min per IP | | POST /api/twitter-links | 100 req/min per IP | | POST /api/embeds/\* | 200 req/min per IP | Rate limit information is included in response headers: ``` X-RateLimit-Limit: 50 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 1704067260000 ``` ## Base URL All API requests should be made to: ``` https://app.kash.bot/api ``` Iframe embeds use these URL patterns: ``` Thread embed: https://app.kash.bot/threads/{threadId}/iframe Market embed: https://app.kash.bot/markets/{marketId}/iframe ``` ## Support Email us at [dev@kash.bot](mailto:dev@kash.bot) for technical questions Contact [partnerships@kash.bot](mailto:partnerships@kash.bot) for revenue sharing details ## Next Steps Get your first embed running in 5 minutes Browse all available API endpoints View complete integration examples # Quickstart Guide Source: https://docs.kash.bot/developer-docs/public-embed-api/quickstart Get your first Kash embed running in 5 minutes Get prediction market embeds on your website in under 5 minutes. ## Step 1: Choose a Thread First, fetch a list of available threads from our API: ```bash cURL theme={null} curl "https://app.kash.bot/api/threads?status=active&limit=5" ``` You'll get a response like this: ```json Response theme={null} { "threads": [ { "id": "abc123-def456", "title": "2024 Election Predictions", "marketCount": 12, "totalVolume": 1250000, "status": "active" } ], "pagination": { ... } } ``` Copy the `id` field from the thread you want to embed. ## Step 2: Add the Iframe Add this HTML to your website, replacing `{threadId}` with the ID from Step 1: ```html HTML theme={null} ``` Replace `your-site-name` in the `source` parameter with your actual website name. The `source` label shows up in embed analytics so you can see where your traffic comes from. ## Step 3: Style the Container (Optional) Wrap your iframe in a container for better responsive design: ```html HTML theme={null}
``` ## Step 4: Add Event Listeners (Optional) Track user interactions with the embed: ```javascript JavaScript theme={null} window.addEventListener('message', (event) => { // Verify origin for security if (event.origin !== 'https://app.kash.bot') return; if (event.data.type === 'kash-iframe-ready') { console.log('Embed loaded successfully'); } if (event.data.type === 'kash-market-clicked') { console.log('User clicked market:', event.data.data.marketId); // Track in your analytics } }); ``` ## Complete Example Here's a complete, production-ready example: ```html HTML theme={null} Prediction Markets

Live Prediction Markets

``` ## Recommended Dimensions Different embed types work best at specific dimensions: | Embed Type | Desktop | Tablet | Mobile | | --------------------- | ------------ | ------------ | ------------ | | Thread (20-tile grid) | 100% × 900px | 100% × 800px | 100% × 700px | | Single Market | 100% × 500px | 100% × 500px | 100% × 400px | Always use `width="100%"` for responsive design. Only the height needs to be fixed. ## Troubleshooting ### Iframe not loading? 1. **Check the thread ID** - Make sure you're using a valid thread ID from the API 2. **Verify the URL** - The format should be exact: `https://app.kash.bot/threads/{id}/iframe` 3. **Check CORS policies** - Some Content Security Policies may block iframes 4. **Test in incognito** - Ad blockers sometimes interfere with embed loading ### Embed looks broken on mobile? Reduce the iframe `height` attribute for mobile devices using CSS media queries: ```css CSS theme={null} @media (max-width: 768px) { iframe { height: 700px !important; } } ``` ### Interested in earning revenue share? Organizations and partners earn a share of trading fees on trades in markets **they create** — embeds are a great way to drive that activity. See [Kash for businesses](/for-businesses/overview) or contact [partnerships@kash.bot](mailto:partnerships@kash.bot). The `?source=` parameter is an analytics label for your embed traffic; it does not by itself set up revenue sharing. ## Next Steps Explore all available API endpoints Learn more about iframe embedding options View React and vanilla JS examples # Authentication Source: https://docs.kash.bot/developer-docs/rest-api/authentication API keys, scopes, IP allowlists, and key rotation. Every data route on `api.kash.bot` requires an API key. The only unauthenticated endpoints are the infra-meta routes: `/v1/health`, `/v1/ready`, `/v1/openapi.json`, `/v1/docs`. ## The `X-API-Key` header Pass your key on every request as the `X-API-Key` header: ```bash theme={null} curl https://api.kash.bot/v1/markets \ -H "X-API-Key: kash_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` Keys are issued via the webapp's **Settings → API Keys** page or the admin CLI. With [`@kashdao/sdk`](/developer-docs/rest-api/sdks/typescript) you don't set the header yourself — construct the client with your key and it authenticates every request (and auto-routes to staging for a `kash_test_*` key, production for `kash_live_*`): ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey: process.env.KASH_API_KEY! }); ``` ## Key format ``` kash_live_<32 base62 chars> # production kash_test_<32 base62 chars> # staging ``` The 32-character random suffix gives roughly 190 bits of entropy. The prefix is informational — the server validates the full key against a stored HMAC under a server-side pepper. **Never log or commit the plaintext.** ## Scopes A scope is a coarse-grained permission. Every endpoint declares the scope(s) it requires. The `requireScope` middleware fails closed with `403 INSUFFICIENT_SCOPE` when a key lacks one. | Scope | What it grants | | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | | `markets:read` | `GET /v1/markets`, `GET /v1/markets/{id}`, `GET /v1/markets/{id}/predictions` | | `markets:quote` | `GET /v1/markets/{id}/quote` | | `portfolio:read` | `GET /v1/portfolio`, `GET /v1/portfolio/positions` | | `trades:read` | `GET /v1/trades`, `GET /v1/trades/{id}`, `GET /v1/traces/{id}` | | `trades:write` | `POST /v1/trades`, `POST /v1/trades/{id}/confirm` | | `webhooks:manage` | `GET /v1/webhooks/events`, `POST /v1/webhooks/events/{id}/redeliver`, `POST /v1/auth/api-keys/me/webhook-secret/rotate` | | `auth:manage` | `GET /v1/account/usage` | **Default scopes by tier.** Self-served `free` and `developer` keys default to `markets:read`, `markets:quote`, `portfolio:read`. Admin-issued `enterprise` and `mm` keys add `trades:read`, `trades:write`. You can override at issuance — request the scopes that match your use case, no more. ## Tiers Each key has a tier that governs default rate limits and spending caps. See **[Rate Limits](/developer-docs/rest-api/rate-limits)** for per-tier quotas. | Tier | How issued | Notes | | ------------ | -------------------------- | ------------------------------------------------------------------ | | `free` | Self-serve via webapp | Conservative caps. Default for new accounts. | | `developer` | Self-serve, paid | Higher rate limit and per-trade caps. | | `enterprise` | Admin-issued (contractual) | Custom rate limit. Required for governance-thresholded operations. | | `mm` | Admin-issued only | Market-maker tier. High caps. Discretionary review required. | Reach out to [partnerships@kash.bot](mailto:partnerships@kash.bot) for `enterprise` or `mm` keys. ## IP allowlists Optional per-key. When set, requests from any IP not in the list fail closed with `403 IP_NOT_ALLOWED` *before* the route handler runs. Format: comma-separated IPv4/IPv6 addresses or CIDR ranges. Set in the webapp UI or via the admin CLI at issuance time. ``` 10.0.0.0/8,2001:db8::/32,203.0.113.45 ``` We strongly recommend setting an allowlist for production keys. It collapses an entire class of "key was leaked from a logfile" incidents. ## Key rotation API keys do not expire automatically. Rotate them when: * A team member with access leaves * A key may have been logged or committed to a repo * You're tightening security posture before a major release To rotate: 1. Issue a new key in the webapp. 2. Roll it out to your services. 3. Verify the new key is in use (check `/v1/account/usage` — request counts on the old key should be zero). 4. Revoke the old key in the webapp. There is no automated dual-validity overlap window for API keys; the rotation is sequential. (Webhook *secrets* do have a 7-day rotation overlap — see **[Secret Rotation](/developer-docs/rest-api/webhooks/secret-rotation)**.) ## Body signing for `trades:write` (optional, hardened) For write routes like `POST /v1/trades`, you can opt into HMAC-signed request bodies as a defence-in-depth layer against captured-request replay and in-flight tampering. Send the signature in `X-Kash-Signature` using the same Stripe-style header format as our webhook signatures: ``` X-Kash-Signature: t=,v1= ``` * **Signing secret = your API key plaintext** (the same value you send in `X-API-Key`). No separate signing secret is issued — this is *not* the key's `webhook_secret`. * **Canonical input** is `...` — the timestamp from `t=`, the HTTP method uppercased, the URL pathname **without the query string** (query params aren't signed because intermediaries often re-order them), and the exact raw body bytes you send. * **Algorithm**: HMAC-SHA256, hex-encoded. * **Timestamp tolerance**: ±5 minutes. Older or newer timestamps are rejected. ```ts theme={null} import { createHmac } from 'node:crypto'; const ts = Date.now(); // unix milliseconds const canonical = `${ts}.POST./v1/trades.${rawBody}`; const sig = createHmac('sha256', apiKeyPlaintext).update(canonical).digest('hex'); // Header: X-Kash-Signature: t=,v1= ``` If the header is present, the server MUST be able to verify it — a mismatch, a malformed header, or a stale timestamp is rejected with `401 REQUEST_SIGNATURE_INVALID`. If absent, the route still works (the API key is the canonical authentication). Multiple comma-separated `v1=` values are accepted (for graceful scheme rotation later). Use body signing when your threat model includes a compromised TLS-terminating proxy or leaky request logs. ## Auth failures | Status | `code` | Meaning | | ------ | --------------------------- | ------------------------------------------------------------- | | 401 | `API_KEY_MISSING` | No `X-API-Key` header. | | 401 | `API_KEY_MALFORMED` | Header present but not a well-formed key. | | 401 | `API_KEY_INVALID` | Key not recognised, or HMAC mismatch. | | 401 | `API_KEY_REVOKED` | Key was found but has been revoked. | | 401 | `API_KEY_EXPIRED` | Key is past its `expires_at` timestamp. | | 403 | `INSUFFICIENT_SCOPE` | Key is valid but doesn't carry the scope this route requires. | | 403 | `IP_NOT_ALLOWED` | Source IP not in this key's allowlist. | | 401 | `REQUEST_SIGNATURE_INVALID` | Body-signing was opted into and failed verification. | All auth failures return RFC 7807 [`application/problem+json`](/developer-docs/rest-api/errors) and are written to the per-key audit log. Repeated failures from the same source trigger the per-IP brute-force lockout. ## Inspecting auth state The `GET /v1/account/usage` endpoint returns telemetry for the *calling* key — including 24h auth-failure and rate-limit-rejection counts. Useful for spotting key misuse early. ```bash theme={null} curl https://api.kash.bot/v1/account/usage \ -H "X-API-Key: $KASH_API_KEY" ``` ## Next Per-tier quotas, the `X-RateLimit-*` headers, and 429 handling. RFC 7807 error format and the full error code catalog. # Endpoint Reference Source: https://docs.kash.bot/developer-docs/rest-api/endpoint-reference The interactive OpenAPI 3.1 reference at /v1/docs and the live spec at /v1/openapi.json. The full endpoint reference is rendered live, in sync with the deployed API, by [Scalar](https://scalar.com) at: Interactive reference for production. Try requests directly from the browser. Same UI, against Base Sepolia. Safe to experiment. The raw OpenAPI 3.1 spec — for SDK generation, contract testing, or import into Postman / Bruno / Insomnia — is at: ``` Production: https://api.kash.bot/v1/openapi.json Staging: https://api-staging.kash.bot/v1/openapi.json ``` ## What's in the spec Every endpoint is documented with: * Path, method, summary, description * Required scope and auth scheme * Request parameters, query, body schemas (Zod-derived JSON Schema) * Response schemas for each status code (`200`, `201`, `202`, `304`, `4xx`, `5xx`) * The full `application/problem+json` envelope on every error response * Webhook payload schemas under the OpenAPI 3.1 `webhooks` section ## Endpoint groups The full surface, grouped by domain: ### Markets * `GET /v1/markets` — list markets (cursor-paginated, filterable) * `GET /v1/markets/{id}` — single market detail * `GET /v1/markets/{id}/quote` — on-chain price quote (buy or sell) * `GET /v1/markets/{id}/predictions` — recent trades against a market ### Trades * `POST /v1/trades` — create a trade (`201` immediate, `202` for high-value) * `POST /v1/trades/{id}/confirm` — release a high-value trade with the one-time token * `GET /v1/trades` — list your trades * `GET /v1/trades/{id}` — single trade detail ### Redemptions * `POST /v1/redemptions` — claim a settled position (redeem a winning outcome, or a refund on a cancelled market). Body is `{ marketId, outcomeIndex }`; requires the `trades:write` scope. Idempotent — a repeat claim returns the original request with `200`. The route inserts a claim and the payout pipeline settles it asynchronously; poll `GET /v1/portfolio` for the resulting USDC. Only available where self-service redemption is enabled (otherwise winnings are auto-paid and the route returns `404 REDEMPTIONS_NOT_ENABLED`). ### Portfolio * `GET /v1/portfolio` — smart-account address + aggregate position summary * `GET /v1/portfolio/positions` — per-market position detail (not paginated in v1; filter with `?marketId=`) ### Webhooks * `GET /v1/webhooks/events` — list webhook deliveries (cursor-paginated, filterable by status) * `POST /v1/webhooks/events/{id}/redeliver` — replay a failed delivery * `POST /v1/auth/api-keys/me/webhook-secret/rotate` — rotate the webhook signing secret ### Account * `GET /v1/account/usage` — per-key telemetry (trades, webhooks, auth failures, latency) ### Traces * `GET /v1/traces/{correlationId}` — walk a trade's full event timeline ### Infra meta (no auth) * `GET /v1/health` — liveness check * `GET /v1/ready` — readiness check * `GET /v1/openapi.json` — this spec * `GET /v1/docs` — Scalar interactive UI ## Generating an SDK from the spec The OpenAPI 3.1 spec is the contract of record. You can use any standard generator: ```bash theme={null} # OpenAPI Generator (multi-language) openapi-generator-cli generate \ -i https://api.kash.bot/v1/openapi.json \ -g typescript-fetch \ -o ./generated-sdk # Speakeasy speakeasy generate sdk --schema https://api.kash.bot/v1/openapi.json --lang go # Stoplight Prism (mock server) prism mock https://api.kash.bot/v1/openapi.json ``` For TypeScript, prefer the hand-written `@kashdao/sdk` — it has typed errors, retry/backoff, idempotency helpers, webhook signature verification, async pagination iterators, and lifecycle hooks. See the [TypeScript SDK guide](/developer-docs/rest-api/sdks/typescript). Multi-language SDK roadmap: Python first, then Go, then Rust. Generated from the same spec. ## Postman / Bruno / Insomnia A maintained Postman collection ships with the public-API repo and tracks the spec automatically: [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/?action=import\&collection=https%3A%2F%2Fraw.githubusercontent.com%2FKashDAO%2Fkash%2Fmain%2Fapps%2Fpublic-api%2Fexamples%2Fpostman%2FKash-API.postman_collection.json) Or import the spec directly into your tool of choice — every modern API client supports OpenAPI 3.1 import. ## Other client formats Each endpoint reference page on this site renders an interactive cURL example — change parameters in place, copy the result. For collection-style usage, two formats are produced from the same OpenAPI spec at every release: * **Postman collection** — `Kash-API.postman_collection.json` — generated via `openapi-to-postmanv2`. Import directly into Postman / Insomnia / Thunder Client. Sourced and shipped alongside each versioned `openapi/v.json` snapshot. * **Bruno collection** — `.bru` files, one per endpoint, ship with the public-api artifact bundle. Bruno is a git-friendly alternative to Postman; collections live as plain text in version control. ## Spec stability and versioning The spec is **the contract**. Breaking changes are signalled via: * `X-Kash-Api-Version` response header on every response (currently `2026-04-29`) * `Sunset` and `Deprecation` headers (RFC 8594) on routes scheduled for removal * `info.version` bump in the spec itself * A 12-month notice period for breaking changes; multiple versions run in parallel during transitions If a route returns `Deprecation: true` and `Sunset: `, you have until that date to migrate. # Errors Source: https://docs.kash.bot/developer-docs/rest-api/errors RFC 7807 Problem Details, the X-Kash-Error-Code header, and the full error code catalog. Every error response from `api.kash.bot` follows [RFC 7807 Problem Details](https://datatracker.ietf.org/doc/html/rfc7807) with `Content-Type: application/problem+json`. ## Wire shape ```json theme={null} { "type": "https://docs.kash.bot/developer-docs/api-errors/INSUFFICIENT_SCOPE", "title": "Insufficient scope", "status": 403, "code": "INSUFFICIENT_SCOPE", "detail": "This route requires the 'trades:write' scope. The supplied API key does not include it.", "instance": "/v1/trades", "requestId": "req_01HZ..." } ``` | Field | Use | | ----------- | ---------------------------------------------------------------------------------------------- | | `type` | URI of the per-code documentation page (RFC 7807 §3.1). Stable; safe to fetch. | | `title` | Short human-readable summary. | | `status` | The HTTP status (echoed for clients that lose it). | | `code` | **Stable machine-readable string.** Branch on this; never on `title` or `detail`. | | `detail` | Sanitised human-readable explanation. May reference the specific bad input. | | `instance` | URI of the request that produced this error. | | `requestId` | Trace handle. Include in support tickets — gets you straight to the right span in our backend. | ## How to handle errors Branch on `code`, not on `status` or message text. Status codes are coarse (a `400` could mean any of a dozen specific failures); `code` is the unambiguous identifier. ```ts theme={null} try { await kash.trades.create(body); } catch (err) { if (err instanceof KashError) { switch (err.code) { case 'INSUFFICIENT_SCOPE': // Re-issue the key with `trades:write`. break; case 'RATE_LIMIT_EXCEEDED': await sleep((err.retryAfterSeconds ?? 1) * 1000); // retry break; case 'IDEMPOTENCY_KEY_CONFLICT': // Same key was used with a different body. Either replay the // original body or generate a new key. break; // ... } } throw err; } ``` The TypeScript SDK throws typed subclasses (`KashRateLimitError`, `KashAuthorizationError`, etc.) so you can catch by error type as well. ## Status code conventions | Status | When | | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | | `200` | Successful read. | | `201` | Successful resource creation (immediate). | | `202` | Accepted, work in progress (high-value trade pending confirmation; redelivery). | | `304` | `If-None-Match` cache hit — no body. See [ETags](/developer-docs/rest-api/etags). | | `400` | Validation failure on the request itself (`VALIDATION_FAILED`, malformed JSON). | | `401` | Auth failure (`API_KEY_MISSING`, `API_KEY_INVALID`, `API_KEY_REVOKED`, `REQUEST_SIGNATURE_INVALID`). | | `403` | Authenticated but not authorised (`INSUFFICIENT_SCOPE`, `IP_NOT_ALLOWED`). | | `404` | Resource doesn't exist (or doesn't exist *to you* — ownership-checked routes return 404 not 403 to prevent enumeration). | | `409` | Conflict (`IDEMPOTENCY_KEY_CONFLICT`, `SPENDING_LIMIT_EXCEEDED`, `TRADE_NOT_AWAITING_CONFIRMATION`, `INSUFFICIENT_BALANCE`, etc.). | | `410` | Gone (a previously valid resource is permanently removed). | | `429` | Rate limit exceeded. Honour `Retry-After`. | | `500` | Server bug. Include `requestId` when reporting. | | `503` | Route temporarily disabled (`ROUTE_DISABLED`) or dependency unavailable (`DEPENDENCY_UNAVAILABLE`). | | `504` | Upstream timeout. | ## Error code catalog Every `code` has a stable per-page documentation entry at `https://docs.kash.bot/developer-docs/api-errors/` — see the [Error catalogue](/developer-docs/api-errors/overview) for the full index. The `type` field on every error is exactly that URL — copy-paste it into a browser to read the page, or fetch it programmatically. The CLI ships an offline copy of the full catalog. `kash explain ` works without a network round-trip: ```bash theme={null} kash explain INSUFFICIENT_SCOPE ``` Common codes to know: | Code | When it fires | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `API_KEY_MISSING` | No `X-API-Key` header. | | `API_KEY_INVALID` | Key not recognised or HMAC mismatch. | | `API_KEY_REVOKED` | Valid format, but the key has been revoked. | | `API_KEY_EXPIRED` | Key is past its `expires_at` timestamp. | | `INSUFFICIENT_SCOPE` | Key lacks the scope this route requires. | | `IP_NOT_ALLOWED` | Source IP not in this key's allowlist. | | `RATE_LIMIT_EXCEEDED` | Per-key quota exceeded. Honour `Retry-After`. The cap is tier-differentiated — see [Rate Limits](/developer-docs/rest-api/rate-limits). | | `SPENDING_LIMIT_EXCEEDED` | Trade would exceed the key's per-trade or daily cap. The `extensions.cap` field is `per_trade` or `daily_volume`. Caps are tier-differentiated — see [Rate Limits](/developer-docs/rest-api/rate-limits) for the matrix and upgrade path. | | `VALIDATION_FAILED` | Request body or query failed schema validation. | | `IDEMPOTENCY_KEY_CONFLICT` | Same `Idempotency-Key` reused with a different body. See [Idempotency](/developer-docs/rest-api/idempotency). | | `MARKET_NOT_TRADEABLE` | Market is FROZEN/RESOLVED or otherwise not tradeable. (Unsupported chain is a distinct code, `CHAIN_NOT_SUPPORTED`.) | | `INSUFFICIENT_BALANCE` | Smart account doesn't have enough USDC for the trade. | | `TRADE_NOT_AWAITING_CONFIRMATION` | Confirm called on a trade that's not in `pending_confirmation`. | | `CONFIRMATION_TOKEN_INVALID` | Confirmation token didn't match. | | `CONFIRMATION_TOKEN_USED` | Confirmation token already redeemed (or by a concurrent confirm). | | `CONFIRMATION_EXPIRED` | Confirmation window passed; the trade auto-failed. | | `RESOURCE_NOT_FOUND` | Trade/market/event id doesn't exist (or doesn't belong to you). | | `DEPENDENCY_UNAVAILABLE` | Upstream (DB, chain RPC) is degraded. Treat like `Retry-After`. | | `ROUTE_DISABLED` | Ops kill switch is active for this route. | | `WEBHOOK_REPLAY_LIMIT_REACHED` | More than 5 manual redeliveries for the same event. | | `WEBHOOK_SECRET_ROTATION_COOLDOWN` | Webhook-secret rotation called within 60s of a prior successful rotation. Protects the rollback slot — see [Secret Rotation](/developer-docs/rest-api/webhooks/secret-rotation). | | `REQUEST_SIGNATURE_INVALID` | Opt-in body-signing on `POST /v1/trades` failed verification. | The full list lives at the [Error catalogue](/developer-docs/api-errors/overview). The CLI's offline copy and these docs pages are both generated from the same source-of-truth markdown at release time, so they stay in sync. ## What's NOT in errors Defensive design choices to be aware of: * **No stack traces.** Internal stack frames are stripped before the response. * **No DB error strings.** Postgres / RPC errors are sanitised to user-safe messages. * **No URLs with credentials.** Defence-in-depth scrubbing in case anything slips into a log line. * **404 instead of 403 for ownership.** Trying to GET someone else's trade returns `RESOURCE_NOT_FOUND`, not `FORBIDDEN`. This prevents id-enumeration attacks (you can't tell whether a UUID exists if it's not yours). * **Generic `DEPENDENCY_UNAVAILABLE` when upstream fails.** We deliberately hide which upstream — DB vs RPC vs Redis — to avoid leaking architecture detail to attackers. Your `requestId` lets us correlate to the precise root cause server-side. ## Reporting bugs Include the **`requestId`** when emailing [dev@kash.bot](mailto:dev@kash.bot) or filing an issue on the relevant public mirror ([SDK](https://github.com/KashDAO/sdk-typescript/issues) · [CLI](https://github.com/KashDAO/cli/issues) · [Protocol SDK (TS)](https://github.com/KashDAO/protocol-sdk-typescript/issues) · [Protocol SDK (Python)](https://github.com/KashDAO/protocol-sdk-python/issues)). One id is enough — we can pull the full distributed trace, every event, and every retry attempt from it. ## Next Safely retry trade creation without producing duplicates. Cursor-based, never offset. How to walk a list endpoint. # ETags & Caching Source: https://docs.kash.bot/developer-docs/rest-api/etags If-None-Match short-circuits — skip re-downloading unchanged data cheaply. Every response from `api.kash.bot` carries an `ETag` header — a content-derived hash of the response body. Send it back on the next request as `If-None-Match` and the server short-circuits with `304 Not Modified` (and an empty body) when nothing changed. ## Why this matters A `304` response: * Is **fast** — the server doesn't reserialize the body or hit the database for the result. * Is **small** — empty body, just the headers. * **Counts toward your rate limit** for accounting, but the cost is much lower per request. For polling workloads (portfolio, trade status, market list), this is the difference between paying for new data and paying for "is there new data?". ## How to use it ### 1. Capture the ETag from your first response ```bash theme={null} curl -i https://api.kash.bot/v1/portfolio \ -H "X-API-Key: $KASH_API_KEY" # HTTP/1.1 200 OK # ETag: "W/\"a1b2c3d4...\"" # Content-Type: application/json # ... # { "portfolio": {...}, "data": {...}, "_meta": {...} } ``` ### 2. Send it back on the next request ```bash theme={null} curl -i https://api.kash.bot/v1/portfolio \ -H "X-API-Key: $KASH_API_KEY" \ -H 'If-None-Match: W/"a1b2c3d4..."' # If unchanged: # HTTP/1.1 304 Not Modified # ETag: "W/\"a1b2c3d4...\"" # (empty body) # If changed: # HTTP/1.1 200 OK # ETag: "W/\"e5f6g7h8...\"" ← new ETag # { "portfolio": { /* updated */ }, ... } ``` When you get `304`, **reuse your last response body**. When you get `200`, replace it (and capture the new ETag). ## TypeScript SDK The SDK does this transparently — pass `--ifNoneMatch` (or set the per-call header) and it handles the 304 path: ```ts theme={null} let etag: string | undefined; let portfolio: PortfolioSummary; // Initial fetch const first = await kash.portfolio.get(); portfolio = first; etag = first._meta?.etag; // pulled from response headers // Subsequent poll try { const next = await kash.portfolio.get({ ifNoneMatch: etag }); portfolio = next; etag = next._meta?.etag; } catch (err) { if (err instanceof KashNotModifiedError) { // Nothing changed; keep using `portfolio`. } else { throw err; } } ``` ## Where ETags help most | Endpoint | Polling pattern | ETag value | | ---------------------------------- | ------------------------------- | --------------------------------------------------------- | | `GET /v1/portfolio` | Refresh balance every N seconds | High — balance only moves on trades you initiated. | | `GET /v1/trades/{id}` | Wait for completion | High — trade is mostly idle until status flips. | | `GET /v1/markets/{id}` | Watch a single market | Medium — moves on every trade against it. | | `GET /v1/markets` | Refresh a market list | Medium — depends on filter and traffic. | | `GET /v1/markets/{id}/quote` | Live price ticker | Low — every quote re-runs RPC. Cache client-side instead. | | `GET /v1/markets/{id}/predictions` | Recent-trades feed | Low — append-only, almost always changes. | For high-cardinality append-only feeds (predictions, webhook events), pagination + a stable cursor is a better strategy than ETag. ## ETag format We use **weak ETags** (the `W/` prefix). Weak ETags compare equal even if byte-level encoding differs (e.g. JSON whitespace). The server may serve different exact bytes for the same logical state — what matters is whether the *resource* changed. ``` ETag: W/"a1b2c3d4e5f6" ``` Don't try to parse the inner string. Treat it as opaque. ## Caching with ETags + `Cache-Control` The `GET /v1/markets/{id}/quote` endpoint also sets a small public `Cache-Control: public, max-age=10`. This lets a CDN or local cache serve the quote for up to 10 seconds without hitting our backend at all. Combine with ETag for the post-cache revalidation path. For private routes (everything authenticated), responses use `Cache-Control: private, no-cache` — your CDN won't serve them to other tenants, but you can still cache client-side using ETags as the freshness key. ## What ETags don't do * **They don't skip rate limit accounting entirely.** A `304` still costs one request from your quota, just dramatically less work for the server. (If you need to poll harder, see if a webhook-driven design works for you.) * **They don't survive across keys.** ETags are computed per (route, query, key). Don't share them between keys. * **They don't apply to write paths.** `POST` and `PATCH` requests don't benefit from ETags; use [Idempotency-Key](/developer-docs/rest-api/idempotency) instead. ## Next The push-based alternative to polling. Real-time delivery, no quota cost. The full interactive spec at `/v1/docs`. # Idempotency Source: https://docs.kash.bot/developer-docs/rest-api/idempotency Safely retry trade creation with the Idempotency-Key header and clientRequestId body field. Network failures happen mid-flight. Idempotency lets you retry a write without risking a duplicate trade. The API supports **two** idempotency mechanisms — the Stripe-style `Idempotency-Key` header (HTTP-level) and the `clientRequestId` body field (domain-level). They serve slightly different purposes; you can use either or both. ## `Idempotency-Key` header (recommended) ```http theme={null} POST /v1/trades HTTP/1.1 X-API-Key: kash_live_... Content-Type: application/json Idempotency-Key: c2b9a1f8-7e6d-4b3a-9c1e-5f0d8a3b7c2e { "marketId": "...", "outcomeIndex": 0, "amount": "100", "side": "buy" } ``` Generate the key client-side as a random UUID per logical operation: ```ts theme={null} const idempotencyKey = crypto.randomUUID(); await kash.trades.create(body, { idempotencyKey }); ``` Behavior: * **First request:** processed normally. The response is cached server-side under the key. * **Same key, same body:** the cached response is returned with `_meta.idempotent = true`. No new trade is created. * **Same key, different body:** `409 IDEMPOTENCY_KEY_CONFLICT`. The first request "owns" the key for 24 hours. * **TTL:** 24 hours. After that, the key can be reused (but you should generate fresh ones). The body comparison is exact (after canonical JSON normalisation), and includes the `metadata` field — see [Body comparison rules](#body-comparison-rules) below. ### When to use it * Network timeouts on `POST /v1/trades` — retry with the same key, get the same trade back. * Distributed retry layers (queues, schedulers) — pin the key to the logical job id. * "Was this trade already sent?" — replay returns the same response, regardless of how many times you ask. ## `clientRequestId` body field ```http theme={null} POST /v1/trades HTTP/1.1 Content-Type: application/json { "marketId": "...", "outcomeIndex": 0, "amount": "100", "side": "buy", "clientRequestId": "trade-job-2026-05-02-abc123" } ``` The `clientRequestId` is stored alongside the trade and used for **trade-level** idempotency: * **First request with a given `clientRequestId`:** trade is created. * **Second request, same `clientRequestId`, same trade body:** the existing trade row is returned. No new trade. * **Second request, same `clientRequestId`, different trade body:** `409 IDEMPOTENCY_KEY_CONFLICT`. Constraints: * 1 to 128 characters * Allowed: `A-Z a-z 0-9 _ - : .` ### When to use `clientRequestId` over `Idempotency-Key`? Pick `clientRequestId` when: * Your idempotency key is meaningful and not random — e.g. `trade-{userId}-{marketId}-{tradeBatchId}`. The key persists with the trade row, so you can look it up later. * You want the idempotency to survive longer than 24 hours (e.g. nightly batch jobs that must never double-create). * You're not in HTTP-retry land — you're at the domain layer, deciding whether to send a trade in the first place. Pick `Idempotency-Key` (or both) when: * You want the protection of "if this exact request was just made, give me the cached response" without thinking about it. * You have transient HTTP retries between a client and the API (proxies, retry middleware, etc.). You can use **both** at the same time; they layer cleanly. Most production systems do. ## Body comparison rules For both mechanisms, the body comparison treats these fields as identity-defining: * `marketId` * `outcomeIndex` * `amount` * `side` * `metadata` (compared as a sorted, shallow-equality `Record`) Differences in any of those → `409 IDEMPOTENCY_KEY_CONFLICT`. These fields are NOT part of identity: * `clientRequestId` (it IS the identity for the second mechanism, not part of the body comparison) * Server-derived fields (the response always reflects the *original* trade, including its server-assigned `id`, `correlationId`, `createdAt`, etc.) ## Detecting an idempotent replay Every response from `POST /v1/trades` includes `_meta.idempotent`: ```json theme={null} { "trade": { ... }, "data": { ... }, "_meta": { "requestId": "req_...", "timestamp": "2026-05-02T12:34:56.789Z", "idempotent": true } } ``` If `idempotent: true`, the trade was created on a previous request — your retry simply rediscovered it. ## High-value confirmation interaction If your trade hits the high-value threshold: * **First request:** `202 Accepted` with a one-time confirmation token. * **Same `Idempotency-Key`, same body, before confirm:** `202` again with the **same** trade and the **same** token. * **Same `Idempotency-Key`, same body, after confirm:** `200` with `idempotent: true`. The trade is already past the gate. The token itself is single-use. If you've already confirmed and lost the success response, just look up the trade by id (`GET /v1/trades/{id}`) — its `status` will be `pending` (or further along). ## Error envelope on conflict ```json theme={null} { "type": "https://docs.kash.bot/developer-docs/api-errors/IDEMPOTENCY_KEY_CONFLICT", "title": "Idempotency key conflict", "status": 409, "code": "IDEMPOTENCY_KEY_CONFLICT", "detail": "This Idempotency-Key was used previously with a different request body. Either use the same body to retrieve the cached response, or generate a fresh Idempotency-Key for the new request." } ``` When you see this, do NOT retry with the same key. Either: 1. Replay the original body — you'll get the original response back. 2. Generate a new key — you'll create a new trade. ## What's not idempotent * **`POST /v1/trades/{id}/confirm`** — the confirmation token is single-use by design. A second confirm with the same token returns `409 CONFIRMATION_TOKEN_USED`. (The server-side flip from `pending_confirmation → pending` is itself atomic — a race between two concurrent confirms can't both succeed.) * **`POST /v1/webhooks/events/{id}/redeliver`** — explicitly *not* idempotent at the HTTP layer. Each call enqueues another delivery attempt (capped at 5 per event to prevent amplification). The customer's webhook endpoint dedupes on `X-Kash-Event-Id`. * **`POST /v1/auth/api-keys/me/webhook-secret/rotate`** — each call rotates the secret. Don't retry blindly; check the rotated-at timestamp first. ## Next Cursor-based pagination on every list endpoint. `If-None-Match` short-circuits — don't pay quota for unchanged data. # Migrating from the Webapp API Source: https://docs.kash.bot/developer-docs/rest-api/migration-from-webapp Mapping table from app.kash.bot/api endpoints to api.kash.bot/v1. The webapp at `app.kash.bot` exposes a `/api/*` surface that has been around longer than the REST API. It exists primarily for the webapp itself and for [Public Embed API](/developer-docs/public-embed-api/overview) consumers — humans, iframes, anonymous traffic. If you're a programmatic consumer (a trading bot, a market maker, a portfolio tool), you should be on `api.kash.bot/v1`. The webapp endpoints will continue to work for their intended audience, but they're not the right surface for you. ## Why migrate | Concern | `app.kash.bot/api` | `api.kash.bot/v1` | | ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ | | Audience | Humans, embedded UIs | Programmatic consumers (bots, agents, MMs) | | Authentication | Anonymous or Privy session | API key (`X-API-Key`) | | Rate limiting | Per-IP | Per-key (much higher quotas, attribution, telemetry) | | Response shape | UI-shaped (rich; tuned for rendering) | Lean wire shape (decimal strings for big numbers) | | Trade execution | Not exposed — frontend signs directly | First-class — `POST /v1/trades` | | Webhooks | None | `trade.confirmation-required`, `.completed`, `.failed`, `.rejected` | | Idempotency | No | `Idempotency-Key` + `clientRequestId` | | Pagination | Offset (legacy) | Cursor (stable under concurrent writes) | | Error format | Various | RFC 7807 Problem Details with stable `code` | | ETags / `If-None-Match` | No | Yes — every response, cheap 304 revalidation (no body transfer; still counts toward quota) | | Versioning | None | URL-versioned (`/v1/`) + `X-Kash-Api-Version` header | | Deprecation policy | Best-effort | RFC 8594 `Sunset` + `Deprecation` headers, 12-month notice | The two will coexist indefinitely — the webapp routes serve a real audience. But for any new programmatic integration, start on `api.kash.bot/v1`. ## Endpoint mapping The five endpoints with logical overlap: ### `GET /api/markets` → `GET /v1/markets` Both list markets, cursor-paginated/offset-paginated respectively. ```diff theme={null} - GET https://app.kash.bot/api/markets?status=active&limit=10&offset=20 + GET https://api.kash.bot/v1/markets?status=active&limit=10&cursor= + Header: X-API-Key: kash_live_... ``` Differences: * The REST API uses `cursor` not `offset`. Walk pages by passing the `pagination.cursor` from the previous response. * The REST API uses `data: [...]` consistently; webapp uses `markets: [...]`. * The REST API response is leaner (no `yesPrice`/`noPrice` convenience fields — those are derivable from `outcomes[].probability`). * The REST API requires the `markets:read` scope. ### `GET /api/markets/{id}` → `GET /v1/markets/{id}` Single market detail. Same caveats as above. The REST API adds the dual-key `data:` alias on every single-resource response. ### `GET /api/markets/{id}/quote` → `GET /v1/markets/{id}/quote` Both fetch on-chain price quotes. **The `api.kash.bot` endpoint is the canonical implementation** — the webapp's quote endpoint was the prototype; the public REST API's is the production-grade version with stricter precision (decimal-string bigints), explicit `units` block, and proper kill-switch behaviour during RPC degradation. ```diff theme={null} - GET https://app.kash.bot/api/markets/{id}/quote?outcomeIndex=0&action=buy&amount=100 + GET https://api.kash.bot/v1/markets/{id}/quote?outcomeIndex=0&action=buy&amount=100000000 + Header: X-API-Key: kash_live_... ``` Differences: * `amount` on the REST API is in **atomic units** (USDC atomic-6 for buys, token WAD-18 for sells). The webapp accepts decimal USDC. Multiply by `1_000_000` going from one to the other. * The REST API requires the `markets:quote` scope (separate from `markets:read` so you can throttle quote traffic independently). * The REST API returns all bigint fields as decimal strings (`tokensOut: "237340124711760000000"` instead of a JS number that loses precision). * The REST API includes a `units` block (`{ usdc: "atomic-6", token: "wad-18" }`) so consumers don't guess. ### `GET /api/portfolio` → `GET /v1/portfolio` Portfolio summary. ```diff theme={null} - GET https://app.kash.bot/api/portfolio - Header: Authorization: Bearer + GET https://api.kash.bot/v1/portfolio + Header: X-API-Key: kash_live_... ``` Differences: * Auth: Privy session JWT → API key. * Response is per-key user, not per-Privy-user. (For most cases these are the same person, but if you have multi-user setups, mind the distinction.) * The REST API requires the `portfolio:read` scope. ### `POST /api/account/api-keys` → (not on `api.kash.bot`; webapp is the issuer) Self-serve API key issuance lives **only** in the webapp. The public REST API reads from the same `api_keys` table but doesn't issue keys — issuance requires a Privy session (the human). If you're automating key provisioning programmatically, use the admin CLI on a machine with admin credentials: ```bash theme={null} kash-admin api-keys issue --tier developer --user \ --scopes markets:read,markets:quote,trades:read,trades:write ``` ## Endpoints unique to the public REST API These have no webapp equivalent — `api.kash.bot/v1` introduces them: * `POST /v1/trades` — **trade execution**. The big one. The webapp doesn't expose this; all trading happens via direct frontend transactions. * `POST /v1/trades/{id}/confirm` — high-value trade confirmation gate. * `GET /v1/trades`, `GET /v1/trades/{id}` — your trade history. * `GET /v1/webhooks/events`, `POST /v1/webhooks/events/{id}/redeliver` — webhook inspection and replay. * `POST /v1/auth/api-keys/me/webhook-secret/rotate` — secret rotation. * `GET /v1/account/usage` — per-key telemetry. * `GET /v1/traces/{id}` — distributed trace by correlation id. ## Endpoints unique to the webapp These won't appear on `api.kash.bot` — they serve UI/embedding workloads: * `GET /api/markets/trending`, `GET /api/markets/featured`, `GET /api/markets/for-you` — engagement/personalisation. * `GET /api/community/leaderboard` — global rankings. * `GET /api/portfolio/analytics`, `GET /api/portfolio/tx/{hash}` — historical breakdowns. * All thread/social/embed endpoints — those are the [Public Embed API](/developer-docs/public-embed-api/overview). * All ramp/onramp/withdrawal flows — those are user account operations, not programmatic. If you need any of these from a programmatic context, file an issue — there may be a path to expose a subset on the public REST API. ## Migration checklist For a typical migration from `app.kash.bot/api/markets*` + `app.kash.bot/api/portfolio` to `api.kash.bot/v1/*`: * [ ] Generate an API key in **Settings → API Keys** with scopes `markets:read`, `markets:quote`, `portfolio:read` (and `trades:read`, `trades:write` if you'll execute trades). * [ ] Store the key in your secrets manager / env config. Never commit it. * [ ] Replace `Authorization: Bearer ` headers with `X-API-Key: `. * [ ] Replace `app.kash.bot/api` base URL with `api.kash.bot/v1`. * [ ] Update pagination from `offset`/`limit` to `cursor`/`limit`. * [ ] Update response field accesses (`response.markets` → `response.data` or `response.market` → `response.data`). * [ ] Update bigint handling — quote fields are now decimal strings, not JS numbers. Use BigInt or a decimal lib. * [ ] Update quote `amount` to atomic units (`* 1_000_000` for USDC). * [ ] Add error handling — branch on RFC 7807 `code` field, not status text. * [ ] Add `Idempotency-Key` to writes. * [ ] Pin to `X-Kash-Api-Version: 2026-04-29` (or whatever's current) and detect upgrades. * [ ] (Optional) Set up webhook receiver — replaces polling. * [ ] (Optional) Adopt the [TypeScript SDK](/developer-docs/rest-api/sdks/typescript) — collapses most of the above into typed method calls. ## A note on the "prototype" quote endpoint The webapp's quote endpoint was the prototype for the `api.kash.bot` one. The public REST API version is now the source of truth. If the two diverge in output (rare; we monitor for divergence), the `api.kash.bot` response is canonical. We don't currently plan to formally deprecate the webapp's quote endpoint — it serves the embed audience well — but new programmatic code should call `api.kash.bot/v1`. ## Need help migrating? [dev@kash.bot](mailto:dev@kash.bot) — happy to walk through specific migrations. `#developers` channel — fastest for "is this the right endpoint for X?" # REST API Overview Source: https://docs.kash.bot/developer-docs/rest-api/overview Programmatic access to Kash markets, trades, portfolios, and webhooks at api.kash.bot The Kash REST API is the programmatic interface to the prediction-marketplace protocol. It's built for developers, automated agents, and market makers — anything that talks to Kash without a human in the loop. **The two surfaces, and when to use which:** * **`api.kash.bot`** (this API) — for **machines**. Authenticated with API keys. Per-key rate limits, telemetry, attribution. Trade execution, webhooks, portfolio access. * **`app.kash.bot/api`** (the [Public Embed API](/developer-docs/public-embed-api/overview)) — for **humans / UIs**. Anonymous reads, designed for embedding markets in websites and apps. No trading. If you're building a trading bot, an analytics dashboard against your own positions, an MM strategy, a portfolio tool, or a connector for a third-party platform, you want the REST API. If you're embedding markets in an article or website, you want the Embed API. **Building a bot? Start here.** Don't hand-roll HTTP — use the official [`@kashdao/sdk`](/developer-docs/rest-api/sdks/typescript) TypeScript client (auth, retries, idempotency, typed errors, webhook verification built in), and follow the end-to-end [**Build a trading bot**](/developer-docs/rest-api/trading-bot) guide. Prefer Python or run your own signer? See the [Protocol SDK](/developer-docs/protocol-sdk/overview). ## What's in v1 Browse markets, fetch detail, get on-chain quotes, list recent trades against a market. Place buy/sell orders, list and inspect your own trades, confirm high-value trades. Smart-account address, position summary, per-market position detail. Receive trade lifecycle events at your endpoint. Stripe-style HMAC-signed, retried, replayable. Per-key telemetry: trade volume, latency p50/p99, webhook success rate, auth failures. Walk a trade's event timeline by correlation id — for debugging and observability. ## Design principles * **Stripe-shaped wire protocol.** RFC 7807 errors, `Idempotency-Key` header, cursor pagination, dual-key envelopes (`{ trade, data }` on every single-resource response), Stripe-style HMAC webhook signatures. If you've integrated with Stripe, this will feel familiar. * **OpenAPI 3.1 is the contract of record.** The live spec at [`https://api.kash.bot/v1/openapi.json`](https://api.kash.bot/v1/openapi.json) is the source of truth. Interactive docs at [`https://api.kash.bot/v1/docs`](https://api.kash.bot/v1/docs). * **Versioning by URL.** Currently `/v1/`. Sunset and Deprecation headers (RFC 8594) announce changes 12 months ahead. * **Always-on auth.** Every data route requires a key — no anonymous fallback. This unlocks per-key rate limits, telemetry, kill switches, and abuse mitigation. * **Decimal strings for big numbers.** USDC and outcome-token amounts are returned as decimal strings (atomic-6 for USDC, WAD-18 for tokens). Never lose precision parsing JSON. ## Base URL ``` Production: https://api.kash.bot/v1 Staging: https://api-staging.kash.bot/v1 ``` The staging environment is wired to Base Sepolia and is the right place to integrate before going live. ## API version The current API version is `2026-04-29`. Every response carries `X-Kash-Api-Version: 2026-04-29`; every webhook envelope includes `apiVersion`. We surface this date so consumers can detect a server upgrade and pin against it. ## Next steps Generate a key, place your first trade, receive your first webhook — under 5 minutes. `X-API-Key`, scopes, IP allowlists, and key rotation. The full live spec, rendered interactively at `/v1/docs`. ## Need help? Email [dev@kash.bot](mailto:dev@kash.bot) for technical questions. Join the `#developers` channel for real-time help and to share what you're building. # Pagination Source: https://docs.kash.bot/developer-docs/rest-api/pagination Cursor-based pagination on every list endpoint. Every list endpoint (`GET /v1/markets`, `GET /v1/trades`, `GET /v1/markets/{id}/predictions`, `GET /v1/webhooks/events`) returns a paginated response with an opaque cursor. `GET /v1/portfolio/positions` is **not** cursor-paginated in v1 — it returns the full position set in a single `{ data, _meta }` response and accepts only a `?marketId=` filter. We deliberately do **not** support offset-based pagination — offsets are inconsistent under concurrent writes and degrade with table size. ## Wire shape ```json theme={null} { "data": [ /* page items */ ], "pagination": { "cursor": "eyJpZCI6IjEyMy4uLiIsInRzIjoxNzMwfQ==", "hasMore": true, "limit": 25 }, "_meta": { "requestId": "...", "timestamp": "..." } } ``` | Field | Use | | --------- | ------------------------------------------------------------------------------------------------ | | `data` | The page items (in resource-natural order — usually newest first, see per-endpoint docs). | | `cursor` | Opaque base64url string. Pass on the next request as `?cursor=`. `null` on the last page. | | `hasMore` | `true` if more pages exist after this one. | | `limit` | The page size that was actually applied. | ## Walking pages ```bash theme={null} # First page curl "https://api.kash.bot/v1/trades?limit=50" \ -H "X-API-Key: $KASH_API_KEY" # → { "data": [...], "pagination": { "cursor": "abc...", "hasMore": true, "limit": 50 } } # Next page curl "https://api.kash.bot/v1/trades?limit=50&cursor=abc..." \ -H "X-API-Key: $KASH_API_KEY" # → { "data": [...], "pagination": { "cursor": null, "hasMore": false, "limit": 50 } } ``` When `pagination.cursor` is `null` (or `hasMore: false`), you've reached the end. ### TypeScript SDK The SDK's `list()` methods return a `Page` that's both the first page (with `data` and `pagination`) and an `AsyncIterable` for walking every page lazily: ```ts theme={null} const page = await kash.trades.list({ limit: 25 }); // Just the first page console.log(page.data); // Or iterate every trade across every page for await (const trade of page) { console.log(trade.id, trade.status); } // Or call .getNextPage() manually const next = await page.getNextPage(); ``` `page.getNextPage()` is memoised — calling it twice returns the same Promise. ## Limits | Endpoint | Default limit | Max limit | | ---------------------------------- | ------------- | --------- | | `GET /v1/markets` | 20 | 100 | | `GET /v1/markets/{id}/predictions` | 50 | 100 | | `GET /v1/trades` | 20 | 100 | | `GET /v1/webhooks/events` | 25 | 100 | Override with `?limit=N`. Requests with `limit > 100` get clamped to `100` silently. ## Filtering Most list endpoints accept filters that compose with pagination: ```bash theme={null} # Trades filtered by status curl "https://api.kash.bot/v1/trades?status=pending,executing&limit=10" \ -H "X-API-Key: $KASH_API_KEY" # Trades for a specific market curl "https://api.kash.bot/v1/trades?marketId=11111111-1111-4111-8111-111111111111" \ -H "X-API-Key: $KASH_API_KEY" # Predictions filtered to one outcome and side curl "https://api.kash.bot/v1/markets/$MARKET_ID/predictions?outcomeIndex=0&side=buy&limit=20" \ -H "X-API-Key: $KASH_API_KEY" ``` The cursor encodes the filter context. Don't change filters mid-walk — generate a new cursor by re-requesting the first page. ## Cursor stability Cursors are stable for **at least 7 days**. If you persist a cursor (e.g. for a daily incremental fetch) and use it later, it'll continue to work as long as the underlying row hasn't been deleted. After 7 days we make no guarantees — restart the walk from the first page. Cursors are tied to the page they came from. Don't try to construct your own — they're opaque on purpose, and the encoding may change. ## Insertion behaviour during a walk Because cursors are anchored to row ids (not offsets), new rows inserted *after* you started a walk don't shift your cursor. You'll never see the same item twice, and you'll never skip an item that existed when you started. Items inserted *during* the walk that fall before your cursor on the natural sort order won't appear in the current walk. Re-fetch from the first page to pick them up. ## Total counts We don't return a total count — counting all rows on every page is expensive at scale. If you need a count for UX: * For trades: `GET /v1/account/usage` returns 24h/7d/30d aggregates. * For markets: walk the full list (cheap with `?limit=100` and ETags). If you really need exact totals for a custom dashboard, the [account telemetry endpoint](/developer-docs/rest-api/endpoint-reference) is your friend. ## Next Combine pagination with `If-None-Match` to skip unchanged pages. The interactive spec with full pagination examples. # Quickstart Source: https://docs.kash.bot/developer-docs/rest-api/quickstart Get a key, place your first trade, and receive your first webhook in under 5 minutes. This guide walks you end-to-end against the **staging** environment (`api-staging.kash.bot`, Base Sepolia). Going live afterwards is the same flow against `api.kash.bot`. ## 1. Create a Kash account If you don't have one already, sign up at [staging.kash.bot](https://staging.kash.bot). The staging webapp is the human-facing front door; you'll use it to create the account that owns your API keys. ## 2. Generate an API key In the webapp: 1. Go to **Settings → API Keys**. 2. Click **Create key**. 3. Give it a name (e.g. `local dev`) and select the scopes you need: * `markets:read` — read market detail & list (granted by default on every tier) * `markets:quote` — fetch on-chain price quotes * `portfolio:read` — read your own positions * `trades:read` — list and inspect your trades * `trades:write` — place trades and confirm high-value ones * `webhooks:manage` — list events, redeliver, rotate the webhook secret 4. (Optional) Set an IP allowlist if you'll always call from a known set of addresses. 5. (Optional) Set a `webhook_url` if you want trade-lifecycle events POSTed to your endpoint. The plaintext key is shown **once**. Capture it immediately: ```bash theme={null} export KASH_API_KEY="kash_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` The `kash_test_` prefix marks staging keys; production keys use `kash_live_`. Key issuance happens in the webapp's **Settings → API Keys** page — the CLI can't issue keys. Once you have one, store it in the CLI with `kash auth set-key` so every `kash` command picks it up. See the [CLI guide](/developer-docs/cli/overview). ## 3. Make your first request ```bash theme={null} curl https://api-staging.kash.bot/v1/markets?limit=5 \ -H "X-API-Key: $KASH_API_KEY" ``` You should get back a `200 OK` with a page of markets. If you get `403 INSUFFICIENT_SCOPE`, your key is missing `markets:read`. If you get `429`, you've hit your tier's rate limit — the response includes `X-RateLimit-Reset` and `Retry-After`. ## 4. Get a quote ```bash theme={null} MARKET_ID="" curl "https://api-staging.kash.bot/v1/markets/${MARKET_ID}/quote?outcomeIndex=0&action=buy&amount=10000000" \ -H "X-API-Key: $KASH_API_KEY" ``` The `amount` here is in **atomic USDC** (`10000000` = 10 USDC). The response carries the expected `tokensOut`, the post-trade pool state, and an `effectivePrice` convenience number. ## 5. Place your first trade ```bash theme={null} curl -X POST https://api-staging.kash.bot/v1/trades \ -H "X-API-Key: $KASH_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "marketId": "'"$MARKET_ID"'", "outcomeIndex": 0, "amount": "10", "side": "buy", "metadata": { "strategy": "first-trade", "source": "quickstart" } }' ``` Note: `amount` in the trade body is a **decimal string** of USDC (`"10"` = 10 USDC), distinct from the atomic-units amount used by the quote endpoint. You'll get back a `201 Created`: ```json theme={null} { "trade": { "id": "...", "status": "pending", ... }, "data": { "id": "...", "status": "pending", ... }, "_meta": { "requestId": "...", "timestamp": "...", "idempotent": false } } ``` The trade's `status` field tracks the lifecycle: `pending → validating → executing → completed` (or `failed`/`rejected`). ## 6. Watch the trade complete The simplest way is to poll: ```bash theme={null} TRADE_ID="" curl https://api-staging.kash.bot/v1/trades/$TRADE_ID \ -H "X-API-Key: $KASH_API_KEY" ``` You'll see the status transition to `completed` with a `txHash` and `tokensOut` populated. The better way is to receive a webhook — see step 7. ## 7. Receive a webhook If you set a `webhook_url` on the key in step 2, the **trade.completed** event was already POSTed to your endpoint. The body looks like: ```json theme={null} { "id": "evt_uuid", "type": "trade.completed", "apiVersion": "2026-04-29", "createdAt": "2026-05-02T12:34:56.789Z", "data": { "tradeId": "trade_uuid", "marketId": "market_uuid", "outcomeIndex": 0, "amount": "10", "side": "buy", "status": "completed", "txHash": "0x...", "tokensOut": "23734012471176000000", "metadata": { "strategy": "first-trade", "source": "quickstart" } } } ``` The headers carry the signature so you can verify the request: ``` X-Kash-Signature: t=1730000000000,v1= X-Kash-Event-Id: evt_uuid X-Kash-Api-Version: 2026-04-29 ``` See **[Verifying webhook signatures](/developer-docs/rest-api/webhooks/verifying)** for the verification recipe (one-line constant change from any Stripe library). ## 8. (Optional) Use the SDK The TypeScript SDK gives you typed everything — including a one-line `constructEvent` for webhook verification: ```bash theme={null} pnpm add @kashdao/sdk ``` ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey: process.env.KASH_API_KEY!, baseUrl: 'https://api-staging.kash.bot/v1', }); const trade = await kash.trades.create({ marketId, outcomeIndex: 0, amount: '10', side: 'buy', metadata: { strategy: 'first-trade' }, }, { idempotencyKey: crypto.randomUUID() }); const final = await kash.trades.waitForCompletion(trade.id); console.log(final.status, final.txHash); ``` Full SDK reference: **[TypeScript SDK](/developer-docs/rest-api/sdks/typescript)**. ## What next? Scopes, IP allowlists, and key rotation. Set up a verified, retried webhook endpoint. Safely retry trade creation without duplicates. # Rate Limits Source: https://docs.kash.bot/developer-docs/rest-api/rate-limits Per-key quotas, X-RateLimit-* response headers, and how to handle 429s. The REST API enforces a per-key sliding-window quota. When a key exceeds it, the next request returns `429 RATE_LIMIT_EXCEEDED` with `Retry-After` and `X-RateLimit-*` headers so you can back off cleanly. ## Per-tier defaults | Tier | Rate limit (req/min) | Daily spend cap | Per-trade cap | High-value confirm gate | | ------------ | -------------------- | --------------- | ------------- | ----------------------- | | `free` | 60 | 1,000 USDC | 500 USDC | none | | `developer` | 300 | 10,000 USDC | 2,500 USDC | none | | `enterprise` | Custom (admin-set) | 100,000 USDC | 25,000 USDC | 25,000 USDC | | `mm` | Custom (admin-set) | 1,000,000 USDC | 50,000 USDC | 50,000 USDC | A tier sets four independent limits: * **Rate limit** — HTTP requests per minute, sliding window. Hit this and you get `429 RATE_LIMIT_EXCEEDED`. * **Daily spend cap** — rolling 24h aggregate USDC volume per key. Hit this and you get `409 SPENDING_LIMIT_EXCEEDED` with `extensions.cap = "daily_volume"`. Sells and close-position intents do NOT draw against this cap. * **Per-trade cap** — single-trade USDC amount. Hit this and you get `409 SPENDING_LIMIT_EXCEEDED` with `extensions.cap = "per_trade"`. * **High-value confirm gate** — for tiers that have one, any trade ≥ this amount returns `202 Accepted` with the token nested as `confirmation: { token, expiresAt }`; you then `POST /v1/trades/{id}/confirm` with `confirmation.token` to actually execute. Two-step flow lets you catch a runaway bot before it commits a six-figure trade. The rate-limit window is **per `user_id`**, not per individual key. If a single user holds two keys, HTTP requests across both share the quota — so issuing extra keys doesn't multiply your throughput. The **spending caps**, however, are **per key** — each key gets its own daily allotment. Each user can hold at most **5 active keys** at a time (revoked keys don't count). This guards against quota-pooling abuse patterns. ## Why these caps exist The caps are not punitive. Five concrete reasons: 1. **Blast radius on key compromise.** API keys leak — committed to git, dumped in error reports, laptop stolen. The cap bounds the loss between leak-time and revoke-time. 2. **Runaway-bot circuit breaker.** A trading loop misfires (off-by-one in your sizing, retry storm on a timeout, missing dedup on a webhook). The cap is a hard stop you cannot disable from inside your own code. 3. **AMM stability.** Kash markets use a Pythagorean AMM with thin per-outcome liquidity. A trade consuming more than \~5% of available liquidity moves the price substantially. Per-trade caps protect the AMM curve for everyone. 4. **Compliance ladder.** Higher daily volumes implicitly require enhanced KYC, ToS attestation, and sanctions screening. The tier names map to that ladder. 5. **Delegation guardrails.** API keys are a scoped delegation against the user's Privy-managed smart account — Kash never holds the keys or the funds, but for the duration of the delegation a leaked key (or a runaway bot) can move balance up to whatever the smart account holds. Caps bound that blast radius. The user can revoke any key at any time; the cap is the runtime safety net while it's active. If you want a surface with no caps, the self-orchestrated track (`@kashdao/protocol-sdk`) is the right product — you sign and submit every transaction yourself with your own signer/RPC/bundler, and bear the full operational risk. Both paths are non-custodial; this one just removes the platform-side guardrails along with the platform-side orchestration. ## Response headers (every response) Every response — `200`, `4xx`, `429` — carries the live state of your bucket: ``` X-RateLimit-Limit: 300 X-RateLimit-Remaining: 287 X-RateLimit-Reset: 1730000060 # Unix epoch seconds; when remaining refills ``` * `X-RateLimit-Limit` — your configured quota for the window. * `X-RateLimit-Remaining` — how many requests you can still send before the window resets. * `X-RateLimit-Reset` — Unix epoch seconds when `Remaining` refills back to `Limit`. Use these to pace yourself proactively rather than waiting for a `429`. ## Hitting the limit When you exceed the quota: ```http theme={null} HTTP/1.1 429 Too Many Requests Content-Type: application/problem+json Retry-After: 38 X-RateLimit-Limit: 300 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1730000098 { "type": "https://docs.kash.bot/developer-docs/api-errors/RATE_LIMIT_EXCEEDED", "title": "Rate limit exceeded", "status": 429, "code": "RATE_LIMIT_EXCEEDED", "detail": "API key has exceeded its 300 req/min quota.", "requestId": "req_..." } ``` **Always** respect `Retry-After` (seconds). The TypeScript SDK does this automatically with exponential backoff and jitter. ## Algorithm The rate limiter uses a **sliding window** for read paths (markets, portfolio, account, traces, webhooks list) and a **token bucket** for write paths (`POST /v1/trades`, `POST /v1/trades/{id}/confirm`). * **Sliding window** — smooth, no burst. Best for sustained polling. * **Token bucket** — allows short bursts (capacity = limit; refill = limit/60 per second). Best for human-driven actions. Both algorithms are keyed on `user:{userId}`. ## Per-route rate limit policies Most endpoints share the per-key tier quota. A few are tighter: | Endpoint pattern | Algorithm | Notes | | ------------------------------------------------- | -------------- | -------------------------------------------------------- | | `GET /v1/markets*`, `GET /v1/portfolio*` | sliding-window | Standard tier quota. | | `GET /v1/markets/{id}/quote` | sliding-window | Standard tier quota; backed by RPC. | | `POST /v1/trades`, `POST /v1/trades/{id}/confirm` | token-bucket | Tighter — write paths cost more. | | `POST /v1/webhooks/events/{id}/redeliver` | token-bucket | Replay amplification cap (max 5 redeliveries per event). | ## Best practices * **Read your headers.** Cheaper than retry-after-rejection. Aim to keep `X-RateLimit-Remaining` above 10% of `X-RateLimit-Limit` under steady-state load. * **Use ETags on read paths.** A `304 Not Modified` still consumes one request from your quota (rate limiting runs before the ETag check) — the savings are bandwidth and server work, not quota. See **[ETags](/developer-docs/rest-api/etags)**. * **Use the SDK.** It handles `Retry-After`, exponential backoff, jitter, and idempotency-safe retries automatically. * **Distribute across keys carefully.** Rate-limit quota is per-`user_id`, so multiple keys for the same user don't help with HTTP throughput. (Spending caps ARE per-key.) If you need higher throughput, the upgrade path is below. * **Don't hammer the quote endpoint.** Quotes are RPC-heavy. Cache results client-side for the freshness window your strategy can tolerate (typically 5–15s). ## Kill switches Independent from rate limiting, every route has an ops-controlled kill switch. When triggered (rare — usually during chain RPC degradation), the route returns: ```http theme={null} 503 Service Unavailable Content-Type: application/problem+json { "code": "ROUTE_DISABLED", "title": "Route temporarily disabled", "detail": "This endpoint is currently disabled by ops. Try again later." } ``` Treat `503 ROUTE_DISABLED` like `Retry-After` — back off and retry. Most kill switches are toggled off within minutes. ## How to upgrade your tier Hitting `RATE_LIMIT_EXCEEDED` or `SPENDING_LIMIT_EXCEEDED` repeatedly during normal operation means you've outgrown your tier — that's the signal to upgrade. Today, every tier above `free` is provisioned manually: * **`free` → `developer`** (5× rate limit, 10× spending caps): email **[support@kash.bot](mailto:support@kash.bot)** with your account email and a one-line description of your use case. Self-serve via Stripe is on the roadmap; until it ships we provision developer-tier keys on a case-by-case basis. * **Anywhere → `enterprise`** ($100k/day, $25k/trade, custom rate limit): **[contact sales](mailto:sales@kash.bot)**. Custom pricing based on volume commitment. * **Anywhere → `mm`** ($1M/day, $50k/trade, full counterparty terms): **[contact partnerships](mailto:partnerships@kash.bot)**. KYB, ToS attestation, and possibly escrow arrangements — this is a relationship, not a pricing tier. `enterprise` and `mm` tiers can override any of the four axes per-key as part of the contract — a key can have a different `daily_spend_limit_usdc`, `per_trade_limit_usdc`, or `high_value_trade_threshold_usdc` than the tier default. ## Telemetry Your per-key telemetry — including 24h `rateLimitRejections` count — is exposed via `GET /v1/account/usage`. If you're seeing unexpected `429`s, that's the first place to check. ```bash theme={null} curl https://api.kash.bot/v1/account/usage \ -H "X-API-Key: $KASH_API_KEY" | jq '.data.auth["24h"].rateLimitRejections' ``` ## Next The full RFC 7807 error format and code catalog. `If-None-Match` short-circuits — free quota for unchanged data. # TypeScript SDK Source: https://docs.kash.bot/developer-docs/rest-api/sdks/typescript @kashdao/sdk — typed everything. Trades, markets, portfolios, webhooks, retries, idempotency. The `@kashdao/sdk` package is a hand-written TypeScript client for the REST API. It's the recommended way to integrate from any TS/JS runtime — Node 22+, modern browsers, Deno, Bun, or Cloudflare Workers. ```bash theme={null} pnpm add @kashdao/sdk # or: npm install @kashdao/sdk # or: yarn add @kashdao/sdk # or: bun add @kashdao/sdk ``` ## Construct the client ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey: process.env.KASH_API_KEY!, // 'kash_live_...' or 'kash_test_...' baseUrl: 'https://api.kash.bot/v1', // staging: https://api-staging.kash.bot/v1 timeoutMs: 10_000, // optional, default 30s maxRetries: 3, // optional, default 3 (with exp backoff + jitter) webhookSecret: process.env.KASH_WEBHOOK_SECRET, // optional, only needed for constructEvent }); ``` The client is a thin façade over typed sub-clients: `kash.markets`, `kash.trades`, `kash.portfolio`, `kash.webhooks`, `kash.account`, `kash.traces`. ## Place a trade ```ts theme={null} const trade = await kash.trades.create({ marketId: '11111111-1111-4111-8111-111111111111', outcomeIndex: 0, amount: '100', side: 'buy', metadata: { strategy: 'momentum-v2', cohort: 'beta' }, }, { idempotencyKey: crypto.randomUUID() }); console.log(trade.id, trade.status, trade.idempotent); if (trade.confirmation) { // High-value gate hit — confirm with the one-time token await kash.trades.confirm(trade.id, { token: trade.confirmation.token }); } // Wait for the on-chain result (polls + respects rate limits) const final = await kash.trades.waitForCompletion(trade.id, { pollIntervalMs: 1000, timeoutMs: 60_000, }); console.log(final.status, final.txHash, final.tokensOut); ``` ## Pagination — async iteration Every list method returns a `Page` that's both the first page and an `AsyncIterable`: ```ts theme={null} const page = await kash.trades.list({ limit: 25, status: 'completed' }); // First page only console.log(page.data.length, page.pagination.hasMore); // Or walk every trade across every page for await (const trade of page) { console.log(trade.id, trade.txHash); } ``` ## Get a quote ```ts theme={null} const quote = await kash.markets.quote({ marketId: '...', outcomeIndex: 0, action: 'buy', amount: '10000000', // atomic USDC (10 USDC) }); console.log(quote.tokensOut, quote.effectivePrice, quote.impliedProbability); ``` ## Verify a webhook ```ts theme={null} import express from 'express'; app.post('/webhooks/kash', express.raw({ type: 'application/json' }), async (req, res) => { try { const event = await kash.webhooks.constructEvent( req.body.toString('utf8'), req.header('x-kash-signature')!, process.env.KASH_WEBHOOK_SECRET! ); switch (event.type) { case 'trade.completed': // event.data is fully typed (TradeCompletedPayload) await onTradeCompleted(event.data); break; case 'trade.failed': await onTradeFailed(event.data); break; case 'trade.confirmation-required': await onConfirmationRequired(event.data); break; } res.sendStatus(200); } catch (err) { // KashWebhookSignatureError on bad signature, expired, malformed res.sendStatus(401); } } ); ``` ## Typed errors Every error is a typed subclass of `KashError`: ```ts theme={null} import { KashError, KashAuthenticationError, // 401 KashAuthorizationError, // 403 KashConflictError, // 409 KashNotFoundError, // 404 KashRateLimitError, // 429 KashServerError, // 5xx KashNetworkError, KashTimeoutError, KashValidationError, KashWebhookSignatureError, KashConfigurationError, } from '@kashdao/sdk'; try { await kash.trades.create(body); } catch (err) { if (err instanceof KashRateLimitError) { console.log(`Backoff for ${err.retryAfterSeconds}s`); } else if (err instanceof KashAuthorizationError && err.code === 'INSUFFICIENT_SCOPE') { console.log('Re-issue the key with trades:write'); } else if (KashError.isKashError(err)) { // Cross-realm-safe brand check (works across worker postMessage boundaries) console.log(err.code, err.requestId); } else { throw err; } } ``` ## Lifecycle hooks Tap into every request, response, retry, and error for logging or observability: ```ts theme={null} const kash = new KashClient({ apiKey, hooks: { onRequest: (e) => log.info('kash.request', { method: e.method, url: e.url, attempt: e.attempt }), onResponse: (e) => log.info('kash.response', { status: e.status, durationMs: e.durationMs }), onRetry: (e) => log.warn('kash.retry', { attempt: e.attempt, reason: e.reason }), onError: (e) => log.error('kash.error', { code: e.code, attempt: e.attempt }), }, }); ``` A throwing hook never breaks the request — exceptions are swallowed and the call proceeds. Hooks are safe to add for telemetry without thinking about resilience. ## Cloning a client `withConfig` returns a new client with overridden fields. Useful for tenant switching or per-request key swaps: ```ts theme={null} const tenantA = new KashClient({ apiKey: 'kash_live_AAAA...' }); const tenantB = tenantA.withConfig({ apiKey: 'kash_live_BBBB...' }); // tenantA and tenantB are independent — same baseUrl, different keys ``` `withConfig` re-validates — passing an invalid override throws `KashConfigurationError`. ## Mock client for tests `@kashdao/sdk/testing` exports a fake client with realistic default fixtures: ```ts theme={null} import { createMockKashClient, DEFAULT_TRADE } from '@kashdao/sdk/testing'; const mock = createMockKashClient({ // Override specific methods trades: { create: async () => ({ ...DEFAULT_TRADE, status: 'pending', idempotent: false }), }, }); // Inject into your code under test const result = await yourTradingBot.run(mock); ``` The defaults pass the wire-shape contract tests, so they're indistinguishable from real production responses for downstream consumers. ## Versioning The SDK follows SemVer. The `KashError` shape, public method signatures, and exported types are SemVer-stable. Internals (private fields, pagination tokens, transport details) are not — pin to a major version range in `package.json`. The SDK auto-includes a `User-Agent` header identifying its version, so we can correlate API logs with SDK versions when investigating issues. ### Pin against a specific API contract version Send the `X-Kash-Api-Version` request header to lock your integration against a specific contract date. The SDK can be configured to send it on every request: ```ts theme={null} const kash = new KashClient({ apiKey: process.env.KASH_API_KEY!, apiVersion: '2026-04-29', // optional; omits the header (server defaults) when not set }); ``` Server response carries `X-API-Version` (the canonical version) plus `Sunset` / `Deprecation` headers when your pinned version is winding down (12-month deprecation window per RFC 8594). The SDK's `onResponse` hook surfaces both — wire it into your monitoring to flag deprecations early. Pinning to an unknown date returns `410 API_VERSION_UNSUPPORTED` with the supported list in `metadata.supported`. ## Quality gates The SDK ships with strict quality gates that are part of the public contract: * **Bundle size budgets** — main entry ≤ 24 KB gzipped, `/testing` subpath ≤ 8 KB. Enforced at publish time via `size-limit`. * **Static type tests** — `pnpm test:types` runs vitest's typecheck mode against `tests/types/public-types.test-d.ts`. The public TypeScript surface is locked: any change to a public type's shape fails compilation, forcing a deliberate CHANGELOG + version bump. * **Public-barrel snapshot** — `tests/unit/barrel-snapshot.test.ts` pins the exact set of exported names. Accidental removals fail the build. * **Wire-shape contract** — `tests/contract/wire-shape.test.ts` asserts every resource schema matches canonical fixtures. Ships to the public mirror. ## Source & changelog * npm: [`@kashdao/sdk`](https://www.npmjs.com/package/@kashdao/sdk) * GitHub: [`KashDAO/sdk-typescript`](https://github.com/KashDAO/sdk-typescript) (public mirror) * Changelog: [`CHANGELOG.md`](https://github.com/KashDAO/sdk-typescript/blob/main/CHANGELOG.md) * Versioned OpenAPI snapshots: the live spec is served at [`api.kash.bot/v1/openapi.json`](https://api.kash.bot/v1/openapi.json); historical pinned versions are listed at [`api.kash.bot/v1/openapi/index.json`](https://api.kash.bot/v1/openapi/index.json) — pin against a specific date for reproducible code generation. * Issues / discussions: [github.com/KashDAO/sdk-typescript/issues](https://github.com/KashDAO/sdk-typescript/issues) ## Next `kash` — the command-line interface. Same primitives, scripting-friendly. The full live spec at `/v1/docs`. # Build a trading bot Source: https://docs.kash.bot/developer-docs/rest-api/trading-bot End-to-end: from an API key to a running bot that quotes, trades, verifies webhooks, manages positions, and claims winnings — with @kashdao/sdk. This is the spine that ties the reference pages together. You'll build a bot that watches a market, places a sized trade on a signal, verifies webhook events, tracks its positions, and claims its winnings after the market resolves. Everything here runs against **staging** (`api-staging.kash.bot`, Base Sepolia) — going live is the same code against `api.kash.bot`. Use the **[`@kashdao/sdk`](/developer-docs/rest-api/sdks/typescript)** TypeScript client, not raw HTTP. It handles auth, retries, idempotency, typed errors, and webhook signature verification for you. Everything below is SDK-first; the one place you'll drop to `fetch` is redemptions (not yet in the SDK — see [step 8](#8-claim-winnings-redeem)). Building on the **self-orchestrated** surface instead — you run your own signer / market-making infra? Use the [Protocol SDK](/developer-docs/protocol-sdk/overview) (and the [Hummingbot connector](/developer-docs/protocol-sdk-python/hummingbot)). This tutorial is the **managed** REST path. ## What you'll build A long-running loop that: 1. Reads a market and quotes a position size. 2. Places a trade with an idempotency key (safe to retry). 3. Confirms the trade if it trips the high-value gate. 4. Learns the outcome asynchronously via a signed webhook (or by polling). 5. Tracks open positions. 6. Redeems winnings once the market resolves. ## 1. Prerequisites * A Kash account — sign up at [staging.kash.bot](https://staging.kash.bot). * An **API key** with these scopes (see [Authentication](/developer-docs/rest-api/authentication)): `markets:read`, `markets:quote`, `trades:read`, `trades:write`, `portfolio:read`, `webhooks:manage`. Keys are created in the webapp (**Settings → API Keys**) and shown once — capture the plaintext. A `kash_test_*` key targets staging automatically. * Node 22+. ```bash theme={null} npm i @kashdao/sdk export KASH_API_KEY="kash_test_…" # staging key export KASH_WEBHOOK_SECRET="whsec_…" # from the webapp, for step 4 ``` ## 2. Construct the client The SDK auto-routes to staging for a `kash_test_` key and to production for a `kash_live_` key — you don't hard-code a base URL. ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey: process.env.KASH_API_KEY! }); // kash_test_* → https://api-staging.kash.bot/v1 (Base Sepolia) // kash_live_* → https://api.kash.bot/v1 ``` ## 3. Read a market and quote Pick a market, then quote before you trade so you size against real on-chain liquidity. `amountUsdcAtomic` is USDC in atomic units (6 decimals → `10_000_000` = 10 USDC). ```ts theme={null} const { data: markets } = await kash.markets.list({ status: 'active' }); const market = markets[0]; const quote = await kash.quotes.buy({ marketId: market.id, outcomeIndex: 0, amountUsdcAtomic: 10_000_000n, // 10 USDC into outcome 0 }); // quote.amountOut = outcome tokens you'd receive; quote.pricesAfterWad = post-trade prices console.log('would receive', quote.amountOut, 'tokens'); ``` ## 4. Place a trade (idempotent) Pass an `idempotencyKey`: if the request times out and your bot retries, you get the **same** trade back rather than a duplicate. The response carries `idempotent: true` when it's a replay. ```ts theme={null} import { randomUUID } from 'node:crypto'; const trade = await kash.trades.create( { marketId: market.id, outcomeIndex: 0, side: 'buy', amount: '10' }, { idempotencyKey: randomUUID() }, ); console.log(trade.id, trade.status, trade.idempotent ? '(replayed)' : '(new)'); ``` Reuse the **same** `idempotencyKey` for a given logical order across retries; generate a **new** one for each distinct order. See [Idempotency](/developer-docs/rest-api/idempotency). ## 5. Confirm high-value trades Trades above your key's per-trade threshold return a one-time `confirmation` token instead of executing immediately (the trade sits in `pending_confirmation`). Confirm it to release execution. ```ts theme={null} if (trade.confirmation) { // trade.confirmation = { token, expiresAt } — the token is shown once, here. await kash.trades.confirm(trade.id, { token: trade.confirmation.token }); } ``` ## 6. Learn the outcome — webhooks (preferred) or poll **Webhooks** are the production path: Kash POSTs a signed event to your endpoint when the trade completes. Verify the signature with the SDK and dedupe on the event id — **never** trust an unverified body. ```ts theme={null} import express from 'express'; const app = express(); // IMPORTANT: verify against the RAW body, so capture bytes, don't pre-JSON-parse. app.post('/webhooks/kash', express.raw({ type: 'application/json' }), async (req, res) => { const raw = req.body.toString('utf8'); const signature = req.header('X-Kash-Signature') ?? ''; const eventId = req.header('X-Kash-Event-Id') ?? ''; let event; try { // Verifies the HMAC signature + timestamp, then parses. Throws on a bad signature. event = await kash.webhooks.constructEvent(raw, signature, process.env.KASH_WEBHOOK_SECRET!); } catch (err) { return res.status(400).send('invalid signature'); } if (alreadyProcessed(eventId)) return res.status(200).send('dup'); // your idempotency store switch (event.type) { case 'trade.completed': /* position is live */ break; case 'trade.failed': case 'trade.rejected': /* surface + retry policy */ break; case 'trade.confirmation-required': /* call kash.trades.confirm */ break; } res.status(200).send('ok'); // 2xx fast; do slow work out of band }); ``` Prefer webhooks in production. For a script or a quick test, **poll** instead: ```ts theme={null} const settled = await kash.trades.waitForCompletion(trade.id); console.log(settled.status); // 'completed' | 'failed' | 'rejected' ``` See [Webhooks → Verifying](/developer-docs/rest-api/webhooks/verifying) for the full event catalog and signature scheme. ## 7. Track positions ```ts theme={null} const summary = await kash.portfolio.get(); // account-wide const { data: positions } = await kash.portfolio.positions({ marketId: market.id }); ``` `positions` is **not** paginated in v1 — it returns everything, filterable by `marketId`. ## 8. Claim winnings (redeem) When a market resolves, your winning outcome tokens sit in your smart account until you claim them. Create a redemption for the `(market, outcome)` you hold; the existing payout pipeline settles it on-chain and your USDC balance increases. Redemptions aren't in `@kashdao/sdk` **yet**, so call the endpoint directly. It reuses your `trades:write` scope and is idempotent — one open claim per `(market, outcome)`; a repeat returns the original with `200`. ```ts theme={null} const res = await fetch('https://api-staging.kash.bot/v1/redemptions', { method: 'POST', headers: { 'X-API-Key': process.env.KASH_API_KEY!, 'Content-Type': 'application/json' }, body: JSON.stringify({ marketId: market.id, outcomeIndex: 0 }), }); // 201 new claim · 200 duplicate (original returned) · 409 POSITION_NOT_CLAIMABLE // · 404 REDEMPTIONS_NOT_ENABLED (feature off for your org) const redemption = await res.json(); ``` There is **no** redemption webhook. Detect settlement by polling `GET /v1/portfolio` until the USDC balance reflects the payout. See the [`POST /v1/redemptions`](/developer-docs/rest-api/endpoint-reference) reference and the [`POSITION_NOT_CLAIMABLE`](/developer-docs/api-errors/POSITION_NOT_CLAIMABLE) / [`REDEMPTIONS_NOT_ENABLED`](/developer-docs/api-errors/REDEMPTIONS_NOT_ENABLED) codes. ## 9. Handle errors and rate limits Every failure is a typed `KashError` subclass — branch on the class, not the message. ```ts theme={null} import { KashRateLimitError, KashAuthorizationError, KashValidationError, KashConflictError, } from '@kashdao/sdk'; try { await kash.trades.create(/* … */); } catch (err) { if (err instanceof KashRateLimitError) { await sleep(err.retryAfterSeconds * 1000); // seconds, honour it } else if (err instanceof KashConflictError) { // e.g. duplicate idempotency key with a different body, or already-claimed } else if (err instanceof KashAuthorizationError) { // missing scope / IP not allowed — fix the key, don't retry } else if (err instanceof KashValidationError) { // 400 — bad input; inspect err and fix the request } else { throw err; } } ``` See [Rate limits](/developer-docs/rest-api/rate-limits) and the [Error catalogue](/developer-docs/api-errors/overview). ## 10. Going live * Swap the key for a `kash_live_*` key — the SDK re-routes to `api.kash.bot` automatically. * Point `webhook_url` at your production endpoint and set the production `KASH_WEBHOOK_SECRET`. * Review [rate limits](/developer-docs/rest-api/rate-limits) for your tier and the [webapp → REST migration notes](/developer-docs/rest-api/migration-from-webapp). ## See also Full `@kashdao/sdk` surface: clients, types, and error classes. Every route, request/response shape, and status code. Event catalog, signing scheme, retries, and secret rotation. Run your own signer / market-making infra instead of the managed REST path. # Webhooks Overview Source: https://docs.kash.bot/developer-docs/rest-api/webhooks/overview Receive trade lifecycle events at your endpoint. HMAC-signed, retried, replayable. Webhooks are how the API tells your code that something happened — a high-value trade needs confirmation, a trade completed on-chain, a trade failed terminally. They're the push-based alternative to polling `GET /v1/trades/{id}`. ## When you'll get webhooks Each API key can carry an optional `webhook_url`. When set, the API POSTs `trade.*` events to that URL throughout the trade lifecycle: | Event | When | Status code in payload | | ----------------------------- | -------------------------------------------------------------- | ---------------------- | | `trade.confirmation-required` | A high-value trade hit the confirmation gate (returned `202`) | `pending_confirmation` | | `trade.completed` | Trade executed on-chain successfully | `completed` | | `trade.failed` | Trade failed terminally (validation, on-chain revert, timeout) | `failed` | | `trade.rejected` | Trade was rejected by the risk engine before execution | `rejected` | Set `webhook_url` in the webapp's **Settings → API Keys** page when issuing or editing a key, or programmatically via the admin CLI. ## Wire shape The HTTP body is JSON: ```json theme={null} { "id": "evt_8f3c2a1b9d0e4f7a8c1b6e5d3f4a2c9b", "type": "trade.completed", "apiVersion": "2026-04-29", "createdAt": "2026-05-02T12:34:56.789Z", "data": { "tradeId": "11111111-1111-4111-8111-111111111111", "marketId": "22222222-2222-4222-8222-222222222222", "outcomeIndex": 0, "amount": "100", "side": "buy", "status": "completed", "txHash": "0xa1b2c3d4...", "tokensOut": "237340124711760000000", "metadata": { "strategy": "momentum-v2", "cohort": "beta" } } } ``` | Field | Use | | ------------ | -------------------------------------------------------------------------------------- | | `id` | Stable event id. **Dedupe on this.** Mirrors the `X-Kash-Event-Id` header. | | `type` | Discriminator — branch on this in your handler. | | `apiVersion` | Pinned at emission time. Stable across retries of the same event. | | `createdAt` | Server timestamp at envelope build (NOT necessarily the underlying event's wall time). | | `data` | Type-specific payload. See per-event sections below. | ### `data` for `trade.confirmation-required` ```json theme={null} { "tradeId": "...", "marketId": "...", "outcomeIndex": 0, "amount": "5000", "side": "buy", "status": "pending_confirmation", "confirmationExpiresAt": "2026-05-02T12:39:56.789Z", "metadata": { ... } } ``` The token itself is **NOT** in the webhook — it's only returned in the synchronous `202` response from `POST /v1/trades`. This is intentional: a leaked webhook log can't be used to confirm a trade. ### `data` for `trade.completed` ```json theme={null} { "tradeId": "...", "marketId": "...", "outcomeIndex": 0, "amount": "100", "side": "buy", "status": "completed", "txHash": "0x...", "tokensOut": "237340124711760000000", "metadata": { ... } } ``` `tokensOut` is in WAD (18 decimals) as a decimal string. ### `data` for `trade.failed` ```json theme={null} { "tradeId": "...", "marketId": "...", "outcomeIndex": 0, "amount": "100", "side": "buy", "status": "failed", "errorCode": "INSUFFICIENT_BALANCE", "errorMessage": "Smart account had insufficient USDC to cover the trade.", "metadata": { ... } } ``` `errorCode` is the same machine-readable code surface as the [REST API error codes](/developer-docs/rest-api/errors). `errorMessage` is sanitised — no stack traces, no internal URLs. ### `data` for `trade.rejected` ```json theme={null} { "tradeId": "...", "marketId": "...", "outcomeIndex": 0, "amount": "100", "side": "buy", "status": "rejected", "errorCode": "trade_too_large", "errorMessage": "Trade rejected: amount exceeds the allowed per-trade size.", "metadata": { ... } } ``` Same shape as `trade.failed`, but the trade never reached the chain — the risk engine rejected it pre-execution. `errorCode` carries the risk evaluation's rejection outcome; `errorMessage` is the sanitised rejection reason. Rejection codes come from trade risk evaluation (lowercase snake\_case, e.g. `trade_too_large`, `daily_limit_exceeded`) — they are a separate namespace from the [REST API error codes](/developer-docs/rest-api/errors). ## The `metadata` field Every `trade.*` payload carries the `metadata` bag from the trade's create call. Use it to route handlers without an extra `GET /v1/trades/{id}` round-trip: ```ts theme={null} function onWebhook(event: WebhookEvent) { if (event.type === 'trade.completed' && event.data.metadata.strategy === 'momentum-v2') { momentumStrategyHandler.onTradeCompleted(event.data); } } ``` Constraints (set at trade-create time): max 10 keys, key chars `[a-zA-Z0-9_\-.]` (1-64), value max 500 chars, strings only. ## Headers ``` Content-Type: application/json; charset=utf-8 X-Kash-Signature: t=1730000000000,v1= X-Kash-Event-Id: evt_8f3c2a1b9d0e4f7a8c1b6e5d3f4a2c9b X-Kash-Api-Version: 2026-04-29 User-Agent: KashDAO-Webhook-Delivery/1.0 ``` * `X-Kash-Signature` — HMAC of the body. **Always verify.** See [Verifying signatures](/developer-docs/rest-api/webhooks/verifying). * `X-Kash-Event-Id` — same as the body's `id`. Dedupe on this. * `X-Kash-Api-Version` — same as the body's `apiVersion`. ## Delivery semantics * **At-least-once.** Network failures and `5xx` responses trigger automatic retries with exponential backoff over \~24 hours. Your handler MUST be idempotent — dedupe on `X-Kash-Event-Id`. * **FIFO per key.** Events for the same API key are delivered in order, never overlapping. Cross-key concurrency is unlimited. * **Distributed circuit breaker.** If your endpoint is consistently failing, our breaker opens and pauses delivery for that endpoint specifically — other customers' webhooks are unaffected. * **Terminal failure after 24h.** If retries can't get through (your endpoint is permanently broken), the event is marked terminally failed. You can manually replay via [`POST /v1/webhooks/events/{id}/redeliver`](/developer-docs/rest-api/webhooks/retries) once you've fixed the receiver. Customer endpoint requirements: * **Respond fast.** Aim for sub-2s response times. We hard-timeout at 10s. * **Respond `2xx` to acknowledge.** Anything else is a failure that triggers retry. * **Drain the request body.** Even if you decide not to process it; otherwise the connection may not close cleanly. ## Discovery & inspection Use `GET /v1/webhooks/events` to list deliveries and inspect their state — handy for debugging "did the webhook fire?" without instrumenting your receiver: ```bash theme={null} curl 'https://api.kash.bot/v1/webhooks/events?status=failed&limit=10' \ -H "X-API-Key: $KASH_API_KEY" ``` The response carries the per-event delivery state: `attempts`, `lastAttemptedAt`, `lastStatusCode`, `lastFailureCode`, `terminalFailureAt`, `lastErrorMessage`. The webapp's **Settings → Webhook Inspector** page renders the same data graphically. ## Building a webhook receiver Minimal Node.js example (Express): ```ts theme={null} import express from 'express'; import { createHmac, timingSafeEqual } from 'crypto'; const app = express(); const SECRET = process.env.KASH_WEBHOOK_SECRET!; app.post('/webhooks/kash', // CRITICAL: get the RAW body. Most JSON middleware mutates it before signature verification. express.raw({ type: 'application/json' }), (req, res) => { const sig = req.header('x-kash-signature'); if (!sig || !verifyKashSignature(req.body.toString('utf8'), sig, SECRET)) { return res.status(401).send('signature invalid'); } const event = JSON.parse(req.body.toString('utf8')); // Dedupe on event.id (or the X-Kash-Event-Id header — same value) if (alreadyProcessed(event.id)) return res.status(200).send('ok'); markProcessed(event.id); switch (event.type) { case 'trade.confirmation-required': /* ... */ break; case 'trade.completed': /* ... */ break; case 'trade.failed': /* ... */ break; case 'trade.rejected': /* ... */ break; } res.status(200).send('ok'); } ); ``` The TypeScript SDK provides a one-line verifier and parser: ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey, webhookSecret: SECRET }); app.post('/webhooks/kash', express.raw({ type: 'application/json' }), async (req, res) => { try { const event = await kash.webhooks.constructEvent( req.body.toString('utf8'), req.header('x-kash-signature')!, SECRET ); // event is fully typed and parsed handleEvent(event); res.status(200).send('ok'); } catch (err) { // KashWebhookSignatureError on bad signature res.status(401).send('invalid'); } } ); ``` ## Next Stripe-compatible verification recipe. One-line constant change from any Stripe library. Backoff schedule, terminal failure, manual replay. Rotate without dropping deliveries — the 7-day overlap window. # Retries & Redelivery Source: https://docs.kash.bot/developer-docs/rest-api/webhooks/retries Backoff schedule, terminal failure, and how to manually replay a webhook event. The webhook delivery worker retries failed deliveries automatically. When that's not enough, you can manually replay any event for up to 30 days. ## Automatic retry behaviour If your endpoint: * **Returns `2xx`** → delivery succeeds. No retries. * **Returns `3xx`** → treated as misconfiguration (we don't follow redirects on webhooks). Retried as a transient failure. * **Returns `4xx` (except 408/429)** → treated as a **terminal client error** the customer must fix. Retries stop after one attempt; the event is marked terminally failed. * **Returns `408`, `429`, or any `5xx`** → retried with exponential backoff. * **Times out** (>10s) → retried. * **Network error** (TCP reset, DNS failure, TLS handshake failure) → retried. ### Retry schedule Failed deliveries are retried with **exponential backoff and jitter**: the first attempt fires immediately, early retries follow within seconds-to-minutes, and later gaps stretch to multiple hours — up to **10 attempts total over roughly 24 hours** from the originating event. After attempt 10 (or the \~24h ceiling), the delivery moves to the dead-letter queue and the event is marked `terminally failed` (`webhook_events.terminal_failure_at` is set). Automatic delivery stops; the event remains in the database for 30 days for manual replay. Delays are jittered to avoid herd retry storms — treat the schedule as approximate, not a contract. ## Distributed circuit breaker Per-endpoint, not per-customer. If your URL has a high recent failure rate (5 consecutive failures, or >50% failures in the last 60s with at least 5 attempts), the circuit opens and we pause delivery for \~30 seconds before half-opening. This prevents one slow customer endpoint from starving the worker — other customers' deliveries are unaffected. When the circuit is open, queued events for the same URL are not lost — they're held in the SQS retry pipeline and resumed when the breaker closes. ## Inspecting delivery state Every webhook event is queryable via `GET /v1/webhooks/events`: ```bash theme={null} curl 'https://api.kash.bot/v1/webhooks/events?status=failed&limit=20' \ -H "X-API-Key: $KASH_API_KEY" ``` ```json theme={null} { "data": [ { "id": "evt_...", "eventType": "trade.completed", "tradeRequestId": "trade_...", "emittedAt": "2026-05-02T12:00:00.000Z", "outboxEmittedAt": "2026-05-02T12:00:00.500Z", "replayCount": 0, "status": "failed", "delivery": { "attempts": 10, "lastAttemptedAt": "2026-05-03T11:42:18.000Z", "lastDeliveredAt": null, "lastStatusCode": 503, "lastFailureCode": "http_5xx", "lastErrorMessage": "Upstream returned 503 Service Unavailable", "terminalFailureAt": "2026-05-03T11:42:18.000Z" } } ], "pagination": { "cursor": "...", "hasMore": true, "limit": 20 } } ``` Filter by status (`pending`, `delivered`, `failed`, `retrying`, `none`) and date range to narrow down. Cursor-paginated. The webapp's **Settings → Webhook Inspector** UI renders the same data with timeline visualisations. ## Manual redelivery After fixing your endpoint (or rotating its URL), replay individual events: ```bash theme={null} curl -X POST https://api.kash.bot/v1/webhooks/events/evt_8f3c2a1b/redeliver \ -H "X-API-Key: $KASH_API_KEY" ``` ```json theme={null} { "event": { "id": "evt_8f3c2a1b", "eventType": "trade.completed", "apiKeyId": "...", "tradeRequestId": "...", "emittedAt": "2026-05-02T12:00:00.000Z", "lastDeliveredAt": null, "deliveryAttempts": 10 }, "data": { /* same as event */ }, "_meta": { "requestId": "...", "timestamp": "...", "message": "Webhook delivery enqueued for replay." } } ``` The replayed delivery uses the **same** `X-Kash-Event-Id` as the original, so a customer that already processed the first delivery treats the replay as a no-op (assuming proper dedupe). This is the whole point of the dedupe pattern: a manual replay can never produce a duplicate side effect. ### Replay amplification cap Each event can be manually replayed at most **5 times** in total. Beyond that, `POST .../redeliver` returns `429 WEBHOOK_REPLAY_LIMIT_REACHED`. This bounds the cost of a runaway replay loop. If you genuinely need more, contact support — we can lift the cap on a per-event basis after auditing. ### Replay scope * Manual replay works on **any** event status (failed, delivered, even successful — useful for testing your handler with a real payload). * The replay enqueues a **new** delivery attempt; it doesn't reset the original delivery's history. * Each replay is itself subject to the auto-retry schedule above. * Replays use the **current** webhook secret (not the one that signed the original delivery). If you've rotated, the new signature reflects the current secret. ## Bulk operations The customer-facing API doesn't expose bulk redelivery — that's deliberate (amplification risk). For incident recovery (e.g. "all events for the last hour failed because my endpoint was down"), use `kash-admin webhooks bulk-redeliver` (admin CLI) which has additional safety guards. ## Tracing a specific event The top-level `kash trace ` CLI command pulls the curated event timeline for the trade behind a delivery — get the correlation id from the originating trade: ```bash theme={null} kash trade status --json | jq -r '.correlationId' kash trace 33333333-3333-3333-3333-333333333333 ``` Output is the trade's end-to-end event chain (intent → funding → execution → webhook delivery). Useful for "why didn't this deliver?" investigations. ## What survives 30 days * Events `succeeded` or with no failures: kept indefinitely for audit. * Events `terminally failed`: retained for 30 days, then archived to S3 (queryable via Athena, not via the API). ## Common patterns ### "I deployed a broken handler — how do I replay everything I missed?" 1. Roll back or fix the handler. 2. List failed events from the deploy window: `GET /v1/webhooks/events?status=failed&from=...&limit=100`. 3. Iterate the cursor-paginated response, calling `POST .../redeliver` on each id. 4. Watch deliveries succeed via `GET /v1/webhooks/events?status=delivered&from=`. ### "I want to test my handler against a real production payload" 1. Find a known-good event id from `GET /v1/webhooks/events?status=delivered`. 2. Point your test endpoint at a tunnel (e.g. `ngrok`). 3. Update the key's `webhook_url` to the tunnel URL. 4. `POST .../redeliver` on the chosen event id. 5. Inspect what hits your handler. 6. Restore the key's `webhook_url` when you're done. A safer alternative: use `kash webhooks replay --target --secret ` (CLI) to sign a locally captured (or synthetic) payload and POST it at your endpoint — no production data round-trip needed. Pair with `kash webhooks verify` to check signatures offline. ## Next Always verify before processing. Rotate without dropping mid-rotation deliveries. # Webhook Secret Rotation Source: https://docs.kash.bot/developer-docs/rest-api/webhooks/secret-rotation Rotate the webhook signing secret without losing a single delivery — the 7-day overlap window. The `webhook_secret` is what your endpoint uses to verify incoming webhook signatures. Rotate it on the same triggers as any sensitive credential: a team change, a leaked log line, or just routine hygiene. The rotation flow has a built-in **7-day overlap window** so deliveries in flight are never dropped. Both the previous and new secret are valid during the window — your verifier just needs to accept either. ## Rotating ```bash theme={null} curl -X POST https://api.kash.bot/v1/auth/api-keys/me/webhook-secret/rotate \ -H "X-API-Key: $KASH_API_KEY" ``` Response (`200 OK`): ```json theme={null} { "webhookSecret": { "secret": "whsec_", "rotatedAt": "2026-05-02T12:00:00.000Z", "previousRetainedUntil": "2026-05-09T12:00:00.000Z" }, "data": { /* same as webhookSecret */ }, "_meta": { "requestId": "...", "timestamp": "...", "message": "Webhook secret rotated. Previous secret remains valid for signature verification until 2026-05-09T12:00:00.000Z." } } ``` The new `secret` plaintext is shown **once** — capture it and roll it out to your endpoint. Required scope: `webhooks:manage`. The key must already have a webhook URL + secret configured at issuance time — rotation only swaps the secret on a configured webhook, it does not provision one. ### Rotation cooldown (60 seconds) Calling rotate again within 60 seconds of a successful rotation returns `429 WEBHOOK_SECRET_ROTATION_COOLDOWN` with a `Retry-After` header counting down to the cooldown's expiry. ```http theme={null} HTTP/1.1 429 Too Many Requests Retry-After: 38 Content-Type: application/problem+json { "code": "WEBHOOK_SECRET_ROTATION_COOLDOWN", "title": "Rotation cooldown active", "status": 429, "detail": "A rotation completed within the last 60s. If you did not capture the new secret from that response, contact support — re-rotating now would overwrite the rollback slot with a secret you never received. Retry after the cooldown expires only if you actually want a new secret." } ``` This guard exists because **rotation is destructive**. The cap protects you from a retry-storm scenario: your HTTP client times out on the rotate POST, retries automatically, and the second rotation overwrites the previous-secret slot with a brand-new secret you never received in the lost first response. Rolling back from there would restore a secret no verifier was ever configured for. **If you genuinely lost the new secret from the previous response, do NOT retry yourself.** Contact support — operations can roll back via the operator path (described below) which preserves the rollback chain. Hammering rotate just makes the situation harder to recover from. ## What happens during the 7-day window While `now < previousRetainedUntil`: * The webhook delivery worker **dual-signs** every delivery. The `X-Kash-Signature` header carries TWO `v1=` entries — one with the new secret, one with the old. * Your verifier should accept signatures matching **either** secret. Both reference implementations and the SDK already do this. Sample signature header during the window: ``` X-Kash-Signature: t=1730000000000,v1=,v1= ``` After `previousRetainedUntil`, dual-signing stops and only the new secret is used. ## Recommended rollout 1. **Rotate the secret.** `POST /v1/auth/api-keys/me/webhook-secret/rotate`. Capture the new plaintext. 2. **Update your endpoint to know about both secrets.** Most setups use a `KASH_WEBHOOK_SECRET` env var — add `KASH_WEBHOOK_SECRET_PREVIOUS` and have your verifier try the new one first, then the previous. Or use a verifier (like the SDK's `constructEvent`) that already accepts multiple `v1=` entries. 3. **Deploy.** Your endpoint now accepts signatures under either secret. 4. **Wait for in-flight deliveries to drain.** Conservatively, a few minutes. The 7-day window gives you generous headroom. 5. **(After 7 days, optional)** Remove `KASH_WEBHOOK_SECRET_PREVIOUS` from your config. After `previousRetainedUntil`, the old secret no longer signs anything; carrying it in your verifier just adds dead code. If your verifier is the SDK or one of the [reference implementations](/developer-docs/rest-api/webhooks/verifying), step 2 collapses to "set the new secret as the only secret" — the dual-`v1=` parsing is already in there. ## What can go wrong ### "I rotated and immediately replaced the secret in my verifier" If your verifier only knows the new secret, deliveries signed during the rotation overlap (with both new and old signatures) still verify because **at least one** `v1=` entry matches. You're fine — but if you'd swapped to the OLD secret only, you'd lose deliveries. The safe rule: during overlap, accept either secret. Never accept *only* the old one after rotation. ### "I lost the new secret" Two cases: * **You captured the previous response and just didn't store the new secret.** Rotate again. Each rotate creates a fresh secret and re-starts the 7-day window. Your previously-captured new-secret becomes the rotation's "previous" — both still valid during the new window. Wait at least 60s between rotations to avoid the cooldown gate. * **The previous rotation response was lost (network timeout, dropped response).** Do NOT just retry rotate yourself — that would silently overwrite the rollback slot with a secret no one ever held. Contact [support@kash.bot](mailto:support@kash.bot); operations can either roll back to the prior known-good secret (preserving your verifier) or perform an operator-path rotation that captures the new secret onto a support channel rather than into the void. ### "Rotation broke my verifier and I need to roll back" Within 7 days of a rotation, operations can roll back to the prior secret via the `kash-admin api-keys rollback-webhook-secret` operator command. This atomically swaps `webhook_secret_previous` back into `webhook_secret`, so your endpoint immediately starts receiving deliveries signed with the secret your verifier knows. To trigger a rollback, email [support@kash.bot](mailto:support@kash.bot) with: * The API key id (UUID — visible in the webapp Settings → API Keys page) * A short description of what broke (so the operator captures it in the audit row) * Confirmation that the prior secret you remember is still configured on your endpoint The operator confirms the now-active secret with you on the support channel. After 7 days the rollback path closes — beyond that, the only recovery is to rotate forward to a fresh secret and update your verifier. ### "My endpoint started rejecting signatures after the window expired" Check that you're verifying with the current secret and that you didn't accidentally remove the `v1=` parsing. After `previousRetainedUntil`, the old secret stops signing; if your verifier was relying on the old secret being valid, it'll start failing. `GET /v1/account/usage` will surface the failure rate via `webhooks.7d.failed` going up. ## Inspecting current state There's no API to read the secret back — once issued, the plaintext lives only in your config. To check rotation timestamps, the webapp's **Settings → API Keys → \[key] → Webhook** page shows `rotatedAt` and `previousRetainedUntil`. ## URL changes If you change `webhook_url` (different domain or path), the API does a one-time validation to confirm the new URL responds `2xx` to a probe before queuing real deliveries. To prevent abuse, URL changes also have a 1-hour cooldown between consecutive changes. The API will warn you if the old URL has unprocessed deliveries pending; redirect them with [manual redelivery](/developer-docs/rest-api/webhooks/retries) once the new URL is live. ## Going dark If you want to temporarily pause webhook delivery without changing your URL or rotating the secret, the cleanest path is to revoke and re-issue the key (no deliveries are queued for a key with no `webhook_url`). For short pauses, just respond `429` with a long `Retry-After` from your endpoint — the breaker opens and we back off. ## Next The verification recipe that handles the dual-`v1=` overlap window. Back to the webhooks overview. # Verifying Webhook Signatures Source: https://docs.kash.bot/developer-docs/rest-api/webhooks/verifying Stripe-compatible HMAC-SHA256 verification — one-line constant change from any Stripe library. The signature format matches Stripe's exactly so any Stripe-compatible verification library works with one constant changed (header name → `X-Kash-Signature`). **Always** verify signatures on incoming webhook requests. An unsigned or invalid request must be rejected — the only way to know an event truly came from Kash is to verify its HMAC. ## The signature format ``` X-Kash-Signature: t=1730000000000,v1= ``` | Part | Meaning | | ----- | ------------------------------------------------------------------------ | | `t=` | Unix epoch milliseconds at signing time. Used for replay protection. | | `v1=` | Hex-encoded HMAC-SHA256 of `.` under your `webhook_secret`. | During the [secret rotation overlap window](/developer-docs/rest-api/webhooks/secret-rotation) the header may carry **two** `v1=` entries (current + previous secret). Accept the request if **any** matches. ``` X-Kash-Signature: t=1730000000000,v1=,v1= ``` ## The verification algorithm 1. Read the raw request body as a string. **Don't reparse JSON** — the signature is over the exact bytes that were sent. 2. Parse the header: split on `,`, then on `=`, into a map. 3. Read `t` (the timestamp). If `|now - t| > 5 minutes`, reject — the signature is too old (replay attack). 4. Compute `expected = HMAC_SHA256(secret, "${t}.${rawBody}")` and hex-encode it. 5. For each `v1=` entry in the header, compare with `expected` using a **constant-time** comparator. If any matches, accept. That's it. A 20-line function in any language with a crypto stdlib. ## Reference implementations ### TypeScript / Node.js (the SDK does this for you) The simplest path — let `@kashdao/sdk` verify and parse in one line: ```ts theme={null} import { KashClient } from '@kashdao/sdk'; const kash = new KashClient({ apiKey, webhookSecret: process.env.KASH_WEBHOOK_SECRET! }); app.post('/webhooks/kash', express.raw({ type: 'application/json' }), async (req, res) => { try { const event = await kash.webhooks.constructEvent( req.body.toString('utf8'), req.header('x-kash-signature')!, process.env.KASH_WEBHOOK_SECRET! ); // event is fully typed (TradeCompletedEvent | TradeFailedEvent | ...) handleEvent(event); res.sendStatus(200); } catch (err) { // KashWebhookSignatureError on bad signature, expired, or missing res.sendStatus(401); } } ); ``` ### TypeScript / Node.js (no SDK) ```ts theme={null} import { createHmac, timingSafeEqual } from 'crypto'; function verifyKashSignature( payload: string, signature: string, secret: string, toleranceMs = 5 * 60 * 1000 ): boolean { const parts = Object.fromEntries( signature.split(',').map(p => { const i = p.indexOf('='); return [p.slice(0, i), p.slice(i + 1)]; }) ); const ts = Number(parts.t); if (!Number.isFinite(ts) || Math.abs(Date.now() - ts) > toleranceMs) return false; const expected = createHmac('sha256', secret).update(`${ts}.${payload}`).digest('hex'); // Accept any v1= entry — supports the rotation overlap window. for (const [k, v] of signature.split(',').map(p => { const i = p.indexOf('='); return [p.slice(0, i), p.slice(i + 1)]; })) { if (k !== 'v1') continue; if (v.length !== expected.length) continue; if (timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v, 'hex'))) return true; } return false; } ``` ### Python ```python theme={null} import hmac, hashlib, time def verify_kash_signature(payload: bytes, signature: str, secret: str, tolerance_ms: int = 5 * 60 * 1000) -> bool: parts = dict(p.split('=', 1) for p in signature.split(',')) ts = int(parts.get('t', '0')) if abs(int(time.time() * 1000) - ts) > tolerance_ms: return False expected = hmac.new( secret.encode('utf-8'), f'{ts}.{payload.decode("utf-8")}'.encode('utf-8'), hashlib.sha256 ).hexdigest() for entry in signature.split(','): k, _, v = entry.partition('=') if k != 'v1': continue if hmac.compare_digest(v, expected): return True return False ``` ### Go ```go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "strconv" "strings" "time" ) func VerifyKashSignature(payload []byte, signature, secret string, toleranceMs int64) bool { parts := map[string][]string{} for _, p := range strings.Split(signature, ",") { kv := strings.SplitN(p, "=", 2) if len(kv) != 2 { continue } parts[kv[0]] = append(parts[kv[0]], kv[1]) } tsStrs := parts["t"] if len(tsStrs) == 0 { return false } ts, err := strconv.ParseInt(tsStrs[0], 10, 64) if err != nil { return false } if absInt64(time.Now().UnixMilli() - ts) > toleranceMs { return false } mac := hmac.New(sha256.New, []byte(secret)) fmt.Fprintf(mac, "%d.%s", ts, payload) expected := hex.EncodeToString(mac.Sum(nil)) for _, v := range parts["v1"] { if hmac.Equal([]byte(v), []byte(expected)) { return true } } return false } func absInt64(x int64) int64 { if x < 0 { return -x }; return x } ``` ### Rust ```rust theme={null} use hmac::{Hmac, Mac}; use sha2::Sha256; use subtle::ConstantTimeEq; type HmacSha256 = Hmac; pub fn verify_kash_signature(payload: &[u8], signature: &str, secret: &str, tolerance_ms: i64) -> bool { let mut t: Option = None; let mut v1s = Vec::new(); for part in signature.split(',') { if let Some((k, v)) = part.split_once('=') { match k { "t" => t = v.parse().ok(), "v1" => v1s.push(v), _ => {} } } } let ts = t.unwrap_or(0); let now = chrono::Utc::now().timestamp_millis(); if (now - ts).abs() > tolerance_ms { return false; } let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).unwrap(); mac.update(format!("{}.", ts).as_bytes()); mac.update(payload); let expected = mac.finalize().into_bytes(); let expected_hex = hex::encode(expected); v1s.iter().any(|v| v.as_bytes().ct_eq(expected_hex.as_bytes()).unwrap_u8() == 1) } ``` ## Common mistakes * **Re-serialising the JSON body before verifying.** The signature is over the *exact bytes* we POSTed. If you parse + re-stringify, whitespace differs and the signature fails. Always verify against the raw body, then parse JSON. * **Using `==` instead of constant-time comparison.** `==` short-circuits on the first differing byte and leaks signature bytes via timing. Use `crypto.timingSafeEqual` (Node), `hmac.compare_digest` (Python), `hmac.Equal` (Go), `ct_eq` (Rust). * **Skipping the timestamp freshness check.** Without `t` validation, an attacker who captures one signed payload can replay it forever. The 5-minute tolerance handles legitimate clock skew. * **Ignoring multiple `v1=` entries.** During the rotation overlap window we send two — accept either. A picky verifier rejects valid traffic mid-rotation. ## Testing your verifier locally The CLI can sign a payload exactly like the production worker (`t=,v1=`) and POST it to your endpoint: ```bash theme={null} kash webhooks replay payload.json \ --target http://localhost:3000/webhooks/kash \ --secret $KASH_WEBHOOK_SECRET ``` This POSTs your captured (or hand-written) event body, properly signed, to your endpoint so you can verify your handler's signature path before going live. Pass `--timestamp-ms` to test your replay-window enforcement, and use `kash webhooks verify` for offline signature checks. ## Reporting verification bugs If the production worker is sending signatures your code can't verify, please open a GitHub issue with: * The raw `X-Kash-Signature` header value * The raw body bytes (or a hash of them) * Your `webhook_secret`'s `rotatedAt` timestamp (don't share the secret itself) * Your verification code We've cross-tested against Stripe's verifier with a header-name swap and it accepts our signatures, so most "doesn't verify" bugs are receiver-side body re-serialisation issues. ## Next What happens when your endpoint fails — and how to manually replay. Rotate without losing a single delivery. # Analytics and earnings Source: https://docs.kash.bot/for-businesses/analytics-and-earnings Daily stats, revenue events, and payouts for your organization Everything your organization generates — trading activity, audience growth, revenue — is measured and visible in the Partner Portal. ## Daily stats The portal's **Analytics** section tracks your organization day by day: | Metric | What it measures | | -------------- | ----------------------------------------- | | Volume | USDC trading volume across your markets | | Trades | Number of trades executed in your markets | | Unique traders | Distinct users who traded in your markets | | Active markets | Markets of yours open for trading | | New users | Users who joined through your markets | Use these to see which markets, threads, and campaigns actually move your audience — and drill into individual markets for a closer look. ## Insights The **Insights** section goes deeper than the daily counters, with audience and market intelligence for your organization. ## Earnings and revenue events The **Earnings** view shows the money side: * **Accrued revenue share** — your share of trading fees builds up automatically as trades happen in markets your organization created. See [Pricing and revenue share](/for-businesses/pricing-and-revenue-share) for how attribution works. * **Revenue events** — a ledger of everything that affects your balance: trading fees, platform fees, subscription charges, payouts, refunds, and settled revenue share. * **Settled payouts** — accrued earnings are paid out periodically after a settlement hold, and each settlement appears as its own revenue event. ## Per-partner attribution Revenue and activity are attributed to the partner responsible, so you can see which of your curators and creators drive volume — useful for deciding who to invest in, and for structuring your own revenue splits with partners. ## Data export Organizations on the premium **Community Intelligence** analytics add-on can export their data for use in their own BI and reporting tools. ## Next steps How earnings accrue and when they're paid. Programmatic access to your organization's data. # API access Source: https://docs.kash.bot/for-businesses/api-access Org-scoped API keys, webhooks, and how embed tracking relates to revenue Organizations can access Kash programmatically with **org-scoped API keys** — API keys tied to your organization rather than an individual user account. They work against the same REST API as regular keys, with the same authentication, scopes, and tier system. ## Same tier ladder as the public API Org-scoped keys follow the public API's tier ladder — `free`, `developer`, `enterprise`, and `mm` — with each tier setting your daily spend cap, per-trade cap, and rate limit. For the current limits per tier, see the public API docs: Key format, scopes, and request signing. Per-tier limits and the rate-limit response headers. For `enterprise` and `mm` tiers (custom limits, negotiated terms), contact [partnerships@kash.bot](mailto:partnerships@kash.bot). ## Scoped keys and webhooks * **Scopes** — keys are issued with explicit scopes, so you can hand a reporting integration read-only access while keeping trading capability on a separate key. * **Webhooks** — subscribe to HMAC-signed webhooks to react to events in real time instead of polling. See the [webhooks overview](/developer-docs/rest-api/webhooks/overview). ## Embed tracking is analytics-only A common point of confusion, worth being precise about: The `?source=` parameter on embeds and the per-render embed tracking are **analytics-only**. They tell you where your embed traffic comes from — they do not determine revenue share. Revenue share is **trade-level**: you earn a share of fees on every trade in markets **your organization creates**, regardless of where the trade originates — the webapp, an embed on any site, or X. Embedding someone else's market does not earn you revenue share; creating markets does. See [Pricing and revenue share](/for-businesses/pricing-and-revenue-share). The two work together: embed tracking shows you which placements drive engagement, while trade-level attribution pays you for the markets you own. ## No-auth option: the Public Embed API If you only need to display markets — no trading, no org data — the [Public Embed API](/developer-docs/public-embed-api/overview) is public, read-only, and requires no API key at all. ## Next steps Endpoints, SDKs, and quickstart for the authenticated API. What the portal shows you without writing any code. # Market creation for organizations Source: https://docs.kash.bot/for-businesses/market-creation From community suggestion to live on-chain market — the curation pipeline Markets on Kash go through a curation pipeline before they go live. For organizations, that pipeline is a feature: your community supplies the ideas, your partners filter for quality, and every market that makes it through carries your brand and earns you revenue share. ## The lifecycle A market starts as a **suggestion** — a title, description, 2–8 outcomes, an end date, and the resolution sources an admin will use to settle it. Suggestions can come from your community members, your partners, or your own team. Suggestions land in a review inbox where your partners — the curators you've invited — approve the good ones and reject the rest. This is your quality filter: only ideas your curators endorse move forward. Your organization elevates an approved suggestion into a **proposal** — the formal request to create the market on-chain. Higher-tier organizations can also propose markets directly, skipping the suggestion step for their own ideas. Every proposal is reviewed by Kash before it goes on-chain. This covers resolvability (are the outcomes and sources clear enough to settle?) and platform policy. Once approved, the market is created on Base and starts trading. You'll see it move through execution to completed in your portal's market queue, and it appears in your live markets list. ## Creation fees Creating a market costs a creation fee (paid in USDC), which seeds the market's liquidity. For organizations, the fee can be paid from your **organization's smart account** — top it up from the portal's **Fund** page — so your partners and creators don't have to pay out of pocket. ## Why it's worth it * **Your branding** — markets your organization creates render with your logo, colors, and name on app.kash.bot and in embeds. See [White-label branding](/for-businesses/white-label-branding). * **Your revenue** — you earn a share of trading fees on every trade in markets your organization creates, wherever the trade happens. See [Pricing and revenue share](/for-businesses/pricing-and-revenue-share). * **Your data** — every market feeds your organization's analytics, with per-partner attribution so you can see which curators drive activity. ## Next steps Where suggestions, proposals, and your market queue live. Track how your markets perform once they're live. # Kash for businesses Source: https://docs.kash.bot/for-businesses/overview Turn your community — even your X account — into a prediction market Turn your community — even your X account — into a prediction market. As a Kash organization, you get your own markets, branded surfaces, embeds, analytics, and a share of the revenue those markets generate. Your audience already debates outcomes — match results, product launches, elections, prices. Kash lets them put a position behind their opinion, on markets that carry your brand, and pays you for the activity you bring. ## Who it's for Run prediction markets under your own name. Your followers trade on your markets from the web or directly from X — and you earn on every trade. Embed live markets alongside your coverage. Real-money probabilities make every story interactive. Give your community a shared game: they suggest markets, your curators pick the best, and everyone trades on the result. Branded prediction markets around your events, launches, and campaigns — with analytics on who showed up and what they traded. ## How it fits together Kash is multi-tenant. Your organization is the top-level entity, and everything below it belongs to you: Your top-level account. It owns your markets, branding, members, API credentials, earnings, and analytics — all managed from the Partner Portal. Curators and creators you invite to work under your brand. Partners review market suggestions from your community and help decide what goes live. Optional — you can run an organization with no partners at all. Curated collections of related markets — a tournament, an election season, a product cycle. Threads are what you typically embed and share. Individual prediction markets with 2–8 outcomes, live on-chain on Base. Trades on markets your organization creates earn you revenue share. ## What you get * **Partner Portal** — a self-serve dashboard for your organization: analytics, earnings, market curation, branding, team management, and billing. * **Branded market pages and embeds** — your markets live under your own namespace on app.kash.bot, and you can embed threads, markets, and your full organization grid as iframes on any site. * **Org-scoped API keys** — programmatic access on the same tier ladder as the public API, scoped to your organization. * **Revenue share** — you earn a share of trading fees on trades in markets your organization creates (per your partner agreement). * **Analytics** — daily volume, trades, unique traders, active markets, and new users, with per-partner attribution. Your markets are tradeable on the Kash webapp and via Twitter/X today, with more platforms coming. ## Explore the section Free to start. How the revenue share model works and what higher tiers unlock. Sign in, create your organization, and complete onboarding — self-serve. From community suggestion to live on-chain market. Logo, wordmark, colors, and fonts across your pages and embeds. Daily stats, revenue events, and payouts in the portal. Org-scoped API keys, webhooks, and how embed tracking relates to revenue. Building an integration? The [Public Embed API](/developer-docs/public-embed-api/overview) lets anyone embed Kash markets with zero setup — no organization account required. ## Talk to us Email [partnerships@kash.bot](mailto:partnerships@kash.bot) for partnership and enterprise conversations. # Partner Portal quickstart Source: https://docs.kash.bot/for-businesses/partner-portal-quickstart Create your organization and get set up in the Partner Portal — self-serve The Partner Portal is the self-serve home for your organization: analytics, earnings, market curation, branding, team, and billing. This page walks you from first sign-in to a fully set-up organization. ## 1. Sign in The portal uses Privy for authentication. Sign in with: * **Email** — a one-time code, no password * **Google** * **X (Twitter)** Signing in with X is a natural fit if you plan to run markets for your X audience, but any method works. ## 2. Create your organization On first sign-in you'll be prompted to create your organization: | Field | What it's for | | ------------- | -------------------------------------------------------------------- | | Name | Your organization's name | | Slug | Your URL handle — this becomes your public namespace on app.kash.bot | | Contact email | Where Kash reaches you about your organization | | Branding | Display name, tagline, and logo — all editable later | New organizations start on the basic tier under the Kash parent organization. You can upgrade at any time from Billing. ## 3. Work through the onboarding checklist The portal dashboard shows a setup checklist that tracks four steps: Done as soon as you complete step 2. Set up a paid subscription in **Billing** if you want tiered features — free-tier organizations can skip this and still curate markets. Connect the platform where your markets will be distributed — for example your X account. Store the delivery credentials for your connected platform so Kash can post and respond on your behalf. ## 4. Find your way around Your landing page: setup progress, headline stats, and recent activity. **Analytics** breaks down volume, trades, unique traders, active markets, and new users over time — see [Analytics and earnings](/for-businesses/analytics-and-earnings). Deeper audience and market intelligence for your organization. Your accrued revenue share and settled payouts, with per-partner attribution. Manage your organization's smart account balance — used, for example, to pay market creation fees on behalf of your creators. The heart of the portal: your community's suggestions, your proposal queue, and your live markets. See [Market creation](/for-businesses/market-creation). Embed codes and share links for your branded markets, threads, and organization grid. Account, team members, partners, platform connections, revenue share, referral programs, and trade access. Your subscription tier and payment details. ## Next steps Get your first market from idea to live on-chain. Make your markets look like yours. # Pricing and revenue share Source: https://docs.kash.bot/for-businesses/pricing-and-revenue-share Free to start — earn a share of trading fees on markets your organization creates Kash organizations are free to start. You can create your organization, curate markets, and begin earning revenue share without paying anything up front. Paid tiers add capability as you grow. The current tier program is subject to change. For live plan details, see the Partner Portal pricing page, or contact [partnerships@kash.bot](mailto:partnerships@kash.bot). ## How the model works * **Free to start.** Creating an organization and getting your first markets live costs nothing beyond per-market creation fees. * **Revenue share scales with your trading volume.** The more trading activity your markets generate, the larger the share of fees you keep. Higher subscription tiers also carry higher shares. * **Higher tiers unlock more capability** — custom branding across your pages and embeds, dedicated infrastructure, and org-scoped API credentials. * **Community Intelligence** — a premium analytics add-on with deeper audience insight and data export. ## How revenue share is earned You earn a share of the trading fees on **every trade in markets your organization creates** — attribution is at the trade level, tied to the market's creator. It doesn't matter where the trade happens. Whether a user trades on app.kash.bot, through an embed on someone else's site, or from X, a trade in your market earns you your share. Embed `?source=` tags are analytics-only — they don't determine who gets paid. See [API access](/for-businesses/api-access#embed-tracking-is-analytics-only). ### From trade to payout Users trade in markets your organization created — on the webapp, in embeds, or via X. Your share of each trade's fees is recorded as it happens. You can watch it accumulate in the portal's [earnings view](/for-businesses/analytics-and-earnings). Accrued earnings are paid out periodically after a settlement hold. Settled payouts appear as revenue events in the portal. ## Enterprise Running serious volume, or need custom terms, dedicated infrastructure, or negotiated rates? Contact [partnerships@kash.bot](mailto:partnerships@kash.bot). ## Next steps Create your organization and start earning — self-serve. Where to see your accrued and settled earnings. Revenue-share rates, attribution, and payout schedules are governed by your partner agreement and may change. # White-label branding Source: https://docs.kash.bot/for-businesses/white-label-branding Your logo, colors, and fonts across your Kash pages and embeds Markets your organization creates don't have to look like Kash — they can look like you. Custom branding puts your identity on every surface where your markets appear. Custom branding is a higher-tier entitlement. See [Pricing and revenue share](/for-businesses/pricing-and-revenue-share) and the Partner Portal pricing page for what your tier includes. ## What you can customise | Element | Details | | ------------- | ---------------------------------------------------------------------------------- | | Logo | Round avatar shown next to your markets and organization (PNG, JPEG, WebP, or SVG) | | Wordmark | Horizontal logo used on share cards and wider layouts | | Primary color | Your accent color across themed surfaces | | Brand font | A TTF/OTF font used to render your org-branded text | | Display name | The public-facing name shown on your surfaces | | Tagline | A short line shown alongside your organization | All of these are managed from the Partner Portal settings — upload assets, pick your color, and changes apply across your surfaces. ## Where your branding shows Your slug gives you a public namespace on app.kash.bot where your markets and threads live under your identity. Your branding carries into embeds — the organization grid, thread embeds, and single-market embeds all render themed with your assets. Your wordmark and colors appear on the cards generated when your markets are shared. Partners you invite operate under your organization's brand. For how to place branded embeds on your own site, see the [organization iframe guide](/developer-docs/public-embed-api/guides/organization-iframe) and the [thread iframe guide](/developer-docs/public-embed-api/guides/thread-iframe). ## Scope Branding applies to your organization's surfaces on app.kash.bot and to your embeds. Custom domains and a fully white-labeled standalone webapp are not currently offered — if that's what you need, talk to [partnerships@kash.bot](mailto:partnerships@kash.bot). ## Next steps Where to upload assets and manage your brand. Everything you can embed, with code examples. # Bug Bounty Source: https://docs.kash.bot/getting-started/ecosystem/bug-bounty-program Help Us Build a Safer Platform Security is fundamental to Kash's mission. We encourage community members and security researchers to report vulnerabilities responsibly. Security contributions may be eligible for recognition and rewards through our Community Incentives program. Hero Light Hero Dark # Security Reporting **Security Issues We Want to Know About** **Critical Security Issues:** * **Smart contract vulnerabilities** affecting user funds or market integrity * **Wallet security issues** related to MPC technology or account access * **Market resolution problems** that could lead to incorrect payouts * **Bot security vulnerabilities** in @kash\_bot interactions **Platform Security Concerns:** * **Authentication bypasses** or unauthorized access methods * **Transaction manipulation** or incorrect fee calculations * **Data privacy issues** or information disclosure vulnerabilities * **Oracle manipulation** or resolution system exploits **Infrastructure Issues:** * **API security vulnerabilities** in platform endpoints * **Database security issues** or data exposure risks * **Network security problems** affecting platform availability * **Integration vulnerabilities** with Base network or third-party services **Responsible Disclosure Process** **Contact Information:** * **Security Email:** [security@kash.bot](mailto:security@kash.bot) for all security-related reports * **Response Time:** We aim to acknowledge reports within 24 hours * **Investigation:** Security team will investigate and provide updates * **Resolution:** We'll work with you to understand and fix the issue **Report Format:** ``` Subject: Security Issue - [Brief Description] 1. Issue Summary: Brief description of the vulnerability 2. Impact Assessment: Potential impact and affected systems 3. Reproduction Steps: Detailed steps to reproduce the issue 4. Supporting Evidence: Screenshots, logs, or proof of concept 5. Suggested Fix: Recommendations for resolution (optional) 6. Contact Info: How we can reach you for follow-up ``` **What to Include:** * **Detailed description** of the security issue * **Step-by-step reproduction** instructions * **Impact assessment** and potential consequences * **Supporting evidence** like screenshots or transaction hashes # Security Guidelines **How to Test Safely and Responsibly** **Acceptable Testing:** * **Use test accounts** for security research when possible * **Limit testing scope** to avoid disrupting other users * **Document findings** thoroughly for clear reporting * **Respect user privacy** and avoid accessing personal data **Testing Best Practices:** * **Start with low-impact tests** before attempting more complex exploits * **Use minimal data** necessary to demonstrate the vulnerability * **Avoid automated scanning** that could impact platform performance * **Test on testnets** when possible to avoid mainnet risks **What Not to Do:** * **Don't access** other users' accounts or private information * **Don't disrupt** platform services or availability * **Don't perform** large-scale automated attacks * **Don't publicly disclose** vulnerabilities before reporting them **Areas of Interest for Security Research** **Smart Contract Security:** * **Market creation** and prediction logic * **Payout calculations** and fund distribution * **Access controls** and permission systems * **Integration security** with Base network **Platform Security:** * **Wallet generation** and MPC security * **Transaction routing** and fee calculations * **Bot interaction** security and command processing * **API endpoints** and data validation **AI System Security:** * **Market resolution** accuracy and manipulation resistance * **Natural language processing** vulnerabilities * **Oracle integration** and data verification * **Anti-manipulation** systems and detection # Community Contribution **How We Recognize Security Contributions** **Community Recognition:** * **Public acknowledgment** for significant security contributions * **Security contributor** status and community recognition * **Direct collaboration** with development team on improvements * **Contribution tracking** for ongoing security research **Potential Rewards:** * **Recognition** for verified security contributions * **Priority access** to new features and beta testing opportunities * **Enhanced platform privileges** for trusted security researchers * **Networking opportunities** with development and security teams **Reward Considerations:** * **Impact assessment** determines the significance of contributions * **Quality of reporting** and responsible disclosure practices * **Collaboration** and assistance with resolution efforts * **Community benefit** and protection of user funds **Building Long-term Security Partnerships** **Security Community:** * **Regular communication** with active security researchers * **Feedback integration** on security improvements and features * **Early access** to new features for security evaluation * **Community forums** for security discussion and collaboration **Professional Development:** * **Skill building** through real-world security research * **Portfolio development** with verified security contributions * **Industry networking** with security professionals * **Career opportunities** in blockchain and DeFi security **Platform Improvement:** * **Continuous security** enhancement through community input * **Proactive vulnerability** identification and resolution * **Security best practices** development and documentation * **Industry leadership** in prediction market security # Advanced Security Research **Advanced Security for High-Stakes Markets** **Circuit Audit Process:** * **Tier 1 Audits:** Standard security review for all circuits by established firms * **Tier 2 Audits:** Enhanced review for high-value market resolution circuits * **Tier 3 Audits:** Formal verification and mathematical proof of circuit correctness * **Continuous Monitoring:** Ongoing security assessment of deployed circuits **Multi-Prover Architecture:** * **Redundant Verification:** Multiple independent provers for critical market resolutions * **Consensus Mechanisms:** Majority agreement required for high-stakes outcomes * **Prover Diversity:** Different implementations to avoid systematic vulnerabilities * **Fallback Systems:** Alternative resolution methods if primary provers fail **High-Stakes Market Criteria:** * **Volume Thresholds:** Markets with >\$100K total volume get enhanced security * **Public Interest:** Markets with significant social or economic impact * **Complexity Assessment:** Markets requiring sophisticated data analysis * **Risk Evaluation:** Markets with potential for manipulation or disputes **Security Benefits:** * **Fault Tolerance:** System continues operating even if individual components fail * **Attack Resistance:** Multiple independent systems must be compromised simultaneously * **Verification Confidence:** Mathematical certainty of resolution correctness * **Transparency:** All audit reports and verification proofs publicly available **Research Opportunities:** * **Circuit Analysis:** Review zero-knowledge circuit implementations * **Prover Verification:** Test multi-prover consensus mechanisms * **Audit Process:** Participate in formal verification processes * **Security Testing:** Evaluate high-stakes market security measures # Getting Started **How to Begin Security Research on Kash** **Preparation Steps:** * **Study platform documentation** to understand system architecture * **Review smart contracts** and open source code repositories * **Understand prediction markets** and unique security considerations * **Familiarize yourself** with Base network and ERC-4337 standards **Research Approach:** * **Start with documentation** review and system understanding * **Identify potential** attack vectors and vulnerability classes * **Develop testing methodology** that respects platform and users * **Plan responsible disclosure** timeline and communication strategy **Best Practices:** * **Collaborate professionally** with the security team * **Document findings** thoroughly and clearly * **Suggest practical** mitigation strategies when possible * **Maintain confidentiality** until issues are resolved **How Regular Users Can Contribute to Security** **Everyday Security Awareness:** * **Report suspicious activity** or unusual platform behavior * **Share security concerns** with the community and support team * **Follow security best practices** for account and fund protection * **Stay informed** about security updates and best practices **Community Vigilance:** * **Watch for phishing** attempts and fraudulent communications * **Report fake accounts** or impersonation attempts * **Verify information** through official channels before acting * **Help educate** other users about security risks and protection **Contribution Methods:** * **General feedback** through support channels and community forums * **Bug reports** for non-security issues through normal support * **Feature suggestions** that could improve platform security * **Community education** and security awareness initiatives Always report security issues privately to [security@kash.bot](mailto:security@kash.bot) before public disclosure. Public disclosure of vulnerabilities before they're fixed can put user funds and the platform at risk. Security research is most effective when combined with deep understanding of the platform's architecture and user needs. Take time to understand how Kash works before looking for potential issues. Learn about platform security Get help from support team # Competitions Source: https://docs.kash.bot/getting-started/ecosystem/competitions-and-tournaments Leaderboards, Competition Mode, and Achievements Kash tracks real trading performance on public leaderboards, runs a competition mode with competition points, and awards achievement badges as you hit milestones. No registration needed — your trading activity counts automatically. Hero Light Hero Dark # Leaderboards **Community and Players Pages** * **[app.kash.bot/community](https://app.kash.bot/community):** community leaderboards and highlights * **[app.kash.bot/players](https://app.kash.bot/players):** browse and compare individual traders Every leaderboard entry links to a public profile, so you can see how top traders perform and follow the ones you rate. **Real Performance Metrics** Traders are ranked by verifiable on-platform performance: * **Realized P\&L** — actual profit and loss from settled positions * **Volume** — total trading activity * **Win rate** — share of predictions that resolved in your favor * **Streak** — consecutive winning predictions Rankings update as markets resolve, and top gainers and losers are highlighted so you can see who's hot and who's not. # Competition Mode **Compete for the Top of the Board** Competition mode scores your trading with **competition points**: trade during a competition and your performance earns points that determine your position on the competition leaderboard. * **Automatic participation** — trade during an active competition and you're in * **Transparent scoring** — points reflect your real trading performance * **Live standings** — watch your rank move as markets resolve **Competitive Integrity** * Rankings are computed from on-chain trading activity — no self-reported results * Market manipulation and multi-account abuse are prohibited and can lead to disqualification * One account per person keeps competitions fair for everyone # Achievements Achievement badges recognize milestones across your Kash journey — trading activity, wins, streaks, and community participation. View your collection at [app.kash.bot/achievements](https://app.kash.bot/achievements), and show it off on your public profile. # World Cup 2026 The biggest tournament gets its own hub: fixtures, knockout brackets, outright winner markets, team pages, news, and analytics — all in one place. Predict every match of the 2026 World Cup The best way to climb the leaderboards is consistent, skilled prediction — focus on accuracy over volume and let your streak build. Begin making predictions See the current rankings # Governance Source: https://docs.kash.bot/getting-started/ecosystem/governance How Kash Is Governed Today — and the Planned DAO **Kash has not launched a token or a DAO.** $KASH and KashDAO governance are planned but not live — all details on this page are **subject to change**. Beware of scams claiming to sell "$KASH" or governance rights; official announcements come only through [official Kash channels](/getting-started/ecosystem/socials). Hero Light Hero Dark # Governance Today Today, Kash is operated by the Kash team. Protocol smart contracts are secured with multisig controls, and market resolution is executed by a multisig oracle. A progressive transition to community governance (KashDAO) is planned. **Kash-Operated with Multisig Security** * **Team-operated:** Product decisions, market curation, and protocol upgrades are made by the Kash team * **Multisig-secured contracts:** Privileged contract actions require multi-party approval rather than a single key * **Multisig oracle resolution:** Market outcomes are resolved through multisig-controlled oracle transactions * **Self-custodial funds:** Your funds sit in your own smart account — governance structure never gives Kash control over user funds **How to influence Kash today:** * Use the in-app feedback widget to report bugs and share ideas * Join the [Discord community](https://discord.com/invite/n9eumqjMPb) to discuss features and markets * Email [support@kash.bot](mailto:support@kash.bot) with product feedback **Progressive Decentralization Roadmap** KashDAO is planned as a decentralized autonomous organization where \$KASH token holders would collectively govern the protocol through transparent, on-chain voting. **Planned Architecture:** * **OpenZeppelin Governance** implementation for secure on-chain voting * **Token-holder voting** on protocol decisions * **Multisig emergency protocols** for crisis situations **Planned Governance Scope:** * Protocol parameter adjustments and fee structures * Treasury fund allocation and spending decisions * Oracle selection and data source improvements * Emergency protocols and crisis management procedures *** # Planned Governance Components **Community-Controlled Protocol Resources (Planned)** Under the proposed design, a governed treasury would collect protocol revenue and fund ecosystem growth: * **Treasury Core Module:** Would store USDC, \$KASH, LP tokens, and sub-fund balances * **Asset Manager Module:** Would deploy idle USDC into pre-approved yield strategies * **Smart Burn Module:** Would execute monthly \$KASH buybacks * **Community Rewards Module:** Would manage grants and community incentives **Planned Oversight:** * Community votes on treasury parameter adjustments * Transparent, on-chain reporting of treasury operations * Multisig security for large fund movements See [Tokenomics](/getting-started/ecosystem/tokenomics) for the full proposed treasury design. **Ensuring Fair Market Outcomes** **Today:** * Markets are resolved by a Kash-operated multisig oracle * Multi-party approval is required before a resolution executes on-chain **Planned:** * DAO-driven oracle updates and data source improvements * Community oversight of resolution accuracy * Community-appointed adjudicators and weighted community votes for disputed or ambiguous outcomes **Crisis Management & Security** **Today:** * Multisig controls (Gnosis Safe) protect privileged protocol actions * The team can pause affected components in response to a security incident **Planned:** * Governance-gated emergency multisig activation * Time-locked actions with mandatory community notification * Post-incident analysis shared with the community *** # Planned Token-Based Voting **Proposed Decision-Making Process:** * **Proposal submission** by community members * **Discussion and debate** in governance forums * **On-chain voting** with transparent results * **Implementation** based on community consensus **Proposed Voting Weight:** * Base voting power determined by \$KASH holdings * Staking multipliers for locked tokens (see [Tokenomics](/getting-started/ecosystem/tokenomics)) **Get Involved Before the DAO Launches** * **Stay informed:** Follow [official channels](/getting-started/ecosystem/socials) for governance and token announcements * **Join the discussion:** The [Discord community](https://discord.com/invite/n9eumqjMPb) is where governance design will be discussed * **Build reputation:** Active, constructive community members will be best positioned to participate meaningfully There is currently \*\*no way to buy $KASH or acquire governance rights**. Anyone offering to sell you $KASH, an allocation, or voting power is running a scam. Until the DAO launches, the best ways to shape Kash are the in-app feedback widget, the Discord community, and [support@kash.bot](mailto:support@kash.bot). The proposed \$KASH token design Live earning opportunities # Rewards Source: https://docs.kash.bot/getting-started/ecosystem/incentives-and-rewards Referrals, Affiliates, and the Earn Vault Kash rewards the people who grow the platform. Two programs are live today — the referral program and the affiliate program — with the Earn vault coming soon. Hero Light Hero Dark # Referral Program **Invite Friends, Earn Together** 1. Open **Portfolio → Referrals** on [app.kash.bot](https://app.kash.bot) 2. Copy your personal referral link 3. Share it with friends — when they sign up and trade, their activity counts toward your milestones **Milestones:** * Referral rewards unlock in milestone tiers as your invited friends become active * Track your progress, active referrals, and unlocked milestones directly on the Referrals page **For Creators and Communities** If you run a community or audience, the affiliate program offers ongoing rewards for the users you bring to Kash — beyond the standard referral milestones. * Apply and manage everything from the Referrals page * Questions? Reach out to [partnerships@kash.bot](mailto:partnerships@kash.bot) # Earn Vault (coming soon) Put idle USDC to work. The Earn vault will deploy deposits into market-making across Kash markets, with your vault shares tracking the vault's performance. Preview how vault deposits, yield, and withdrawals will work # Leaderboards & Achievements Skill gets recognized too: public leaderboards rank traders by realized P\&L, volume, win rate, and streaks, and achievement badges mark your milestones. Leaderboards, competition mode, and achievements \*\*About $KASH incentives:** Kash has not launched a token. Any future token-based incentive programs will be announced through [official channels](/getting-started/ecosystem/socials) — see [Tokenomics](/getting-started/ecosystem/tokenomics) for the proposed design. Treat any current "$KASH rewards" offer as a scam. Begin making predictions Get your referral link # Community Source: https://docs.kash.bot/getting-started/ecosystem/socials Join the Kash Community Connect with the global Kash community. Share strategies, stay updated on the latest developments, and be part of the future of prediction markets. Hero Light Hero Dark # Where to Find Us **Markets, Announcements, and Social Trading** Follow [@kash\_bot](https://x.com/kash_bot) for new markets, platform announcements, and community highlights. **What You'll Find:** * Market posts on trending topics * Platform updates and feature announcements * Community predictions and highlights **How to Engage:** * Follow @kash\_bot for market notifications * Repost markets with your predictions and tag friends * Trade directly from tweets once you've authorized tweet trading in **Settings → Trading bot** on [app.kash.bot](https://app.kash.bot) **Deep Discussions and Community Support** Join our Discord server for in-depth conversations, strategy sharing, and community support. **What You'll Find:** * Strategy discussions and market analysis * Platform help from the community * Community events and competitions * Announcements and updates **How to Join:** * Join via the [invite link](https://discord.com/invite/n9eumqjMPb) * Introduce yourself in the welcome channel * Explore channels based on your interests **Fast Updates and Announcements** Stay connected with instant notifications and quick community updates through our Telegram channel. **What You'll Find:** * Breaking news and market alerts * Platform updates and announcements * Community highlights **How to Connect:** * Join [t.me/kashDAO](https://t.me/kashDAO) * Enable notifications for important updates Tutorials, market analysis, and educational content to help you get the most out of prediction markets — [@kash\_bot](https://youtube.com/@kash_bot). Detailed technical articles, market insights, and ecosystem updates — [@kashbot](https://medium.com/@kashbot). Short-form content about prediction markets and community highlights — [@kash\_bot](https://tiktok.com/@kash_bot). Public repositories, SDKs, and developer resources — [KashDAO](https://github.com/KashDAO). Explore the [Developer Docs](https://docs.kash.bot) to build on Kash. **Quick Access Directory** | Platform | Handle/Link | Primary Purpose | | --------------- | ---------------------------------------------- | ---------------------------------------------------------------------- | | **Website** | [kash.bot](https://kash.bot) | Marketing site — the app lives at [app.kash.bot](https://app.kash.bot) | | **X (Twitter)** | [@kash\_bot](https://x.com/kash_bot) | Markets and announcements | | **Discord** | [Kash](https://discord.com/invite/n9eumqjMPb) | Community hub | | **Telegram** | [t.me/kashDAO](https://t.me/kashDAO) | Quick updates and announcements | | **TikTok** | [@kash\_bot](https://tiktok.com/@kash_bot) | Community content | | **GitHub** | [KashDAO](https://github.com/KashDAO) | Open source development | | **YouTube** | [@kash\_bot](https://youtube.com/@kash_bot) | Educational content | | **Medium** | [@kashbot](https://medium.com/@kashbot) | Technical articles | | **Reddit** | [r/kashbot](https://www.reddit.com/r/kashbot/) | Community content | **Contact Information:** * General inquiries: [hello@kash.bot](mailto:hello@kash.bot) * Technical support: [support@kash.bot](mailto:support@kash.bot) * Security issues: [security@kash.bot](mailto:security@kash.bot) * Partnership opportunities: [partnerships@kash.bot](mailto:partnerships@kash.bot) Kash has **not launched a token**. Anyone promoting a "\$KASH" token, presale, or airdrop in community channels is running a scam. Official announcements come only from the channels listed above. *** # Ready to Join? **Get Connected in Minutes** 1. **Create an account** at [app.kash.bot](https://app.kash.bot) 2. **Follow [@kash\_bot](https://x.com/kash_bot)** for markets and announcements 3. **Join [Discord](https://discord.com/invite/n9eumqjMPb)** to connect with the community 4. **Subscribe to [Telegram](https://t.me/kashDAO)** for instant updates **Pro Tips:** * Introduce yourself in Discord's welcome channel * Start with small predictions to learn the platform * Ask questions — the community loves helping newcomers **How to Be a Great Community Member** **Do:** * Share genuine insights and analysis * Help newcomers learn and succeed * Report bugs and suggest improvements — the in-app feedback widget goes straight to the team * Engage respectfully with different viewpoints **Don't:** * Spam or excessively self-promote * Share false or misleading information * Harass other members * Attempt market manipulation Whether you're new to prediction markets or an experienced trader, there's a place for you in the Kash community. Connect with the community hub Stay updated on markets # Tokenomics Source: https://docs.kash.bot/getting-started/ecosystem/tokenomics The Planned $KASH Token Economy **Kash has not launched a token.** $KASH is planned but does not exist yet — there is no contract address, presale, or airdrop. Everything on this page describes a proposed design and is **subject to change** before any launch. Beware of scams claiming to be $KASH; official announcements come only through [official Kash channels](/getting-started/ecosystem/socials). This page is not an offer or solicitation of any kind. \$KASH is the planned native token of the Kash ecosystem, designed around automated buybacks, community incentives, and decentralized governance participation. Hero Light Hero Dark # Proposed Token Distribution The planned supply would be allocated with a community-first approach: 65% to community, 25% to team/advisors, 10% to investors. ### Community Allocations (65% - Governance Controlled) | **Category** | **% of Supply** | **Available at TGE** | **Lock-Up (months)** | **Monthly Vesting** | **Purpose** | | ---------------------------- | --------------- | -------------------- | -------------------- | ------------------- | ------------------------------------------------------ | | **Community Incentives** | 25.00% | 0.00% | 0 | 36 | Ecosystem growth, liquidity incentives | | **DAO Treasury** | 28.00% | 5.00% | 9 | 36 | Protocol development, security audits, buyback funding | | **Marketing & Partnerships** | 4.00% | 50.00% | 0 | 36 | User acquisition, KOL partnerships | | **CEX Listings & Liquidity** | 4.00% | 50.00% | 0 | 12 | Exchange listings, market making | | **Strategic Partnerships** | 4.00% | 0.00% | 0 | 36 | Technology integrations, corporate collaborations | ### Team & Advisor Allocations (25% - Fixed) | **Category** | **% of Supply** | **Available at TGE** | **Lock-Up (months)** | **Monthly Vesting** | **Purpose** | | ----------------------- | --------------- | -------------------- | -------------------- | ------------------- | ---------------------------------------- | | **Team & Contributors** | 20.00% | 0.00% | 9 | 36 | Core team, early contributors | | **Advisors** | 5.00% | 0.00% | 9 | 24 | Expert guidance, partnership development | ### Investor Allocations (10% - Fixed) | **Category** | **% of Supply** | **Available at TGE** | **Lock-Up (months)** | **Monthly Vesting** | **Purpose** | | --------------------- | --------------- | -------------------- | -------------------- | ------------------- | ----------------------------------------------------------------------------------- | | **Private Investors** | 10.00% | 0-5% | 12 | 18-24 | Investors across multiple rounds (proposed allocation — no token sale has occurred) | Under the proposed design, community allocations (65%) could be re-bucketed or adjusted through DAO governance, while team and investor allocations (35%) would remain fixed. All figures above are provisional until a token launch is officially announced. *** # Planned Token Utility **Primary Intended Uses** **Prediction Currency:** * Planned alternative to USDC for placing predictions * Would be automatically swapped to USDC when predicting with \$KASH **Governance Rights:** * Vote on protocol upgrades and changes * Influence fee structures and parameters * Direct community fund allocation decisions **Staking:** * Stake for enhanced governance weight * Longer locks would grant greater voting power **\$KASH in the Ecosystem (Planned)** **Governance Voting:** * \$KASH holders would vote on protocol upgrades, fee structures, and treasury allocation * Voting weight would increase with staking duration **Staking:** * Flexible lock durations from 30 days to 365 days **Community Incentives Allocation:** * 25% of total supply would be allocated to Community Incentives * Programs would be announced via official channels and governed by DAO decisions **Proposed Lock Periods and Governance Weight** | Lock Duration | Governance Weight | Additional Benefits | | ------------- | ----------------- | ------------------------- | | 30 days | 1.2x multiplier | Basic governance rights | | 90 days | 1.5x multiplier | Priority support access | | 180 days | 2.0x multiplier | Beta feature access | | 365 days | 3.0x multiplier | Maximum governance weight | **Proposed Participation Thresholds:** * Minimum 1,000 \$KASH for basic voting * 10,000 \$KASH for proposal submission * 100,000 \$KASH for major protocol changes *** # Proposed Buyback Mechanism **Planned Monthly Treasury Operations** **Intended Revenue Sources:** * **Protocol trading fees:** Primary revenue from prediction market trades * **Treasury yield:** Interest earned on idle treasury USDC * **Strategic partnerships:** Revenue sharing from ecosystem integrations **Proposed Monthly Buyback Process:** 1. Treasury accumulates USDC from platform fees 2. Smart contract calculates optimal buyback amount 3. Automated purchase from a USDC/KASH liquidity pool 4. Bought tokens distributed for community incentives **Proposed Buyback Formula:** ``` Bₘ = min((0.005 × S / V̄) × Vₘᵉᵐᵃ, 0.01 × S) Where: - Vₘᵉᵐᵃ = γVₘ + (1-γ)Vₘ₋₁ᵉᵐᵃ (exponential moving average) - γ = 0.3 (smoothing factor, variable based on market metrics) - V̄ = 1/12 × Σ(Vₘ₋ᵢᵉᵐᵃ) for i=1 to 12 (12-month average) - S = total supply of $KASH tokens ``` **Proposed Constraints:** * Maximum 1% of supply bought back per month * Targets \~0.5% of supply in the liquidity pool * Formula engineered to yield around a 0.5% buyback rate **Proposed Treasury Design** **Treasury Flow Diagram:** ```mermaid theme={null} graph TD A["Platform Fees"] --> B["Treasury Core Module"] B --> C["Asset Manager Module"] C --> D["Yield Strategies"] D --> E["Yield Returns"] E --> B B --> F["Smart Burn Module"] F --> G["Monthly $KASH Buybacks"] G --> H["Community Rewards Module"] B --> I["Bond Depository Module"] style A fill:#e1f5fe style B fill:#fff3e0 style F fill:#f3e5f5 style G fill:#e8f5e8 style H fill:#fff8e1 ``` **Planned Module Interactions:** * **Treasury Core Module:** Central hub storing USDC, \$KASH, LP tokens, and sub-fund balances * **Asset Manager Module:** Would deploy idle USDC to conservative yield strategies * **Smart Burn Module:** Would calculate and execute monthly \$KASH buybacks from accumulated fees * **Bond Depository Module:** Would enable LPs to buy \$KASH at a discount for liquidity bootstrapping * **Community Rewards Module:** Would distribute bought-back tokens for community incentives **Planned Governance Controls:** * **Parameter adjustment:** Community would be able to modify buyback formulas and allocation percentages * **Yield strategy:** Governance would approve yield protocols and risk parameters * **Emergency powers:** Multisig could pause operations in extreme circumstances * **Transparency:** All treasury operations would be publicly auditable on-chain **Intended Protocol Economics** **Revenue Model:** * Protocol fees on trading volume (see [Market Mechanics](/getting-started/features/market-mechanics) for the current live fee schedule) * Yield on treasury deposits * Transparent fee distribution **Intended Community Benefits:** * Community incentives would drive engagement * Governance participation would increase utility **Growth Mechanics:** * Higher trading volume → larger buybacks **Risk Management:** * Maximum 1% monthly buyback limit * Controlled token release schedules * Emergency pause mechanisms * Community governance oversight The proposed $KASH tokenomics prioritize community ownership and sustainable growth. Follow [official channels](/getting-started/ecosystem/socials) for any future token announcements — and treat any unannounced "$KASH" token as a scam. See the live rewards programs Current and planned governance # Account Source: https://docs.kash.bot/getting-started/faqs/account Common questions about account creation and management Hero Light Hero Dark # Account Creation and Setup **Sign In With the Method You Prefer** Go to [app.kash.bot](https://app.kash.bot) and sign in with any of the supported login methods (powered by Privy): * **X (Twitter)** account * **Google** account * **Email** (one-time code, no password to remember) * **External wallet** you already own **What happens on signup:** * A self-custodial wallet and smart account are created for you automatically * No seed phrase to write down * No identity documents required to get started **Recommended next step:** add a **recovery email** in Settings so you can regain access if you ever lose your primary login method. **One Account Per Person** * Multiple accounts per person are not permitted * Account sharing is prohibited * Each account has its own unique wallet, history, and reputation **Why:** one-account-per-person keeps markets, leaderboards, competitions, and referral rewards fair, and prevents manipulation. Accounts cannot be merged, and funds cannot be transferred between accounts. **Access Methods** * **Webapp:** visit [app.kash.bot](https://app.kash.bot) and sign in — works on desktop and mobile browsers, and you can install it as an app (PWA) on your phone * **Tweet trading (optional):** after authorizing your X account in **Settings → Trading bot**, you can also trade by tweeting at **Kash's dedicated trading account** — find the current handle on the [@kash\_bot](https://x.com/kash_bot) profile Your login method (X, Google, email, or wallet) is your key — there's no separate Kash password. # Account Security and Recovery **Recovery Options** * **Recovery email:** if you've set a recovery email in Settings, you can use it to regain access to your account — set it up now if you haven't * **Primary login recovery:** regaining access to your X/Google/email account through that provider restores your Kash access * **Key export:** you can export your wallet key from Settings at any time, giving you an independent backup of access to your funds **Prevention checklist:** * Add a recovery email in Settings * Enable 2FA on the account you use to log in (X or Google) * Consider exporting and securely storing your wallet key **Security Best Practices** **Login security:** * Enable two-factor authentication on your X or Google account * Use a strong, unique password on your login provider * Add a recovery email in Kash Settings **Platform security:** * Always verify you're on the **app.kash.bot** domain before signing in * Kash will never ask for your password or exported key * Review your transaction history regularly **Wallet model:** * Your funds are held in your own smart account, controlled by a Privy-powered embedded wallet that only you can authorize * Kash cannot withdraw your funds; trades execute only under permissions you authorize * See the [Security FAQ](/getting-started/faqs/security) for the full custody model **Yes — Your Wallet Is Yours** * **Key export:** export your embedded wallet's key from **Settings** to use your wallet outside of Kash * **Transaction history:** all trading activity is recorded on the Base blockchain and viewable on [basescan.org](https://basescan.org) * **In-app history:** the Portfolio page shows your positions, trades, and claims **Privacy note:** blockchain activity is public. Your on-chain trades are visible to anyone who knows your wallet address. # Account Management **Profile Management** * Profile details sync from the account you signed in with * Settings (language, theme, notifications, privacy, API keys, trading bot) live in **Settings** on the webapp — see the [Settings FAQ](/getting-started/faqs/settings) * Adding or changing your recovery email is also done in Settings **Account Deletion via Support** To delete your account, contact **[support@kash.bot](mailto:support@kash.bot)** from the email associated with your account (or include your account details). **Before you request deletion:** * **Withdraw your funds** — move your balance to an external wallet or off-ramp first * **Consider exporting your key** so you retain access to the wallet independently **What can't be deleted:** * On-chain transaction history is permanent and public — blockchain records cannot be erased * Deletion removes your Kash account and platform data, not the public blockchain record **Support Channels** * **Email:** [support@kash.bot](mailto:support@kash.bot) * **In-app feedback widget:** report a bug or share an idea (with screenshots) directly from the webapp * **Discord:** [community support](https://discord.com/invite/n9eumqjMPb) * **Help center:** [docs.kash.bot](https://docs.kash.bot) See the [Support FAQ](/getting-started/faqs/support) for details. Protect the account you use to sign in to Kash — it controls your wallet. Enable 2FA on your login provider and add a recovery email in Settings. Your trading activity is recorded on the Base blockchain and is publicly visible. Consider the privacy implications before participating in prediction markets. Learn about account security Configure your account # Legal Source: https://docs.kash.bot/getting-started/faqs/legal Common questions about legal matters and compliance Hero Light Hero Dark # Platform Availability and Compliance **Availability and Compliance Restrictions** **Availability:** * Kash is available in supported regions. Access is blocked where law or policy requires — including sanctioned regions and other restricted jurisdictions. * Eligibility requirements, including the current list of restricted regions, are in the Terms of Service available in the app. **Restricted Activities:** * **Illegal markets:** no markets on illegal activities * **Age restrictions:** must be 18+ or the legal age in your jurisdiction * **AML compliance:** adherence to anti-money-laundering requirements **Support for Global Users:** * Interface available in English, Spanish, Portuguese, Russian, and Chinese * Markets display in your local time **Age Requirements** * **Minimum age:** you must be 18 years or older to use Kash * **Local laws:** some jurisdictions may impose higher age requirements — the stricter rule applies * **Self-declaration:** by using the platform, you confirm you meet the age requirement Kash monitors for misuse and may restrict accounts that violate age requirements. **Identity Verification Requirements** **Basic use:** * No identity verification is required to create an account — sign in with X, Google, email, or a wallet * Only essential information is collected **When verification applies:** * Some fiat deposit/withdrawal providers require KYC for their services * Additional verification may be required by regulation in certain regions or for certain volumes * Requirements may change as regulations evolve **Data protection:** * Minimal data collection and storage * Clear disclosure of data practices # Tax and Financial Responsibilities **Tax Reporting and Compliance** **User responsibility:** * You are responsible for reporting gains and losses under your jurisdiction's tax laws * We recommend consulting a tax professional * Keep detailed records of all transactions **Available information:** * All transactions are recorded on the Base blockchain and verifiable on basescan.org * The Portfolio page shows your trade and claim history **Typical taxable events:** * Profits from winning predictions **Considerations:** * Whether earnings are treated as income or capital gains varies by jurisdiction * Losses may be deductible in some jurisdictions * Cross-border implications for international users **Regulatory Compliance Framework** **Current measures:** * **Geographic restrictions:** geo-blocking of OFAC-sanctioned regions * **AML monitoring:** anti-money-laundering screening measures * **Age requirements:** 18+ only * **Content moderation:** no markets on personal harm, death, or criminal outcomes **Data protection:** * Privacy considerations built into platform features * Data minimization — only necessary information is collected * Handling complies with applicable privacy laws **Ongoing development:** * Continuous monitoring of regulatory changes * Features adapted to maintain compliance as requirements evolve **Regulatory Change Management** * **Monitoring:** continuous tracking of regulatory developments with expert consultation * **Communication:** advance notice of impactful changes when possible, in plain language * **Adaptation:** features or regional availability may be adjusted to maintain compliance * **Continuity:** service maintained wherever legally possible, with reasonable transition periods when features are restricted Because Kash is self-custodial, your funds remain in your own smart account regardless of platform-level changes. # Community Guidelines and Privacy **Community Standards and Behavior** **Core principles:** * Treat all community members with respect * Engage honestly in prediction markets * Keep discussions relevant and appropriate **Prohibited activities:** * Market manipulation or fraudulent activity * Multiple accounts to circumvent limits * Harmful, offensive, or inappropriate content * Using the platform for any illegal purpose **Enforcement:** * Violations can lead to restrictions or account action * Report inappropriate behavior via the in-app feedback widget or [support@kash.bot](mailto:support@kash.bot) **Privacy and Data Protection** **Data collection:** * Minimal collection — only what's needed to provide the service * Login data comes from the provider you sign in with (X, Google, email, or wallet) * Transaction data is recorded on the public blockchain **Data usage:** * Used only for platform functionality * Personal data is not sold to third parties * Anonymous, aggregated analytics for platform improvement **Privacy considerations:** * **Public blockchain:** all on-chain trading activity is publicly visible * Your public profile is associated with your trading activity * Blockchain records are permanent and cannot be deleted * To delete your Kash account data, contact [support@kash.bot](mailto:support@kash.bot) — see the [Account FAQ](/getting-started/faqs/account) Legal and regulatory requirements vary by jurisdiction. You are responsible for ensuring your use of Kash complies with all applicable local laws and regulations. All your prediction activity is recorded on the Base blockchain and is publicly visible. Keep detailed records for tax and legal compliance purposes. Contact support Custody and platform security # Notifications Source: https://docs.kash.bot/getting-started/faqs/notifications Manage your Kash notifications and preferences Hero Light Hero Dark # Notifications **Notification Categories** * **Trades:** confirmations and updates for your trades * **Market updates:** resolutions, freezes, and status changes for markets you're in * **Payouts & claims:** when winnings or refunds become claimable, and when payouts complete * **Account:** account-related activity and security events * **Vault:** updates on your Earn vault deposits and redemptions (once Earn launches) * **Kash announcements:** platform news and feature updates Notifications link to the relevant market or page so you can act on them directly. **Delivery Channels** * **In-app:** the notification center in the webapp at [app.kash.bot](https://app.kash.bot) * **Browser / device push:** enable push notifications in **Settings → Notifications** to get alerts even when the tab is closed * **iOS:** install app.kash.bot as a PWA (Share → Add to Home Screen) to receive push notifications on iPhone **Tweet trading:** if you trade by tweeting at **Kash's dedicated trading account** (the current handle is linked from [@kash\_bot](https://x.com/kash_bot)), the bot replies to your tweet with the trade result. Neither @kash\_bot nor the trading account ever DMs or @mentions you first — they only respond to your interactions, and DMs are not a notification or support channel. **Settings → Notifications** * Toggle in-app and push delivery independently * Choose which categories you want to receive * Grant your browser or device permission when prompted to enable push If push notifications aren't arriving, check that your browser/OS allows notifications from app.kash.bot, and on iOS make sure you're using the installed PWA. Kash will never DM you first or ask for credentials in a notification. Be cautious with links from unknown sources and verify you're on app.kash.bot. Enable push notifications to hear the moment a market you're in resolves — winnings become claimable as soon as resolution lands. Configure your preferences Get help and support # Security Source: https://docs.kash.bot/getting-started/faqs/security Common questions about platform and account security Hero Light Hero Dark # How Custody Works **You Do — Kash Is Self-Custodial** Here's the actual model: * When you sign up, a **Privy-powered embedded wallet** (a standard EOA) is created for you * That wallet is the signer for an **ERC-4337 smart account** — the smart account is what holds your funds * Only your wallet can authorize transactions from your smart account * **Kash cannot withdraw your funds.** The platform prepares transactions; they execute only under permissions you authorize **Proof of control:** you can **export your wallet key from Settings** at any time and use the wallet entirely outside of Kash. **No Seed Phrase to Manage** * Your embedded wallet is secured by Privy's infrastructure and unlocked by your login — there's no 12-word phrase to write down or lose * **Recovery:** regain access through your login method (X, Google, email, or wallet) plus your **recovery email** if you've set one in Settings * **Independent backup:** exporting your key from Settings gives you access to your wallet no matter what happens to your Kash login **Set up now:** add a recovery email in Settings and enable 2FA on the account you log in with. **Security Model** * **Self-custodial wallets:** you control your funds; Kash can't withdraw them — trades execute only under permissions you authorize * **ERC-4337 smart accounts:** your funds live in an audited account-abstraction smart account on Base * **Privy wallet infrastructure:** enterprise-grade embedded wallet security * **Base network:** built on an established Ethereum Layer 2 * **Gasless transactions:** gas is sponsored via a paymaster, so you never handle ETH for gas * **Multisig-secured contracts:** privileged protocol actions require multi-party approval **Audits:** security audits of Kash smart contracts are conducted. For audit or security questions, contact **[security@kash.bot](mailto:security@kash.bot)**. # Account Security Best Practices **Security Checklist** **Login security (most important):** * Enable 2FA on the X or Google account you sign in with * Use a strong, unique password on your login provider * Add a **recovery email** in Kash Settings **Platform hygiene:** * Always verify the **app.kash.bot** domain before signing in * Only trust the verified **@kash\_bot** account on X — and only trust a trading account that @kash\_bot itself links to * Review your transaction history and notifications regularly **Key export safety:** * If you export your wallet key, store it offline and securely * Never share your exported key with anyone — including anyone claiming to be Kash support **Immediate Response** 1. **Secure your login provider:** change the password and enable 2FA on the X/Google/email account you use to sign in 2. **Contact support:** email **[support@kash.bot](mailto:support@kash.bot)** with your account details, timestamps, and screenshots 3. **Review activity:** check your transaction history and wallet on [basescan.org](https://basescan.org) 4. **Document evidence:** capture screenshots of any suspicious activity For suspected platform vulnerabilities (rather than account compromise), email **[security@kash.bot](mailto:security@kash.bot)**. **Device Loss Recovery** 1. From another device, change the password on your login provider (X/Google/email) and enable 2FA 2. Sign in to [app.kash.bot](https://app.kash.bot) from the new device — your wallet, balances, and history are restored automatically after authentication 3. Monitor your account for unauthorized activity and contact [support@kash.bot](mailto:support@kash.bot) if anything looks wrong Your funds live on-chain in your smart account — losing a device doesn't put them at risk as long as your login remains secure. # Common Security Concerns **No.** * **What Kash can do:** prepare transactions for your approval, sponsor gas, resolve markets, and execute payouts to your own smart account * **What Kash cannot do:** move your funds without your authorization, see or use your exported key, or freeze your wallet If the platform disappeared tomorrow, you could export your key from Settings and access your smart account directly on-chain. **Recognizing and Avoiding Scams** **Red flags:** * Anyone asking for your password, exported key, or a "wallet validation" * Lookalike accounts imitating @kash\_bot or Kash's trading account, or DMs from either — the real accounts never DM you first; only trust a trading account linked from the verified @kash\_bot profile * Lookalike domains — the only official domains are **kash.bot** (marketing site) and **app.kash.bot** (the app) * **Token scams:** Kash has **not launched a token**; anyone selling "\$KASH" is a scammer * Urgent demands for immediate action **If you encounter phishing:** * Don't click links or provide information * Report the account to X and email [support@kash.bot](mailto:support@kash.bot) * Warn the community in [Discord](https://discord.com/invite/n9eumqjMPb) **Eligibility and Geo-Blocking** * **18+ only:** you must be at least 18 years old (or the legal age in your jurisdiction) * **Geo-blocking:** access from OFAC-sanctioned regions is blocked * Compliance restrictions are enforced at the platform level and updated as regulations change See the [Legal FAQ](/getting-started/faqs/legal) for details. # Reporting Security Issues **Responsible Disclosure** * Email **[security@kash.bot](mailto:security@kash.bot)** with a detailed description, reproduction steps, and impact assessment * Please allow time for the team to review before any public disclosure * Smart contract vulnerabilities are in scope for the protocol bug bounty — see the [Bug Bounty page](/getting-started/ecosystem/bug-bounty-program) **Do not** test vulnerabilities against other users' funds or post exploits publicly. Security is a shared responsibility. Kash provides self-custodial infrastructure, but you must protect your login account and never share credentials or exported keys with anyone. Three things cover most of your risk: 2FA on your login provider, a recovery email in Settings, and never sharing your exported key. Learn about account protection Report security issues # Settings Source: https://docs.kash.bot/getting-started/faqs/settings Common questions about account settings and preferences Hero Light Hero Dark # Settings Overview **Settings on app.kash.bot** Open **Settings** in the webapp to manage: * **Language:** English, Spanish, Portuguese, Russian, and Chinese * **Theme:** light and dark mode * **Notifications:** in-app and browser/device push preferences * **Privacy:** control your data and profile visibility * **API keys:** create and manage keys for the [Kash API](https://docs.kash.bot) * **Trading bot:** authorize your X account for tweet-based trading Changes take effect immediately and persist across your sessions and devices. **Display Preferences** * **Language:** pick from English, Spanish, Portuguese, Russian, or Chinese in Settings — the interface updates instantly * **Theme:** switch between light and dark mode Your choice is saved to your account, so it follows you across devices. **Settings → Notifications** * Toggle **in-app** notifications for the notification center * Enable **browser / device push** to get alerts even when the tab is closed — your browser will ask for permission * **iOS:** install app.kash.bot as a PWA (Share → Add to Home Screen) to receive push notifications on iPhone See the [Notifications FAQ](/getting-started/faqs/notifications) for the available categories. **Settings → Trading Bot** Tweet-based trading is opt-in: 1. Open **Settings → Trading bot** 2. Authorize your X account 3. Once connected, you can place trades by tweeting at **Kash's dedicated trading account** — the current handle is linked from the [@kash\_bot](https://x.com/kash_bot) profile You can revoke the authorization at any time from the same page. **Settings → API Keys** * Create API keys for programmatic access to your account * Copy the key at creation time — treat it like a password * Revoke keys you no longer use See the [Developer Docs](https://docs.kash.bot) for authentication, scopes, and rate limits. **Privacy Settings** * Manage your data and profile visibility from **Settings → Privacy** * Keep in mind that on-chain activity is inherently public: trades from your wallet are visible on the Base blockchain regardless of in-app settings # Account-Level Settings Add or update your recovery email in **Settings**. This is your safety net if you lose access to your primary login method — set it up early. See the [Account FAQ](/getting-started/faqs/account) for the full recovery picture. You can export your embedded wallet's key from **Settings**. This gives you independent access to your smart account outside of Kash. Store it securely and never share it — see the [Security FAQ](/getting-started/faqs/security). # Troubleshooting Settings **Quick Fixes** 1. **Refresh** the page and try again 2. **Clear cache** and cookies for app.kash.bot 3. **Try another browser** to rule out extensions or compatibility issues 4. **Check your connection** — settings save to your account and need a stable connection Still stuck? Use the in-app feedback widget to report a bug (attach a screenshot), or email **[support@kash.bot](mailto:support@kash.bot)**. * Confirm push is enabled in **Settings → Notifications** * Check that your browser/OS allows notifications from app.kash.bot * On iOS, push only works from the installed PWA — add app.kash.bot to your home screen first Most preferences live in one place: **Settings** on app.kash.bot. Language, theme, notifications, privacy, API keys, and the trading bot are all managed there. Learn about security settings Manage your account # Support Source: https://docs.kash.bot/getting-started/faqs/support Common questions about getting help and support Hero Light Hero Dark # Getting Support **Official Support Channels** * **Email:** **[support@kash.bot](mailto:support@kash.bot)** — the primary channel for account, funds, and trading issues * **In-app feedback widget:** report a bug or share an idea directly from the webapp, with screenshots attached * **Discord:** [community support](https://discord.com/invite/n9eumqjMPb) from other users and the team * **Help center:** self-service guides and FAQs at [docs.kash.bot](https://docs.kash.bot) **Security issues:** email **[security@kash.bot](mailto:security@kash.bot)** (responsible disclosure — see the [Bug Bounty page](/getting-started/ecosystem/bug-bounty-program)). DMs to @kash\_bot or to Kash's trading account are **not** a support channel — neither account handles support requests, and anyone DMing you "support" is likely a scammer. Kash support will never ask for your password or exported wallet key. **Help Us Help You Faster** **Account information:** * The login method/email associated with your Kash account * Approximate time the issue occurred **Issue details:** * Clear description of the problem * Exact text of any error messages * What you were trying to do, and what happened instead **Technical information:** * Browser and device you're using * Transaction hash (from basescan.org) if it's a transaction issue * Screenshots — the in-app feedback widget attaches these for you **Choosing the Right Channel** | Issue | Best channel | | ------------------------------------------- | ------------------------------------------------ | | Account access, funds, deposits/withdrawals | [support@kash.bot](mailto:support@kash.bot) | | Bug on the website | In-app feedback widget (report a bug) | | Feature suggestion | In-app feedback widget (share an idea) | | How-to questions, strategy, general chat | [Discord](https://discord.com/invite/n9eumqjMPb) | | Self-service answers | [docs.kash.bot](https://docs.kash.bot) | | Security vulnerability | [security@kash.bot](mailto:security@kash.bot) | # Self-Service Resources **Help Resources at docs.kash.bot** * **Getting Started:** guides for accounts, deposits, trading, and claiming winnings * **Features:** how markets, the AMM, and trading options work * **FAQs:** answers to the most common questions * **Developer Docs:** API, SDK, and embed documentation The webapp also includes **Ask Kash**, a built-in help assistant for quick questions. **Before Contacting Support** 1. Check the [Troubleshooting FAQ](/getting-started/faqs/troubleshooting) for known fixes 2. Hard-refresh the page or try another browser 3. Verify transactions independently on [basescan.org](https://basescan.org) 4. Check Discord for announcements about ongoing maintenance If the issue persists, email [support@kash.bot](mailto:support@kash.bot) with the details above. # Bugs, Ideas, and Security **The Feedback Widget Is the Fast Path** * Open the **feedback widget** in the webapp * Choose **Report a bug** or **Share an idea** * Add a description — screenshots are captured automatically to give the team context Bug reports and ideas go straight to the team. You can also discuss ideas with the community in Discord. **Responsible Disclosure** * Email **[security@kash.bot](mailto:security@kash.bot)** with a detailed description, reproduction steps (if safe), and impact assessment * Allow time for the team to review before public disclosure * Smart contract vulnerabilities are in scope for the protocol bug bounty — see the [Bug Bounty page](/getting-started/ecosystem/bug-bounty-program) **If you suspect your own account is compromised:** secure your login provider (change password, enable 2FA) and email [support@kash.bot](mailto:support@kash.bot) immediately. For anything involving your funds or account access, email [support@kash.bot](mailto:support@kash.bot). For everything else, the feedback widget and Discord will usually get you an answer fastest. Get community support Fix common issues # Transaction Source: https://docs.kash.bot/getting-started/faqs/transactions Common questions about transactions and fees Hero Light Hero Dark # Fees | Action | Fee | | ---------------------------------------- | ---------------------------------------------------------------- | | **Buying** outcome shares | 1% protocol fee | | **Selling** outcome shares | 1% protocol fee + 1% AMM fee | | **Claiming winnings** (redemption) | No fee | | **Claiming a refund** (cancelled market) | No fee | | **Creating a market** | Creation fee (shown in the creation flow) | | **Deposits** | No Kash fee — payment providers charge their own processing fees | | **Gas** | Sponsored by Kash (see gas exception below) | Fees are charged at the time of the trade — nothing extra is deducted from winning payouts, and there are no subscription, maintenance, or hidden fees. ``` Buy: $100 on Yes Protocol fee: 1% = $1.00 Total cost: $101.00 Sell later: position worth $150 Protocol fee (1%) + AMM fee (1%) = $3.00 You receive: $147.00 Or hold to resolution and win: Claim your payout — no redemption fee (mainnet claims may deduct a small gas amount in USDC) ``` Kash wallets are ERC-4337 smart accounts, and transaction gas is paid by a **paymaster** — so you never need to hold ETH: * **Covered:** trades, deposits credited to your account, position management * **One exception:** on mainnet, **claiming winnings may deduct a small gas amount in USDC from the payout** — you receive the net USDC. This is the only time gas touches your balance, and it's taken from winnings, never charged separately. # Trades 1. You confirm a trade on app.kash.bot (or via tweet-trading if you've enabled it) 2. The trade executes on-chain from your own smart wallet on Base — gas sponsored 3. Your position and balance update immediately, with a transaction hash you can verify on a block explorer The trade ticket always shows the price, fees, and your **estimated payout if you win** before you confirm. Confirmed blockchain transactions are final — there's no reversal mechanism. But you're never locked in: * **Sell your position** anytime before the market closes, at the current market price (1% protocol + 1% AMM fee) * **Partial sales** work too — reduce your position while keeping some exposure * Large positions may face price impact when exiting, since your own sale moves the price Common causes: * **Insufficient balance** — the amount plus the 1% buy fee exceeds your available USDC * **Market closed** — trading stops at the market deadline * **Price moved** — the price shifted beyond your slippage tolerance between quote and execution * **Rate limits** — a small number of actions (like withdrawals, capped at 5 per hour) are rate-limited If a trade fails, nothing is charged — check the market status and your balance, then retry. If it keeps failing, contact [support@kash.bot](mailto:support@kash.bot). # Claims and payouts When a market resolves and your outcome wins, the position becomes **claimable**: 1. Open your **Portfolio** on app.kash.bot 2. Press **Claim** on the resolved position 3. Your winning shares redeem for a **pro-rata share of the market reserve**, and USDC lands in your wallet On mainnet, the claim may deduct a small gas amount in USDC — you receive the net payout. There is no Kash fee on redemption. Cancelled markets work the same way with a **Claim refund** button, refunding each outcome at its price snapshotted at cancellation. Full details in the [winning guide](/getting-started/guides/winning). Winning shares redeem for a proportional share of the market's USDC reserve — not a fixed amount per share. The **estimated payout if you win** shown on the trade ticket is your best guide; the final number is fixed at resolution based on the reserve and the total supply of winning shares. If multiple outcomes win, they share the reserve. # Withdrawals Withdrawals are processed automatically (no manual approval) and **typically complete within minutes**: 1. **Pending** — request validated and queued 2. **Processing** — executing on-chain 3. **Confirming** — awaiting blockchain confirmation 4. **Completed** — funds at your destination address Withdrawals to networks other than Base are bridged automatically and take roughly 10–15 minutes. Exchanges may add a few minutes before crediting. | Limit | Value | | ----------------------- | -------- | | Minimum per transaction | \$1 | | Maximum per transaction | \$25,000 | | Daily maximum | \$10,000 | | Weekly maximum | \$50,000 | | Withdrawals per hour | 5 | Supported destination networks: Base, Ethereum, Arbitrum, Optimism, Polygon. Cross-chain withdrawals may include bridging costs, shown in the quote before you confirm. # Tracking and problems * **In the app:** your Portfolio history lists every trade, claim, deposit, and withdrawal with live status * **On-chain:** every transaction has a hash you can look up on a Base block explorer (basescan.org) — all activity is publicly verifiable and permanent Confirmed blockchain transactions cannot be reversed, and funds go to whoever controls the destination address — Kash cannot recover misdirected funds. **Prevention:** * Copy-paste addresses, never type them * Confirm the destination network matches what the recipient supports * Send a small test amount first when using a new address 1. Check the transaction status in the app and on a block explorer first 2. Contact **[support@kash.bot](mailto:support@kash.bot)** or use the in-app feedback widget 3. Include: the transaction hash (if any), your wallet address, what you expected, and what happened Screenshots help. Never share your login credentials with anyone — support will never ask for them. All confirmed transactions on Kash are final. Verify amounts, addresses, and networks carefully before confirming — completed transactions cannot be reversed. Kash sponsors gas for your transactions, so you only pay the trading fees: 1% on buys, 1% + 1% on sells, and nothing to claim winnings. Learn about wallet management Transaction security best practices # Troubleshooting Source: https://docs.kash.bot/getting-started/faqs/troubleshooting Common issues and solutions for platform problems Hero Light Hero Dark # Common Issues and Solutions **Website Access Issues** **Quick fixes:** * **Hard refresh:** Ctrl+F5 (PC) or Cmd+Shift+R (Mac) * **Clear cache:** clear browser cache and cookies for app.kash.bot * **Try a different browser:** Chrome, Firefox, Safari, or Edge * **Incognito mode:** rules out extension conflicts **Network troubleshooting:** * Verify your internet connection works on other sites * Try mobile data or a different WiFi network * Disable VPN if you're using one **Platform status:** * Check [Discord](https://discord.com/invite/n9eumqjMPb) for maintenance announcements * Follow [@kash\_bot](https://x.com/kash_bot) for updates **Trade Issues in the Webapp** **Common causes:** * **Insufficient balance:** not enough USDC in your account * **Market closed or frozen:** the market may have closed, frozen, or resolved while you were trading * **Slippage:** prices moved between quote and execution — retry with the updated quote * **Network congestion:** Base network may be busy **Troubleshooting steps:** 1. Check your balance in Portfolio 2. Confirm the market is still open and active 3. Retry the trade — quotes refresh automatically 4. Allow a minute for blockchain confirmation before retrying If a trade appears stuck, note the details and report it via the in-app feedback widget or [support@kash.bot](mailto:support@kash.bot). **Balance Display Issues** **Common causes:** * **Pending transactions:** deposits, trades, or withdrawals still processing * **Sync delay:** the app may briefly lag behind the blockchain * **Stale cache:** your browser is showing old data **Resolution steps:** 1. Hard-refresh the page 2. Check your recent activity in Portfolio 3. Verify your wallet directly on [basescan.org](https://basescan.org) 4. Clear cache and reload **Still incorrect?** Screenshot the balance, note any relevant transaction hashes, and email **[support@kash.bot](mailto:support@kash.bot)** or use the feedback widget. **The trading bot isn't responding to my tweets** Tweet trading is an opt-in companion feature — the webapp at [app.kash.bot](https://app.kash.bot) is always available as the primary way to trade. **Check these first:** 1. **Authorization:** tweet trading must be enabled in **Settings → Trading bot** on app.kash.bot 2. **Account match:** you must tweet from the X account you authorized 3. **Right handle:** make sure you're tweeting at Kash's current trading account — its handle cycles over time, and the current one is always linked from the [@kash\_bot](https://x.com/kash_bot) profile 4. **Balance:** confirm you have sufficient USDC 5. **Bot status:** check Discord for any maintenance announcements If the bot still doesn't respond, place the trade in the webapp and report the issue via the feedback widget. # Platform Access Issues **Mobile Troubleshooting** * **Update your browser** and OS to the latest version * **Clear the mobile browser cache** * **Test WiFi vs mobile data** to isolate network problems * **iOS:** check Safari privacy settings and disable iCloud Private Relay if pages fail to load * **Android:** disable data saver mode and check Chrome permissions **Tip:** install app.kash.bot as a PWA (Add to Home Screen) for the best mobile experience, including push notifications on iOS. **Sign-In Issues** 1. **Check your login provider:** make sure you can sign in to X/Google/your email directly 2. **Clear cookies** for app.kash.bot and your login provider, then retry 3. **Try another browser** or incognito mode 4. **Pop-up blockers:** allow pop-ups for app.kash.bot — some login flows open a window If you've lost access to your login method entirely, use your **recovery email**, or contact **[support@kash.bot](mailto:support@kash.bot)** — see the [Account FAQ](/getting-started/faqs/account). **Regional Access Limitations** * Access from **OFAC-sanctioned regions is blocked** for regulatory compliance * Blocking is based on your location, and restricted visitors are redirected to a blocked page * You must also be **18+** (or the legal age in your jurisdiction) to use Kash If you believe you've been blocked in error (for example, a VPN exit node in a restricted region), disable the VPN and try again. Kash does not provide workarounds for restricted regions — always comply with your local laws. # Technical Issues **Browser Support** **Supported:** current versions of Chrome, Firefox, Safari, and Edge. **Required features:** * JavaScript enabled * Cookies allowed (needed for authentication) * Local storage available **If things look broken:** 1. Update to the latest browser version 2. Disable extensions (especially aggressive privacy/ad blockers) and retry 3. Try a different supported browser **Performance Problems** * **Test your connection:** slow or unstable internet is the most common cause * **Clear cache** if pages load slowly * **Close unused tabs and apps** to free memory * **Try off-peak hours** if the Base network is congested If the app is consistently slow while other sites are fast, report it via the feedback widget with your browser and device details. **Blockchain Transaction Issues** **Common causes:** * **Network congestion:** the Base network is busy — wait and retry * **Insufficient balance:** not enough USDC for the trade * **Market state change:** the market closed or froze during your transaction **Good to know:** * **Gas is sponsored:** Kash covers gas for platform transactions via a paymaster — you don't need ETH * Transactions are verifiable on [basescan.org](https://basescan.org) **If a transaction fails repeatedly:** note the error message and transaction hash (if any) and contact **[support@kash.bot](mailto:support@kash.bot)**. # Getting Help **The Fast Path** 1. **In-app feedback widget:** choose **Report a bug**, describe the issue — screenshots are attached automatically 2. **Email [support@kash.bot](mailto:support@kash.bot)** for account, funds, or persistent issues 3. **Discord** for quick community help and to check whether others see the same issue **Include:** what you did, what you expected, what happened, exact error text, browser/device, and any transaction hashes. **Critical Issue Response** **Suspected account compromise:** 1. Change the password and enable 2FA on your login provider (X/Google/email) immediately 2. Email **[support@kash.bot](mailto:support@kash.bot)** with details and screenshots 3. Check your wallet activity on [basescan.org](https://basescan.org) **Platform outage:** * Check Discord and [@kash\_bot](https://x.com/kash_bot) for status updates * Your funds remain safe in your own smart account on-chain during an outage **Security vulnerability:** email **[security@kash.bot](mailto:security@kash.bot)** (responsible disclosure). If you suspect unauthorized account access, secure your login provider immediately and email [support@kash.bot](mailto:support@kash.bot). Never share your password or exported wallet key with anyone — including anyone claiming to be support. Most issues resolve with a hard refresh, a cache clear, or a different browser. For everything else, the feedback widget and [support@kash.bot](mailto:support@kash.bot) are your fastest paths to a fix. Contact support channels Security troubleshooting # Wallet & Funds Source: https://docs.kash.bot/getting-started/faqs/wallet-and-funds Common questions about wallet management and fund storage Hero Light Hero Dark # Your Kash wallet Your funds live in your own **smart wallet** on the Base network — an ERC-4337 smart account created for you when you sign up. * **Your account, on-chain:** the smart account belongs to you and holds your USDC and market positions * **Non-custodial:** deposits, trades, and payouts all execute from your own account — Kash never takes custody of your funds * **Signing:** the smart account is controlled by an embedded wallet (powered by Privy) tied to your login * **Gasless:** transactions are sponsored via a paymaster, so you never need ETH for gas * **Transparent:** everything is verifiable on the Base blockchain Sign in at [app.kash.bot](https://app.kash.bot) and open your **Portfolio**: * **Available balance** — USDC ready to trade or withdraw * **Open positions** — the current value of your market positions * **Claimable winnings** — resolved positions waiting for you to claim * **History** — every deposit, trade, claim, and withdrawal Balances update in real time as you trade, and everything is independently verifiable on a Base block explorer. **USDC only.** All balances, trades, payouts, and withdrawals on Kash are in USDC — a stablecoin pegged to the US dollar. * Kash does **not** accept or auto-convert other tokens (no ETH, WETH, or DAI) * Fiat deposits (card, Apple Pay, Google Pay) are converted to USDC by the payment provider before reaching your wallet * **Does Kash have a token?** Kash has not launched a token. If that ever changes, it will be announced through official channels — treat anything else as a scam. # Deposits Two ways, both from the deposit flow on app.kash.bot: **1. Card, Apple Pay, or Google Pay** — all fiat processing is handled by third-party payment providers; Kash never sees or stores your card or bank details: | Provider | Fee | KYC | | -------- | ------------- | --------------------- | | Halliday | 2.9% + \$0.30 | Basic (government ID) | Additional payment options may appear in the deposit flow — fees and limits are always shown before you confirm. **2. Direct crypto transfer** — send USDC from any wallet or exchange on **Base, Ethereum, Polygon, Arbitrum, Optimism, Avalanche, BNB Chain, Linea, Scroll, Mantle, Mode, or Blast** ($1 – $100,000 per transaction, limits subject to change; no identity verification currently required, subject to compliance screening). Anything sent on a network other than Base is automatically bridged to Base for you. See the [depositing guide](/getting-started/guides/depositing) for step-by-step instructions. * **Card / Apple Pay / Google Pay:** usually within a few minutes of payment completing * **USDC on Base:** a couple of minutes * **USDC on other networks:** roughly 10–15 minutes, since the funds are bridged to Base automatically # Withdrawals Open **Withdraw** in your wallet on app.kash.bot, enter an amount and destination address, and choose a destination network. Withdrawals are always in USDC. **Supported destination networks:** Base, Ethereum, Arbitrum, Optimism, Polygon. Withdrawals to networks other than Base are bridged automatically; bridging costs are shown in the quote. A fiat cash-out option, handled through Kash's third-party payment providers, is also available in the withdrawal flow. Withdrawals are processed automatically and typically complete within minutes. See the [withdrawing guide](/getting-started/guides/withdrawing) for the full walkthrough. | Limit | Value | | ----------------------- | -------- | | Minimum per transaction | \$1 | | Maximum per transaction | \$25,000 | | Daily maximum | \$10,000 | | Weekly maximum | \$50,000 | | Withdrawals per hour | 5 | Limits are current as of publication — the app always shows live limits before you confirm. No. There's no minimum balance, no maintenance fees, and no lock-ups. You can hold a zero balance, and every feature works regardless of how much you hold. The only hard minimum is \$1 per withdrawal. # Account access and security Your wallet is tied to your Kash account. You can sign in with **X (Twitter), Google, email, or an external wallet** — whichever you used to create your account. A recovery email can be added in Settings so you're not dependent on a single login method. * **Secure your login method** — enable 2FA on the X or Google account you sign in with, and add a recovery email * **Verify addresses** before every withdrawal — blockchain transactions can't be reversed * **Watch for phishing** — only use app.kash.bot and official channels; Kash will never DM you asking for credentials * **Monitor your activity** — review your history regularly and contact [support@kash.bot](mailto:support@kash.bot) about anything suspicious 1. **Balance not updating?** Refresh the page; deposits need blockchain confirmation first 2. **Deposit not showing?** Verify you sent USDC on a supported network to the exact address shown in the deposit flow 3. **Withdrawal delayed?** Check its status in the app — cross-chain withdrawals take longer 4. **Still stuck?** Contact [support@kash.bot](mailto:support@kash.bot) or use the in-app feedback widget with the transaction details Blockchain transactions are final. Always double-check destination addresses and networks — Kash cannot recover funds sent to a wrong address or an unsupported network. Learn about wallet security Understand transaction process # Custom AMM Bonding Curve Source: https://docs.kash.bot/getting-started/features/custom-amm How the Kash Pythagorean AMM prices multi-outcome markets Kash markets run on a custom automated market maker (AMM) — a deterministic, weighted Pythagorean scoring rule built for multi-outcome prediction markets. Every market has tradeable liquidity from the moment it's created, with no external liquidity providers required. Hero Light Hero Dark # What the AMM Does Every Kash market holds a single USDC reserve that backs all of its outcomes. When you buy an outcome, your USDC (minus fees) goes into the reserve and the AMM mints outcome tokens to you along a fixed mathematical curve. When you sell, tokens are burned and USDC comes back out of the reserve. The curve is purely deterministic — the same trade against the same market state always produces the same result. There are no order books, no counterparties to match with, and no social-signal or engagement inputs to the pricing. It's just math against the reserve. # Why It's Built This Way Most prediction market AMMs are designed around binary YES/NO pairs. The Kash curve prices any number of outcomes in one market — Kash markets use 2 to 8 outcomes, and the contracts support up to 100. Each outcome has its own token supply and weight, and all outcomes share the same USDC reserve. The curve also supports **multi-winner** resolutions: if more than one outcome is declared a winner, all winning tokens share the reserve pro-rata. A new market doesn't wait for anyone to deposit liquidity. It's seeded at creation by the **market creation fee**, which becomes the initial reserve and token supplies. Kash's vault system can allocate additional USDC to markets to deepen liquidity (public deposits open with the upcoming [Earn vault](/getting-started/features/earn)), but trading works from the first second either way. The headline percentage you see on a market card is the **implied chance** of each outcome. It's computed from how the market's capital is concentrated across outcomes, it always sums to 100%, and it equals the break-even probability for buying that outcome at the margin. Implied chance is *not* a fixed token price. Kash outcome tokens don't "pay \$1" — winning tokens redeem pro-rata against the market's reserve. That's why trade previews show an **estimated payout if this outcome wins** instead of a per-share dollar figure. See [Claiming Your Winnings](/getting-started/guides/winning) for how redemption works. Like any convex AMM curve, larger trades move the price more. Buying an outcome pushes its implied chance up as you buy, so a big order gets a progressively worse average rate than a small one — this is ordinary price impact, not a penalty or an anti-whale mechanism. Every trade preview shows exactly what you'll receive before you confirm, and every trade carries an on-chain minimum-output guard, so the transaction reverts rather than executing if the market moves against you between quote and execution. # Seeding and Fees **Creation fee becomes liquidity** When a market is created, the creation fee is converted into the market's initial reserve and outcome supplies, with implied chances starting from the market's configured weights. There is no bootstrap period. Kash's vault system may additionally allocate USDC into markets as a market-making strategy — this deepens liquidity and reduces price impact for traders. Public vault deposits open with the upcoming [Earn vault](/getting-started/features/earn). **Buys** — a 1% protocol fee is taken from your USDC before it enters the curve. The rest goes into the market reserve. **Sells** — two fees apply: a 1% AMM sell fee that **stays in the market reserve** (benefiting remaining token holders), and a 1% protocol fee taken from your payout. **Redemption** — claiming winnings or refunds carries **no fee**. Neither fee ever distorts the curve itself: protocol fees are skimmed at the transfer layer and the invariant is unchanged. See [Market Mechanics](/getting-started/features/market-mechanics) for the full fee schedule. # For the Curious: The Math The AMM is a weighted Pythagorean scoring rule. For a market with reserve `R`, outcome supplies `s_i`, and static weights `a_i` (which sum to 1), define: ``` A = Σ a_i · s_i² (weighted sum of squared supplies) N = √A c = R / N (the curve invariant) ``` * **Buys and sells** move along this curve: the reserve and the supplies change together so that the trade is consistent with the invariant. The cost of pushing an outcome's supply up grows convexly — that's where price impact comes from. * **Implied chance** for outcome `i` is its capital share: `q_i = a_i · s_i² / A`. These always sum to 100% and represent how the market's risk is concentrated. * **Marginal prices** (the instantaneous token exchange rate, in USDC per token) are a different quantity from implied chance. They're shown in the app only as an advanced "AMM token rate" detail, because they are exchange rates, not probabilities. * **Redemption** is pro-rata: when a market resolves, each winning token redeems for `R × tokens / (total winning supply)`. Cancelled markets snapshot each outcome's marginal price and refund at that frozen rate. The full derivations, overflow bounds, and invariant tests are documented in the AMM contract repository for auditors and integrators. Because pricing is deterministic and fully on-chain, anyone can verify a quote independently — what you see in the trade preview is exactly what the contract will do. *** # Learn More Market lifecycle, resolution, fees, and claims Place your first prediction # Earn Source: https://docs.kash.bot/getting-started/features/earn Coming soon — how the Kash liquidity vault will work **Coming soon.** The Earn vault will let you put your USDC to work: deposit into the vault, receive shares, and your position grows as the vault earns fees from providing liquidity to Kash's prediction markets. It will live at [app.kash.bot/earn](https://app.kash.bot/earn). This page describes how the vault will work at launch — details may be refined before general availability. Hero Light Hero Dark # How the vault works The Earn vault is an **ERC-4626 vault** on Base. When you deposit USDC you receive vault shares, and the value of those shares tracks the vault's net asset value (NAV) — as the vault earns, each share is worth more USDC. Vault deposits are at risk. Your USDC is deployed into live prediction markets, and market-making can lose money as well as earn it — returns are not guaranteed, and the value of your shares can go down as well as up. Smart contract interactions also carry their own risks. Only deposit what you can afford to lose. The vault's USDC is deployed as liquidity across prediction markets on Kash. The vault acts as a **market maker** — providing the liquidity that lets traders buy and sell outcome tokens — and earns trading fees plus market-making returns from that activity. As markets resolve, results flow back to the vault and are reflected in the share price. Returns are **not fixed**: they depend on trading volume and how the vault's market positions perform. The more activity on the platform, the more fees the vault collects. Enter the amount of USDC you want to deposit and confirm. You receive vault shares at the current share value, and your position starts accruing from that point. * **No minimum** — deposit any amount of USDC * **No lock-up** — you can request a withdrawal at any time, with no early-exit penalty * Any entry fee is shown in the deposit quote before you confirm Withdrawals are processed in **batches**. You request a redemption of your shares, the request is queued for the next batch, and once the batch settles you claim your USDC. Your share of the vault is valued at the share price when the redemption is processed. If the vault is ever paused, new deposits are temporarily disabled — withdrawals and claims remain available. The protocol enforces on-chain **caps** on vault fees: | Fee | Cap | | --------------- | --------- | | Entry fee | up to 5% | | Exit fee | up to 5% | | Performance fee | up to 20% | These are maximums, not current rates — the live values are set on-chain and may be lower. The entry fee applied to your deposit is always displayed in the quote before you confirm. # Tracking your position Your Earn page shows your position and history in one place: * **Your stake** — shares held and their current USDC value * **Total earned** — how your position has performed against what you deposited * **Activity** — a history of your deposits and withdrawals, each linked to its on-chain transaction Like everything on Kash, the vault is **non-custodial**: funds are held in smart contracts, and interactions happen from your own smart account with sponsored gas. The vault provides liquidity to Kash markets. Its performance depends on market-making results, which can be negative — it is not a savings product. How the markets your deposit supports actually work Get USDC into your Kash account # Market Mechanics Source: https://docs.kash.bot/getting-started/features/market-mechanics How Kash markets are created, traded, resolved, and paid out Every Kash market lives on the Base blockchain, trades in USDC, and follows the same lifecycle: created → active → frozen → resolved or cancelled → claim. This page covers each stage, plus resolution, fees, and the guaranteed-exit mechanism. Hero Light Hero Dark # Market Structure Kash markets have **2 to 8 outcomes** (the contracts support up to 100). A binary market is simply the two-outcome case: Yes and No. Markets are also **multi-winner capable** — a resolution can declare more than one outcome a winner, in which case all winning tokens share the market's reserve pro-rata. Each market issues its outcomes as **ERC-1155 tokens** — one contract per market, with a token ID per outcome. Buying an outcome mints tokens to your wallet; selling burns them. Outcome tokens are **not** fixed "\$1 if it wins" shares. Winning tokens redeem **pro-rata against the market's USDC reserve** — that's why the app shows an *estimated payout if this outcome wins* on every trade preview rather than a per-share dollar promise. See the [Custom AMM](/getting-started/features/custom-amm) page for the pricing model. The headline percentage on each outcome is its **implied chance** — how the market's capital is concentrated across outcomes. Implied chances always sum to 100% and equal the break-even probability of buying that outcome at the margin. # Market Lifecycle Markets are created through the webapp's creation flow: you describe the market, set outcomes, duration, and resolution rules, then submit it for review. A **market creation fee** (currently 20 USDC) is charged when the market is approved and published — the fee seeds the market's initial liquidity, so trading is possible immediately. See [Creating Opportunities](/getting-started/guides/creating-opportunities). Trading is open 24/7. Prices move with every buy and sell along the AMM curve. Anyone can trade, discuss the market in its chat room, and share it. When the market's trading window ends, trading freezes. Positions are locked while resolution is pending — no new buys or sells are accepted. The oracle resolves the market by declaring the winning outcome (or outcomes), with the resolution recorded on-chain. If the underlying event is voided or the market can't be fairly resolved, the market is cancelled instead — cancellation snapshots each outcome's price at that moment for refunds. Once a market is resolved or cancelled, your payout becomes claimable directly from your [Portfolio](/getting-started/guides/winning) — **Claim winnings** on resolved markets, **Claim refund** on cancelled ones. Redemption always executes from your own smart account; Kash never takes custody of your winnings. # Resolution Kash markets are resolved by the **Kash oracle** — a multisig operated by the team, transitioning to a **Gnosis Safe M-of-N** setup. The oracle reviews the market's resolution rules and sources, then submits the winning outcome (or outcomes) on-chain, where the resolution and its evidence are permanently recorded. An **LLM-council resolution system** — where a panel of independent AI models evaluates evidence and proposes the outcome — is on the roadmap. It is planned, not live: today, resolution is a human-operated multisig process. Recovery doesn't depend on trusting the oracle. If a market passes its resolve time and the oracle does not act, **anyone** can cancel the market after a grace period expires — this is enforced by the contract itself. On cancellation, each outcome's price is snapshotted, and every holder can redeem their tokens at that frozen price. By construction, the sum of all refunds equals the market's reserve — recovery from a stuck market doesn't depend on Kash or the oracle, because the cancellation path is enforced by the contract itself (subject to normal smart-contract risk). * **Resolved markets:** winning tokens redeem pro-rata against the reserve — `payout = reserve × your tokens / total winning supply`. Losing tokens are worth zero. * **Multi-winner markets:** all winning outcomes share the reserve via their combined supply. * **Cancelled markets:** each outcome refunds at its snapshotted cancellation price. No new fees are charged on cancelled redemptions, but trading fees already paid are not refunded — so a refund is not necessarily your entry price. * **No redemption fee** in either case. # Fees | Action | Fee | | --------------------------- | ----------------------------------------------------------------------------------------------------------- | | Buy | 1% protocol fee (taken from your USDC before it enters the curve) | | Sell | 1% protocol fee + 1% AMM sell fee (the AMM fee stays in the market's reserve, benefiting remaining holders) | | Market creation | Creation fee (currently 20 USDC), charged on approval — seeds the market's liquidity | | Claiming winnings / refunds | No fee | Every trade preview shows the exact fee breakdown and net amounts before you confirm. # Infrastructure * **Base network:** all Kash contracts run on Base, Ethereum's low-cost L2. * **USDC:** every market trades and settles in USDC. * **ERC-4337 smart accounts:** every user gets a smart account with sponsored gas — you never need to hold ETH to trade. (On mainnet, self-service claims may net a small gas cost in USDC out of your winnings.) Every trade carries an on-chain minimum-output guard: if the market moves between your quote and execution beyond your tolerance, the transaction reverts instead of filling at a worse price. The same guards apply to redemption paths. Understanding the lifecycle — especially that payouts are pro-rata against the reserve and claimable from your portfolio — is the single most useful thing to know before trading. Read [Claiming Your Winnings](/getting-started/guides/winning) next. How pricing and liquidity actually work Redemption, refunds, and claims # Natural Language Source: https://docs.kash.bot/getting-started/features/natural-language Create and trade markets with plain English You don't need to learn a trading interface to use Kash. The market creation flow drafts a full market from a one-line prompt, tweet trading parses the text of your tweets, and the in-app Ask Kash assistant answers questions as you go. Hero Light Hero Dark # Create Markets from a Prompt The webapp's market creation flow is a conversation, not a form. You describe your prediction in plain English — *"Will BTC hit \$100k today?"* — and the assistant drafts the rest with sensible defaults. Type (or dictate — voice input is supported) what you want the market to be about. The assistant drafts the title, description, duration, resolution rules, category, and outcomes for you. Keep talking to change anything: *"Change the deadline to Friday"*, *"Change the outcomes to…"*, *"Update the resolution sources"*. The preview updates as you go. You can pick between binary (yes/no) and multi-option markets with 2–8 outcomes, attach reference links, live data feeds, and a cover image. When the summary looks right, say "create" or hit publish. Markets go through review before going live, and the creation fee (currently 20 USDC) is only charged once your market is approved and published. Market creation is rolling out gradually — it's currently available to approved accounts, with access expanding over time so that anyone will be able to submit a market. See [Creating Opportunities](/getting-started/guides/creating-opportunities) for the full flow. # Trade by Tweeting With the opt-in trading bot connected (**Settings → Trading bot** in the webapp), Kash parses the text of your tweets to place trades — reply to a market tweet with your side and amount, and the trade executes from your smart account. If you create a market, the bot can also broadcast it from your own X handle so your followers can trade it by replying. More detail in [Trading Options](/getting-started/features/trading-options). There are no conversational DM commands. Kash does not process direct messages for trading, balance checks, withdrawals, or market creation — natural-language interaction happens in the webapp creation flow and via public tweets with the opt-in bot. # Ask Kash: The In-App Assistant The webapp includes **Ask Kash**, an assistant for getting help — not for trading. Use it for: * **How-to questions** — how markets, fees, claims, and features work * **Tutorials** — replay the onboarding walkthrough at any time * **Feedback** — report bugs or send ideas to the team * **Settings help** — like switching your language * **Market ideas** — brainstorm what to create or trade The pattern across all three surfaces is the same: you say what you mean, and Kash handles the structured details — market parameters, trade execution, or the right help article — behind the scenes. From prompt to published market All the ways to trade # Parlays Source: https://docs.kash.bot/getting-started/features/parlays Build a slip of crypto up/down picks and place it in one go The parlay builder at [app.kash.bot/parlay](https://app.kash.bot/parlay) lets you back **up or down** on the day's top coins — BTC, ETH, SOL, and more — stack multiple picks into one slip, and place them all together. Hero Light Hero Dark # Building a slip Tap **Up** or **Down** on any coin to add it to your slip. Tap the same side again to remove it, or the other side to switch. Each coin shows the market's current chance of finishing up or down. Choose your **amount per pick**. The slip shows your total amount and the **potential payout if all predictions are correct**, based on each pick's current market prices — the less likely an outcome, the more it pays if correct. Hit **Place parlay**, review your picks, and confirm. You need to be signed in with USDC in your account. You can also copy a link to your slip and share it — anyone who opens it gets your picks pre-loaded. # How picks resolve Each coin's market resolves at **00:00 UTC**, based on whether its price closed higher or lower than it opened that day. Winning picks pay out at the market odds you traded at. **Today, each pick executes as an independent trade** — one leg can win even if another loses, and each pays out on its own. That also means a slip can partially fill: if one leg fails to place, the others still go through and you can retry the rest. True single-ticket parlays, where all picks must hit for a combined payout, are **coming soon**. Because legs are independent, you can even **hedge** — back both up and down on the same coin — though only one side of a coin can win. # Under the hood Parlay picks are ordinary positions in daily crypto prediction markets, placed from your smart account with sponsored gas. After placing a slip, track every pick in your [Portfolio](/getting-started/guides/winning) like any other position. The slip's multiplier assumes every pick hits — the honest way to read it is as a best case, not an expectation. Since legs are independent today, your realistic outcome is a mix of winning and losing picks. All the ways to trade on Kash Track positions and claim your winnings # Social Interface Source: https://docs.kash.bot/getting-started/features/social Chat, follow, compete, and share — prediction markets are better together Every Kash market is a conversation as much as a trade. The app has real-time chat on every market and thread, feeds from people you follow, groups, leaderboards, achievements, and one-tap sharing to X. Hero Light Hero Dark # Talk About It: Chat Rooms Every market and every [thread](/getting-started/features/threads) has its own real-time chat room, right next to the price chart. * **Live discussion** — messages appear instantly; see who's online and who's typing * **Reactions** — react to messages with emoji * **Always in context** — the chat sits beside the market on desktop and opens as a drawer on mobile, so the debate and the odds stay on one screen # Follow, Feeds, and Groups Follow other traders to keep up with what they're predicting. Your feed surfaces activity from people you follow, and the **For You** tab personalizes market discovery based on your interests and activity. Join groups to trade and talk with your people — a shared space for a community's markets and conversation. Every user has a public profile with their prediction stats and realized P\&L. Your track record is your reputation — built on results, not claims. # Compete: Leaderboards, Competitions, and Achievements The [Community](https://app.kash.bot/community) page ranks traders by realized P\&L, volume, win rate, and streak — see where you stand, browse top players, and jump to anyone's profile. During competition periods, leaderboards switch to **competition mode**: rankings by competition points with dedicated Winners and Losers boards. Big events get their own hubs — like the [World Cup 2026 hub](/getting-started/features/world-cup) with schedules, knockout brackets, and outright markets. Earn achievements for milestones across trading, accuracy, and participation. They live on your profile as a permanent record of what you've done on the platform. # Share It: X Integration and Embeds Every market and thread has a share button: copy a link, post to X, or quote the tweet a market is based on. Kash generates the prediction tweet for you — one tap from a position to a post. With the opt-in trading bot (**Settings → Trading bot**), you can trade by replying to market tweets, and broadcast markets you create from your own X handle so your followers can trade them. See [Trading Options](/getting-started/features/trading-options). Markets and threads can be embedded as iframes on any website — a live market card or a full thread heatmap, with real odds and trade buttons. Publishers and communities use embeds to bring prediction markets to their own audience. See [For Businesses](/for-businesses/overview). The social loop is simple: find a market in your feed, argue about it in the chat, put your prediction on the line, and share the result. Your wins (and your reasoning) build a track record everyone can see. Curated collections of related markets Place your first prediction # Threads Source: https://docs.kash.bot/getting-started/features/threads Curated collections of related prediction markets A thread is a curated collection of related markets around one story or event — an election night, a tournament, a product launch. Instead of hunting for individual markets, you get the whole picture in one place, with a chat room attached. Hero Light Hero Dark # What's in a thread Every thread has its own page with everything about the event: The core of a thread is its list of markets, each showing its current implied chance, predictions, and volume. The thread summary rolls up total volume, the number of markets, and time remaining. Larger threads can be organized into **chapters** — groups of related markets within the thread. You can search within a thread to find a specific market. Active threads have a **Multi-Market Predictions** mode: a tile board of the thread's markets where you tap tiles to build a batch of Yes/No predictions across the whole thread at once. Each prediction is \$5, and you can stack multiple predictions on a tile, and you can mix Yes and No picks across different markets. Tile sizes reflect each market's relative volume within the thread. Each thread has its own chat room, so discussion happens next to the markets it's about. Kash also has chat rooms on individual markets — the thread room is where the event-wide conversation lives. You can **save** a thread to your bookmarks to find it again quickly, and share it with a link. Upcoming threads are listed before their markets go live, so you can see what's coming. # Threads for partners Threads are also Kash's unit of syndication: partners can embed a full thread — markets, prices, and branding — on their own site via an iframe. If you run a publication or community and want prediction markets around your coverage, see the developer docs for thread embeds. Threads are the best way to follow a live event on Kash. During something like an election night or a cup final, one thread page gives you every relevant market, live prices, and the crowd's conversation in a single view. Discover markets and threads worth trading Place your first prediction # Trading Options Source: https://docs.kash.bot/getting-started/features/trading-options The ways you can trade on Kash Kash trading is straightforward: buy and sell outcome tokens against the AMM in the webapp, or — if you opt in — trade by tweeting. Everything settles in USDC on Base, gas-free. Hero Light Hero Dark # Trading in the Webapp The primary way to trade is at [app.kash.bot](https://app.kash.bot): open any market, pick an outcome, enter an amount, and confirm. Tap an outcome (e.g. **Buy Yes**) and enter your USDC amount. Before you confirm, the quote shows exactly what you're getting: * **You pay** — your USDC in, with the 1% protocol fee broken out * **You receive** — the number of outcome tokens minted to you * **Estimated payout if this outcome wins** — what those tokens would redeem for against the market reserve, based on the post-trade state * The market's **implied chance** after your trade Larger trades move the price more (ordinary AMM price impact), and every trade has on-chain slippage protection — it reverts rather than filling worse than your quoted minimum. You can exit any position before the market freezes. The sell quote shows the gross AMM release, the 1% AMM sell fee (which stays in the market's reserve), the 1% protocol fee, and the **net USDC you receive**. Use it to lock in profits or cut losses without waiting for resolution. Positions are real on-chain assets: each market issues its outcomes as **ERC-1155 tokens** (one contract, a token ID per outcome), held by your own smart account. They are not fixed "\$1 if it wins" shares — winning tokens redeem pro-rata against the market's reserve, which is what the estimated-payout figure previews. Details in [Market Mechanics](/getting-started/features/market-mechanics). # Tweet Trading (Opt-In) Kash's companion X (Twitter) integration lets trading happen where the conversation is. Tweet-based trades are parsed from the text of your tweet — for example replying to a market posted by **Kash's dedicated trading account** with your side and amount. The trading account's handle cycles over time; the current one is always linked from the [@kash\_bot](https://x.com/kash_bot) profile. Kash reads the intent, executes the trade from your smart account, and confirms. Tweet trading is **opt-in**: connect and authorize your X account in **Settings → Trading bot** in the webapp. You can disconnect at any time, which revokes Kash's access to your account — your existing positions stay tradeable in the app. If you create a market, the trading bot can broadcast it **from your own X handle**, and your followers can reply to trade on it. The bot is torn down automatically when your market ends. See [Creating Opportunities](/getting-started/guides/creating-opportunities). There are no X direct-message (DM) trading commands — trading via X works through public tweets with the opt-in trading bot only. # Parlays The [parlay builder](/getting-started/features/parlays) lets you back several crypto markets at once — pick **up or down** on each coin (BTC, ETH, SOL, and more), set your stake, and place the whole slip together. Today, each leg of a parlay executes as an **independent trade** — one pick can win even if another loses. True single-ticket parlays, where all picks must hit for a combined payout, are coming soon. # Portfolio Management Your [Portfolio](/getting-started/guides/winning) shows all open positions with live values and unrealized P\&L, plus a history of resolved and closed positions with realized P\&L. From any open position you can add to it or sell out of it. When a market resolves (or is cancelled), your payout becomes claimable directly from your portfolio — **Claim winnings** or **Claim refund**. Redemption executes from your own smart account; there is no redemption fee. On mainnet, claims may net a small gas cost in USDC out of the payout. # What Powers It * **Base network** — low-cost L2 execution * **USDC** — the single trading and settlement currency * **ERC-4337 smart accounts** — sponsored gas, so you never need ETH; session-key delegation means no wallet popup for every trade * **Deterministic AMM** — every quote is verifiable on-chain; see the [Custom AMM](/getting-started/features/custom-amm) page Kash intentionally keeps the trading surface simple: market buys and sells against the AMM. There are no limit orders, leverage, or margin products — your maximum loss is always what you put in. Multi-leg crypto up/down slips Create and trade with plain English # World Cup 2026 Source: https://docs.kash.bot/getting-started/features/world-cup Trade every match, team, and moment of the tournament The World Cup 2026 hub at [app.kash.bot/worldcup](https://app.kash.bot/worldcup) brings the whole tournament into one place: the full match schedule, group tables, the knockout bracket, outright winner markets, team pages, news, and live analytics. Hero Light Hero Dark # Inside the hub The full fixture list, filterable by date, with live matches flagged as they happen. Every fixture has its own match page, and fixtures with a live Kash market link straight to it. Where no market is live yet, indicative projections from Kash's model are shown — clearly labeled as model projections, not market prices. Group tables track standings (played, won, drawn, lost, goal difference, points) alongside each team's chance of winning its group. The knockout view shows the bracket — round of 32 through the final — with slots projected from current group standings until the real draw is confirmed. Season-long markets that run across the whole tournament: **tournament winner**, **group winners**, and **player awards** such as top scorer and best player. Team pages collect a team's info, markets, and coverage in one place. The news section aggregates World Cup coverage with social sentiment, and highlights which teams and players are generating the most buzz. Live market intelligence across the tournament: total volume, market counts, unique traders, and which markets are moving right now. The hub also compares Kash prices against other prediction platforms on the same fixtures, and pairs market odds with social sentiment data — a signal, not a prediction. # Leaderboard and prizes The hub runs a tournament leaderboard during the World Cup: rankings are based on points earned from realized P\&L on World Cup markets within the tournament window. The hub's **rules page** has the current prize tiers, the exact scoring formula, and eligibility terms (18+, void where prohibited). There is also a free-play option — trade World Cup markets with play-money balance on Kash testnet and still climb the leaderboard. # Suggest a market See a question the community should be trading — a stat line, a milestone, a storyline? Use **Suggest a market** in the hub. Suggestions go to the moderation queue, and approved markets go live for everyone with credit to you on the hub. During match days the hub is the fastest way to trade the tournament: live fixtures, moving markets, and community sentiment are all one tap from a trade. How to place a prediction on any market Follow curated collections of related markets # Creating an Account Source: https://docs.kash.bot/getting-started/guides/creating-an-account Sign in with X, Google, email, or a wallet — your Kash account and smart wallet are created automatically Creating a Kash account takes seconds. Pick a sign-in method, and your wallet is set up for you — no seed phrase to write down. Kash is for users 18+ in supported regions. Hero Light Hero Dark ## Sign-in options Kash uses [Privy](https://privy.io) for authentication. Go to [app.kash.bot](https://app.kash.bot), click **Sign in**, and choose: | Method | How it works | | ------------------- | ---------------------------------------------------------------------- | | **X (Twitter)** | One-click OAuth — also required if you want to use tweet-trading later | | **Google** | One-click OAuth with your Google account | | **Email** | Enter your email and confirm a one-time code | | **External wallet** | Connect an existing wallet you already control | Any of the options above works — you can start with whichever is most convenient. Privy creates an **embedded wallet** for you, and Kash deploys an **ERC-4337 smart account** on Base that holds your funds. This is where your USDC and positions live — under your control, not Kash's. During onboarding you'll be prompted to set a **recovery email**. If you signed up with X and ever lose access to your X account, this email is how you recover your Kash account — set it, and use an address you'll keep. You're ready to browse and trade. Optionally [deposit funds](/getting-started/guides/depositing) right away, or skip it and explore first. *** ## How your wallet works * Your funds sit in **your own smart account** on Base — Kash never takes custody * Trades, payouts, and withdrawals execute from your wallet, verifiable on-chain * You can **export your wallet key** at any time from **Settings**, so you're never locked in * No seed phrase to write down or lose * Access is tied to your sign-in method (X, Google, email, or wallet) * Your **recovery email** is the fallback if you lose access to your primary login * The protocol sponsors gas via ERC-4337 account abstraction * You never need to hold ETH or approve gas prompts * Everything is priced and settled in USDC ## Keep your account safe * **Enable 2FA** on the account you sign in with (X, Google, or email) * **Set your recovery email** during onboarding — don't skip it * **Never share** login codes or your exported key with anyone * If you connect an external wallet, standard wallet-security practice applies Creating an account is free, and you can browse all markets before depositing anything. Fund your wallet to start predicting Discover markets to trade on # Creating Opportunities Source: https://docs.kash.bot/getting-started/guides/creating-opportunities How to create your own prediction market on Kash Market creation is rolling out gradually — it's currently available to approved accounts, with access expanding so that anyone will be able to submit a market. You submit it through the create form, it goes through review, and the creation fee is charged once your market is approved and goes live. Hero Light Hero Dark ## How it works Click **Create Market** in the sidebar at [app.kash.bot](https://app.kash.bot). * **Title** — the question the market answers * **Outcomes** — 2 to 8 possible answers * **Resolve time** — when the outcome will be known * **Rules and sources** — what counts as each outcome, and where the result will be verified Your submission goes to Kash for review — checking that the question is clear, the outcome is verifiable from the stated sources, and the content is allowed. Once approved, a one-time creation fee is charged from your balance. It seeds the market's initial liquidity and keeps submissions serious. The market is deployed on-chain and appears on app.kash.bot for everyone to trade. Suggestions can also be surfaced to organizations and partners on Kash, who curate ideas from their communities and elevate the strongest ones into live markets. ## What makes a good market * **A precise question** — "Will BTC close above \$150,000 on March 1?" beats "Will Bitcoin moon?" * **Mutually exclusive outcomes** — exactly one should be able to win * **A verifiable result** — resolvable from public, reliable sources * **A sensible resolve time** — soon enough to stay interesting, late enough that the answer is knowable Markets on public events with objective outcomes: sports results, elections, economic data, product launches, awards, crypto milestones. Timely questions tied to things people are already talking about attract the most volume. * Predictions involving death, harm, or violence * Private individuals or personal information * Outcomes the creator or traders could manipulate * Illegal content or markets involving minors * Ambiguous questions that can't be objectively resolved ## Creating markets for a community Organizations and partners can create and curate markets for their own audiences — with branded pages, embeds, and revenue share on trades in markets they create. If you run a community, see [Market creation for organizations](/for-businesses/market-creation). The best market creators write questions people already argue about, with resolution rules nobody can argue about. Discover markets to trade on Learn how to place predictions # Depositing Source: https://docs.kash.bot/getting-started/guides/depositing How to fund your wallet and start predicting Fund your Kash balance with a card, Apple Pay, Google Pay, or a direct USDC transfer. Deposits typically arrive within minutes. Hero Light Hero Dark # How deposits work Everything on Kash runs on **USDC** (a US dollar stablecoin) on the **Base** network. When you deposit, USDC is delivered directly to your own Kash smart wallet — Kash never takes custody of your funds. * **USDC only** — Kash does not accept or auto-convert other tokens (no ETH, WETH, or DAI) * **Delivered to Base** — fiat purchases arrive on Base directly; crypto sent on other networks is automatically bridged to Base for you * **No deposit fee from Kash** — payment providers charge their own processing fees (listed below) # Deposit methods **Buy USDC directly with a card or mobile wallet** On [app.kash.bot](https://app.kash.bot), open your wallet and choose **Deposit** (or **Add funds**). Select how much USDC you want and pay with card, Apple Pay, or Google Pay. Fees and the amount you'll receive are shown before you confirm. USDC is sent straight to your Kash wallet, usually within a few minutes. **Payment provider** All fiat processing is handled by third-party payment providers — Kash never sees or stores your card or bank details. | Provider | Methods | Fee | KYC | | ------------ | --------------------------- | ------------- | --------------------- | | **Halliday** | Card, Apple Pay, Google Pay | 2.9% + \$0.30 | Basic (government ID) | Additional payment options may appear in the deposit flow. Provider fees are current as of publication — the app always shows live fees and limits before you confirm. **Send USDC from an exchange or external wallet** In the deposit flow on app.kash.bot, choose **Crypto** and select the network you're sending from. Kash shows you the exact address (and QR code) to send to. Transfer USDC from any wallet or exchange. Supported networks: * **Base** (fastest — arrives in minutes) * **Ethereum** * **Polygon** * **Arbitrum** * **Optimism** * **Avalanche** * **BNB Chain** * **Linea**, **Scroll**, **Mantle**, **Mode**, and **Blast** The full list is always shown in the deposit flow. USDC sent on any network other than Base is automatically bridged to Base and credited to your Kash wallet. Cross-chain deposits typically take around 10–15 minutes; Base deposits arrive in a couple of minutes. * **Limits:** $1 – $100,000 per transaction (limits subject to change) * **Fees:** Kash charges nothing — you only pay the sending network's gas (and bridge costs on cross-chain transfers) * Crypto deposits don't currently require identity verification; Kash applies compliance screening, and verification requirements may change Send **USDC only**, on one of the supported networks, to the exact address shown in the deposit flow. Other tokens are not supported and may be unrecoverable. *** # After your deposit * **Balance updates automatically** once the deposit is confirmed * **Ready to predict immediately** — no waiting periods * **Transaction history** is available in your portfolio * **Withdraw anytime** — no lock-ups; see the withdrawing guide for limits Start with a small first deposit to get comfortable with the flow, and save your deposit address for future crypto transfers. Learn how to place predictions How to withdraw your winnings # Finding Opportunities Source: https://docs.kash.bot/getting-started/guides/finding-opportunities How to discover markets worth trading on app.kash.bot The best trades come from markets where you know something the price doesn't reflect yet. Here's how to find them. Hero Light Hero Dark ## The home feed The home page at [app.kash.bot](https://app.kash.bot) has three main tabs: The most active markets right now, plus featured threads. Good default — high volume means tight prices and plenty of counterparties. Personalized picks based on your activity (signed-in users only). The more you trade, the better it gets. Markets that repeat on a schedule — like daily and weekly crypto price predictions. Same question, fresh odds every cycle. Each market card shows the question, current odds, time remaining, and volume — enough to decide whether it's worth a closer look. ## Narrow it down * **Categories** — filter by Crypto, Politics, Sports, Entertainment, Technology, and more * **Search** — find markets by keyword * **Sort** — by volume, newest, or ending soon * **Bookmarks** — hit the bookmark icon on any market and find it later in your **Saved** tab ## Threads: related markets, bundled [Threads](/getting-started/features/threads) group related markets into one collection — every match of a tournament, every question about one event. If you know a topic well, a thread lets you trade your whole view of it, not just one question. ## The World Cup hub For World Cup 2026, Kash has a dedicated [hub](/getting-started/features/world-cup) with the full schedule, knockout bracket, outright winner markets, team pages, and news — everything in one place. *** ## Finding your edge Markets where you have real knowledge — your industry, your sport, your local scene — are where you're most likely to spot mispriced odds. Depth beats breadth. New markets and pre-event windows often have prices that haven't fully caught up with available information. Prices firm up as resolution approaches. Each market page shows a price history chart, recent trades, a per-market leaderboard, and a live chat. Odds that just moved sharply — or haven't moved despite news — are both worth investigating. Start with one or two categories you genuinely know, bookmark the markets you're watching, and check the Recurring tab for repeatable setups. Place your first trade Track positions and claim winnings # Predicting Source: https://docs.kash.bot/getting-started/guides/predicting How to make predictions Turn your insights into returns — pick a market on app.kash.bot, choose Yes or No, and confirm. That's a prediction. Hero Light Hero Dark Making predictions walkthrough # Understanding market odds ## How prediction markets work Every market card shows an **implied chance** — the market's current estimate of how likely each outcome is, based on real money traded: * **Yes at 72%** means the market currently implies a 72% chance the event happens * The implied chance moves as people trade and new information emerges * Buying an outcome pushes its implied chance up; selling pushes it down * Markets can have more than two outcomes — the same logic applies to each one * When you set up a trade, the ticket shows an **estimated payout if you win** * The lower the implied chance when you buy, the higher your estimated payout if you're right * The estimate can shift as others trade — the final payout is your pro-rata share of the market's reserve at resolution * If your outcome loses, your position is worth nothing at resolution * **Markets stay liquid** — you can exit positions at any time at the current market price * **Early predictions** often get better prices before consensus forms * **Breaking news** can rapidly shift the implied chance * **Large trades** move prices more than small ones * **Arbitrage** keeps prices roughly in line with real-world probabilities # How to make predictions **The primary way to trade — app.kash.bot** Browse trending markets, categories, or search on [app.kash.bot](https://app.kash.bot). Each card shows the question and the current implied chance. Tap **Yes** or **No** (or the specific outcome in multi-outcome markets). The trade ticket opens. Enter how much USDC to spend. The ticket shows your shares and the **estimated payout if you win** before you confirm. Confirm the trade — it executes gaslessly from your Kash wallet, and your position appears in your Portfolio immediately. **Optional: trade by replying on X (Twitter)** Kash can turn your X account into a trading interface. This is **opt-in** — enable it under **Settings → Trading bot** on app.kash.bot first. Trades go through **Kash's dedicated trading account** — find the current handle on the [@kash\_bot](https://x.com/kash_bot) profile. ## Simple format Reply to or repost any Kash market post with: * **"YES 50"** — put \$50 on the event happening * **"NO 100"** — put \$100 on it not happening * **"YES \$25"** — dollar sign optional, case doesn't matter ## What happens next 1. Kash confirms your prediction intent 2. Checks your wallet balance 3. Executes the trade at the current market price 4. Replies with your position details **Example interaction:** ``` You: "YES 100" Kash trading bot: "Bought Yes at 72% implied chance Total cost: $100 Estimated payout if Yes wins: ~$137" ``` **How outcomes are determined** ## Market close * Trading stops at the market's deadline * Existing positions are locked in * The market awaits resolution ## Resolution and claiming * The market's oracle sets the winning outcome on-chain * Winning positions become **claimable in your Portfolio** * If a market can't be resolved, it can be cancelled and positions become refundable See the [winning guide](/getting-started/guides/winning) for exactly how payouts, claims, and refunds work. *** # Prediction strategies * **Research before predicting** — read news, analyze trends, understand context * **Follow reliable sources** — trust verified accounts and expert analysis * **Spot market inefficiencies** — find implied chances that don't match reality * **Act on breaking news** — be first to capitalize on new information * **Diversify across markets** — don't put all eggs in one basket * **Don't trade your entire balance** — keep reserves for better opportunities * **Take profits regularly** — lock in gains instead of letting them ride * **Size predictions to your confidence level** * **Watch successful predictors** — follow proven track records on the leaderboards * **Monitor sentiment shifts** — spot when opinion is changing before the price does * **Learn from patterns** — recognize recurring market behaviors # Managing your positions * Your **Portfolio** shows every open position, its current value, and live profit and loss * Watch how the implied chance moves after your entry * Plan your exit — sell early or hold to resolution * **Lock in profits early** — secure gains before the market closes * **Cut losses** — minimize damage when you're clearly wrong * Sell from the market page or your Portfolio at the current market price * Sells pay a 1% protocol fee plus a 1% AMM fee * Buy the same outcome again anytime to increase your stake * Adding at a different price averages your effective entry * No limit on how many trades you can place # Pro tips * **Early markets** often have better prices before consensus forms * **Breaking news** creates temporary price inefficiencies * **Start small** while learning market dynamics — fees are 1% on buys, 1% + 1% on sells * **Join the market chat** — every market has a discussion where traders share their reasoning Every expert predictor started with their first prediction. Start small, learn the dynamics, and let your edge compound over time! Discover markets to predict on Understand winnings and payouts # Winning Source: https://docs.kash.bot/getting-started/guides/winning How claims, refunds, and payouts work When a market resolves in your favor, your winnings become claimable — head to your Portfolio and hit **Claim** to receive your USDC. Hero Light Hero Dark # Claiming your winnings ## When a market resolves 1. **The oracle resolves the market** — the winning outcome (or outcomes) is set on-chain 2. **Your winning position becomes claimable** — you'll see it flagged in your Portfolio 3. **You claim** — press **Claim** on the position, and your winning shares are redeemed for USDC 4. **USDC lands in your wallet** — redemption executes from your own smart wallet; Kash never holds your winnings On mainnet, a claim may deduct a small gas amount in USDC from your winnings — you receive the net USDC. There is no Kash fee on redemption. ## When a market is cancelled If a market can't be resolved (for example, the event was called off), it can be cancelled instead. Your position becomes refundable, and you claim it the same way — press **Claim refund** in your Portfolio. * **Refund value:** each outcome is refunded at its market price **snapshotted at the moment of cancellation** — not your entry price. If you bought at $0.30 and the price at cancellation was $0.45, you're refunded at \$0.45 per share (and vice versa). * **Trading fees you already paid are not refunded**, but no new fees are charged on the refund itself. **Contract-enforced exit:** if a market's oracle ever goes unresponsive, *anyone* can cancel the market once the resolution deadline plus a grace period has passed. Recovery from a stuck market doesn't depend on Kash or the oracle — the cancellation path is enforced by the contract itself (subject to normal smart-contract risk). # Understanding payouts ## How payouts are calculated Winning shares don't pay a fixed amount. Instead, winning shares redeem for a **pro-rata share of the market's reserve** — the pool of USDC that traders paid in: * **Your payout = your winning shares' proportion of all winning shares × the market reserve** * The trade ticket shows an **estimated payout if you win** at the moment you trade — this is your best guide to what a position is worth * The estimate can shift as others trade, because the final reserve and share supply are only fixed at resolution * In markets where **multiple outcomes win**, all winning outcomes share the reserve ## Fees * **Buys:** 1% protocol fee * **Sells:** 1% protocol fee + 1% AMM fee * **Redemption:** no fee — you claim your full pro-rata payout (minus a small USDC gas deduction on mainnet claims) * **Gas:** sponsored by Kash for trading — no ETH needed * Fees are charged **at the time of the trade** — nothing extra is taken from winning payouts * Holding positions costs nothing * Kash charges no deposit fee; payment providers charge their own processing fees ## Payout example ``` You spend $100 on Yes when the implied chance is 40%. The trade ticket shows: "Estimated payout if you win: ~$240" The market resolves Yes. Your position becomes claimable in Portfolio. You claim and receive your pro-rata share of the market reserve — close to the estimate, adjusted for trading that happened after your entry. ``` The "estimated payout" on the ticket is exactly that — an estimate based on the market state when you trade, not a guaranteed fixed amount per share. # Ways a position can end Your outcome is among the winners at resolution. Your shares redeem for a pro-rata share of the market reserve — claim from your Portfolio. You sold before resolution. You locked in your profit (or cut your loss) at the market price and received USDC immediately — no claim needed. Sells pay the 1% protocol + 1% AMM fee. Your outcome didn't win. Losing shares are worth nothing at resolution — this is why position sizing matters. The market was cancelled. Claim your refund from Portfolio — each outcome refunds at its cancellation-snapshot price, with no new fees. # Managing positions and risk * Never risk more than you can afford to lose completely * Spread across markets instead of going all-in on one * Scale up gradually as your track record grows * You don't have to wait for resolution — selling early locks in gains and removes resolution risk * Cut losses when you're clearly wrong instead of hoping * Remember sells carry the extra 1% AMM fee; factor it into exit decisions * Specialize in categories you genuinely understand * Early positions, before consensus forms, often offer the best prices * Review both wins and losses — your Portfolio history shows exactly how each position played out # Track your performance Kash has built-in performance tracking on the webapp: * **Portfolio** — open positions, claimable winnings, realized profit and loss, and full history * **Leaderboards** — see how you rank against other predictors by profit and volume on the community page * **Achievements** — unlock badges for prediction milestones Winning feels great, but consistent winning comes from discipline, research, and bankroll management. Claim your winnings, review what worked, and find the next opportunity. Learn how to place predictions The Earn vault will let you put idle USDC to work # Withdrawing Source: https://docs.kash.bot/getting-started/guides/withdrawing How to withdraw your winnings and funds Your funds, your control — withdraw your USDC to any external wallet anytime. Withdrawals are processed automatically and typically complete within minutes. Hero Light Hero Dark # Withdrawal overview Withdrawals are always in **USDC**, sent from your Kash smart wallet to any external address you choose. You can withdraw to Base directly, or have Kash bridge your USDC to another supported network. **Supported destination networks:** * **Base** (fastest — no bridging needed) * **Ethereum** * **Arbitrum** * **Optimism** * **Polygon** Withdrawals to networks other than Base are bridged automatically — you just enter the destination address and pick the network. Cross-chain withdrawals take a little longer and may include bridging costs, which are shown in the quote before you confirm. Kash also supports cashing out to fiat through Kash's third-party payment providers — look for the cash-out option in the withdrawal flow. Fees and limits are always shown before you confirm. ## Withdrawal limits | Limit | Value | | ----------------------- | -------- | | Minimum per transaction | \$1 | | Maximum per transaction | \$25,000 | | Daily maximum | \$10,000 | | Weekly maximum | \$50,000 | | Withdrawals per hour | 5 | Limits are current as of publication — the app always shows live limits before you confirm. ## How processing works Withdrawals are processed **asynchronously**: your request is validated instantly, then queued and executed on-chain automatically — no manual approval step. 1. **Pending** — request validated and queued 2. **Processing** — transaction being executed on-chain 3. **Confirming** — waiting for blockchain confirmation 4. **Completed** — funds arrived at your destination address Most withdrawals typically complete within minutes. Cross-chain withdrawals can take roughly 10–15 minutes depending on the destination network. # How to withdraw Sign in at [app.kash.bot](https://app.kash.bot), open your wallet, and choose **Withdraw**. * Enter the amount (or select your full available balance) * Paste the destination address * Choose the destination network — make sure the address you're sending to supports that network Double-check the address, network, and amount. For cross-chain withdrawals, review the bridging quote before confirming. The app shows live progress until the withdrawal completes, including the transaction hash so you can verify it on a block explorer. ## Withdrawing to an exchange You can withdraw directly to a centralized exchange (Binance, Kraken, OKX, and others): 1. In your exchange account, open **USDC deposits** and select a network that both the exchange and Kash support (Base is usually fastest) 2. Copy the exchange's deposit address 3. Withdraw from Kash to that address, on that exact network 4. Exchanges may take a few extra minutes to credit the deposit after it confirms on-chain Always verify the destination address and network before confirming. Blockchain transactions are final — funds sent to a wrong address or unsupported network cannot be recovered. ## Security best practices * **Double-check destination addresses** — there is no undo * **Match the network** — the destination must support the network you selected * **Start with a small test amount** when withdrawing to a new address * **Keep transaction records** for tax and tracking purposes # Troubleshooting If your withdrawal doesn't arrive: 1. **Check the status** in the app — it updates as the withdrawal progresses 2. **Look up the transaction hash** on a block explorer for the destination network 3. **Allow extra time** for cross-chain withdrawals and exchange crediting 4. **Contact support** at [support@kash.bot](mailto:support@kash.bot) with the withdrawal details if something looks stuck Your funds are always yours. Kash provides the prediction platform, but you keep full control over deposits and withdrawals. Understand winnings and payouts Fund your wallet to start predicting # How do I use Kash? Source: https://docs.kash.bot/getting-started/quick-start/how-kash The core loop: sign in, deposit, trade, track, claim Hero Light Hero Dark ## The core loop Go to [app.kash.bot](https://app.kash.bot) and sign in with **X (Twitter), Google, email, or an external wallet** (via Privy). An embedded wallet and ERC-4337 smart account on Base are created for you automatically — no seed phrase needed. Details in [Creating an Account](/getting-started/guides/creating-an-account). Fund your account with a card, Apple Pay, Google Pay, or a direct crypto transfer. Everything on Kash trades in USDC on Base. See [Depositing](/getting-started/guides/depositing). Browse **Trending**, **For You**, and **Recurring** tabs, filter by category, search by keyword, or explore [threads](/getting-started/features/threads) and the [World Cup hub](/getting-started/features/world-cup). More in [Finding Opportunities](/getting-started/guides/finding-opportunities). On a market page, pick the outcome you believe in, enter an amount, review the quote (price, estimated payout, 1% fee), and confirm. The trade executes gas-free from your smart account. See [Predicting](/getting-started/guides/predicting). Your [Portfolio](/getting-started/guides/winning) shows open positions with live P\&L, your trade history, and your balance. You can sell out of a position any time while the market is open (1% protocol + 1% AMM fee on sells). When a market resolves, winning positions become claimable — hit **Claim Winnings** in your Portfolio. If a market is cancelled instead, a **Claim Refund** option appears. Details in [Winning](/getting-started/guides/winning). *** ## Optional: trade from tweets If you enable the trading bot in **Settings → Trading bot**, you can also place trades on X by tweeting at **Kash's dedicated trading account** with a market link and your position (for example, an amount and the outcome you're backing) — find the current handle on the [@kash\_bot](https://x.com/kash_bot) profile. The bot executes the trade from your Kash account and replies with a confirmation. Tweet-trading is opt-in and covers placing trades only — deposits, withdrawals, claims, and everything else happen in the webapp. *** ## Step-by-step guides # What is Kash? Source: https://docs.kash.bot/getting-started/quick-start/what-kash A prediction marketplace on Base — trade real-world outcomes in USDC, gas-free Hero Light Hero Dark Kash is a prediction marketplace at [app.kash.bot](https://app.kash.bot). You buy shares in the outcome of a real-world event — a match result, a price target, an election — and if you're right, you claim your share of the market's pool when it resolves. Everything settles in **USDC on Base** (an Ethereum Layer-2), from a smart account that you control. Kash sponsors gas, so trading feels like a normal app. *** ## The basics Kash markets aren't limited to Yes/No. A market can have **2 to 8 outcomes** — "Who wins the World Cup final?" can list every plausible answer, each with its own live price. Prices reflect the market's implied probability for each outcome. When you sign in, Kash creates an embedded wallet and an **ERC-4337 smart account** for you. Your funds sit in your own account — Kash never takes custody — and the protocol sponsors gas, so you never need ETH or a browser extension. Every market trades against an automated market maker (AMM), seeded at creation. You can buy or sell at any time while the market is open — no order book, no waiting for a counterparty. Markets are resolved by Kash's oracle (a multisig). If a market ever can't be resolved, it can be cancelled and every position is refunded at its last market price — a recovery path enforced by the contract itself. See [Market Mechanics](/getting-started/features/market-mechanics) for details. *** ## How markets get created Markets are proposed from the **Create** form in the webapp: write the question, define the outcomes, and set the resolve time. The market goes through a short review, and the creation fee is charged once it's approved and live. Market creation is rolling out gradually — it's currently available to approved accounts, with access expanding so that anyone will be able to submit. Organizations and partners can also create markets for their communities — see [Creating Opportunities](/getting-started/guides/creating-opportunities). ## Fees | Action | Fee | | --------------- | ---------------------------- | | Buy an outcome | 1% protocol fee | | Sell an outcome | 1% protocol fee + 1% AMM fee | | Claim winnings | No fee | | Create a market | One-time creation fee | Fees are current as of publication — the app always shows live fees before you confirm. ## Where you can use it The webapp at app.kash.bot is the product: markets, [portfolio](/getting-started/guides/winning), [threads](/getting-started/features/threads), [parlays](/getting-started/features/parlays), leaderboards, the [World Cup hub](/getting-started/features/world-cup), and the upcoming [Earn vault](/getting-started/features/earn). If you opt in, you can also trade from tweets via **Kash's trading account** (the current handle is linked from [@kash\_bot](https://x.com/kash_bot)), and partners can embed Kash markets on their own sites. More in [Where do I use Kash?](/getting-started/quick-start/where-kash) The full loop, from sign-in to claiming winnings Sign in with X, Google, email, or a wallet # When do I use Kash? Source: https://docs.kash.bot/getting-started/quick-start/when-kash How market timing works — from open trading to freeze, resolution, and claims Hero Light Hero Dark Markets are open 24/7 — the only clock that matters is each market's own **resolve time**, shown on every market card. ## The life of a market From the moment a market goes live until shortly before its resolve time, you can buy and sell any outcome. Prices move with every trade, so odds reflect the latest information. Before resolution, the market freezes. No new trades are accepted; positions are locked in while the outcome is determined. Kash's oracle resolves the market to its winning outcome(s). Winning positions become claimable — use **Claim Winnings** in your [Portfolio](/getting-started/guides/winning). In the rare case a market is cancelled, every position is refunded at its last market price, and a **Claim Refund** option appears in your Portfolio — a recovery path enforced by the contract itself. ## Picking your moment * **Early positioning** — new markets and scheduled events (matches, earnings, elections) often offer better prices before the crowd arrives * **When news breaks** — prices adjust as information spreads; if you know something the market hasn't priced in yet, that's your edge * **Any time before the freeze** — you can enter or exit a position whenever the market is open; you don't have to hold until resolution There's no fixed maximum duration — each market runs until its own resolve time. Check the countdown on the market page before you trade. Discover prediction opportunities Learn how to predict # Where do I use Kash? Source: https://docs.kash.bot/getting-started/quick-start/where-kash The app.kash.bot webapp is the product — with tweet-trading, embeds, and an API alongside it Hero Light Hero Dark ## The webapp — app.kash.bot **[app.kash.bot](https://app.kash.bot)** is where everything happens: * Browse, search, and trade markets * Track positions and claim winnings in your [Portfolio](/getting-started/guides/winning) * Deposit and withdraw USDC * Explore [threads](/getting-started/features/threads), [parlays](/getting-started/features/parlays), the [World Cup hub](/getting-started/features/world-cup), and the upcoming [Earn vault](/getting-started/features/earn) * Chat, leaderboards, groups, and achievements It works on desktop and mobile browsers — no download required. ## Trading from X (Twitter) — opt-in If you enable the trading bot in **Settings → Trading bot**, you can place trades by tweeting at **Kash's dedicated trading account** with a market link and your position — the current handle is always linked from the [@kash\_bot](https://x.com/kash_bot) profile. The bot processes the tweet and executes the trade from your Kash account. Tweet-trading is optional and off by default. Always verify the trading account you're interacting with is the one linked from the official **@kash\_bot** profile, and manage everything else — deposits, withdrawals, claims — in the webapp. ## Embedded on partner sites Organizations and partners can embed Kash markets and threads as iframes on their own sites, and run branded market pages for their communities. See [For Businesses](/for-businesses/overview). ## For developers Kash has a public REST API, CLI, and SDKs (TypeScript and Python) for building on top of the platform — from market data to programmatic trading. Start at the [Developer Docs](/developer-docs/introduction). *** ## Availability * **24/7** — markets trade around the clock * **Global**, subject to regulatory restrictions: users in blocked jurisdictions cannot access the platform * Built on **Base** (an Ethereum L2), so all activity is verifiable on-chain Learn the process Get started today # Who is Kash for? Source: https://docs.kash.bot/getting-started/quick-start/who-kash Traders, market creators, organizations, and developers Hero Light Hero Dark **Anyone with an opinion about a future event** * Trade multi-outcome markets on sports, crypto, politics, tech, and entertainment * Start with any amount — no minimum, no trading experience required * Track P\&L in your [Portfolio](/getting-started/guides/winning), climb the [leaderboards](/getting-started/ecosystem/competitions-and-tournaments), and earn achievements * Optional extras: [parlays](/getting-started/features/parlays), tweet-trading on X, and the upcoming [Earn vault](/getting-started/features/earn) Sign-in takes seconds, gas is sponsored, and your funds stay in your own smart account. **People who spot questions worth trading** * Propose markets from the Create form: question, outcomes, resolve time * Pay a creation fee; markets go live after review * Well-made markets attract volume and community discussion See [Creating Opportunities](/getting-started/guides/creating-opportunities) for the full flow. **Communities, media, and brands** * Create and curate markets for your audience * Embed markets and threads on your own site via iframes * Run a branded market page and earn revenue share on trades in markets you create Start at [For Businesses](/for-businesses/overview). **Builders integrating prediction markets** * Public REST API for market data, portfolios, and trading * Official CLI, TypeScript SDK, and Python SDK * Webhooks and embeds for real-time integrations Start at the [Developer Docs](/developer-docs/introduction). *** ## What makes it accessible * **No crypto expertise needed** — sign in with X, Google, or email; a wallet is created for you, and gas is sponsored * **No minimums** — trade with whatever amount you're comfortable with * **Self-custody** — funds live in your own smart account, not with Kash * **Global, 24/7** — available worldwide, subject to regulatory restrictions Kash is operated by the Kash team today. A token and community governance are planned but have not launched — any references to \$KASH or a DAO describe future plans, not the current product. Create your account How Kash works # Why would I use Kash? Source: https://docs.kash.bot/getting-started/quick-start/why-kash What actually sets Kash apart — honestly stated Hero Light Hero Dark Prediction markets are a great idea with a rough onboarding problem: wallets, gas, seed phrases, order books. Kash's goal is to remove that friction without taking custody of your money. ## The differentiators Sign in with X, Google, or email. An embedded wallet and ERC-4337 smart account are created for you, and the protocol sponsors gas — no ETH, no browser extension, no seed phrase to write down. It feels like a normal app; it settles on Base. Markets support 2–8 outcomes, each priced by an automated market maker seeded at creation. You can enter or exit any time the market is open, without waiting for a counterparty. See [Custom AMM](/getting-started/features/custom-amm) for how pricing works. Your USDC and positions live in **your own smart account** — Kash never holds your funds. Trades and payouts execute from your wallet, all verifiable on Base. You can export your key from Settings at any time. Markets are resolved by Kash's oracle, but recovery doesn't depend on it: if a market isn't resolved after its resolve time plus a grace period, anyone can trigger cancellation and every position is refunded at its last market price — enforced by the contract itself, subject to normal smart-contract risk. Trade from the webapp, from tweets (opt-in), or on partner sites that [embed Kash markets](/for-businesses/overview). [Threads](/getting-started/features/threads) bundle related markets, and chat, leaderboards, and groups make trading social rather than solitary. ## At a glance | | Typical platforms | Kash | | -------------------- | -------------------------- | --------------------------------- | | **Onboarding** | Wallet setup, seed phrases | Social login, wallet auto-created | | **Gas fees** | Paid by you | Sponsored by the protocol | | **Custody** | Platform holds funds | Your own smart account | | **Market structure** | Often binary only | 2–8 outcomes per market | | **Stuck markets** | At platform's discretion | Contract-enforced refund path | | **Fees** | Varies | 1% on buys, 1% + 1% on sells | Fees shown are current as of publication — the app always shows live fees before you confirm. Kash won't promise you "trustless everything" — resolution is handled by Kash's oracle today. What it offers instead: your funds stay in your own smart account, and the contracts provide a recovery path that doesn't depend on Kash or the oracle. Understand the process Create your account