Webhooks
Receive a signed HTTP callback when a map, map set, or tracked object changes state, instead of polling the API for status.
A map takes minutes to process, and an object or map set takes as long as its inputs need. Until now the only way to find out when one finished was to poll Map Details on a timer.
Webhooks invert that. Register one HTTPS endpoint, subscribe to the events you care about, and MultiSet POSTs a signed JSON payload to your server the moment a resource changes state.
Quick start
Build a receiver. A single route that accepts
POST, returns200quickly, and can echo a value back from the request body. See Verifying your endpoint.Register it. Portal, or
POST /v1/webhook/endpointwith aurland theeventsyou want.Save the signing secret. It is returned once at registration and never again.
Verify the signature on every request before you act on it. See Verifying the signature.
One endpoint per account. To change where events go, update or delete the existing endpoint.
Verifying your endpoint
Registration is not complete until MultiSet has proved your URL reaches real code. As soon as you register, a signed endpoint.verify event is POSTed to it:
{
"id": "evt_verify_9f2b1c9d4e7a6b3f",
"type": "endpoint.verify",
"apiVersion": "2026-09-01",
"occurredAt": "2026-09-08T09:14:22.184Z",
"livemode": false,
"accountId": "66eb01b11d9025665dbbba79",
"data": { "challenge": "ch_4f8a2b7c1d9e3f6a8b2c5d7e9f1a3b4c" }
}Your endpoint must reply 200 with a JSON body that echoes the challenge back:
The challenge has a 10 second timeout. On success the endpoint becomes active and events start flowing. On failure it stays pending_verification, the response tells you why, and you can fix your server and re-run verification without re-registering.
An endpoint only receives events while its status is active. Events raised while it is pending_verification or disabled are dropped, not queued.
Events
Twelve event types, across three entities:
Entity
created
pending
active
failed
map
Map record created, upload not finished
Upload landed, processing started
Processing finished, map is queryable
Processing failed
mapset
Map set created
Overlap computation started
At least one overlap succeeded
The only target failed
object
Object record created
Upload landed, tracking started
Tracking finished, object is queryable
Tracking failed
Subscribe with exact names, an entity wildcard, a state wildcard, or everything:
events entry
Matches
map.active
That one type
map.*
Every map event
*.failed
Every failure, on all three entities
*
Everything, including event types added later
A typo in a subscription is rejected at registration, so map.faild fails loudly instead of silently never firing.
The data block
data is shaped per entity. Every event carries the resource's codes, its new status, and the status it came from:
A failed event adds an error object:
type is USER_ERROR when the input was the problem and re-uploading a good file will fix it, or SYSTEM_ERROR when it was ours. message is truncated to 500 characters.
The delivered request
POST with Content-Type: application/json and these headers:
Multiset-Event-Id
evt_1a0636bc890d38c1f72d2b9d122, unique per event. Dedupe on this.
Multiset-Event-Type
map.active
Multiset-Signature
t=1788876000,v1=5257a869e7...
User-Agent
Multiset-Webhooks/1.0
The body wraps your data block in a fixed envelope:
livemode is false for verification and test events, true for real ones.
Verifying the signature
Anyone who learns your URL can POST to it. The signature is what tells you a request came from MultiSet, so verify it before you act on the body.
Multiset-Signature holds a timestamp and an HMAC:
v1 is HMAC_SHA256(secret, "{t}.{rawBody}"), hex encoded. This is the same scheme Stripe uses, so if you already verify Stripe webhooks the code carries over.
The timestamp is there so you can reject a replay of an old but validly signed request. A tolerance of 300 seconds matches what MultiSet uses.
Responding
Return any 2xx to acknowledge. Anything else, or no response within 10 seconds, counts as a failure and is retried.
Redirects are not followed. A 301 or 302 is recorded as redirect_refused, because the destination has not been through the address checks the original URL passed. Register the final URL.
Retries
A failed delivery is attempted five times with a widening gap:
1
immediately
2
1 minute after attempt 1
3
5 minutes after attempt 2
4
30 minutes after attempt 3
5
2 hours after attempt 4
After the fifth attempt the delivery is marked exhausted and never tried again. The whole chain spans about 2 hours 36 minutes, so a receiver that comes back inside that window still gets its events, and one that is down longer does not.
Sustained failure disables the endpoint. After 20 consecutive failed deliveries the endpoint is set to disabled and delivery stops. Fix your server, then re-run verification to re-enable it, which also resets the failure count. Consecutive means across events, not attempts within one chain, so a single flaky hour will not disable you.
Idempotency and ordering
Delivery is at-least-once. A timeout on your side after you already processed the event still counts as a failure, so the same event can arrive twice. Keep a record of Multiset-Event-Id values you have handled and ignore repeats.
Events can arrive out of order. Use occurredAt, set by the service that raised the event, rather than the order requests reach you. A map.active arriving before the map.pending for the same map is possible under retry.
Test events
Send a sample event to your endpoint on demand from the portal, or with POST /v1/webhook/endpoint/{id}/test. It is signed exactly like a real event, carries livemode: false, is never retried, and reports back what your server answered, including the response code, the timing, and the first 2KB of your response body.
This is the fastest way to debug a receiver: it turns a silent integration into a request you can watch.
Delivery log
Every attempt is recorded. Query it from the portal or with GET /v1/webhook/delivery, filtered by event type, status, date range, or resourceId to see everything that happened to one map. Fetch a single delivery by its event id to read the exact payload that was sent, plus one entry per attempt with the response code and timing.
Use it to answer "did you send it?" without guessing.
Endpoint requirements
HTTPS in production.
Publicly resolvable. Private, loopback, and link-local addresses are rejected, and the address is re-checked at delivery time, not just at registration, so an endpoint whose DNS is later repointed inward stops being delivered to.
Responds within 10 seconds.
No redirects.
Returns JSON containing
challengefor theendpoint.verifyevent.
Rotating the secret
POST /v1/webhook/endpoint/{id}/rotate-secret returns a new secret once. The previous secret stops verifying immediately, so there is no overlap window: deploy code that reads the new secret from configuration, then rotate, or accept a brief period of signature failures. Those deliveries are retried, so nothing is lost if you rotate and redeploy inside the retry window.
Rotate if the secret may have been exposed, for example in a log, a screenshot, or a committed config file.
Last updated
Was this helpful?

