Outbound Webhooks
Benecaster can notify external systems when subscription events occur by sending HTTP POST requests to a URL you configure. Use this to trigger Zapier or Make automations, sync subscriber data to a CRM, update an external database, or drive any custom integration without polling the REST API.
Configuration
Webhook settings are configured via the REST API. A Settings UI is planned for a future release.
Set the webhook URL
POST /wp-json/benecaster/v1/webhook-settings
Content-Type: application/json
Authorization: Bearer <application-password>
{
"url": "https://your-receiver.example.com/hooks/benecaster",
"enabled": true
}
To disable delivery without removing the URL:
POST /wp-json/benecaster/v1/webhook-settings
{
"enabled": false
}
To retrieve current settings:
GET /wp-json/benecaster/v1/webhook-settings
Generate a signing secret
Webhooks are signed with a shared HMAC secret. Generate your secret with:
POST /wp-json/benecaster/v1/webhook-settings/rotate
The raw secret is returned once in this response and never again. Copy it immediately and store it securely in your receiving application. If you lose it, rotate to get a new one — the old secret is invalidated immediately.
Verifying Signatures
Every request Benecaster sends includes an X-Benecaster-Signature header:
X-Benecaster-Signature: sha256=<hex-encoded HMAC-SHA256>
To verify the request came from Benecaster and was not tampered with, compute the HMAC-SHA256 of the raw request body using your shared secret and compare it to the value in the header. Use a constant-time comparison to prevent timing attacks.
PHP example:
function verify_benecaster_webhook( string $raw_body, string $signature_header, string $secret ): bool {
$expected = 'sha256=' . hash_hmac( 'sha256', $raw_body, $secret );
return hash_equals( $expected, $signature_header );
}
Node.js example:
const crypto = require('crypto');
function verifyBenecasterWebhook(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
Always verify the signature before processing the payload. Reject requests with a missing or invalid signature.
Payload Format
All events share a common base structure:
{
"event": "subscription.activated",
"show_id": 42,
"user_id": 1007,
"email": "subscriber@example.com",
"tier_slug": "silver",
"occurred_at": "2026-08-21T14:32:00Z"
}
| Field | Type | Description |
|---|---|---|
event |
string | Event name — see Event Catalog below |
show_id |
integer | WordPress post ID of the show |
user_id |
integer | WordPress user ID of the subscriber |
email |
string | Subscriber email address |
tier_slug |
string | The subscriber’s current tier slug after this event |
occurred_at |
string | ISO 8601 UTC timestamp |
Event-specific fields are included alongside these base fields where applicable.
Raw tokens are never included in any webhook payload. The token.generated and token.reset events confirm that a token action occurred, but do not expose the token value.
Event Catalog
| Event | When it fires |
|---|---|
subscription.activated |
A subscriber’s membership becomes active for the first time |
subscription.cancelled |
A subscriber’s membership is cancelled |
subscription.tier_changed |
A subscriber moves from one tier to another (upgrade or downgrade) |
subscription.payment_failed |
A recurring payment attempt fails |
token.generated |
A new feed token is created for a subscriber |
token.reset |
A subscriber’s feed token is reset (admin or self-service) |
subscription.tier_changed
Includes previous_tier_slug alongside the base fields:
{
"event": "subscription.tier_changed",
"show_id": 42,
"user_id": 1007,
"email": "subscriber@example.com",
"tier_slug": "gold",
"previous_tier_slug": "silver",
"occurred_at": "2026-08-21T14:32:00Z"
}
Delivery Behavior
Webhook dispatch is fire-and-forget. Benecaster sends the request and does not retry on failure. Your receiver should be idempotent — designed to handle the same event delivered more than once without side effects.
Benecaster does not queue or batch events. Each event fires one HTTP request as soon as the triggering action completes.
Developer Filter
Add-ons and custom code can modify or suppress outgoing webhook payloads using the benecaster_webhook_payload filter:
add_filter( 'benecaster_webhook_payload', function( array $payload, string $event ): ?array {
// Return null to suppress this delivery entirely.
if ( $event === 'token.reset' ) {
return null;
}
// Add custom fields to the payload.
$payload['site_id'] = get_current_blog_id();
return $payload;
}, 10, 2 );
Returning null suppresses the delivery for that event. Returning the array (modified or unchanged) allows it to proceed.
See the benecaster_webhook_payload filter reference for full argument documentation.