Skip to main content

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.

See Also