HyphaBase

Customer events

Keep your own database in step without asking us every minute. A worked receiver, and the two things that will bite you.

Sarah's accountant uses different software, and it needs to know when a customer's details change. Rather than exporting weekly, she has us tell it.

The events

EventWhen
clients.client.createdSomeone's added — one at a time or by import
clients.client.updatedAny field changes, including a restore
clients.client.deletedSomeone's deleted
clients.note.addedA note is added — useful if your crew app shows access instructions

data.object is the whole customer, so a create or update carries everything you need without calling back.

Check the live list, not this table

The endpoint dialog under Webhooks shows what's actually available, and it grows without an API version bump. Trust it over a table in docs.

Note there's no separate "restored" event. A restore arrives as clients.client.updated, which means your receiver should treat every update as "re-check this" rather than assuming its own copy is still structurally sound.

Subscribe

Webhooks → New endpoint, then tick the three clients.* events.

The new webhook endpoint dialog, with a URL field and events grouped by module
One endpoint covers every site under the app. Each event tells you which site it came from.

With no site filter, you hear about all of them — each event's location_id says whether it was Portland or Salem.

Keep the whsec_ secret you're given; the code below needs it.

A receiver worth copying

Webhooks arrive server to server, and checking one needs the signing secret — so a browser can never receive them directly.

To live-update a screen: your server receives and verifies, then tells the browser over a WebSocket or server-sent events, or simply invalidates a cache so the next render refetches.

// React to your own server, not to ours.
source.addEventListener('customer.changed', (event) => {
  const { id } = JSON.parse(event.data);
  queryClient.invalidateQueries({ queryKey: ['customers', id] });
});

The two things that will bite you

Snapshots go stale. data.object is the customer as they were when the event fired. If retries land out of order, the newest event isn't necessarily the newest state. Guard with the timestamp you already store:

if (existing && existing.updatedAt >= customer.updated_at) return;

Fields can arrive starred out. Webhook payloads are built the same way reads are, so if the credential context lacks clients.view_email you'll receive dan***** rather than a real address. If your copy needs real contact details, make sure the app's keys carry the view_* scopes — otherwise you'll quietly build a database of masked emails and only notice when something tries to send one.

Imports produce a lot of events

Sarah's 312-row import fires 312 clients.client.created events.

That's correct, and worth designing for. Batch your writes, and don't make a third-party call per event. If you're importing and mirroring the same data, consider switching the endpoint off for the duration and reconciling afterwards with an export — much less work than processing three hundred deliveries.

Next

On this page