402 with error.reason subscription_inactive.
Choose event types
Webhook events returns this catalog. Put the types you want inevent_types when you create the endpoint.
live_sports_updatedsends one pulse per game, and at most one every 20 seconds per game. A game going live or final is sent straight away. The game clock moving on its own never sends one. See Live game pulses.suspicious_trade_flaggedandinsider_radar_flag_raisedare one event under two spellings. Subscribe with either name; listing both on one endpoint stores one. Your endpoint receives every delivery under the spelling it registered, in thex-0xinsider-event-typeheader and in the payload’stype, so an endpoint created before September 23, 2026 keeps receivinginsider_radar_flag_raised.large_trades_inserted(and its older spellingwhale_trades_inserted) carries a count of new large trades, not the trades themselves. Read the trades from Large trades.large_trade_inserted_v2carries one trade per delivery. Settrade_filterswhen you create or update the endpoint to require acondition_id,wallet, minimummin_grade, or minimummin_size_usd; all supplied filters must match.gradeis captured at publication and may benull, which never passes a minimum-grade filter. The delivery id stays stable across retries.large_trades_insertedandwhale_trades_inserted, andtrader_syncedandwhale_trader_synced, are each one event under two spellings, like the suspicious-trade pair below: an endpoint receives the spelling it registered.- The four
export_job_*events go only to endpoints owned by the account that submitted the export. - When a Pro subscription lapses, every queued delivery of a Pro-only type moves to
dead_letterwithlast_errorset todelivery owner is no longer authorized for this event.
Create and verify the endpoint
1
Create it
signing_secret (whsec_...) and verification.token (whv_...) once each. Store both now, because no later read returns either one. The endpoint receives nothing until step 2.2
Verify it within 24 hours
webhook.verification challenge to your URL. Your server has to answer 2xx within 10 seconds before the endpoint becomes active. Holding the token is not enough on its own.{"type":"webhook.verification","token":"<verification_token>","webhook_id":<id>}. It is signed the same way a delivery is, and it carries x-0xinsider-timestamp, x-0xinsider-signature, and x-0xinsider-event-type webhook.verification. It carries none of the event, delivery, or idempotency headers listed below.
Two answers mean the endpoint did not activate:
422witherror.paramurl: your server answered outside2xx, took longer than 10 seconds, or is not reachable from the public internet.400witherror.paramverification_token: the token is wrong or has expired. Delete the endpoint and create it again.
What a delivery carries
A delivery is aPOST with a JSON body and the User-Agent 0xinsider-webhooks/1.0. Answer with any 2xx within 15 seconds, then do your processing after you have replied.
Live game pulses
live_sports_updated sends one pulse per game. A pulse goes out when a material field moves since that game’s last pulse: scores, status, period, live, or ended. The game clock moving on its own never sends one, and the clock still rides every pulse.
Two rules bound the volume. An ordinary change waits out 20 seconds since that game’s last pulse: the pulse goes out on the first frame the provider sends after that. A game going live or final ignores the wait, and so does a game’s first pulse, so you never miss a start or a final.
A game that stops sending frames stops sending pulses. Live games send a frame about every 20 seconds, and the frame that ends a game is exempt from the wait, so a finished game always sends its last pulse.
A change that arrives inside the 20 seconds is not dropped. The next pulse lists it in changed and its body carries the current state, so you see every field that moved, but not every value it passed through.
Check the signature
The signature covers the timestamp and the raw body, joined by a dot.- Sign the raw request bytes. Parsing the JSON and serializing it again changes the HMAC.
- Use the whole
whsec_...string as the key, prefix included. - Reject a request whose
x-0xinsider-timestampis more than 300 seconds from your own clock. - Split
x-0xinsider-signatureon commas and accept the request when any candidate matches. A staged rotation sends two candidates for an hour. - Compare each candidate in constant time.
- Deduplicate on
x-0xinsider-event-idbefore you act on the event.
Retries
An attempt fails when your server answers outside2xx, the connection errors, or the request runs past the 15-second deadline. A delivery gets 8 attempts in total. After the eighth failure its status becomes dead_letter and 0xinsider stops.
Each failed attempt doubles the ceiling on the next wait: 60 seconds after the first failure, then 120, 240, 480, 960, 1,920, and 3,600 seconds. The delivery worker wakes every 10 seconds, so a wait can run up to 10 seconds longer than its ceiling.
The wait you get inside that ceiling depends on how the attempt failed. Each row below names the value the delivery log reports in retry_schedule_reason.
Retry-After is read as a number of seconds or as an HTTP-date. A missing, malformed, or already-past value is ignored, and the delivery falls back to its ordinary wait.
Webhook deliveries reports status, last_error, next_attempt_at, and retry_schedule_reason on each row, and keeps rows for 7 days. Two more reasons appear there: manual_redelivery after Redeliver a webhook delivery, and configuration_changed when an edit to the endpoint released an attempt that had not been sent. Both next_attempt_at and retry_schedule_reason are null while an attempt is in flight and once the delivery is delivered or dead_letter.
One delivery can therefore keep retrying for up to 7,380 seconds, a little over 2 hours. The endpoint itself is usually disabled long before that:
failure_counton the endpoint counts consecutive failed attempts across all of its deliveries. Any success resets it to 0.- At 8 consecutive failures the endpoint’s
statusbecomesdisabledand 0xinsider emails the account owner. - A busy endpoint that goes down is therefore disabled within minutes, not hours.
Replay after downtime
Event replay is the only durable catch-up, and it coverslarge_trades_inserted (whale_trades_inserted) alone. Every other type is delivered, retried, and then dropped.
- Store
next_cursorfrom every replay response. - After the outage, call
/api/v1/events/feed/since?cursor=<the cursor you stored>. - Process each event in
data, then store the response’s newnext_cursor. - Repeat while
has_moreistrue.
Rotate the signing secret
Rotate the secret when it reaches git, a log, a screenshot, or anyone who should not have it. There are two ways to do it.Staged rotation, with a 1-hour overlap
Use this one unless you are racing a leak. Both secrets verify while you deploy the new one, so no delivery is rejected on the way.1
Prepare the new secret
signing_secret and sets secret_rotation.status to pending. Deliveries keep using the current secret. Deploy the new one to every receiver before you move on.2
Activate it
secret_rotation.status becomes overlap, and secret_rotation.overlap_expires_at is the instant the overlap ends, 1 hour later. Until then x-0xinsider-signature carries two comma-separated v1=<hex> candidates: the new secret’s signature first, the old secret’s second.3
Retire the old secret
secret_rotation.status goes back to idle and deliveries carry one signature again. If you never call it, the overlap ends on its own at overlap_expires_at.Idempotency-Key. Repeating prepare with the same key while a secret is still pending returns that same secret, so a lost response costs you nothing. Changing the endpoint’s url on Update a webhook discards a prepared or overlapping secret.
Immediate rotation, after a leak
Rotate a webhook secret replaces the secret in a single call.signing_secret to every receiver immediately: a delivery your server rejects is retried about a minute later and counts toward the 8 consecutive failures that disable the endpoint. A 409 with error.reason webhook_delivery_in_progress means a delivery is in flight, so send the request again once it finishes.
What it does not do
- Deliver to a laptop or a private network. The URL has to be public HTTPS on port 443. To read events from a machine with no public address, open the Stream instead.
- Carry the trades themselves.
large_trades_insertedsays how many new large trades there are, not which ones. - Replay any event type other than
large_trades_inserted(whale_trades_inserted). - Filter by market, size, or grade. Subscribe to the type and filter in your own code; Alert bot shows that loop.
- Return
signing_secreta second time. Webhook and Webhooks never carry it, so rotate the secret if you lose it.