Skip to main content

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 signed POST 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 under https://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):
secret appears only in this response and in the response of rotate-secret. Store it now. Later reads return "secret": null and only secret_hint, the last 4 characters.
A new endpoint receives only events that happen after it was created. Past events are not sent.

URL rules

PolySimulator checks the URL when you create or change an endpoint, and again before every delivery:
  1. The scheme must be https://.
  2. localhost and hostnames ending in .internal or .local are refused.
  3. 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.
  4. Redirects are not followed. A 3xx answer counts as a failed attempt.
A refused URL returns 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:
  1. market.created is not about your account: it is sent for every new market, to every endpoint subscribed to it, including "*".
  2. trader.new_trade is sent only for traders you follow whose profile is public and shows their trades and positions.
The test call sends one more type, webhook.test, which you cannot subscribe to.

What a delivery looks like

Each delivery is an HTTP POST 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:
  1. Read the raw request body as bytes. Do not parse and re-serialize the JSON first: the signature covers the exact bytes sent.
  2. Reject the request if X-PolySim-Timestamp is more than 5 minutes away from your clock. This stops an old request from being replayed.
  3. Compute HMAC-SHA256 over <timestamp> + . + <raw body>. The key is your secret as text (its UTF-8 bytes), not the hex-decoded bytes.
  4. Compare "sha256=" + <hex digest> with X-PolySim-Signature-V2 using 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

  1. Answer with any 2xx status within 10 seconds. The response body is ignored.
  2. 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.
  3. Retried: 5xx, 408, 425, 429, 3xx, timeouts and connection errors.
  4. Not retried: any other 4xx. The delivery ends at once as failed_terminal.
  5. 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 the status inside order or position, 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 (PATCH with {"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.
If your account loses its Pro or Pro+ plan, queued deliveries stop: each one ends as failed_terminal with the error alerts.webhook entitlement no longer active.