Skip to main content
A webhook sends event notifications to a public HTTPS URL you control. Use it when your application should react to new trades, grade changes, or other supported events without polling. Each request is signed so your server can check that it came from 0xinsider.

Before you start

You need a public HTTPS endpoint on port 443 and a Pro or Max API key or appropriately scoped OAuth token. Keep the signing secret on your server. If you cannot host a public endpoint, use the event stream. Set OXINSIDER_API_KEY for the examples below. Webhook management requires Pro or Max access; an inactive subscription returns 402 with error.reason: "subscription_inactive".

Choose event types

The access column describes the event’s data. Webhook management itself still requires Pro or Max access, including for events marked No. Choose the events your application needs, then put their names in event_types when you create the webhook. List webhook events returns the supported catalog and current status.
  • live_sports_updated sends 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_flagged and insider_radar_flag_raised are 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 the x-0xinsider-event-type header and in the payload’s type, so an endpoint created before September 23, 2026 keeps receiving insider_radar_flag_raised.
  • large_trades_inserted (and its older spelling whale_trades_inserted) carries a count of new large trades, not the trades themselves. Read the trades from Large trades.
  • large_trade_inserted_v2 carries one trade per delivery. Set trade_filters when you create or update the endpoint to require a condition_id, wallet, minimum min_grade, or minimum min_size_usd; all supplied filters must match. grade is captured at publication and may be null, which never passes a minimum-grade filter. The delivery id stays stable across retries.
  • large_trades_inserted and whale_trades_inserted, and trader_synced and whale_trader_synced, are each one event under two spellings, like the suspicious-trade pair below: an endpoint receives the spelling it registered.
  • sharp_money_flow_detected and smart_money_flow_detected are also aliases. An endpoint receives the name it registered.
  • The 4 export_job_* events go only to endpoints owned by the account that submitted the export.
  • When paid access lapses, every queued delivery of a paid type moves to dead_letter with last_error set to delivery owner is no longer authorized for this event.

Create and verify your webhook

1

Create the webhook

Save the returned signing_secret and verification.token immediately; later reads do not return them. Configure your receiver with the signing secret before the next step. The webhook receives events only after verification.
2

Verify it within 24 hours

Replace 42 with the returned webhook ID and the token placeholder with verification.token. This call sends a signed webhook.verification challenge to your URL. Your server must verify it and return 2xx within 10 seconds for the webhook to become active.
The challenge body is {"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. If verification fails, use the response to choose the next action:
  • 422 with error.param url: your server answered outside 2xx, took longer than 10 seconds, or is not reachable from the public internet.
  • 400 with error.param verification_token: the token is wrong or has expired. Delete the endpoint and create it again.

Receive a delivery

Each delivery is a POST with a JSON body and User-Agent: 0xinsider-webhooks/1.0. Verify the signature, save the event durably, and return 2xx within 15 seconds. Process the saved event afterward, so a crash after acknowledgment does not lose it.

Live game pulses

live_sports_updated reports changes to a game’s scores, status, period, live, or ended fields. A clock change alone does not send an event, but each event includes the latest clock. Ordinary updates are limited to one event per game every 20 seconds. The next provider update after that interval triggers the event. A game’s first update, start, and final state can be sent immediately. Events depend on provider updates. If the provider stops sending updates, the webhook also stops receiving them. Changes during the waiting interval appear in the next event’s changed array. The body contains the current values, rather than every intermediate value.

Check the signature

Verify the signature before parsing JSON. It covers the timestamp, a dot, and the exact request body bytes.
  • Calculate the HMAC from the raw request bytes. Parsing and re-serializing JSON can change those bytes.
  • Use the whole whsec_... string as the key, prefix included.
  • Reject a request whose x-0xinsider-timestamp is more than 300 seconds from your own clock.
  • Split x-0xinsider-signature on 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 ordinary events on x-0xinsider-event-id before acting. The verification challenge has no event ID; handle it separately.
Use these verifier functions in your request handler. Pass the raw body and the two signature headers to verify before parsing JSON. For an ordinary event, save and deduplicate it before acknowledging; for a verification challenge, return a timely 2xx after the signature passes.

Handle retries and disabled webhooks

0xinsider retries a delivery when your server returns a non-2xx status, the connection fails, or the 15-second deadline expires. Each delivery has at most 8 attempts. After the eighth failure, its status becomes dead_letter. The default retry delay grows after each failure: 60, 120, 240, 480, 960, 1,920, then 3,600 seconds. Some failures use a shorter random delay or the receiver’s Retry-After, as shown below. Delivery scheduling can add up to 10 seconds. The delivery log reports the next attempt time and 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. The webhook also counts consecutive failures across all its deliveries:
  • failure_count on the endpoint counts consecutive failed attempts across all of its deliveries. Any success resets it to 0.
  • At 8 consecutive failures the endpoint’s status becomes disabled and 0xinsider emails the account owner.
  • Several failing deliveries can reach this limit before any one delivery uses all 8 attempts.
Disabling an endpoint moves every queued delivery to dead_letter. Re-enabling it with {"enabled": true} on Update a webhook does not resend them. Requeue each one with Redeliver a webhook delivery, or catch up with the replay below.

Replay after downtime

Event replay lets you catch up on large_trades_inserted and its alias whale_trades_inserted. It does not replay other event types. For those, inspect delivery logs or read the current API state after an outage.
  1. Store next_cursor from every replay response.
  2. After the outage, call /api/v1/events/feed/since?cursor=<the cursor you stored>.
  3. Process each event in data, then store the response’s new next_cursor.
  4. Repeat while has_more is true.

Rotate the signing secret

Use staged rotation for routine secret changes. Use immediate rotation if the secret is exposed in source control, logs, screenshots, or elsewhere.

Staged rotation, with a 1-hour overlap

Staged rotation lets you deploy a new secret while both signatures remain valid for 1 hour. This avoids rejecting deliveries during the change.
1

Prepare the new secret

Prepare a staged webhook secret returns the new 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

Activate a staged webhook secret promotes the prepared secret and returns it once more. 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

Retire a staged webhook secret ends the overlap as soon as every receiver accepts the new 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.
Use an Idempotency-Key for each operation and reuse that key if its response is lost. Repeating prepare while the same secret is pending returns that secret again. Changing the webhook’s url discards any prepared or overlapping secret.

Immediate rotation, after a leak

Rotate a webhook secret replaces the secret in a single call.
The old secret stops working when the call returns, and there is no overlap. Deploy the new 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.

Limits

  • 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.
  • The count event large_trades_inserted does not contain individual trades. Choose large_trade_inserted_v2 for one trade per delivery.
  • Replay any event type other than large_trades_inserted (whale_trades_inserted).
  • Count events cannot filter individual trades. For large_trade_inserted_v2, set trade_filters to filter by market, wallet, minimum grade, or minimum size.
  • Return signing_secret a second time. Webhook and Webhooks never carry it, so rotate the secret if you lose it.