SeloraXDEVELOPERS

Event Topics

Event Topics

This page is the definitive reference for every webhook event topic on the SeloraX platform. For each topic you will find the exact trigger condition, the full JSON schema, a realistic example payload, and any important notes.


Envelope Structure

Every webhook delivery wraps the event-specific data inside a standard envelope:

{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_topic": "order.status_changed",
  "store_id": 42,
  "timestamp": "2026-03-01T15:00:00.000Z",
  "data": { }
}
FieldTypeDescription
event_idstring (UUID)Unique identifier for this event. Use for deduplication.
event_topicstringThe topic that triggered this delivery (e.g. order.created).
store_idintegerThe store where the event occurred.
timestampstring (ISO 8601)When the event was published.
dataobjectEvent-specific payload. Documented per-topic below.

The examples below show the complete delivered payload including the envelope.


Order Events

Order events are automatically enriched before delivery. Even if the internal system publishes a minimal payload (just order_id and status), the webhook pipeline fetches the full order context — customer name, phone, total, tracking, and store name — so your app always receives a complete payload.

Payload Enrichment

Order payloads are enriched from the delivery address first, then from the customer profile. This means customer_name and customer_phone reflect the shipping address when available, falling back to the customer's account details.


order.created

Fires when: A new order is placed through the storefront, checkout, or API.

Schema:

FieldTypeRequiredDescription
order_idintegerYesUnique order identifier
order_numberstringYesStore-facing serial order number (e.g. "1234")
customer_namestringYesCustomer name (from delivery address or profile)
customer_phonestringYesCustomer phone (from delivery address or profile)
totalnumberYesGrand total of the order
tracking_idstringYesTracking code (empty string for new orders)
store_namestringYesName of the store
statusstringYesAlways "pending" for new orders
order_statusstringYesSame as status (raw field from database)
grand_totalnumberYesGrand total (raw field)
user_idintegerYesCustomer's user ID

Example payload:

{
  "event_id": "a3f1d920-4e7b-4a12-b8c1-9f2e3d4a5b6c",
  "event_topic": "order.created",
  "store_id": 42,
  "timestamp": "2026-03-01T15:00:00.000Z",
  "data": {
    "order_id": 14308,
    "order_number": "14308",
    "customer_name": "Jahin Tahsin Apon",
    "customer_phone": "+8801754588533",
    "total": 2310.00,
    "tracking_id": "",
    "store_name": "Gadget Hub BD",
    "status": "pending",
    "order_status": "pending",
    "grand_total": 2310.00,
    "user_id": 1234
  }
}

Note

The initial payload contains both status (enriched) and order_status / grand_total (raw fields from the order creation). After enrichment, order_number, customer_name, customer_phone, total, tracking_id, and store_name are always present.


order.updated

Fires when: Order details are modified — items changed, address updated, notes added, courier removed, or any non-status field is edited. This does not fire for status-only changes; use order.status_changed for those.

Schema:

FieldTypeRequiredDescription
order_idintegerYesUnique order identifier
order_numberstringYesStore-facing serial order number
customer_namestringYesCustomer name
customer_phonestringYesCustomer phone
totalnumberYesGrand total
tracking_idstringYesTracking code
store_namestringYesName of the store
statusstringYesCurrent order status
changesarray of stringNoWhat changed (e.g. ["order_items"], ["courier_removed"]). Present when the platform can identify the specific change.
fieldsarray of stringNoList of field names that were updated (e.g. ["note", "address"]). Present for general field-level updates.

Example payload (items updated):

{
  "event_id": "b7e2f830-5c9a-4d13-a9d2-0e3f4a5b6c7d",
  "event_topic": "order.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T15:12:00.000Z",
  "data": {
    "order_id": 14308,
    "order_number": "14308",
    "customer_name": "Jahin Tahsin Apon",
    "customer_phone": "+8801754588533",
    "total": 2800.00,
    "tracking_id": "",
    "store_name": "Gadget Hub BD",
    "status": "pending",
    "changes": ["order_items"]
  }
}

Example payload (general field update):

{
  "event_id": "c8f3a941-6d0b-4e24-bad3-1f4a5b6c7d8e",
  "event_topic": "order.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T15:15:00.000Z",
  "data": {
    "order_id": 14308,
    "order_number": "14308",
    "customer_name": "Jahin Tahsin Apon",
    "customer_phone": "+8801754588533",
    "total": 2800.00,
    "tracking_id": "",
    "store_name": "Gadget Hub BD",
    "status": "pending",
    "fields": ["note", "address"]
  }
}

Dual events

Some actions fire both order.updated and order.status_changed simultaneously. For example, editing order items may also change the status back to processing. Always handle both events independently and use event_id to deduplicate if needed.


order.status_changed

Fires when: An order transitions between statuses — merchant confirms, marks as shipped, courier status syncs from a 3PL provider, customer cancels, etc. This is the most commonly subscribed event topic.

Schema:

FieldTypeRequiredDescription
order_idintegerYesUnique order identifier
order_numberstringYesStore-facing serial order number
customer_namestringYesCustomer name
customer_phonestringYesCustomer phone
totalnumberYesGrand total
tracking_idstringYesCourier tracking code (empty string if not yet assigned)
store_namestringYesName of the store
statusstringYesThe new status after the transition
previous_statusstringYesThe old status before the transition

Example payload:

{
  "event_id": "d9a4b052-7e1c-4f35-cbe4-2a5b6c7d8e9f",
  "event_topic": "order.status_changed",
  "store_id": 42,
  "timestamp": "2026-03-01T16:30:00.000Z",
  "data": {
    "order_id": 14308,
    "order_number": "14308",
    "customer_name": "Jahin Tahsin Apon",
    "customer_phone": "+8801754588533",
    "total": 2310.00,
    "tracking_id": "SFD-4829301",
    "store_name": "Gadget Hub BD",
    "status": "shipped",
    "previous_status": "processing"
  }
}

Order status values:

StatusDescription
pendingOrder placed, awaiting confirmation
confirmedOrder confirmed by merchant
processingOrder is being prepared / packed
shippedOrder handed to courier
in_transitCourier has the package (from 3PL sync)
deliveredOrder delivered to customer
cancelledOrder cancelled
returnedOrder returned

3PL Status Sync

Orders shipped via Steadfast, RedX, or Pathao are synced automatically. When the courier updates the delivery status, SeloraX fires order.status_changed with the new status (e.g. in_transit, delivered, returned). Your app does not need to poll courier APIs.


Product Events

Product events fire when products are created, modified, or deleted through the merchant dashboard or API. The payload includes the product's identifying fields.


product.created

Fires when: A new product is added to the store via the dashboard or API.

Schema:

FieldTypeRequiredDescription
product_idintegerYesUnique product identifier
namestringYesProduct name
slugstringYesURL-friendly slug

Example payload:

{
  "event_id": "e0b5c163-8f2d-4046-dcf5-3b6c7d8e9f0a",
  "event_topic": "product.created",
  "store_id": 42,
  "timestamp": "2026-03-01T10:00:00.000Z",
  "data": {
    "product_id": 567,
    "name": "LDNIO BTS13 Bluetooth Speaker",
    "slug": "ldnio-bts13-bluetooth-speaker"
  }
}

Fetching Full Product Details

The event payload is intentionally minimal. To get full product details (price, images, variants, inventory), call the Products API using the product_id:

GET /api/apps/v1/products/567

product.updated

Fires when: A product's details are modified — name, price, description, images, variants, SKU combinations, or activation status changes. Fires for any product edit, including variant and combination updates.

Schema:

FieldTypeRequiredDescription
product_idintegerYesUnique product identifier
namestringYesProduct name (current value after update)
sourcestringNoWhat triggered the update. One of: "combination_update", "variants_update", "easy_update", "activation". Omitted for standard full product updates.

Example payload (variant update):

{
  "event_id": "f1c6d274-9a3e-4157-edf6-4c7d8e9f0a1b",
  "event_topic": "product.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T11:30:00.000Z",
  "data": {
    "product_id": 567,
    "name": "LDNIO BTS13 Bluetooth Speaker",
    "source": "variants_update"
  }
}

Example payload (standard update):

{
  "event_id": "a2d7e385-0b4f-4268-fea7-5d8e9f0a1b2c",
  "event_topic": "product.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T11:45:00.000Z",
  "data": {
    "product_id": 567,
    "name": "LDNIO BTS13 Bluetooth Speaker (Updated)"
  }
}

Source values:

SourceDescription
combination_updateSKU combination (variant matrix) was modified
variants_updateProduct variant options were added, removed, or edited
easy_updateQuick-edit update (name, price, or status only)
activationProduct was activated or deactivated
(omitted)Standard full product update (all fields)

product.deleted

Fires when: A product is deleted from the store. This is a soft delete — the product is marked as deleted but the record remains in the database.

Schema:

FieldTypeRequiredDescription
product_idintegerYesUnique product identifier
namestringYesProduct name (at time of deletion)

Example payload:

{
  "event_id": "b3e8f496-1c5a-4379-afb8-6e9f0a1b2c3d",
  "event_topic": "product.deleted",
  "store_id": 42,
  "timestamp": "2026-03-01T14:00:00.000Z",
  "data": {
    "product_id": 567,
    "name": "LDNIO BTS13 Bluetooth Speaker"
  }
}

Deleted Products

After this event fires, the product will return 404 from the Products API. If your app maintains a product cache, remove it when you receive this event.


Customer Events

Customer events fire when customer accounts are created or modified. Customers can be created through multiple channels: phone login, guest checkout, newsletter subscription, or spin-the-wheel campaigns.


customer.created

Fires when: A new customer account is created on the store — via phone OTP login, guest checkout, newsletter signup, or promotional campaign registration.

Schema:

FieldTypeRequiredDescription
user_idintegerYesUnique customer identifier
phonestringNoCustomer phone number. Present when created via phone login or guest checkout.
emailstringNoCustomer email. Present when created via newsletter or campaign signup.
is_guestbooleanNotrue if this is a guest checkout account. Omitted otherwise.
sourcestringNoCreation source. One of: "newsletter", "spin-the-wheel". Omitted for phone login and guest checkout.

Example payload (phone login):

{
  "event_id": "c4f9a5a7-2d6b-4480-b0c9-7f0a1b2c3d4e",
  "event_topic": "customer.created",
  "store_id": 42,
  "timestamp": "2026-03-01T09:00:00.000Z",
  "data": {
    "user_id": 1234,
    "phone": "+8801754588533"
  }
}

Example payload (guest checkout):

{
  "event_id": "d5a0b6b8-3e7c-4591-c1da-8a1b2c3d4e5f",
  "event_topic": "customer.created",
  "store_id": 42,
  "timestamp": "2026-03-01T09:05:00.000Z",
  "data": {
    "user_id": 1235,
    "phone": "+8801812345678",
    "is_guest": true
  }
}

Example payload (newsletter signup):

{
  "event_id": "e6b1c7c9-4f8d-46a2-d2eb-9b2c3d4e5f6a",
  "event_topic": "customer.created",
  "store_id": 42,
  "timestamp": "2026-03-01T09:10:00.000Z",
  "data": {
    "user_id": 1236,
    "email": "[email protected]",
    "source": "newsletter"
  }
}

Payload Varies by Source

The fields present in the payload depend on how the customer was created. Phone-based signups include phone; email-based signups include email. Check for the presence of each field rather than assuming all fields exist.


customer.updated

Fires when: A customer record is modified — name, email, phone, or address updated via the dashboard or Customers API.

Not yet auto-published

The customer.updated topic is a valid subscription topic and will be delivered when published, but automatic publishing from the dashboard customer edit flow is not yet implemented in all code paths. Apps that update customers via PUT /api/apps/v1/customers/:user_id will not trigger this event (the API updates the record directly). This topic is primarily reserved for future internal dashboard triggers.

Schema:

FieldTypeRequiredDescription
user_idintegerYesUnique customer identifier
namestringNoUpdated customer name
emailstringNoUpdated email
phonestringNoUpdated phone number

Example payload:

{
  "event_id": "f7c2d8da-5a9e-47b3-e3fc-0c3d4e5f6a7b",
  "event_topic": "customer.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T10:30:00.000Z",
  "data": {
    "user_id": 1234,
    "name": "Jahin Tahsin Apon",
    "email": "[email protected]",
    "phone": "+8801754588533"
  }
}

Inventory Events

Inventory events fire when stock levels change through the Inventory API. There are two payload shapes depending on whether the update was a bulk stock set or a single variant adjustment.


inventory.updated

Fires when: Stock levels are changed for one or more product variants — via bulk stock update (PUT) or single variant adjustment (POST adjust).

Bulk Stock Update

Triggered by PUT /api/apps/v1/inventory/:product_id — setting absolute stock levels for multiple variants at once.

Schema (bulk):

FieldTypeRequiredDescription
product_idintegerYesThe product that was updated
updatesarrayYesArray of variant stock changes
updates[].sku_idintegerYesVariant SKU identifier
updates[].previous_quantityintegerYesStock count before the change
updates[].quantityintegerYesStock count after the change
updates[].availableinteger (0 or 1)YesWhether the variant is in stock
store_idintegerYesStore identifier

Example payload (bulk update):

{
  "event_id": "a8d3e9eb-6b0f-48c4-f4ad-1d4e5f6a7b8c",
  "event_topic": "inventory.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T12:00:00.000Z",
  "data": {
    "product_id": 567,
    "updates": [
      {
        "sku_id": 1023,
        "previous_quantity": 50,
        "quantity": 45,
        "available": 1
      },
      {
        "sku_id": 1024,
        "previous_quantity": 0,
        "quantity": 20,
        "available": 1
      }
    ],
    "store_id": 42
  }
}

Single Variant Adjustment

Triggered by POST /api/apps/v1/inventory/:product_id/adjust — applying a relative stock change (e.g. +10 restock, -3 damaged).

Schema (adjustment):

FieldTypeRequiredDescription
product_idintegerYesThe product that was updated
sku_idintegerYesThe specific variant SKU that was adjusted
previous_quantityintegerYesStock count before the adjustment
quantityintegerYesStock count after the adjustment
adjustmentintegerYesThe signed change amount (e.g. 5 for restock, -3 for removal)
reasonstring or nullYesOptional reason for the adjustment
store_idintegerYesStore identifier

Example payload (single adjustment):

{
  "event_id": "b9e4f0fc-7c1a-49d5-a5be-2e5f6a7b8c9d",
  "event_topic": "inventory.updated",
  "store_id": 42,
  "timestamp": "2026-03-01T12:15:00.000Z",
  "data": {
    "product_id": 567,
    "sku_id": 1023,
    "previous_quantity": 45,
    "quantity": 42,
    "adjustment": -3,
    "reason": "Damaged items removed",
    "store_id": 42
  }
}

Distinguishing Payload Types

Check for the updates field to distinguish bulk updates from single adjustments. If updates is present, it is a bulk update. If sku_id and adjustment are present at the top level, it is a single adjustment.


App Lifecycle Events

App lifecycle events notify your app when it is installed or removed from a store. These events fire regardless of which scopes your app has — every app receives them automatically.


app.installed

Fires when: Your app is installed on a store, either through the marketplace OAuth flow or as a custom app auto-install.

Schema:

FieldTypeRequiredDescription
app_idintegerYesYour app's unique identifier
store_idintegerYesThe store that installed your app
installation_idintegerYesUnique installation identifier (use this for API calls)

Example payload:

{
  "event_id": "c0f5a1ad-8d2b-4ae6-b6cf-3f6a7b8c9d0e",
  "event_topic": "app.installed",
  "store_id": 42,
  "timestamp": "2026-03-01T08:00:00.000Z",
  "data": {
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

Initialization Hook

Use this event to set up any store-specific data your app needs — default settings, welcome messages, initial sync of products or orders, etc.


app.uninstalled

Fires when: Your app is removed from a store by the merchant. All associated webhook subscriptions are deactivated, and OAuth tokens for this installation are revoked.

Schema:

FieldTypeRequiredDescription
app_idintegerYesYour app's unique identifier
store_idintegerYesThe store that removed your app
installation_idintegerYesThe installation that was removed

Example payload:

{
  "event_id": "d1a6b2be-9e3c-4bf7-c7da-4a7b8c9d0e1f",
  "event_topic": "app.uninstalled",
  "store_id": 42,
  "timestamp": "2026-03-01T20:00:00.000Z",
  "data": {
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

Cleanup Required

After receiving this event, your app should clean up any store-specific resources. API calls using the old installation's tokens will fail with 401. If the merchant re-installs your app later, a new installation_id is created.


Billing Events

Billing events notify your app about the lifecycle of charges created through the Billing API. All billing event payloads share the same schema.

Shared billing schema:

FieldTypeRequiredDescription
charge_idintegerYesUnique charge identifier
charge_typestringYesOne of: "one_time", "recurring", "usage", "wallet_topup"
namestringYesCharge name / description
amountnumberYesCharge amount
currencystringYesCurrency code (e.g. "BDT")
statusstringYesCurrent charge status after this event
app_idintegerYesYour app's ID
store_idintegerYesStore the charge belongs to
installation_idintegerYesInstallation the charge belongs to
renewal_datestring (ISO 8601)NoNext renewal date. Only present on subscription.renewal_pending.

charge.created

Fires when: A new charge is created (one-time, recurring, or usage-based) via the Billing API. The charge starts in pending status, awaiting merchant approval.

Example payload:

{
  "event_id": "e2b7c3cf-0f4d-4ca8-d8eb-5b8c9d0e1f2a",
  "event_topic": "charge.created",
  "store_id": 42,
  "timestamp": "2026-03-01T10:00:00.000Z",
  "data": {
    "charge_id": 101,
    "charge_type": "one_time",
    "name": "Premium Feature Unlock",
    "amount": 500.00,
    "currency": "BDT",
    "status": "pending",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

charge.activated

Fires when: The merchant approves a charge and payment is successfully collected through the payment gateway.

Example payload:

{
  "event_id": "f3c8d4da-1a5e-4db9-e9fc-6c9d0e1f2a3b",
  "event_topic": "charge.activated",
  "store_id": 42,
  "timestamp": "2026-03-01T10:05:00.000Z",
  "data": {
    "charge_id": 101,
    "charge_type": "one_time",
    "name": "Premium Feature Unlock",
    "amount": 500.00,
    "currency": "BDT",
    "status": "active",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

Unlock Features Here

This is the event to listen for when gating features behind a paywall. Only grant access to premium features after receiving charge.activated, not charge.created.


charge.declined

Fires when: The merchant explicitly declines a pending charge from the approval screen.

Example payload:

{
  "event_id": "a4d9e5eb-2b6f-4ec0-f0ad-7d0e1f2a3b4c",
  "event_topic": "charge.declined",
  "store_id": 42,
  "timestamp": "2026-03-01T10:10:00.000Z",
  "data": {
    "charge_id": 101,
    "charge_type": "one_time",
    "name": "Premium Feature Unlock",
    "amount": 500.00,
    "currency": "BDT",
    "status": "declined",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

charge.cancelled

Fires when: A charge is cancelled by the app, the merchant, or the platform. Applies to both one-time and recurring charges.

Example payload:

{
  "event_id": "b5e0f6fc-3c7a-4fd1-a1be-8e1f2a3b4c5d",
  "event_topic": "charge.cancelled",
  "store_id": 42,
  "timestamp": "2026-03-01T18:00:00.000Z",
  "data": {
    "charge_id": 102,
    "charge_type": "recurring",
    "name": "Pro Plan Monthly",
    "amount": 299.00,
    "currency": "BDT",
    "status": "cancelled",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

charge.expired

Fires when: A pending charge expires because the merchant did not approve or decline it within the 48-hour approval window.

Example payload:

{
  "event_id": "c6f1a7ad-4d8b-4ae2-b2cf-9f2a3b4c5d6e",
  "event_topic": "charge.expired",
  "store_id": 42,
  "timestamp": "2026-03-03T10:00:00.000Z",
  "data": {
    "charge_id": 101,
    "charge_type": "one_time",
    "name": "Premium Feature Unlock",
    "amount": 500.00,
    "currency": "BDT",
    "status": "expired",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

charge.payment_failed

Fires when: The merchant approved the charge, but the payment gateway failed to collect payment (insufficient funds, card error, gateway timeout, etc.).

Example payload:

{
  "event_id": "d7a2b8be-5e9c-4bf3-c3da-0a3b4c5d6e7f",
  "event_topic": "charge.payment_failed",
  "store_id": 42,
  "timestamp": "2026-03-01T10:06:00.000Z",
  "data": {
    "charge_id": 102,
    "charge_type": "recurring",
    "name": "Pro Plan Monthly",
    "amount": 299.00,
    "currency": "BDT",
    "status": "payment_failed",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

subscription.renewal_pending

Fires when: A recurring charge is due for renewal in 48 hours. Use this to notify the merchant or prepare for the upcoming billing cycle.

Example payload:

{
  "event_id": "e8b3c9cf-6f0d-4ca4-d4eb-1b4c5d6e7f8a",
  "event_topic": "subscription.renewal_pending",
  "store_id": 42,
  "timestamp": "2026-03-26T00:00:00.000Z",
  "data": {
    "charge_id": 102,
    "charge_type": "recurring",
    "name": "Pro Plan Monthly",
    "amount": 299.00,
    "currency": "BDT",
    "status": "active",
    "renewal_date": "2026-03-28T00:00:00.000Z",
    "app_id": 5,
    "store_id": 42,
    "installation_id": 78
  }
}

Topic Summary

Platform Events

#TopicFires WhenKey Data Fields
1order.createdNew order placedorder_id, status, total, customer_name, customer_phone
2order.updatedOrder details modified (non-status)order_id, status, changes or fields
3order.status_changedOrder status transitionorder_id, status, previous_status, tracking_id
4product.createdNew product addedproduct_id, name, slug
5product.updatedProduct details modifiedproduct_id, name, source
6product.deletedProduct removed (soft delete)product_id, name
7customer.createdNew customer accountuser_id, phone or email, source
8customer.updatedCustomer record modifieduser_id, name, email, phone
9inventory.updatedStock levels changedproduct_id, sku_id, previous_quantity, quantity
10app.installedApp installed on storeapp_id, store_id, installation_id
11app.uninstalledApp removed from storeapp_id, store_id, installation_id

Billing Events

#TopicFires WhenKey Data Fields
12charge.createdCharge createdcharge_id, charge_type, amount, status
13charge.activatedPayment collectedcharge_id, charge_type, amount, status
14charge.declinedMerchant declinedcharge_id, charge_type, amount, status
15charge.cancelledCharge cancelledcharge_id, charge_type, amount, status
16charge.expired48h without actioncharge_id, charge_type, amount, status
17charge.payment_failedPayment gateway failedcharge_id, charge_type, amount, status
18subscription.renewal_pendingRenewal in 48hcharge_id, charge_type, amount, renewal_date

GDPR Compliance Events

#TopicFires WhenKey Data Fields
19customers/data_requestCustomer requests data exportCustomer PII
20customers/redactCustomer requests data deletionCustomer PII
21shop/redactStore deletionStore data

GDPR Topics

GDPR compliance topics use a different naming convention (customers/data_request instead of dot notation). These topics are only available for app-created webhook subscriptions via the Webhooks API, not merchant-created webhooks from the dashboard.


Payload Filtering (Merchant Webhooks)

Merchant-created webhook subscriptions (from the dashboard) can have permission-based payload filtering applied. This removes sensitive fields from the payload based on which permissions the subscription has. See the Permissions Reference for details.

App-created webhook subscriptions always receive unfiltered payloads.

PermissionFields Removed if Missing
read_customerscustomer_name, customer_phone, customer_email, email, phone
read_orderstotal, grand_total, tracking_id, tracking_code, address, items
read_productsproduct_title, product_price, variants, sku
read_inventoryquantity, stock, inventory_quantity

Test Any Event

Select a topic, customize the payload, and generate a cURL command with proper HMAC-SHA256 signature to test your webhook endpoint.