ytec/module-webhook

Configurable outgoing webhooks for Magento 2 with Liquid payload templating, conditions, authorization, retries and delivery logs.

Maintainers

Package info

github.com/matheusmarqui1/magento2-ytec-webhook

Type:magento2-module

pkg:composer/ytec/module-webhook

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-07-22 15:01 UTC

This package is auto-updated.

Last update: 2026-07-30 07:53:03 UTC


README

Configurable outgoing webhooks for Magento 2.

Ytec_Webhook lets a store define, from the admin panel, HTTP callbacks that fire when sales events happen. Each webhook has its own target URL, method, headers, authorization scheme, Liquid-templated payload, trigger conditions, store scoping and retry policy. Deliveries are queued, retried with configurable backoff, and every attempt is logged with the full request/response for auditing.

  • Vendor: ytec/module-webhook
  • Version: 1.0.0
  • Magento: 2.4.x (Open Source & Commerce)
  • PHP: 8.1 / 8.2 / 8.3
  • License: MIT

Table of contents

Features

  • Admin-managed webhooks. Create, duplicate, enable/disable and delete webhook configurations from a grid + form UI, no code or deployment required.
  • Liquid payload templating. Bodies and URLs are Liquid templates rendered against the triggering entity, with an in-form variable suggestion panel built by reflecting the Magento sales API interfaces and the entity's database columns.
  • Condition builder. Nested AND/OR condition groups with 21 operators decide whether a given entity actually fires the webhook.
  • Six authorization schemes. None, Basic, Bearer, API key, OAuth 1.0a (signed) and OAuth 2.0 (client credentials, with token fetching).
  • Asynchronous delivery. Messages are published to a queue and delivered by a consumer, so the checkout/admin request is never blocked. A per-webhook "send immediately" flag is available when synchronous delivery is required.
  • Configurable retries. Per-webhook retry toggle, max attempts, explicit interval sequence and the list of HTTP status codes considered retryable.
  • Full delivery log. Every attempt stores URL, method, headers, body, response status, response headers, response body, duration in milliseconds and any error, with a fulltext index for searching.
  • Health dashboard. Success/failure rates and delivery trends over time.
  • Manual retry. Retry a failed message from the admin grid, individually or in bulk.
  • Store scoping. Each webhook can be limited to specific store views.
  • Built-in test endpoint. A REST endpoint that answers 200 most of the time and 500/504 occasionally, so retry behaviour can be exercised without a third-party server.
  • Simulation command. Generate real webhook messages in bulk from the CLI to load-test a configuration.

Requirements

  • Magento 2.4.x (Open Source or Commerce)
  • PHP 8.1, 8.2 or 8.3
  • ext-curl
  • ytec/base (provides the shared ModuleConfiguration and the admin menu/ACL root)
  • liquid/liquid ^1.4.8
  • A running message-queue consumer (AMQP/RabbitMQ or the DB queue) for asynchronous delivery
  • Magento cron running, for retries and log cleanup

Installation

Composer (from this repository)

composer config repositories.ytec-webhook vcs https://github.com/matheusmarqui1/magento2-ytec-webhook
composer require ytec/module-webhook

Manual

Copy the module to app/code/Ytec/Webhook, then install the ytec/base and liquid/liquid dependencies with Composer.

Enable

bin/magento module:enable Ytec_Base Ytec_Webhook
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy
bin/magento cache:flush

Start the queue consumer (or let consumers_runner do it via cron):

bin/magento queue:consumers:start ytec.webhook.message.send

Configuration

Stores → Configuration → Ytec → Webhook (ytec_webhook):

Path Default Scope Description
ytec_webhook/general/enabled 0 Store view Master switch for the module.
ytec_webhook/logs/enabled 1 Default Record a log row per delivery attempt.
ytec_webhook/logs/retention_hours 730 Default Hours to keep logs before the cleanup cron deletes them (~30 days).

Webhooks themselves are managed under Ytec → Webhook → Webhook Configurations. Each configuration holds:

  • name, enabled flag and target store views
  • the trigger event
  • request configuration: URL, HTTP method, headers, body template and authorization
  • payload parsing timing
  • trigger conditions
  • retry policy: enabled, max retries, interval sequence, retryable status codes

Triggers

Webhooks are bound to one sales event each:

Trigger Magento event Fires when
Invoice creation sales_order_invoice_save_after An invoice is saved
Invoice pay sales_order_invoice_pay An invoice is paid
Invoice register sales_order_invoice_register An invoice is registered against the order
Credit memo creation sales_order_creditmemo_save_after A credit memo is saved
Credit memo refund sales_order_creditmemo_refund A credit memo is refunded

Entity types understood by the payload/entity loader are invoice, order, shipment and credit_memo.

Delivery lifecycle

  1. An observer catches the sales event and hands the entity to WebhookTriggerHandler.
  2. The handler loads every enabled webhook configuration bound to that trigger and applicable to the entity's store, deduplicating within the request.
  3. Conditions are evaluated against the entity. A webhook whose conditions do not match is skipped.
  4. A ytec_webhook_message row is created with status pending.
  5. The message is published to the ytec.webhook.message.send queue, unless the configuration has send immediately enabled, in which case it is sent inline.
  6. The consumer renders the payload (if the parsing timing is at sending), applies authorization, performs the request and writes a log row.
  7. On success the message becomes processed and delivered_at is stamped. On a retryable failure it becomes failed with a next_retry_at. Once the attempts are used up it becomes exhausted.

Message statuses: pending, processing, processed, failed, exhausted, error.

Payload templating

Bodies and URLs are Liquid templates. The admin form ships a payload editor and a URL editor with a variable-suggestion panel; suggestions are derived at runtime from the relevant Magento interface (OrderInterface, InvoiceInterface, CreditmemoInterface, ShipmentInterface, their item and address interfaces) plus the entity table columns, so they stay accurate even with custom attributes.

{
  "invoice": "{{ invoice.increment_id }}",
  "order": "{{ invoice.order.increment_id }}",
  "grand_total": {{ invoice.grand_total }},
  "customer_email": "{{ invoice.order.customer_email }}",
  "items": [
    {% for item in invoice.items %}
      { "sku": "{{ item.sku }}", "qty": {{ item.qty }} }{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ]
}

URLs are templated too, so a webhook can post to a per-entity path:

https://api.example.com/webhook/{{ invoice.increment_id }}

Parsing timing

Value Behaviour
webhook_trigger Render the body when the event fires and store the rendered payload on the message. The payload reflects the entity exactly as it was at trigger time.
at_sending Render the body when the message is actually delivered. The payload reflects the entity's current state, which matters for delayed or retried deliveries.

Conditions

Conditions are nested groups combined with AND / OR, each leaf comparing a resolved entity field against a value.

Available operators: eq, neq, gt, gte, lt, lte, contains, not_contains, starts_with, ends_with, regex, in, not_in, is_null, is_not_null, is_empty, is_not_empty, is_true, is_false.

Field paths use the same dotted notation as the payload templates, for example invoice.order.customer_group_id or invoice.grand_total.

Authorization

Selected per webhook in the authorization editor. Secret values are stored encrypted.

Type What it does
none No authorization header.
basic HTTP Basic with username/password.
bearer Authorization: Bearer <token>.
api_key Key/value pair sent as a header or query parameter.
oauth1 OAuth 1.0a request signing (RFC 5849).
oauth2 OAuth 2.0 client credentials: fetches an access token from the token endpoint and attaches it.

New schemes are added by implementing AuthorizationApplierInterface and registering the class in the AuthorizationApplierFactory DI pool.

Retries

Retries are configured per webhook:

  • Retry enabled toggles the whole mechanism.
  • Max retries caps the number of automatic attempts (default 5).
  • Retry intervals is a comma-separated list of delays in seconds. Attempt n uses interval n; once the list is exhausted, the last value repeats. For example 60,300,900 means retry after 1 minute, then 5 minutes, then every 15 minutes. Left empty it falls back to 60,300,900,3600,14400.
  • Retryable status codes is a comma-separated list of HTTP status codes that count as retryable; anything else is a permanent failure. Left empty it falls back to 408,429,500,502,503,504.

The ytec_webhook_retry_scheduler cron runs every minute and re-queues messages whose next_retry_at has passed. Manual retries from the admin grid are counted separately from automatic ones.

Logging and retention

Every attempt writes a ytec_webhook_message_log row containing the request URL, method, headers and body, the response status, headers and body, the round-trip duration in milliseconds, a success flag and any error message. A fulltext index over the URL, bodies and error message makes the log grid searchable.

Logs older than ytec_webhook/logs/retention_hours are deleted daily at 03:00 by the ytec_webhook_log_cleanup cron. Logging can be turned off entirely.

Health dashboard

Ytec → Webhook → Health Dashboard charts delivery volume and success/failure rates over time, per webhook configuration. The chart library is loaded from a CDN; on a store with a strict CSP or no outbound access, either allow the CDN or bundle the library locally.

Test endpoint

The module exposes a REST endpoint that simulates a flaky third party, useful for exercising retry configuration end to end:

POST /rest/V1/ytec-webhook/test
GET  /rest/V1/ytec-webhook/test

It answers 200 roughly 98% of the time and 500 or 504 the rest of the time. It is protected by the Ytec_Webhook::webhook_test ACL resource.

CLI commands

# Run the retry scheduler on demand (processes messages due for retry)
bin/magento ytec:webhook:retry-scheduler

# Create real webhook messages for a configuration, to load-test it
bin/magento ytec:webhook:simulate \
    --id=1 \            # -i  webhook configuration ID (required)
    --count=50 \        # -c  number of messages to create
    --delay=100 \       # -d  delay between creations, in milliseconds
    --start=1000 \      # -s  starting entity ID for the simulated entities
    --entity=invoice \  # -e  invoice | order | shipment | credit_memo
    --immediate         #     send inline instead of publishing to the queue

Cron jobs

Job Schedule Purpose
ytec_webhook_retry_scheduler * * * * * Re-queue messages whose retry time has come.
ytec_webhook_log_cleanup 0 3 * * * Delete logs past the retention window.

Message queue

Topic Exchange Queue Consumer
ytec.webhook.message.send magento ytec.webhook.message.send ytec.webhook.message.send

ACL resources

Nested under Ytec_Base::base_module:

Ytec_Webhook::webhook
├── Ytec_Webhook::webhook_config
│   ├── Ytec_Webhook::webhook_config_save
│   └── Ytec_Webhook::webhook_config_delete
├── Ytec_Webhook::webhook_dashboard
├── Ytec_Webhook::webhook_messages
├── Ytec_Webhook::webhook_logs
├── Ytec_Webhook::webhook_test
└── Ytec_Webhook::webhook_alerting
    ├── Ytec_Webhook::webhook_alerting_manage
    └── Ytec_Webhook::webhook_alerting_config

The webhook_alerting branch is declared here as an extension point for a companion alerting module; it is inert without one.

Database schema

Table Purpose
ytec_webhook_config Webhook definitions: name, enabled, store IDs, trigger, send-immediately flag, parsing timing, request config (JSON), conditions (JSON) and retry policy.
ytec_webhook_message One row per triggered delivery: config, trigger, entity reference, status, rendered body, automatic/manual retry counters, next retry and delivery timestamps. Cascades on config delete.
ytec_webhook_message_log One row per attempt: attempt number, request URL/method/headers/body, response status/headers/body, duration, success flag and error message. Cascades on message and config delete.

Extending the module

The module is built around interfaces in Ytec\Webhook\Api, all wired through etc/di.xml preferences, so behaviour can be replaced without touching the module:

Interface Responsibility
WebhookTriggerHandlerInterface Turn an event + entity into webhook messages.
BodyResolverInterface / BodyParserInterface Build the template context for an entity type.
ConditionEvaluatorInterface / FieldResolverInterface Evaluate conditions and resolve field paths.
AuthorizationApplierInterface Apply an authorization scheme to an outgoing request.
WebhookSenderInterface Perform the HTTP request and log the attempt.
BackoffCalculatorInterface Decide whether and when to retry.
EntityLoaderInterface Reload an entity by type and ID at send time.
DashboardDataProviderInterface Feed the health dashboard.

To support a new entity type, add a BodyParserInterface implementation and register it in the BodyParserFactory DI pool; to support a new event, add an observer that calls WebhookTriggerHandlerInterface::handle() and a case on the WebhookTrigger enum.

License

MIT. See LICENSE.