POST to that address.
Manage subscriptions with the Webhooks endpoints
(create, list, retrieve, and update).
Topics
You can subscribe to the following topics:A brand may register multiple webhooks, and the same address may subscribe to
the same topic more than once. In non-development environments, callback
addresses must use HTTPS.
user/create means the user record was created in Sotto, not that the person
is new to your business. Importing a customer who already exists elsewhere
still produces user/create the first time Sotto sees them.Your own writes do not come back to you
When you change a user through the API, Sotto does not send you a callback for that change. This applies to all of your subscriptions on that topic, not just one: if you have registered two addresses foruser/update, neither is
called for a change you made yourself. Other partners are unaffected and still
receive it. This keeps a write from bouncing straight back into the system that
made it.
You still receive callbacks for changes made by anyone else, including your
brand’s own users in the Sotto portal.
One case looks like an exception and is not. If you create a user through the
sign-up endpoint, the consent that Sotto records for them is its own event, so
you do receive user/opt-in for a user you just created. That callback
describes the consent outcome rather than replaying your write.
Callback requests
Sotto delivers events as aPOST to your registered address. Callbacks are
signed with the same HMAC scheme as API requests (see
Authentication) and include these additional headers:
string
The API version for the callback (e.g.
v1).string
The UUID of the webhook subscription that triggered this callback.
string
The topic of the event (e.g.
user/opt-in).Payload
The body contains the time the event occurred, the topic that produced it, and the associated resource:string
When the event occurred, in UTC.
string
The topic that produced this event, matching the
x-bigco-topic header. The
two can never disagree, so you can route on whichever is more convenient. If
you queue or store callback bodies without their headers, route on this.string | null
The lifecycle change behind the event:
create, update, opt_in or
opt_out. Useful if you want one handler across the user/* topics. It is
null on topics that describe no lifecycle change, such as user/tap and
ticket/updated, so it is not a substitute for topic.object | null
The tap behind the event. Populated on
user/tap and null on every other
topic. The key is always present, so the body keeps one shape.object
The resource the event is about. For
user/* topics this is a User object.user/* topics, data is a User
object.
A brand can turn off custom data or tap location for an individual
subscription. With custom data off,
custom_data arrives as null on every
user/* topic for that subscription; tap location is covered under
Taps. Either change applies from the moment the brand makes it,
including to retries of events first sent before it. The keys are always
present, so check for null rather than for a missing key.Taps
Onuser/tap, the same User object arrives in data and the tap itself rides
beside it in tap:
device is null for a plain QR scan with no device behind it, and location
is null when the device has none recorded or when the brand has turned off
location for this subscription. labels is an empty array when the device
carries none. As with tap itself, these keys are always present, so check for
null rather than for a missing key.
labels carries your own taxonomy. canonical is the key:value form, so you
do not need a second lookup to match on it.
Responding
Your endpoint must return a2xx status code within 10 seconds. Any other
response, or a timeout, is treated as a failure and the callback is retried up
to 20 times before being dropped.
Sotto waits about 10 minutes between attempts, so a subscriber that is down for
a short window catches up on its own once it recovers.
Sotto does not follow redirects. A 3xx response counts as a failed delivery,
the same as a 5xx, so register the final address your endpoint serves from.
An endpoint that redirects, for example to add a trailing slash or move to a new
host, fails every delivery and is eventually paused.
Paused subscriptions
An endpoint that keeps failing is not retried forever. When three events in a row are dropped after exhausting their retries, Sotto pauses the subscription: no further callbacks are attempted, and events that occur while it is paused are not queued for later. One successful delivery in between resets the count, so an outage shorter than the retry window (about three hours and twenty minutes) never pauses anything. A paused subscription is not abandoned. Sotto probes it with one new event, no sooner than ten minutes after the pause or the previous probe. A probe needs an event to carry it, so on a quiet topic the next probe waits for the next event. If your endpoint answers2xx, the subscription resumes on its own and
normal delivery, with retries, continues from that event. If the probe fails it
is not retried, and the subscription stays paused until the next probe. So once
your endpoint is healthy again, deliveries resume with the first event after
the ten-minute interval, without any action from you.
To resume sooner, or after moving to a new URL, update the subscription with a
new address using the update endpoint.
Repointing it clears the pause immediately; re-saving the same address does
not. Events still queued or retrying when you change the address are delivered
to the new address, signed for it, rather than to the old one. The brand can also resume it, or switch a subscription off and on, from
their side.
Best practices
Make handlers idempotent
Make handlers idempotent
Your endpoint may receive the same event more than once. Retries are the
common cause, but a delivery can also be repeated if Sotto restarts partway
through fanning an event out to several subscribers. Use
x-bigco-webhook-id together with date_occurred to detect and safely
ignore duplicates.Verify every signature
Verify every signature
Always validate the HMAC signature on incoming callbacks before acting on
them, and serve your endpoint over HTTPS.
Acknowledge fast, process async
Acknowledge fast, process async
Queue the event and return
200 immediately rather than doing heavy work
inline. This keeps you within the 10-second window and avoids unnecessary
retries.