TR Book a demo Start free
Home › Blog › Anatomy of a packet

How to set up webhooks in a CRM: the anatomy of one packet, from signature to delivery log

O Ohana360 Team • October 15, 2026 • 12 min read
Blog cover showing the webhook delivery log and a signed JSON packet on the Ohana360 Setup screen

Setting up a CRM webhook means having the record sent as JSON to an address you provide the moment it is created, updated or deleted. In Ohana360 this takes four steps on the Setup > Webhooks and API screen: pick a name, target address, object and events; save the signing secret in the external system; send a trial packet with Send Test; read the result in the Deliveries table.

Ohana360 is a business cloud that keeps sales, service and finance records in one database; a webhook is how that cloud tells the outside world what happened. Instead of listing features we follow one packet. On the morning of October 15 Selin Arda, sales rep at the custom furniture maker Boran Mobilya, moved the "Vadi Konakları showroom" opportunity to Closed Won. Within five seconds a signed packet reached a Make scenario connected to the accounting software, and an invoice draft was opened there. Below is the anatomy of that packet, then five diagnoses from the same morning's delivery log.

Anatomy of a webhook packet: four headers, five fields Boran Mobilya's opportunity was moved to Closed Won; within 5 seconds this packet reached the accounting address. OUTGOING REQUEST • OCT 15, 2026 11:41:12 POST https://hook.make.com/p3x9… Content-Type: application/json X-Ohana-Signature: sha256=9f2c41…e7b0 User-Agent: Ohana360-Webhook/1.0 BODY (JSON) { "event": "record.updated", "obj": "opportunity", "record": { "id": "006K3V9Q2RT1AAA", "name": "Vadi Konakları showroom", "stage": "Closed Won", "amount": 480000, "accountId": "001M7Q2…", "owner": "Selin Arda", "cf_proje_kodu": "VK-2026-18", … }, "orgId": 27, "sentAt": "2026-10-15T08:41:12+00:00" } 1 The event is one of three values record.created, record.updated or record.deleted. It does not say which field changed. 2 The record goes in full The latest state, custom fields (cf_) included. The previous value (prev) is not in the packet. 3 The signature is computed on the body HMAC-SHA256(body, signing secret). The receiver runs the same calculation and compares. 4 The clock is UTC sentAt arrives with +00:00; add three hours for Türkiye time. 5 The reply must come within 5 seconds Connect 3 s, total 5 s. A 2xx is accepted; any other code is a red row, with no retry.

What is a webhook, and where is it set up in Ohana360?

A webhook is Ohana360 sending a POST request to an external system on its own when a record event happens; it is the reverse of the REST API, where the external system has to ask Ohana360. On the Setup > Integrations > Webhooks and API screen the New Webhook button opens a five-field window, and Boran Mobilya's first webhook was set up in two minutes:

After Save the row shows in the list with an Active badge, an object chip and event chips; the checkbox on the row switches the webhook off without deleting it. Four counters sit at the top of the screen: active webhooks, successful deliveries, failed deliveries and event types. The same numbers are read as "Webhook Deliveries (successful / total)" on the External Systems card of Setup > Automations and Integrations. The webhook screen is used by an admin who can open Setup; Send Test and Deliveries pass an admin check on the server.

What is inside a webhook packet?

A packet is four headers and a JSON body with five fields: event, obj, record, orgId and sentAt. The image above is the packet that went out for Selin's opportunity; let's read it line by line.

  1. The POST line and Content-Type. The packet always goes as POST with an application/json body; there are no query parameters or form fields.
  2. X-Ohana-Signature. The HMAC-SHA256 signature of the body with the sha256= prefix. The subject of the next section.
  3. User-Agent. Ohana360-Webhook/1.0. The easiest way to spot the packet in server logs.
  4. event. record.created, record.updated or record.deleted. The packet does not say which field changed; an opportunity moved to Closed Won and an opportunity whose amount was corrected both arrive as record.updated. The receiver decides by looking at record.stage.
  5. obj. The record's object name: opportunity, lead, case, account, contact, invoice or a custom object key starting with cobj_. The values do not depend on the interface language.
  6. record. The latest state of the record with all of its fields: name, stage, amount, owner, the account link and custom fields starting with cf_. Opportunity stages are stored in English, so the stage goes out as "Closed Won" even when the Turkish interface shows Kazanıldı. The old value is not in the packet; on a delete event the record is sent as it was before deletion.
  7. orgId and sentAt. The org number and a UTC timestamp. The Make scenario received the packet at 08:41 UTC, 11:41 in Türkiye.

The body is built on the server on every write; the app in the browser never sees the packet and never touches the signing secret. That is why delivery happens even if Selin's computer is closed: the write is committed to the database, the reply goes back to Selin, and the packet is sent after that.

How do you verify that the packet really came from Ohana360?

Ohana360 runs the raw body of the packet through HMAC-SHA256 with the webhook's signing secret and writes the result to the X-Ohana-Signature header; the receiver runs the same calculation with the same secret and compares the two values. A short Node service on Boran Mobilya's accounting side does this in one line: it computes the HMAC of the body, drops the sha256= prefix from the header, compares the two and rejects the packet with 401 if they differ.

Ohana360 Setup screen, Webhooks and API page: four counters, two webhook rows, a five-row Deliveries table and the open New Webhook window
Boran Mobilya's Webhooks and API screen: four counters at the top, two webhooks and the Deliveries table in the middle, and the New Webhook window with a ready signing secret on the right.

The signature is computed on the raw body; a service that parses the JSON and writes it out again gets a different result because spacing and field order change, and it rejects every packet. When the secret changes, both sides are updated together: the Refresh button changes the secret in Ohana360, and the copy in the external system is renewed by hand. Tools such as Make and Zapier do not verify the signature on their own; in those scenarios the address is kept private and an HMAC step is added inside the scenario if needed. The way to try the setup is Send Test: a signed packet goes out with the event ping, the obj test and a single record named "Ohana360 webhook testi", and the result appears within seconds as a toast and as the first row in Deliveries.

Which saves produce a webhook, and which do not?

A webhook is produced by every path that commits the record to the database: Save on the screen, inline editing, bulk update, import, a REST API write, a Web-to-Lead or Web-to-Case form. The server compares the old and new copies and builds the event list; the same list feeds follow notifications and the field history. One week at Boran Mobilya:

Write pathWebhookNote
Selin dragged the card to the Closed Won column on the kanbanSentrecord.updated, Opportunity
The Web-to-Lead form on the website created a leadSentrecord.created, Lead; the assignment rule runs first, so the owner is filled in the packet
Accounting set the invoice status to Paid through the APISentrecord.updated, Invoice; an API write is an event too
A scheduled flow updated a field every morning at 09:00Not senta scheduled flow's own field update produces no record event; it is sent if a Call Webhook action is added to the flow
A 420-row Excel import was loaded in one goPartlyat most 20 deliveries per request; the first 20 events go out, the rest do not appear in the log
Send TestSentevent ping, obj test

The limit of twenty keeps an import from flooding the external system with hundreds of requests; for bulk data moves, list the records through the REST API after the import instead of relying on webhooks. A packet goes only to active webhooks with a valid address; no event is produced for a webhook that is switched off, and nothing is sent retroactively when it is switched on again.

How do you send only under a condition: a Setup webhook or a flow action?

A webhook definition in Setup picks an object and events but takes no condition; for a rule such as "send only when won", a record-triggered flow is built under Setup > Flows with Call Webhook as the action. In the first week Selin had left the Updated event open on the Opportunity object; accounting received a packet on every amount correction, and the Make scenario had to filter by looking at record.stage. In the second week she separated the two routes:

The action runs on the server: even when the flow is triggered in the browser, the packet is signed and sent by the server, and the run shows under Setup > Flows > Run History for 90 days. A scheduled flow has the same action; "tell the external system every morning about orders pending for three days" is built that way. The condition and action logic of flows is covered in the automated reminders guide.

How do you read the delivery log?

The Deliveries table lists every send the server made with time, event, object, address and HTTP code; a green badge means a 2xx was received, a red badge means the address is wrong, the service is down, the signature was rejected or no reply came within 5 seconds. The last 100 deliveries per org are kept, the last 50 are shown on the screen, and the Refresh button reloads them. Boran Mobilya's morning of October 15 had five rows:

The delivery log: five rows, five diagnoses Boran Mobilya's morning of October 15. A green badge means 2xx; every other code and a lost connection is a red row. TIME EVENT OBJECT TARGET ADDRESS RESULT 1 11:41 Updated Opportunity hook.make.com/p3x9… HTTP 200 2 11:42 Created Lead hooks.slack.com/services/T0… HTTP 404 3 11:50 Updated Opportunity api.accounting-example.com/ohana HTTP 401 4 12:05 Delete Contact hook.make.com/p3x9… error 5 12:12 ping test boranmobilya.com/webhook HTTP 301 1 Received: the Make scenario took the packet and opened an invoice draft in accounting. Nothing to do. 2 No such address: the incoming webhook in Slack was deleted; get a new one and change the URL with Edit. 3 Signature rejected: the key on the accounting side is stale; copy the current secret and save it there again. 4 No reply: no answer within 5 seconds or no connection; the service is down. The packet is not resent. 5 Redirect: the http address redirects to https and Ohana360 does not follow redirects; write the address with https.

The log is a diagnosis tool, not a recovery tool. The body that was sent and the receiver's reply text are not stored; the row shows only the code. A failed delivery is not retried and produces no bell notification, and the webhook does not switch itself off; until the Slack address returning 404 is fixed, every new lead writes a red row. The way to get a missed record into the external system is to read it again through the REST API. The address is cut at 300 characters in the log, so compare by the start of the address.

What is used when the external system needs to write into Ohana360?

The reverse direction is the REST API: a key starting with ohk_ is generated under the user menu > API Keys and every request carries an Authorization: Bearer header. When Boran Mobilya's accounting software issues the invoice, it updates the invoice record in Ohana360 with PATCH; the same screen manages both directions.

Store orders have a separate incoming endpoint: POST /api/commerce/order creates or updates the record by order number with a Bearer key; the ready connectors for WooCommerce, Shopify and ikas are in the multichannel order guide. The record structure on the sales side is on the Sales360 page.

Not included: what the webhook does not do

What should be checked when the first webhook is set up?

A five-minute checklist prevents the first red row and the first loop. Boran Mobilya's list:

  1. Is the address written with https? If an http address redirects to https, the delivery fails with 301.
  2. Is Send Test green? Real events are not switched on until the ping packet gets a 2xx.
  3. Is the signing secret saved in the external system? If the secret was refreshed, are both sides current?
  4. Is the event selection narrow enough? If only won deals are needed, use a conditional flow with Call Webhook rather than a Setup webhook.
  5. Does the system that writes back recognize its own write? Every write that returns through the API is a new event.
  6. Who will watch the log? Because a failed delivery produces no notification, Deliveries and the counter under Automations and Integrations are checked once a week.

Read next