Custom Webhooks – WP Webhook Framework
2 Stars
View on GitHub
Create custom webhook implementations using the registry pattern.
Creating a Webhook
Section titled “Creating a Webhook”Extend the abstract Webhook class and implement init():
class Custom_Webhook extends \juvo\WP_Webhook_Framework\Webhook {
public function __construct() { parent::__construct( 'my_custom_webhook' );
$this->max_retries( 3 ) ->delivery_mode( \juvo\WP_Webhook_Framework\Delivery_Mode::SCHEDULED ) ->max_consecutive_failures( 5 ) ->timeout( 30 ) ->webhook_url( 'https://api.example.com/custom' ) ->headers( array( 'Authorization' => 'Bearer token123' ) ); }
public function init(): void { add_action( 'my_custom_action', array( $this, 'handle_action' ) ); }
public function handle_action( array $data ): void { $this->emit( 'action_triggered', 'custom', $data['id'], array( 'custom_data' => $data, 'timestamp' => time(), ) ); }}Registering Webhooks
Section titled “Registering Webhooks”add_action( 'wpwf_register_webhooks', function ( Webhook_Registry $registry ): void { $registry->register( new Custom_Webhook() );} );Configuration Methods
Section titled “Configuration Methods”| Method | Description | Default |
|---|---|---|
delivery_mode(Delivery_Mode) | Delivery strategy (SCHEDULED or IMMEDIATE) | Delivery_Mode::SCHEDULED |
max_retries(int) | Retry attempts per failed event | 0 |
max_consecutive_failures(int) | Failures before blocking (0 disables) | 10 |
timeout(int) | HTTP timeout in seconds (1-300) | 30 |
webhook_url(string) | Custom endpoint URL | None |
headers(array) | Additional HTTP headers | [] |
notifications(array) | Notification handlers to enable | [] |
See Configuration for detailed options.
Plugin Integration Examples
Section titled “Plugin Integration Examples”WooCommerce Orders
Section titled “WooCommerce Orders”class WooCommerce_Order_Webhook extends \juvo\WP_Webhook_Framework\Webhook {
public function __construct() { parent::__construct( 'woocommerce_orders' );
$this->max_consecutive_failures( 3 ) ->timeout( 45 ) ->webhook_url( 'https://api.example.com/woocommerce/orders' ); }
public function init(): void { add_action( 'woocommerce_new_order', array( $this, 'on_new_order' ) ); add_action( 'woocommerce_order_status_changed', array( $this, 'on_status_changed' ), 10, 4 ); }
public function on_new_order( int $order_id ): void { $order = wc_get_order( $order_id ); $this->emit( 'created', 'woocommerce_order', $order_id, array( 'total' => $order->get_total(), 'status' => $order->get_status(), ) ); }
public function on_status_changed( int $order_id, string $old_status, string $new_status ): void { $this->emit( 'status_changed', 'woocommerce_order', $order_id, array( 'old_status' => $old_status, 'new_status' => $new_status, ) ); }}Contact Form 7
Section titled “Contact Form 7”class CF7_Webhook extends \juvo\WP_Webhook_Framework\Webhook {
public function __construct() { parent::__construct( 'cf7_submissions' ); $this->timeout( 20 ); }
public function init(): void { add_action( 'wpcf7_mail_sent', array( $this, 'on_submit' ) ); }
public function on_submit( $contact_form ): void { $submission = \WPCF7_Submission::get_instance(); if ( ! $submission ) { return; }
$this->emit( 'submitted', 'cf7_form', $contact_form->id(), array( 'form_title' => $contact_form->title(), 'data' => $submission->get_posted_data(), ) ); }}Gravity Forms
Section titled “Gravity Forms”class Gravity_Forms_Webhook extends \juvo\WP_Webhook_Framework\Webhook {
public function __construct() { parent::__construct( 'gravity_forms' ); $this->timeout( 30 ); }
public function init(): void { add_action( 'gform_after_submission', array( $this, 'on_submit' ), 10, 2 ); }
public function on_submit( array $entry, array $form ): void { $this->emit( 'submitted', 'gravity_form', $form['id'], array( 'form_title' => $form['title'], 'entry_id' => $entry['id'], ) ); }}Multiple Endpoints for the Same Entity
Section titled “Multiple Endpoints for the Same Entity”Register additional instances of a built-in webhook class with a unique name. Each instance has its own URL, retry policy, timeout, and failure tracking:
add_action( 'wpwf_register_webhooks', function ( Webhook_Registry $registry ): void { $analytics = new \juvo\WP_Webhook_Framework\Webhooks\Post_Webhook( 'post_analytics' ); $analytics->webhook_url( 'https://analytics.example.com/posts' ) ->timeout( 10 ); $registry->register( $analytics );} );This is recommended over modifying the built-in webhooks whenever different endpoints require different configuration (timeouts, retries, headers, etc.).
See Configuration for a detailed example.
Conditional Registration
Section titled “Conditional Registration”Check plugin availability before registering:
add_action( 'wpwf_register_webhooks', function ( Webhook_Registry $registry ): void { if ( class_exists( 'WooCommerce' ) ) { $registry->register( new WooCommerce_Order_Webhook() ); }
if ( defined( 'WPCF7_VERSION' ) ) { $registry->register( new CF7_Webhook() ); }
if ( class_exists( 'GFForms' ) ) { $registry->register( new Gravity_Forms_Webhook() ); }} );Registry Methods
Section titled “Registry Methods”| Method | Returns | Description |
|---|---|---|
register(Webhook) | void | Register and initialize |
get(string) | Webhook|null | Get by name |
has(string) | bool | Check existence |
get_all() | array | Get all webhooks |
get_enabled() | array | Get enabled only |
unregister(string) | bool | Remove webhook |
Webhook Statefulness
Section titled “Webhook Statefulness”Webhook instances are singletons - never store per-emission data on the instance.
// CORRECT: Pass data directly to emit()public function handle( array $data ): void { $this->emit( 'action', 'entity', $data['id'], array( 'data' => $data ) );}
// WRONG: Storing on instance causes race conditionsprivate array $current_data; // NEVER DO THISSee Webhook Statefulness for details.
Best Practices
Section titled “Best Practices”- Only register what you need - registering a webhook activates it immediately
- Check plugin existence before registering integration webhooks
- Keep webhooks stateless - pass data to
emit(), don’t store on instance - Use meaningful names - lowercase with underscores
- Set delivery mode intentionally - use
IMMEDIATEonly when same-request delivery is required - Set appropriate timeouts - consider endpoint response times