Skip to content

Custom Webhooks – WP Webhook Framework

Create custom webhook implementations using the registry pattern.

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(),
) );
}
}
add_action( 'wpwf_register_webhooks', function ( Webhook_Registry $registry ): void {
$registry->register( new Custom_Webhook() );
} );
MethodDescriptionDefault
delivery_mode(Delivery_Mode)Delivery strategy (SCHEDULED or IMMEDIATE)Delivery_Mode::SCHEDULED
max_retries(int)Retry attempts per failed event0
max_consecutive_failures(int)Failures before blocking (0 disables)10
timeout(int)HTTP timeout in seconds (1-300)30
webhook_url(string)Custom endpoint URLNone
headers(array)Additional HTTP headers[]
notifications(array)Notification handlers to enable[]

See Configuration for detailed options.

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,
) );
}
}
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(),
) );
}
}
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'],
) );
}
}

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.

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() );
}
} );
MethodReturnsDescription
register(Webhook)voidRegister and initialize
get(string)Webhook|nullGet by name
has(string)boolCheck existence
get_all()arrayGet all webhooks
get_enabled()arrayGet enabled only
unregister(string)boolRemove webhook

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 conditions
private array $current_data; // NEVER DO THIS

See Webhook Statefulness for details.

  1. Only register what you need - registering a webhook activates it immediately
  2. Check plugin existence before registering integration webhooks
  3. Keep webhooks stateless - pass data to emit(), don’t store on instance
  4. Use meaningful names - lowercase with underscores
  5. Set delivery mode intentionally - use IMMEDIATE only when same-request delivery is required
  6. Set appropriate timeouts - consider endpoint response times
  • PHP100%