Webhooks

Listo pushes domain events to URLs you register on a per-installation basis. You receive HTTP POSTs from gateway.listoglobal.com shortly after the underlying change occurs in Listo, signed with HMAC-SHA256 according to the Standard Webhooks v1 specification.

This page is the entry point. Skip directly to:


At a glance

PropertyValue
TransportHTTPS POST, Content-Type: application/json
User-AgentListoGlobal-Gateway/1.0
Signature schemeStandard Webhooks v1 — HMAC-SHA256, base64-encoded
Required headerswebhook-id, webhook-timestamp, webhook-signature
Per-attempt timeout30 seconds
RetriesUp to 3 attempts (1 initial + 2 retries), exponential backoff (~2s, ~4s, ~8s)
Replay window±300 seconds (5 minutes), enforced on producer and verifier
OrderingBest-effort. Not guaranteed.
Idempotency keywebhook-id header (= the body's top-level id), stable across retries
VersioningTwo-axis: envelope specVersion + per-type dataVersion

Anatomy of a delivery

Every webhook is a single HTTP POST whose body is a DomainEvent envelope (described below) — no outer wrapper — and whose headers carry the Standard Webhooks signature triplet:

POST /your/webhook/path HTTP/1.1
Host: hooks.your-domain.com
Content-Type: application/json
User-Agent: ListoGlobal-Gateway/1.0
webhook-id: lglsoevt_uZJK2rPmA1nGqvE5w
webhook-timestamp: 1735689600
webhook-signature: v1,3v+OYVnOwzbGcpRz1aRr7T0pYXukB0z2yDoa2QH1vNw=

{
  "id": "lglsoevt_uZJK2rPmA1nGqvE5w",
  "type": "client.created",
  "specVersion": 1,
  "dataVersion": 1,
  "occurredAt": "2026-05-02T10:14:32.184Z",
  "entity": { "type": "client", "id": "lglsocli_uZIIHfKqYBwyaRGGs" },
  "data": { /* type-specific — see the event reference */ }
}

Headers

Listo follows Standard Webhooks header naming exactly. Every delivery carries:

HeaderRequiredDescription
webhook-idyesThe event's globally unique id (lglsoevt_…). Stable across retries — your dedupe key.
webhook-timestampyesUnix epoch seconds at signing. Fresh per attempt; powers the 5-minute replay window.
webhook-signatureyesv1,<base64-hmac-sha256>. May contain multiple space-separated values during signing-key rotation.
Content-TypeyesAlways application/json.
User-AgentyesAlways ListoGlobal-Gateway/1.0. Useful for inbound-traffic firewall rules.

The DomainEvent envelope

Every body shares this shape, regardless of type:

FieldTypeDescription
idstringGlobally unique event id (lglsoevt_…). Equals webhook-id.
typestringEvent type, e.g. client.created. Hierarchical, dot-delimited.
specVersionintegerEnvelope version. Currently 1. Bumps only on envelope-level breaking changes.
dataVersionintegerPer-type data version. Bumps independently when a single event type's payload shape breaks.
occurredAtISO-8601 UTCWhen the event was produced at the source. Use this for ordering / auditing.
entity.typestringDomain entity type — one of "client", "user", "worker", "workerContract".
entity.idstringPublic id of the entity (matches the relevant data.*Id).
dataobjectType-specific payload. See the event-reference pages.

data.links.self (when present) is a relative path you can append to https://gateway.listoglobal.com and call to fetch the entity's current state. Listo substitutes your installation's instanceIntegrationId into the path before signing the body, so the link is ready to call as-is.


Event-type catalog

TypeDescriptionData version
client.createdA new client has been created on the instance.1
user.createdA new dashboard user has been created for a client on the instance.1
worker.onboardedA worker's first contract for a client has been executed (one event per worker per client).1
worker.updatedA worker's profile fields (email, name, address, phone, date of birth, organization unit, organization location) have changed.1
worker.contract.updatedA worker's contract status, job title, or manager has changed (suppressed for the EXECUTED transition that triggers worker.onboarded).1

The list will grow over time. Design your handler to ignore unknown event types gracefully (HTTP 200 + log) rather than reject them with 4xx — returning a non-2xx for an unknown type would consume your retry budget for no benefit.


Versioning policy

Two independent axes — pin your handler against the (type, dataVersion) pair, never against specVersion alone.

ChangeWhat bumpsMigration window
New optional field on dataNothingTolerate unknown fields. Not announced as a breaking change.
New event typeNothingTolerate unknown types. Subscribe explicitly to opt in.
Breaking shape change on a specific type's dataThat type's dataVersionOld and new versions delivered in parallel for a deprecation window.
Envelope-level breaking change (rename of type, etc.)specVersionAnnounced in advance with parallel delivery during deprecation.

🛈 Tolerate unknowns. Your verifier should accept and log unfamiliar type values and unfamiliar fields under data. Listo's contract is to not break additive changes; rejecting them on your side defeats that.


Where to next