Docs Evenzo – Events Manager for WooCommerce

Webhooks

Webhooks#

Webhooks send a POST request to an external URL when something happens in the plugin. Use them to connect other services, trigger automations, or sync data.

How webhooks work#

  1. You create a webhook through the REST API with a URL and one topic
  2. When that topic fires, the plugin queues a delivery with WP-Cron
  3. Your server receives a signed JSON payload

There is no admin screen for webhooks. Management is done through the REST API only. The Webhooks checkbox under Events > Settings > Advanced is saved but not read by the delivery code, so it has no effect.

Managing webhooks#

All routes except topics require the manage_options capability. See the REST API page for authentication.

MethodRoutePurpose
GET/wp-json/emwc/v1/webhooksList webhooks
POST/wp-json/emwc/v1/webhooksCreate a webhook
GET/wp-json/emwc/v1/webhooks/{id}Get one webhook
PUT/wp-json/emwc/v1/webhooks/{id}Update a webhook
DELETE/wp-json/emwc/v1/webhooks/{id}Delete a webhook
POST/wp-json/emwc/v1/webhooks/{id}/testSend a test delivery
GET/wp-json/emwc/v1/webhooks/topicsList topics (public)

Webhook fields#

FieldRequiredDescription
nameOn createReference name
urlOn createDelivery URL
topicOn createOne topic per webhook (see below)
secretNoSigning secret. A random 32-character secret is generated if omitted
statusNoactive (default), paused, or disabled

Responses also include id, created_at, updated_at, delivery_url, and failures. The secret is returned in GET responses, so restrict who holds manage_options.

Create a webhook#

curl -X POST \
  -H "Content-Type: application/json" \
  -u admin:application_password \
  -d '{"name":"CRM sync","url":"https://example.com/hook","topic":"attendee.created","secret":"my-secret"}' \
  https://yoursite.com/wp-json/emwc/v1/webhooks

Test a webhook#

curl -X POST -u admin:application_password \
  https://yoursite.com/wp-json/emwc/v1/webhooks/1/test

The test is sent straight away (not through cron) with topic test and "test": true in the payload. The response contains success and response_code. Test deliveries count toward the failure limit described below.

Webhooks are stored in the emwc_webhooks option. The last 100 deliveries are stored in the emwc_webhook_logs option. There is no endpoint for reading the log.

Topics#

Only one topic can be set per webhook. Create several webhooks to cover several topics.

Topics that fire#

TopicFires when
event.createdAn event is created in the admin or through the REST API
event.updatedAn event is saved in the admin or updated through the REST API
event.deletedAn event is permanently deleted, or deleted through the REST API
attendee.createdAn attendee record is created when tickets are generated for an order
attendee.updatedAn attendee is updated through PUT /attendees/{id}
attendee.checked_inAn attendee is checked in (admin or REST)
attendee.checked_outAn attendee is checked out (admin or REST)

Events created or updated through the REST API fire once, from the REST action.

Topics that are listed but never fire#

These are accepted by the topic field and appear in /webhooks/topics, but nothing in the plugin triggers them: attendee.cancelled, ticket.sold, ticket.refunded, capacity.reached, event.reminder.

Webhook payload#

Payload structure#

{
  "topic": "attendee.created",
  "webhook_id": 1,
  "timestamp": "2025-01-15 10:30:00",
  "data": {}
}

timestamp is the site’s local time in Y-m-d H:i:s format.

Event payload (event.created, event.updated)#

{
  "data": {
    "id": 123,
    "title": "Tech Conference",
    "status": "publish",
    "start_date": "2025-03-15",
    "start_time": "09:00",
    "end_date": "2025-03-15",
    "end_time": "17:00",
    "venue_id": "45",
    "organizer_id": "12",
    "capacity": "500",
    "event_status": "scheduled",
    "url": "https://yoursite.com/events/tech-conference/"
  }
}

Event deleted payload#

{
  "data": {
    "id": 123,
    "force": true
  }
}

Attendee payload (all attendee.* topics)#

{
  "data": {
    "id": 456,
    "event_id": 123,
    "event_name": "Tech Conference",
    "order_id": 789,
    "first_name": "John",
    "last_name": "Doe",
    "email": "john@example.com",
    "ticket_code": "EMWC-ABC123DEF",
    "status": "checked_in"
  }
}

Security#

Request headers#

HeaderValue
Content-Typeapplication/json
X-EMWC-Webhook-IDWebhook ID
X-EMWC-TopicTopic name
X-EMWC-SignatureHMAC-SHA256 of the raw body, hex encoded
X-EMWC-DeliveryUnique delivery UUID
X-EMWC-TimestampSame value as timestamp in the payload

Signature verification#

The signature is the plain hex digest. There is no sha256= prefix.

$payload   = file_get_contents( 'php://input' );
$signature = $_SERVER['HTTP_X_EMWC_SIGNATURE'];

$expected = hash_hmac( 'sha256', $payload, $your_secret );

if ( hash_equals( $expected, $signature ) ) {
    // Valid webhook
}

HTTPS#

Use HTTPS URLs. SSL certificates are verified on delivery.

Delivery#

Timing#

Deliveries are queued with wp_schedule_single_event on the emwc_deliver_webhook hook and sent on the next WP-Cron run. If WP-Cron is disabled on your site, make sure a system cron calls wp-cron.php.

Timeout#

Requests time out after 30 seconds and follow up to 5 redirects. Return a 2xx status quickly and process the payload afterwards.

Retries#

Each delivery is attempted once. The plugin contains a retry queue handler on the emwc_webhook_retry hook, but nothing schedules it and nothing adds failed deliveries to the queue, so failed deliveries are not retried.

Failure handling#

ResponseResult
Status below 400Success, failure counter reset to 0
Status 400 or higherLogged as failure, counter incremented
Connection error or timeoutLogged as failure, counter incremented

After 5 consecutive failures the webhook status is set to disabled. Set status back to active with a PUT request to resume.

Pausing#

Set status to paused to stop deliveries. Paused webhooks skip triggers; nothing is queued for later.

Use cases#

Slack notifications#

  1. Create a Slack Incoming Webhook
  2. Create a webhook with the Slack URL and topic attendee.created
  3. New registrations post to Slack

Zapier integration#

  1. Create a Zapier webhook trigger
  2. Use the Zapier URL as the webhook url
  3. Build Zaps with the payload data

CRM sync#

  1. Create an endpoint in your CRM or middleware
  2. Create a webhook with topic attendee.created
  3. Create contacts from the payload

Troubleshooting#

Webhook not firing#

  • Check status is active (it becomes disabled after 5 failures)
  • Check the topic is one that fires (see the list above)
  • Check WP-Cron is running on the site
  • Check the REST API is enabled under Events > Settings > Advanced

Delivery failures#

  • Check the URL is reachable and the certificate is valid
  • Check the server responds within 30 seconds
  • Read the emwc_webhook_logs option for response codes and error messages

Signature validation failing#

  • Verify the secret matches the one stored on the webhook
  • Hash the raw request body, not re-encoded JSON
  • Compare against the plain hex digest without a prefix