Subscribing to Webhooks
Webhook subscriptions are managed by Listo on your behalf. You do not self-serve subscriptions through a dashboard or a public REST endpoint; you give Listo the inputs (URL, event types) and Listo registers the subscription against the relevant Instance Integration (your External Integration's installation on a customer's tenant). This page describes what you need to send Listo, what Listo sends back, and the rules the system enforces under the hood so you can plan for them.
Prerequisites
Before you request a subscription on an installation:
- Your destination URL is HTTPS and lives on a hostname you have pre-registered with the Listo team as an allowed domain. This is the same domain list that gates
x-api-keyuse on the REST API. - You have an active External Integration with Listo, and the installation is already provisioned on the customer's tenant.
- You're prepared to receive the webhook signing key from Listo through a secure channel (see below). It is shown once at generation time — Listo only stores an encrypted copy and cannot recover the plaintext later.
What to send Listo
To register a subscription for an installation, contact Listo with:
| Input | Required | Notes |
|---|---|---|
| Installation reference | yes | Either the customer's tenant identifier or the instanceIntegrationId if you already have it. |
| Webhook URL | yes | HTTPS, on one of your allowed domains. One subscription per (installation, URL) pair. |
| Event types | yes | Either an explicit list (e.g. ["client.created", "worker.onboarded"]) or all to receive every type Listo emits — including future ones. |
| Environment | yes | Production vs. staging. Listo treats the two as separate registrations with separate signing keys. |
| Operations contact | recommended | Whom Listo should reach if your endpoint starts erroring or you exhaust the retry budget repeatedly. |
You will repeat this request per installation. There is no global "one URL for everything" — subscriptions are scoped to a single installation.
What Listo sends back
Once Listo registers the subscription, you receive:
- The webhook signing key (
whk_…) for that External Integration, if one wasn't already issued. Format:whk_<base64url>. Shown once. - Confirmation of the registered URL, event-type list, and (where applicable) the
instanceIntegrationIdthe subscription is bound to.
❗ Store the signing key in a secret manager immediately. Listo only stores an encrypted copy and cannot show it to you again after delivery. If you lose it, the only recovery is rotation, which invalidates the previous key with no overlap window — see Rotating the signing key.
The same signing key is used across all installations of your External Integration. Different customers' subscriptions verify with the same key on your side.
Choosing event types
Tell Listo one of two things:
"All events"
You receive every type Listo currently emits, plus every type Listo adds in the future, automatically. This is the most resilient choice for long-lived integrations.
Requirement: your handler must tolerate unknown type values — return 200 OK and log, do not return 4xx. See Verifying signatures and the Overview for the rationale.
"An explicit list"
You receive only the types you specify. New types Listo introduces will not be delivered until you ask Listo to update the subscription.
Use this when you want explicit opt-in semantics (e.g. compliance) or when you operate multiple endpoints and want to route different types to different URLs.
🛈 Mix strategies across installations. Each installation is its own subscription record on Listo's side, so you can route everything to a central handler for one customer and only opt into worker events for another.
Rules enforced by the system
Listo registers subscriptions against the same constraints whether they're created on your behalf or directly in code, so you can plan for them:
-
The pair
(installation, webhookUrl)is unique per installation — the system rejects a second subscription on the same URL for the same installation. If you genuinely need two endpoints on the same path, differentiate with a query-string parameter (e.g.?env=staging). -
URL normalization. Inputs are normalized before storage so trivially-different forms collapse to the same row:
- Scheme + host are lowercased (URL scheme and DNS are case-insensitive).
- Default ports are dropped (
:443forhttps,:80forhttp). - The fragment (
#…) is dropped (it is never sent over HTTP anyway). - A trailing slash is dropped from non-root paths (
/hook/→/hook). - The query string is preserved verbatim.
In practice that means
https://x.com/hook,HTTPS://X.COM/hook/, andhttps://x.com:443/hookall collapse to the same subscription. -
Signing-key precondition. Listo will not register a subscription whose External Integration has no signing key. The signing key is generated as part of the same onboarding flow if it doesn't already exist.
Updating or pausing a subscription
Send Listo the change you want made — same channel as the original request. Listo can:
- Update the URL. Same uniqueness check applies after normalization.
- Update the event-type list (including switching between an explicit list and "all").
- Pause delivery by deactivating the subscription. While paused, events still flow through Listo but are not pushed to your URL. There is no backfill when delivery resumes — events emitted while paused are lost for that subscription. Use this if you need a planned receiver outage and don't want retries piling up.
- Delete the subscription entirely.
Rotating the signing key
Rotation is a no-overlap operation: regenerating the signing key immediately invalidates the previous one with no grace period. Plan a brief receiver maintenance window when you rotate.
Practical sequence:
- Tell Listo you want to rotate. Schedule the change.
- At the agreed time, Listo regenerates the key and shares the new plaintext with you securely.
- Update the secret in your secret manager and redeploy your receiver so it loads the new key at boot.
- Listo's producer cuts over to signing with the new key.
During the cut-over window, Listo's producer side may emit a delivery whose webhook-signature header carries multiple space-separated v1,… values (old and new). Your verifier must iterate over all values and accept on the first match — the official standardwebhooks libraries already do this. See Verifying signatures for the verifier-side details.
Rotate proactively if you suspect the key has been exposed. There is no self-service revoke — go through Listo.
Receiving your first event end-to-end
The shortest path to a working integration:
- Stand up an HTTPS endpoint that:
- Captures the raw request body (do not parse JSON before verifying — see Verifying signatures).
- Verifies the signature with the
standardwebhookslibrary. - Returns
200 OKquickly (within 30 seconds).
- Confirm the endpoint's hostname is on your allowed-domains list with Listo.
- Send Listo the registration request (URL, event types, environment).
- Once Listo confirms the subscription is live, trigger an action that emits the event you want to test (e.g. creating a client to fire
client.created). - Confirm signature verification passes on your side and the response is
200 OKwithin the 30-second window.
If something goes wrong during integration testing, Listo's audit trail records every attempt's status, response code, and (truncated) response body — share the webhook-id of the affected delivery with Listo and they can pull the records.
Where to next
Updated 1 day ago
