Webhooks
A webhook endpoint is a URL on your server. When something happens on your PolySimulator account (an order is placed, fills or is cancelled, a position settles, a wallet balance changes), PolySimulator sends a signedPOST to that
URL. All of these events come from simulated trading: PolySimulator never places
orders on Polymarket or on any other venue.
Developer webhooks ship with the next PolySimulator release. Without a key,
GET /v1/developer/webhooks/event-types returns 401 on an API host that
has them. A 404 means that host does not serve them yet.Before you start
You can also manage endpoints in the developer console: API Keys → Webhooks.
Endpoints
All paths are underhttps://api.polysimulator.com.
An endpoint that is not yours returns
404 NOT_FOUND, the same as one that
does not exist.
Create an endpoint
Response (
201):
URL rules
PolySimulator checks the URL when you create or change an endpoint, and again before every delivery:- The scheme must be
https://. localhostand hostnames ending in.internalor.localare refused.- Every address the hostname resolves to must be a public address.
Private, loopback, link-local, carrier-grade NAT (
100.64.0.0/10), reserved, multicast and IPv4-mapped IPv6 addresses are refused. - Redirects are not followed. A
3xxanswer counts as a failed attempt.
400 INVALID_URL with the reason in message.
Event types
GET /v1/developer/webhooks/event-types returns the current list, with these descriptions:
Two notes:
market.createdis not about your account: it is sent for every new market, to every endpoint subscribed to it, including"*".trader.new_tradeis sent only for traders you follow whose profile is public and shows their trades and positions.
webhook.test, which you cannot subscribe to.
What a delivery looks like
Each delivery is an HTTPPOST with a JSON body.
Headers
Body
Payload by event type
Every payload has"version": 1.
wallet can be null when the event has no wallet. balance is the balance
after the change.
Verify the signature
Verify every request before you act on it:- Read the raw request body as bytes. Do not parse and re-serialize the JSON first: the signature covers the exact bytes sent.
- Reject the request if
X-PolySim-Timestampis more than 5 minutes away from your clock. This stops an old request from being replayed. - Compute HMAC-SHA256 over
<timestamp>+.+<raw body>. The key is your secret as text (its UTF-8 bytes), not the hex-decoded bytes. - Compare
"sha256=" + <hex digest>withX-PolySim-Signature-V2using a constant-time comparison.
X-PolySim-Signature is the older scheme: HMAC-SHA256 of the raw body alone,
in the same sha256=<hex> form. It has no timestamp, so it cannot stop a
replayed request. Prefer X-PolySim-Signature-V2.
Use POST /v1/developer/webhooks/{endpoint_id}/test to check your receiver. It
sends a signed webhook.test event at once and returns what happened. It is not
written to the delivery log:
status_code is null when the request never completed (refused URL, DNS
failure, timeout).
Responding, retries and timeouts
- Answer with any 2xx status within 10 seconds. The response body is ignored.
- A delivery is tried up to 5 times. After a failed attempt the next one is scheduled 15 seconds, 60 seconds, 5 minutes and 30 minutes later, so the last attempt comes about 36 minutes after the first.
- Retried:
5xx,408,425,429,3xx, timeouts and connection errors. - Not retried: any other
4xx. The delivery ends at once asfailed_terminal. - An event is picked up for delivery at least 10 seconds after it happens, in a background pass that runs every few seconds.
Duplicates and order
- Deduplicate on
event_id. Retries and replays send the same event again, and if two of your endpoints subscribe to the same type, each receives it. - Deliveries are sent in parallel and retried independently, so they can arrive
out of order. Use
ts, and thestatusinsideorderorposition, rather than arrival order.
Delivery log
limit is 1 to 200 (default 50). To page back, pass the response’s
next_before_id as before_id; it is null on the last page.
Each delivery has id, event_id, event_type, status, attempts,
next_attempt_at, delivered_at, response_code, error, created_at, and
attempts_log (the last 20 attempts: attempt_number, started_at,
duration_ms, success, response_status, error).
Replay a failed delivery
POST /v1/developer/webhooks/deliveries/{delivery_id}/replay puts a
failed_terminal delivery back in the queue. It keeps its delivery_id and its
attempt count: a delivery gets 5 attempts in total, so only one that stopped
early (a non-retried 4xx, for example) can be replayed.
Pause, resume, rotate, delete
- Pause (
PATCHwith{"status": "paused"}): nothing is sent to the endpoint, and events that happen while it is paused are not sent later. - Resume (
{"status": "active"}): deliveries start again with new events. Deliveries that were already queued before the pause are sent. - Rotate (
rotate-secret): the new secret applies at once, to every later attempt, including retries of earlier events. There is no period in which the old secret still works, so update your receiver right away. - Delete: removes the endpoint and its delivery log.
204, no body.
failed_terminal with the error
alerts.webhook entitlement no longer active.