Webhooks
PlusWase calls the plugin's REST routes on the connected site, each request signed with the connection's shared secret.
Routes
| Request | Sent when | Body |
|---|---|---|
POST/wp-json/plw/v1/purge | A product published to the site was saved, published, unpublished, moved to To be removed or restored, or the image domain changed. | { "scope": "all" } |
POST/wp-json/plw/v1/verify | The owner presses Connect. | { "nonce": "<32 hex>" } |
POST/wp-json/plw/v1/fulfillment, /tracking | Not sent yet; they answer 501 until shipments are tracked in PlusWase. |
purge drops the plugin's cached catalog answers, so the site shows the change within a minute. verify answers { "proof": hex HMAC-SHA256(shared secret, "verify.<nonce>") }, which proves the plugin holding this connection's secret runs on the domain.
Signature
| Header | Value |
|---|---|
x-plw-timestamp | Unix seconds when PlusWase sent it. |
x-plw-signature | sha256= followed by the hex HMAC-SHA256 of <timestamp>.<raw body> with the shared secret. |
Refuse a request whose timestamp is more than 5 minutes away from now, and compare signatures in constant time. This is what the plugin does:
function plw_verify_webhook(WP_REST_Request $req): bool {
$secret = plw_shared_secret();
if ($secret === '') return false;
$sig = (string) $req->get_header('x-plw-signature');
$ts = (int) $req->get_header('x-plw-timestamp');
if ($sig === '' || !$ts || abs(time() - $ts) > 300) return false;
return hash_equals('sha256=' . hash_hmac('sha256', $ts . '.' . $req->get_body(), $secret), $sig);
}
Delivery
- Purges are queued on PlusWase's worker. Several changes to one site while a purge waits become one call.
- A purge that fails (no answer, or not 2xx) is retried after 1, 5 and 15 minutes, 1 hour and 6 hours.
- Only connected sites are called, and only on addresses that resolve to public IPs. Redirects are not followed.