Documentation Index
Fetch the complete documentation index at: https://docs.digifist.com/llms.txt
Use this file to discover all available pages before exploring further.
This page is the authoritative payload reference for every webhook Galantis registers with Shopify and every webhook event it receives from Meta. It covers the topic, the expected payload structure, the internal handler, and what the webhook drives in Galantis.
For the processing behavior and downstream effects of each webhook, see Integrations — Shopify Webhooks and Integrations — Meta Webhooks. This page focuses on payload structure for developer reference.
Security
Shopify webhooks — All incoming Shopify webhooks are validated via HMAC signature verification using the X-Shopify-Hmac-Sha256 header before processing. Requests with invalid or missing signatures are rejected with a 401 response and never reach the handler.
Meta webhooks — All incoming Meta webhooks are validated via signature verification using the X-Hub-Signature-256 header before processing. Invalid signatures are rejected.
Both verifications use the app secret for their respective platform. Webhook payloads are never processed without a valid signature.
Shopify webhooks
Customer webhooks
customers/create
Handler: ProcessShopifyCustomerCreatedJob
{
"id": 123456789,
"email": "customer@example.com",
"first_name": "Jane",
"last_name": "Smith",
"phone": "+521234567890",
"tags": "vip, newsletter",
"accepts_marketing": true,
"email_marketing_consent": {
"state": "subscribed",
"opt_in_level": "single_opt_in"
},
"sms_marketing_consent": {
"state": "subscribed",
"opt_in_level": "single_opt_in"
},
"locale": "es",
"created_at": "2025-01-15T10:00:00-05:00",
"updated_at": "2025-01-15T10:00:00-05:00"
}
customers/update
Handler: ProcessShopifyCustomerUpdatedJob
Same structure as customers/create. All fields are included in the payload — Galantis diffs the incoming data against the stored record to identify changes.
customers/delete
Handler: ProcessShopifyCustomerDeletedJob
Only the customer ID is included. Galantis uses the ID to locate and remove the corresponding contact record.
customers/marketing_consent_updated
Handler: ProcessShopifyCustomerMarketingConsentUpdatedJob
{
"id": 123456789,
"email_marketing_consent": {
"state": "subscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "2025-01-15T10:05:00-05:00"
},
"sms_marketing_consent": {
"state": "subscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "2025-01-15T10:05:00-05:00"
},
"phone": "+521234567890",
"updated_at": "2025-01-15T10:05:00-05:00"
}
Galantis maps the SMS/phone marketing consent state to CustomerMarketingStateEnum for the marketing_state field.
customer_tags/added
Handler: ProcessShopifyCustomerTagsAddedJob
{
"customer_id": 123456789,
"tags_added": ["vip", "loyalty-gold"]
}
customer_tags/removed
Handler: ProcessShopifyCustomerTagsRemovedJob
{
"customer_id": 123456789,
"tags_removed": ["loyalty-silver"]
}
Order webhooks
orders/create
Handler: ProcessShopifyOrderCreatedJob
{
"id": 987654321,
"order_number": 1042,
"customer": {
"id": 123456789,
"phone": "+521234567890"
},
"total_price": "149.00",
"currency": "MXN",
"line_items": [
{
"id": 111111,
"title": "Running Shoes",
"quantity": 1,
"price": "149.00",
"product_id": 555555,
"variant_id": 666666,
"vendor": "NikeMX",
"properties": []
}
],
"tags": "",
"fulfillment_status": null,
"financial_status": "paid",
"created_at": "2025-01-15T11:00:00-05:00"
}
orders/cancelled
Handler: ProcessShopifyOrderCancelledJob
Same structure as orders/create with cancelled_at and cancel_reason fields added:
{
"id": 987654321,
"order_number": 1042,
"customer": { "id": 123456789 },
"total_price": "149.00",
"cancelled_at": "2025-01-16T09:00:00-05:00",
"cancel_reason": "customer"
}
orders/updated (used for shipping)
Handler: ProcessShopifyOrderShippedJob
{
"id": 987654321,
"order_number": 1042,
"customer": { "id": 123456789 },
"fulfillment_status": "fulfilled",
"fulfillments": [
{
"id": 222222,
"status": "success",
"tracking_company": "DHL",
"tracking_number": "1234567890",
"tracking_url": "https://track.dhl.com/...",
"created_at": "2025-01-16T14:00:00-05:00"
}
]
}
ProcessShopifyOrderShippedJob only fires OrderShipped when fulfillments is non-empty and contains at least one fulfillment with "status": "success". Other orders/updated events without fulfillment data are processed or ignored based on their content.
Product and collection webhooks
products/create
Handler: ProcessShopifyProductCreatedJob
{
"id": 555555,
"title": "Running Shoes",
"body_html": "<p>Full product description...</p>",
"vendor": "NikeMX",
"tags": "shoes, running, sport",
"images": [
{ "id": 777777, "src": "https://cdn.shopify.com/...", "width": 1000, "height": 1000 }
],
"variants": [
{
"id": 666666,
"title": "Size 42 / Blue",
"price": "149.00",
"compare_at_price": "180.00",
"sku": "RUN-42-BLU",
"inventory_quantity": 15,
"option1": "42",
"option2": "Blue",
"image_id": 777777
}
],
"options": [
{ "name": "Size", "values": ["40", "41", "42", "43"] },
{ "name": "Color", "values": ["Blue", "Black", "White"] }
],
"status": "active",
"created_at": "2025-01-15T09:00:00-05:00"
}
products/update
Handler: ProcessShopifyProductUpdatedJob
Same structure as products/create. Galantis processes the full payload — updated fields overwrite stored values, and inventory_quantity changes are checked for the 0→>0 Back-in-Stock restock pattern.
products/delete
Handler: ProcessShopifyProductDeletedJob
collections/create, collections/update
Handlers: ProcessShopifyCollectionCreatedJob, ProcessShopifyCollectionUpdatedJob
{
"id": 888888,
"title": "Running Gear",
"handle": "running-gear",
"products_count": 24,
"updated_at": "2025-01-15T12:00:00-05:00"
}
collections/delete
Handler: ProcessShopifyCollectionDeletedJob
Billing and app lifecycle webhooks
app_subscriptions/update
Handler: MonitorBillingSubscriptionsJob
{
"app_subscription": {
"admin_graphql_api_id": "gid://shopify/AppSubscription/123",
"name": "Galantis Pro Plan",
"status": "ACTIVE",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z",
"currency": "USD"
}
}
app/uninstalled
Handler: Tenant deactivation
{
"id": 12345678,
"myshopify_domain": "your-store.myshopify.com"
}
GDPR webhooks
customers/redact
Handler: ProcessShopifyCustomersRedactJob
{
"shop_id": 12345678,
"shop_domain": "your-store.myshopify.com",
"customer": {
"id": 123456789,
"email": "customer@example.com",
"phone": "+521234567890"
},
"orders_to_redact": [987654321, 987654322]
}
shop/redact
Handler: ProcessShopifyShopRedactJob
{
"shop_id": 12345678,
"shop_domain": "your-store.myshopify.com"
}
messages (inbound)
{
"object": "whatsapp_business_account",
"entry": [{
"id": "{waba_id}",
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "521234567890",
"phone_number_id": "{phone_number_id}"
},
"contacts": [{
"profile": { "name": "Jane Smith" },
"wa_id": "521234567890"
}],
"messages": [{
"from": "521234567890",
"id": "wamid.{message_id}",
"timestamp": "1705320000",
"type": "text",
"text": { "body": "Hello, I have a question about my order" }
}]
},
"field": "messages"
}]
}]
}
For QUICK_REPLY button responses, the messages[0].type is "interactive" and the payload includes:
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "{button_id}",
"title": "{button_label}"
}
}
message_status (status update)
{
"object": "whatsapp_business_account",
"entry": [{
"id": "{waba_id}",
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "521234567890",
"phone_number_id": "{phone_number_id}"
},
"statuses": [{
"id": "wamid.{message_id}",
"status": "delivered",
"timestamp": "1705320030",
"recipient_id": "521234567890"
}]
},
"field": "messages"
}]
}]
}
"status" values: "sent", "delivered", "read", "played", "failed".
For "failed" status, an "errors" array is included:
"errors": [{
"code": 131047,
"title": "Re-engagement message",
"message": "Message failed to send because more than 24 hours have passed since the customer last replied to this number.",
"error_data": { "details": "..." }
}]
message_template_status_update
{
"object": "whatsapp_business_account",
"entry": [{
"id": "{waba_id}",
"changes": [{
"value": {
"event": "APPROVED",
"message_template_id": 123456,
"message_template_name": "order_confirmation_es",
"message_template_language": "es",
"reason": null
},
"field": "message_template_status_update"
}]
}]
}
"event" values: "APPROVED", "REJECTED", "PAUSED", "DISABLED".
For "REJECTED", the "reason" field contains Meta’s rejection explanation.