Webhooks

Last updated: September 2026, plugin 1.5.2

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

EventWhendata
form.createdA form on the Forms tab or the WBS Forms bridge created a recordform (form key, or bridge:<slug>), entity_set, id (GUID, empty when unknown), record (the payload that was written)
health.failureThe health monitor recorded a failure it alerted on: first of an episode, a changed failure class, or the cooldown elapsedstate, class (auth, network, api, form), detail, context, since (Unix time)
health.recoveryThe connection works again after a failure episodeSame fields as health.failure
test.pingThe Test link on the Webhooks tabwebhook (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:

HeaderValue
Content-Typeapplication/json; charset=utf-8
X-WBS-EventThe event slug
X-WBS-Signaturesha256=<hex HMAC-SHA256 of the raw body with the secret>; only when a secret is set
User-AgentWBS-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 sig parameter 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): the wp_remote_post arguments 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_webhooks option and removed on uninstall.

See also