View Categories

Change Notifications

Change notifications post an event to a URL you choose whenever a snippet is created or its code changes. Use them to review new code, keep an audit trail outside WordPress, or post to a chat channel through a service such as Zapier, Make, or n8n.

Setting it up #

  1. Go to Settings and find Change notifications.
  2. Enter the webhook URL that should receive the events.
  3. Enter a signing secret. The receiving end uses it to check that a request really came from your site. The field stays blank after saving; leave it blank to keep the saved secret, or type a new one to replace it.
  4. Choose the events to send, then save.

Nothing is sent until both a URL and a secret are saved. Clear the URL and save to turn notifications off.

Options #

Option Default What it does
A snippet is created On Sends a created event for each new snippet.
A snippet’s code changes On Sends a code_changed event when a snippet’s code is different after a save, update, or revision restore. Renaming a snippet, changing its tags, or activating it does not send an event.
Include snippets created by an import Off JSON imports and imports from the Code Snippets plugin can create many snippets at once, so they are left out unless you check this. Snippets added by a GitHub collection are always included.
Ignore changes made by Empty Usernames or email addresses, one per line, whose changes are not sent — for example your own, so you are only told about changes other people make. Matching ignores letter case. Changes made through the Abilities API count as the user the request authenticated as. Scheduled updates that run without a signed-in user are always sent.

Multisite #

On a multisite network, a network administrator can set Applies to to Every site in this network on the main site’s Settings tab. One webhook then covers every site in the network, including sites added later, and site administrators cannot turn it off. A site can still add a webhook of its own; both receive the site’s events. A site webhook with the same URL as the network webhook is skipped, so the endpoint does not receive each event twice.

Choosing one scope on the main site removes the other, so the main site keeps a single webhook.

The request #

Each event is a POST with a JSON body:

{
  "event": "created",
  "occurred_at": "2026-10-22T14:03:11+00:00",
  "origin": "save",
  "site": { "id": 3, "name": "Example Site", "url": "https://example.com/", "network_url": "https://example.com/" },
  "snippet": {
    "id": 42,
    "name": "Hide the admin bar",
    "previous_name": null,
    "description": null,
    "type": "php",
    "status": "inactive",
    "collection_id": 0,
    "version": null,
    "edit_url": "https://example.com/wp-admin/admin.php?page=..."
  },
  "user": { "id": 7, "login": "jane", "email": "jane@example.com", "display_name": "Jane" }
}

origin says how the change happened, using the origins listed in Actions and filters. user is null when no one was signed in, such as during a scheduled update. The snippet’s code is never included; follow edit_url to read it.

These headers are sent with each request:

Header Value
X-GOCSM-Event created or code_changed.
X-GOCSM-Delivery A unique ID for the event, kept the same across retries.
X-GOCSM-Timestamp The Unix time the request was signed.
X-GOCSM-Signature sha256= followed by the hex HMAC-SHA256 of the timestamp, a period, and the raw body, keyed with your signing secret.

Verifying a request #

Recompute the signature from the raw body and compare it in constant time. Reject requests whose timestamp is more than a few minutes old so a captured request cannot be replayed.

$body      = file_get_contents( 'php://input' );
$timestamp = $_SERVER['HTTP_X_GOCSM_TIMESTAMP'] ?? '';
$expected  = 'sha256=' . hash_hmac( 'sha256', $timestamp . '.' . $body, $secret );

if ( ! hash_equals( $expected, $_SERVER['HTTP_X_GOCSM_SIGNATURE'] ?? '' ) || abs( time() - (int) $timestamp ) > 300 ) {
    http_response_code( 401 );
    exit;
}

Delivery and retries #

Events are sent at the end of the request that made the change, so saving a snippet does not wait for them. Any 2xx response counts as delivered. Anything else is retried after 5 minutes and again after 30 minutes. Each failure is recorded in the Sync Log as webhook_failed: a warning while retries remain, and an error after the last attempt.

Retries run on WP-Cron. If your site has little traffic and no server cron, a retry can be later than scheduled.

The URL must resolve to a public address. Requests to local or private network addresses are refused, which shows up in the Sync Log as a failed delivery.

Developer hooks #

gocsm_webhook_should_send filters whether an event is sent (bool $send, array $payload, string $scope, Snippet $snippet). gocsm_webhook_payload filters the payload before it is signed (array $payload, string $scope, Snippet $snippet). $scope is network or site. See Actions and filters.