Overview
AdminLocks Cloud communicates with your WordPress sites through inbound webhooks. When an operator takes an action in the Cloud dashboard — such as approving a client request, triggering a snapshot, or deploying a policy — Cloud sends a signed HTTP POST request to your site's webhook endpoint.
This architecture means your WordPress site does not need to poll the Cloud for updates. Actions initiated in the Cloud dashboard are delivered to your site in near real-time, typically within one to two seconds.
The webhook system supports three action types:
- approval_reviewed — syncs approval decisions made in Cloud back to the WordPress site
- create_snapshot — triggers a new site snapshot remotely
- update_policy — deploys or updates a policy from Cloud to the WordPress site
Endpoint URL
The webhook endpoint is registered automatically when AdminLocks is activated. The full URL is:
POST https://yoursite.com/wp-json/adminlocks/v1/cloud-webhook
This endpoint is publicly accessible (it does not require WordPress authentication) because it uses its own HMAC-based authentication scheme. Your site must be reachable from the internet for Cloud webhooks to work — sites behind HTTP basic auth, IP restrictions, or maintenance mode may need exceptions for requests from cloud.adminlocks.com.
If your site uses a non-standard REST API prefix (changed via the rest_url_prefix filter), adjust the endpoint URL accordingly. AdminLocks uses the standard WordPress REST API registration, so the endpoint follows whatever prefix your site is configured to use.
Authentication
Every webhook request includes an X-AdminLocks-Signature header containing an HMAC-SHA256 signature of the request body. The signature is computed using your site's Cloud API key as the secret.
The verification process on your WordPress site works as follows:
- AdminLocks reads the raw request body
- It retrieves the stored Cloud API key from the
adminlocks_cloud_api_keyoption - It computes
hash_hmac('sha256', $request_body, $api_key) - It compares the computed hash to the value in the
X-AdminLocks-Signatureheader using a timing-safe comparison - If the signatures do not match, the request is rejected with a
403 Forbiddenresponse
// How AdminLocks verifies the signature (simplified)
$body = file_get_contents('php://input');
$api_key = get_option('adminlocks_cloud_api_key');
$expected = hash_hmac('sha256', $body, $api_key);
$signature = $_SERVER['HTTP_X_ADMINLOCKS_SIGNATURE'] ?? '';
if (!hash_equals($expected, $signature)) {
return new WP_Error('forbidden', 'Invalid signature', ['status' => 403]);
}
If you rotate your Cloud API key, webhooks will fail until the new key is saved on both the WordPress site and in the Cloud dashboard. Always update both sides simultaneously to avoid downtime.
Supported Actions
Every webhook payload follows the same envelope structure:
{
"action": "action_name",
"data": {
// Action-specific fields
}
}
The action field determines how the data object is processed. Unrecognized actions are ignored and return a 200 OK response to prevent retry loops.
approval_reviewed
Sent when an operator approves or denies a client request from the Cloud dashboard. This syncs the decision back to the WordPress site so the local approval record is updated.
| Field | Type | Description |
|---|---|---|
approval_id | integer | The local WordPress approval ID |
status | string | New status: approved or denied |
note | string | Optional reviewer note explaining the decision |
{
"action": "approval_reviewed",
"data": {
"approval_id": 42,
"status": "approved",
"note": "Safe to proceed — plugin is on our approved list"
}
}
When processed, AdminLocks updates the local approval record's status and note, then logs an audit event recording that the approval was reviewed via Cloud.
create_snapshot
Triggers a new site snapshot on the WordPress site. This is commonly used before deploying changes or as part of a scheduled maintenance workflow from Cloud.
| Field | Type | Description |
|---|---|---|
label | string | Human-readable label for the snapshot |
{
"action": "create_snapshot",
"data": {
"label": "Pre-deployment snapshot — March 2026"
}
}
The snapshot is created synchronously. The webhook response includes the snapshot ID and file path on success, or an error message if snapshot creation fails (e.g., insufficient disk space).
update_policy
Deploys a new policy or updates an existing one on the WordPress site. Policies are matched by slug — if a policy with the given slug already exists, it is updated (upsert behavior). If no matching slug is found, a new policy is created.
| Field | Type | Description |
|---|---|---|
slug | string | Unique policy identifier used for matching |
name | string | Display name for the policy |
description | string | Human-readable description |
rules | object | Policy rules (deny_menus, deny_pages, deny_capabilities) |
roles | string | Comma-separated target roles |
is_active | boolean | Whether the policy should be active immediately |
priority | integer | Evaluation priority (lower numbers run first) |
{
"action": "update_policy",
"data": {
"slug": "agency-standard-v2",
"name": "Agency Standard v2",
"description": "Updated agency lockdown policy deployed from Cloud",
"rules": {
"deny_menus": ["plugins.php", "themes.php", "users.php", "tools.php"],
"deny_pages": ["plugin-install.php", "theme-install.php"],
"deny_capabilities": [
"install_plugins", "activate_plugins", "delete_plugins",
"install_themes", "switch_themes", "delete_themes",
"create_users", "delete_users", "promote_users"
]
},
"roles": "editor,author",
"is_active": true,
"priority": 10
}
}
The upsert-by-slug behavior makes it safe to send the same policy to multiple sites. Each site will create the policy on first receipt and update it on subsequent deliveries, ensuring all sites stay in sync with the Cloud-defined policy.
Testing
You can test the webhook endpoint locally using curl. First, compute the HMAC signature, then send the request with the signature header.
Step 1: Compute the Signature
# Set your API key and payload
API_KEY="your_cloud_api_key_here"
PAYLOAD='{"action":"create_snapshot","data":{"label":"Test snapshot"}}'
# Compute HMAC-SHA256
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$API_KEY" | awk '{print $2}')
echo "Signature: $SIGNATURE"
Step 2: Send the Webhook
curl -X POST "https://yoursite.com/wp-json/adminlocks/v1/cloud-webhook" \
-H "Content-Type: application/json" \
-H "X-AdminLocks-Signature: $SIGNATURE" \
-d "$PAYLOAD"
Expected Responses
| Status | Meaning |
|---|---|
200 OK | Action processed successfully |
200 OK | Unknown action (ignored gracefully) |
403 Forbidden | Invalid or missing HMAC signature |
400 Bad Request | Malformed JSON or missing required fields |
500 Internal Server Error | Processing failed (check debug.log) |
Testing approval_reviewed
PAYLOAD='{"action":"approval_reviewed","data":{"approval_id":1,"status":"approved","note":"Test approval from CLI"}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$API_KEY" | awk '{print $2}')
curl -X POST "https://yoursite.com/wp-json/adminlocks/v1/cloud-webhook" \
-H "Content-Type: application/json" \
-H "X-AdminLocks-Signature: $SIGNATURE" \
-d "$PAYLOAD"
Testing update_policy
PAYLOAD='{"action":"update_policy","data":{"slug":"test-policy","name":"Test Policy","description":"Deployed via webhook test","rules":{"deny_menus":["plugins.php"]},"roles":"editor","is_active":false,"priority":99}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$API_KEY" | awk '{print $2}')
curl -X POST "https://yoursite.com/wp-json/adminlocks/v1/cloud-webhook" \
-H "Content-Type: application/json" \
-H "X-AdminLocks-Signature: $SIGNATURE" \
-d "$PAYLOAD"
When testing update_policy, set is_active to false to avoid accidentally enforcing restrictions. You can activate the policy through the WordPress admin once you have verified it was created correctly.