Get told when a seller changes
Subscribe a signed webhook to observed price, recipient and liveness changes for the hosts you depend on. Admission key required.
Get told when a seller you depend on changes
A subscription names up to 20 seller hosts and one public HTTPS webhook. Every two minutes the service compares each host's latest public observations with your subscription and, when something moved, delivers one signed batch of price_changed, recipient_changed and liveness_changed events. Alerts fire on the same observations the endpoint pages count: a change is reported when a check observed it, never from a catalog feed alone. An admission key (X-402Signal-Key) is required; ask at ross@402signal.com.
Subscribe
curl -sS -X POST https://402signal.com/alerts \
-H "X-402Signal-Key: $KEY" -H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/402signal","hosts":["api.example.com"],"events":["price","recipient","liveness"]}'HTTP 201 returns the subscription id, hosts_known (whether each host has listings today) and signing_secret, shown once. The URL must be public HTTPS with public DNS: private, loopback and link-local addresses, plain HTTP, credentials in the URL and 402signal.com itself are refused at creation and again on every delivery. Up to 10 subscriptions per key, 20 hosts each; events defaults to all three.
What you receive
One POST per scan when something changed. Headers: X-402Signal-Event (402signal.alerts, or 402signal.ping for a test), X-402Signal-Delivery (the delivery id, also in the body) and X-402Signal-Signature (t=<unix seconds>,v1=<hex HMAC-SHA256> over <t>.<raw body>).
{"type": "402signal.alerts", "delivery_id": "9f1c2d3e4a5b6c7d", "subscription_id": "0123456789abcdef",
"generated_at": "2026-09-13T18:20:00Z",
"events": [
{"event": "price_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"amount_atomic": "20000", "changed_at": "2026-09-13T18:19:41Z", "endpoint_page": "https://402signal.com/endpoints/api.example.com"},
{"event": "recipient_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"payTo": "0xabc...", "observed_payTo": "0xdef...", "changed_at": "2026-09-13T18:19:41Z", "endpoint_page": "..."},
{"event": "liveness_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"live": false, "miss_reason": "timeout", "observed_at": "2026-09-13T18:19:52Z", "endpoint_page": "..."}
]}price_changed carries the new observed atomic amount on the same asset. recipient_changed carries the address on record and the newly observed one; it stays pending until a second observation confirms it, with no second alert for the confirmation. liveness_changed reports the latest observation flipping between answering with a valid challenge and not. Delivery is at least once: the same event, url and changed_at seen twice is the same change twice. Answer with any 2xx within five seconds; a 3xx is not followed and counts as a failure.
Verify the signature
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes, tolerance_s: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
ts = int(parts.get("t", "0"))
if abs(int(time.time()) - ts) > tolerance_s:
return False
expected = hmac.new(secret.encode(), b"%d." % ts + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Failures and management
A delivery that raises, times out or answers outside 2xx counts as a failure. Retries back off from one minute to one hour and the undelivered changes stay owed, so the next successful delivery carries them. After 20 consecutive failures the subscription is disabled and shows active: false with the reason; a successful test ping re-enables it.
| Call | Result |
|---|---|
| GET /alerts | Your subscriptions, no secrets, and the limits. |
| GET /alerts/<id> | One subscription with its last 20 deliveries: time, kind, HTTP status, event count, error class. |
| POST /alerts/<id>/test | A signed 402signal.ping now; delivered: true on a 2xx. |
| DELETE /alerts/<id> | Removes it, HTTP 204. |
Every call answers only for the key that created the subscription. The signing secret is stored on the private writer volume beside the session store; to rotate it, delete and recreate the subscription. Alerts are not uptime monitoring: a host nobody checks stays silent. Check your own key and credits any time with GET /keys/usage.