Webhooks
A webhook posts a JSON message to a URL of your choice when something happens on the site: a form created a record, or the Dataverse connection failed or recovered. Use it to start a Power Automate flow, post to a Teams channel or feed any HTTP endpoint. With a secret set, every message is signed so the receiver can verify it.
Setting up a webhook
On the Webhooks tab add a webhook with a label, the URL, the events it subscribes to, an optional secret and the enabled switch. The secret field is pre-filled with a random value; copy it to the receiver before saving, it is not shown again. Deliveries are fire-and-forget with a 5 second timeout; the site never waits for the receiver.
Events
| Event | When | data |
|---|---|---|
| form.created | A form on the Forms tab or the WBS Forms bridge created a record | form (form key, or bridge:<slug>), entity_set, id (GUID, empty when unknown), record (the payload that was written) |
| health.failure | The health monitor recorded a failure it alerted on: first of an episode, a changed failure class, or the cooldown elapsed | state, class (auth, network, api, form), detail, context, since (Unix time) |
| health.recovery | The connection works again after a failure episode | Same fields as health.failure |
| test.ping | The Test link on the Webhooks tab | webhook (id), label |
Record updates from update-mode forms fire the wbs_dataverse_connect_record_updated action but no webhook event.
Payload schema
Every message is one JSON object with four members. time is UTC in ISO 8601, site is the home URL of the sending site.
{
"event": "form.created",
"site": "https://example.com/",
"time": "2026-09-07T10:15:30+00:00",
"data": {
"form": "inquiry",
"entity_set": "wbs_inquiries",
"id": "00000000-0000-0000-0000-000000000000",
"record": {
"wbs_name": "Jane Doe",
"wbs_email": "jane@example.com",
"wbs_Listing@odata.bind": "/wbs_listings(00000000-0000-0000-0000-000000000000)"
}
}
}
Headers on every delivery:
| Header | Value |
|---|---|
| Content-Type | application/json; charset=utf-8 |
| X-WBS-Event | The event slug |
| X-WBS-Signature | sha256=<hex HMAC-SHA256 of the raw body with the secret>; only when a secret is set |
| User-Agent | WBS-Connect-for-Dataverse/1.5.0; https://example.com/ |
The record member carries the data the visitor submitted. If personal data must not leave the site, trim it with the wbs_dataverse_connect_webhook_payload filter, which receives the receiving webhook so you can shape the body per endpoint.
Verifying the signature
The receiver recomputes the HMAC over the raw request body, byte for byte, and compares it with the header in constant time.
PHP
$secret = 'the secret from the Webhooks tab';
$body = file_get_contents('php://input');
$given = $_SERVER['HTTP_X_WBS_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expect, $given)) {
http_response_code(401);
exit;
}
$message = json_decode($body, true);
// $message['event'], $message['data'] ...
Power Automate
Use the trigger When an HTTP request is received and paste its URL into the webhook. The flow's expression language has no HMAC function, so the flow cannot recompute the signature itself. Two workable patterns:
- Treat the trigger URL as the shared secret: it carries its own
sigparameter and is unguessable. Add a Condition that checks the event header before doing anything, and leave the webhook secret empty or ignore the signature header. - When you need the signature verified, put an Azure Function or Logic App with inline code in front of the flow: it verifies the HMAC as in the PHP example and forwards the body.
Condition for the first pattern, comparing the event header with the expected value:
triggerOutputs()?['headers']?['X-WBS-Event'] is equal to form.created
Read the body in later steps with triggerBody()?['data']?['record']?['wbs_email'], or add the payload above as the trigger's JSON schema sample so the fields appear in the dynamic content picker.
The Test link
Every webhook in the list has a Test link. It sends a test.ping to the URL, waits for the answer and shows the HTTP status the endpoint returned (or the transport error). A 200 to 299 counts as delivered. Use it after pasting a Power Automate URL: the flow run history then shows the sample message with its headers.
Developers
wbs_dataverse_connect_webhook_payload($payload, $event, $hook): reshape the body per endpoint, for example an adaptive card for Teams.wbs_dataverse_connect_webhook_args($args, $hook, $event): thewp_remote_postarguments before signing; a rewritten body still gets a matching signature.wbs_dataverse_connect_webhook_dispatched($event, $data, $sent): action after an event was handed to its webhooks, with the number of deliveries.- Webhooks are stored in the
wbsdvc_webhooksoption and removed on uninstall.