worker.updated Event

Emitted when profile-level fields on a worker change. Specifically,
fired on changes to:

  • email
  • firstName, lastName (and the derived fullName)
  • phone
  • dateOfBirth
  • address (any line)
  • organizationUnit (the primary org-unit assignment)
  • organizationUnits (the many-to-many org-unit assignments)
  • organizationLocation

Fan-out is per-client: a single change at the underlying
WorkerProfile (e.g. updating a phone number) emits one event per
Worker row
that has at least one EXECUTED contract — i.e. one event
per client where the person is actively engaged. The shared
workerProfileId lets you correlate the events as the same underlying
person.

Workers without any executed contract (e.g. drafts, pending signature)
are not eligible — you do not receive updates for them.

🛈 Location deletion detaches workers. When an organization location
is deleted (see organization_location.deleted),
every worker assigned to it has its location cleared and emits a
worker.updated whose organizationLocation is now null.

PropertyValue
Typeworker.updated
Data version1
Entityentity.type = "worker", entity.id = data.workerId
Fan-outOne event per client the worker has at least one EXECUTED contract under
Payload shapeIdentical to worker.onboarded — pure full snapshot

Sample delivery

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_uZM3rPSsBJ7NbXfD2
webhook-timestamp: 1746271800
webhook-signature: v1,Tk2pVxQ3rZ4cNwLyB8EHmRYbFGJaCQ5dKHxsXM7uTvQ=

{
  "id": "lglsoevt_uZM3rPSsBJ7NbXfD2",
  "type": "worker.updated",
  "specVersion": 1,
  "dataVersion": 1,
  "occurredAt": "2026-05-03T12:10:00.231Z",
  "entity": { "type": "worker", "id": "lglsowkr_uZIIQiu8Q5zhUXgbR" },
  "data": {
    "clientId": "lglsocli_uZIIHfKqYBwyaRGGs",
    "workerId": "lglsowkr_uZIIQiu8Q5zhUXgbR",
    "workerProfileId": "lglsowpr_uYZRk9GHTcCqMpL4n",
    "email": "[email protected]",
    "firstName": "Alex",
    "lastName": "Kim",
    "fullName": "Alex Kim",
    "phone": "+12025550199",
    "dateOfBirth": "1992-08-14",
    "address": {
      "address1": "742 Evergreen Terrace",
      "address2": "Apt 3",
      "city": "Springfield",
      "isoCountryCode": "US",
      "postalCode": "97402",
      "zoneCode": "OR"
    },
    "organizationUnit":     { "id": "lglsooun_uVwPa92qXz", "name": "Engineering" },
    "organizationUnits": [
      { "id": "lglsooun_uVwPa92qXz", "name": "Engineering" }
    ],
    "organizationLocation": { "id": "lglsoolc_uTpQa83jYx", "name": "Springfield HQ" },
    "contracts": [
      {
        "id": "lglsoctr_uXYZ1aBcD2EfG3hJ4",
        "clientId": "lglsocli_uZIIHfKqYBwyaRGGs",
        "name": "Alex Kim - Senior Engineer",
        "status": "EXECUTED",
        "billingFrequency": "MONTHLY",
        "isoCountryCode": "US",
        "jobTitle": "Senior Engineer",
        "startDate": "2026-05-02",
        "contractEndDate": null,
        "createdAt": "2026-04-28T17:09:11.000Z",
        "engagementType": "EOR",
        "managerId": "lglsousr_uXYZxLtq9ABvCdEf2"
      }
    ],
    "links": {
      "self": "/v2/instance-integrations/inst_abc123xyz/clients/lglsocli_uZIIHfKqYBwyaRGGs/workers/lglsowkr_uZIIQiu8Q5zhUXgbR"
    }
  }
}

data schema

Identical to worker.onboarded
— the two events share the WorkerSnapshotData shape, including both the
single organizationUnit and the many-to-many organizationUnits[] array.
Refer to that page for full field definitions; only the semantics differ.

🛈 Pure snapshot — no changedFields. The body does not tell you
which field changed. Diff against your prior stored state if you care.
Listo deliberately keeps the payload minimal and authoritative; a
changedFields listing would be one more thing to keep in sync as the
snapshot grows.


Fan-out semantics in detail

A single edit at WorkerProfile fans out to one event per matching
Worker row
. Concretely:

  • Person at one client: one event.
  • Person at three clients (three executed contracts): three events,
    one per client. Each carries that client's clientId in the routing
    fields, the client-scoped workerId, and the same workerProfileId.
  • Person at three clients but only one has an executed contract: one
    event (only the executed-contract Worker row is eligible).

The workerProfileId field is the join key for treating the multiple
deliveries as the same person. Do not compare workerIds across
deliveries to identify a person — workerId is per-client.


Snapshot semantics

All embedded fields are a snapshot at emit time. If multiple updates
happen close together you will receive multiple events; each carries the
full snapshot at its own emit moment. Combined with at-least-once delivery
and best-effort ordering, this means a late retry of an older event can
arrive after a newer one — refetch via links.self if you need strict
"latest state."


Ordering and duplication

  • Duplicates are normal. Dedupe on webhook-id.
  • Order is not guaranteed. Two worker.updated events for the same
    worker may arrive out of occurredAt order. Use occurredAt to
    order on your side
    ; never rely on arrival order.
  • A worker.updated may arrive before the corresponding
    worker.onboarded for the same worker — this happens when the order
    in which Listo's queue drains diverges from the order things changed.
    Tolerate it: upsert on (clientId, workerId) rather than insisting on
    a specific arrival sequence.

Common patterns

"Keep a worker mirror in sync"

1. Verify signature, dedupe on webhook-id.
2. UPSERT INTO workers (client_id, worker_id) VALUES (...) ON CONFLICT
   DO UPDATE SET <fields>, last_event_at = data.occurredAt
   WHERE workers.last_event_at < data.occurredAt;
3. Respond 200.

The last_event_at guard ensures a delayed retry of an older event does
not overwrite a more recent state.

"Diff for downstream automation"

If you need to fire an internal alert when a worker's address changes,
compare incoming data.address.* against your stored copy. Listo does
not provide change deltas; the diff is yours to compute. Consider hashing
the canonical JSON of the snapshot for cheap "did anything change"
checks before computing field-by-field diffs.

"Treat one person across many clients as one record"

Index your worker mirror by workerProfileId, not workerId, when the
business question is "is this the same person at this company?" Use
workerId (or the pair (clientId, workerId)) when the question is
"this engagement specifically."


Did this page help you?