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 pollingDocumentation Index
Fetch the complete documentation index at: https://docs.kash.bot/llms.txt
Use this file to discover all available pages before exploring further.
GET /v1/trades/{id}.
When you’ll get webhooks
Each API key can carry an optionalwebhook_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 |
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:| 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
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
tokensOut is in WAD (18 decimals) as a decimal string.
data for trade.failed
errorCode is the same machine-readable code surface as the REST API error codes. errorMessage is sanitised — no stack traces, no internal URLs.
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:
[a-zA-Z0-9_\-.] (1-64), value max 500 chars, strings only.
Headers
X-Kash-Signature— HMAC of the body. Always verify. See Verifying signatures.X-Kash-Event-Id— same as the body’sid. Dedupe on this.X-Kash-Api-Version— same as the body’sapiVersion.
Delivery semantics
- At-least-once. Network failures and
5xxresponses trigger automatic retries with exponential backoff over ~24 hours. Your handler MUST be idempotent — dedupe onX-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}/redeliveronce you’ve fixed the receiver.
- Respond fast. Aim for sub-2s response times. We hard-timeout at 10s.
- Respond
2xxto 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
UseGET /v1/webhooks/events to list deliveries and inspect their state — handy for debugging “did the webhook fire?” without instrumenting your receiver:
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):Next
Verifying Signatures
Stripe-compatible verification recipe. One-line constant change from any Stripe library.
Retries & Redelivery
Backoff schedule, terminal failure, manual replay.
Secret Rotation
Rotate without dropping deliveries — the 7-day overlap window.