Skip to main content
The Webhooks module has two distinct functions:
  1. Outgoing webhooks — the plugin POSTs a notification payload to an external URL whenever a product order reaches a configured status (completed, processing, refunded, etc.).
  2. Inbound REST API — the plugin exposes a GET /wp-json/utb/v1/orders endpoint that external systems can query at any time to fetch complete order data.
These operate independently. The REST API is always active as long as an API Key is configured. Webhooks are opt-in per product. In the WordPress admin sidebar, click UTB Builder, then click Webhooks. The page opens on the Configuración tab and shows summary statistics for the last 24 hours:
StatDescription
Exitosos (24h)Webhook deliveries that received a 2xx HTTP response
Fallidos (24h)Deliveries that timed out or received a non-2xx response
Total EnviadosAll-time total webhook deliveries
Productos ActivosNumber of products with webhook delivery enabled

Configuring outgoing webhooks per product

Each WooCommerce product has its own webhook configuration row on the Configuración tab.
1

Enable the webhook toggle

Find the product in the list. Click the toggle switch on the left side of its row to turn webhook delivery ON. The row gains a green left border indicating it is active.
2

Select trigger events

Check the order status events that should trigger a delivery:
EventWooCommerce status
Orden Completadacompleted
Orden Procesandoprocessing
Orden Pendientepending
Orden Reembolsadarefunded
Orden Canceladacancelled
Orden Fallidafailed
Orden Completada is selected by default.
3

Enter the destination URL

Type the full HTTPS URL of the external endpoint in the URL del Webhook field:
https://erp.external-system.com/api/inscripciones
4

Enter a notification email (optional)

If you want an email alert on each delivery, enter an address in the Email de Notificación field.
5

Configure authentication (if required)

Under Autenticación de API, select the auth type your destination endpoint expects:
  • Ninguna — no authorization header
  • Bearer Token — sends Authorization: Bearer <token>; enter the token in the field that appears
  • Basic Auth — sends Authorization: Basic <base64>; enter usuario:password in the token field
  • OAuth 2.0 (Client Credentials) — enter the token endpoint URL, credentials (client_id:secret or pre-encoded Base64), and optional scope; the plugin fetches a token before each delivery
6

Enable a custom JSON payload (optional)

By default, the plugin sends the full order data object. To send a smaller, custom-shaped payload, check Usar JSON Personalizado and write a template in the text area:
{
  "proceso": "wc.orden.completado",
  "id_unico": {order_id},
  "accion": "patch",
  "fecha_envio": "{date_iso}"
}
Available template variables:
VariableValue
{order_id}WooCommerce order ID
{order_key}Unique order key
{order_status}Order status string
{order_total}Order total amount
{order_currency}Currency code (COP, USD, etc.)
{date_iso}Current date in ISO 8601 format
{date_mysql}Current date in MySQL format (Y-m-d H:i:s)
{customer_id}WordPress user ID
{customer_email}Billing email address
{customer_name}Full billing name
{product_id}WooCommerce product ID
{product_name}Product name
{product_sku}Product SKU
{flow_id}Assigned flow ID (utb_cep_programs, certificados_academicos, etc.)
String variables must be wrapped in double quotes in your template. Numeric variables ({order_id}, {order_total}, {customer_id}, {product_id}) do not need quotes.
7

Save the configuration

Click Guardar Configuración at the bottom of the page. Configuration is stored in the _utb_order_metadata post meta on each product.

Webhook payload structure

When no custom JSON template is configured, the plugin sends the full order data object built by OrderDataBuilder::build_complete_order_data(). The payload is a JSON object with at minimum:
{
  "order": {
    "id": 12345,
    "status": "completed",
    "total": "150000",
    "currency": "COP",
    "date_created": "2026-03-18T10:30:00"
  },
  "customer": {
    "id": 42,
    "email": "[email protected]",
    "first_name": "Ana",
    "last_name": "García"
  },
  "items": [
    {
      "product_id": 789,
      "product_name": "Programa de Liderazgo",
      "sku": "CEP-LID-2026",
      "flow_id": "utb_cep_programs",
      "form_data": { ... }
    }
  ]
}

HMAC signature validation

Every webhook delivery includes an X-UTB-Webhook-Signature header containing an HMAC-SHA256 signature of the payload:
X-UTB-Webhook-Signature: <sha256_hex>
X-UTB-Webhook-ID: <order_id>
X-UTB-Webhook-Attempt: <attempt_number>
To verify the signature on the receiving server, compute:
$expected = hash_hmac('sha256', $raw_body, UTB_WEBHOOK_SECRET);
hash_equals($expected, $received_signature); // true if valid
The secret key is read from UTB_WEBHOOK_SECRET defined in wp-config.php. If that constant is not defined, the plugin falls back to the WordPress auth salt. Set a dedicated secret in wp-config.php:
define('UTB_WEBHOOK_SECRET', 'your-random-secret-here');
If UTB_WEBHOOK_SECRET is not defined, the plugin uses the WordPress auth salt as the signing key. This still provides signature integrity, but rotating the secret requires updating the salt, which invalidates all other WordPress security tokens. Always define UTB_WEBHOOK_SECRET explicitly.

Retry logic

If a delivery fails (network error, timeout, or a 5xx HTTP response), the plugin schedules automatic retries using WordPress Cron (wp_schedule_single_event):
AttemptDelay after previous attempt
1Immediate (on order status change)
25 seconds
315 seconds
4 (final)60 seconds
The maximum number of attempts is 3 retries (4 total including the initial attempt). Client-side errors (4xx responses) are not retried — only server-side errors (5xx) and connection timeouts trigger the retry schedule.
Retry scheduling depends on WordPress Cron running. If your site uses a real system cron (DISABLE_WP_CRON = true) instead of the default WordPress pseudo-cron, ensure your cron daemon calls wp-cron.php on a regular interval (every 1–2 minutes) so retries fire on schedule.

Viewing webhook logs

Click the Logs (últimos 100) tab on the Webhooks page to see recent delivery history.

Log filters

Filter entries by status:
  • Todos — all entries
  • Exitosos — successful deliveries (HTTP 2xx)
  • Fallidos — failed deliveries
  • Rastreo — internal debug trace entries

Log columns

ColumnDescription
OrdenWooCommerce order ID, linked to its edit page
Estadosuccess, failed, or debug
IntentoAttempt number out of 3
HTTP CodeResponse code received from the destination
URL DestinoThe endpoint the plugin sent the request to
FechaTimestamp and relative time (“2 minutes ago”)
AccionesView full payload details; resend button for failed entries

Database table

All log entries are written to the wp_utb_webhook_logs table with these columns:
CREATE TABLE wp_utb_webhook_logs (
  id          INT AUTO_INCREMENT PRIMARY KEY,
  order_id    INT NOT NULL,
  webhook_url VARCHAR(500),
  status      VARCHAR(20),   -- 'success', 'failed', 'debug'
  attempt     TINYINT,
  response_code SMALLINT,
  response_body TEXT,
  error_message TEXT,
  created_at  DATETIME
);
You can query this table directly in tools like phpMyAdmin or WP-CLI:
SELECT * FROM wp_utb_webhook_logs
WHERE status = 'failed'
ORDER BY created_at DESC
LIMIT 50;

REST API info panel per product

Each product row on the Configuración tab has a collapsible Consultar REST API panel. Expand it to find:
  • The GET endpoint URL pre-filled with this product’s ID:
    /wp-json/utb/v1/orders?product_id=<ID>
    
  • The configured API Key (masked, with a copy button)
  • Available query filter parameters:
    • ?status=completed — filter by order status
    • ?date_from=2026-01-01 — orders from this date
    • ?per_page=50 — results per page

Configuring the REST API key

Click the Configurar API Key tab to manage the UTB_API_KEY constant used to authenticate REST API requests. The REST API accepts the key via either:
GET /wp-json/utb/v1/orders
X-UTB-API-Key: your_api_key_here
or:
GET /wp-json/utb/v1/orders
Authorization: Bearer your_api_key_here
The Bearer format is provided as a workaround for environments where Microsoft Azure SSO plugins strip custom headers. To define the API key, add the following to wp-config.php before the /* That's all, stop editing! */ line:
define('UTB_API_KEY', 'utb_your_secure_random_key_here');
The Configurar API Key tab can generate a secure key automatically and write it to wp-config.php if the file is writable by the web server. If the file is not writable, the tab provides the define() snippet to copy and paste manually.

Build docs developers (and LLMs) love