Use this webhook when you want a GoHighLevel Workflow to send an official WhatsApp Cloud API template message through WAsndr.
https://panel.wasndr.com/webhookDML-cloud/workflow/template/send
This endpoint is designed for GHL Workflow Custom Webhook actions. It is different from the regular GHL SMS step/custom SMS provider path.
The webhook sends a WhatsApp Cloud API template message using an explicit JSON payload from your GHL Workflow.
It supports:
This is the recommended production path for workflow automations that need to send WhatsApp messages reliably.
Use this webhook when a GHL Workflow should send an approved WhatsApp template, especially when the message may be sent outside the WhatsApp 24-hour customer service window.
Good use cases include:
Do not use this endpoint for:
For normal conversations, use the WAsndr inbox or the regular GHL conversation/SMS provider integration.
A normal GHL SMS step can be routed through WAsndrβs custom SMS provider, but that payload does not reliably tell WAsndr that the message came from a workflow or that it should use a WhatsApp template.
WhatsApp Cloud API has stricter rules than SMS:
For production WhatsApp automation, use this dedicated webhook instead of pretending every SMS step is a WhatsApp message.
Before using this webhook, your subaccount must have:
If you do not have the webhook secret yet, contact your WAsndr admin or support.
The secret is generated inside WAsndr for each GHL Cloud subaccount/install. It is not the same as your GHL marketplace credentials and it is not your WhatsApp Cloud API token.
At the moment, this is generated through the WAsndr backend/admin endpoint. A workspace admin can rotate a new secret with:
POST https://panel.wasndr.com/workspace/{locationUuid}/ghl-cloud/template-webhook/rotate-secret
Example request:
curl -X POST "https://panel.wasndr.com/workspace/{locationUuid}/ghl-cloud/template-webhook/rotate-secret" \
-H "Authorization: Bearer YOUR_WASNDR_DASHBOARD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enable": true}'
The response includes the secret one time only:
{
"enabled": true,
"endpointUrl": "https://panel.wasndr.com/webhookDML-cloud/workflow/template/send",
"secretPrefix": "dml_wh_abc12",
"hasSecret": true,
"lastUsedAt": null,
"secret": "FULL_SECRET_VALUE_SHOWN_ONCE",
"message": "Store this secret now. It will not be shown again."
}
Copy the secret value immediately and store it somewhere safe. After this response, WAsndr only keeps a hash and the prefix; the full secret cannot be viewed again.
Use the returned secret in your GHL Custom Webhook header:
Authorization: Bearer FULL_SECRET_VALUE_SHOWN_ONCE
You can check whether a secret exists with:
GET https://panel.wasndr.com/workspace/{locationUuid}/ghl-cloud/template-webhook/config
You can enable or disable the webhook with:
POST https://panel.wasndr.com/workspace/{locationUuid}/ghl-cloud/template-webhook/enable
POST https://panel.wasndr.com/workspace/{locationUuid}/ghl-cloud/template-webhook/disable
If you do not have access to these WAsndr admin endpoints yet, contact your WAsndr admin/support and ask them to rotate a GHL Cloud workflow template webhook secret for your subaccount.
In your GHL Workflow, add a Custom Webhook action.
Use:
Method: POST
URL: https://panel.wasndr.com/webhookDML-cloud/workflow/template/send
Content-Type: application/json
Add one of these authentication headers:
Authorization: Bearer YOUR_WORKFLOW_WEBHOOK_SECRET
or:
X-DML-Workflow-Secret: YOUR_WORKFLOW_WEBHOOK_SECRET
{
"type": "DML_SEND_WHATSAPP_CLOUD_TEMPLATE",
"version": "2026-05-03",
"locationId": "{{location.id}}",
"contactId": "{{contact.id}}",
"recipient": {
"phone": "{{contact.phone}}"
},
"template": {
"name": "appointment_reminder",
"language": {
"code": "en_US"
},
"variables": {
"body": [
"{{contact.first_name}}",
"{{appointment.start_time}}"
]
}
},
"metadata": {
"idempotencyKey": "{{contact.id}}-appointment-reminder-{{appointment.id}}",
"source": "ghl_workflow_custom_webhook",
"workflowId": "{{workflow.id}}",
"actionId": "send-whatsapp-template",
"executionId": "{{workflow.execution_id}}"
},
"options": {
"previewText": "Appointment reminder sent via WhatsApp"
}
}
locationIdThe GHL location/subaccount ID.
"locationId": "{{location.id}}"
template.nameThe exact approved WhatsApp template name.
"name": "appointment_reminder"
template.languageThe template language code.
"language": { "code": "en_US" }
metadata.idempotencyKeyA unique key for this intended send. This prevents duplicate WhatsApp messages if GHL retries the webhook.
"idempotencyKey": "{{contact.id}}-{{workflow.id}}-{{appointment.id}}"
metadata.sourceMust be:
"source": "ghl_workflow_custom_webhook"
If your WhatsApp template has body placeholders, pass them in template.variables.body in the same order as the template placeholders.
Example template:
Hi {{1}}, your appointment is scheduled for {{2}}.
Payload:
"variables": {
"body": [
"John",
"Monday at 2:00 PM"
]
}
You can also send header text or URL button variables when your approved template requires them.
Example:
"variables": {
"headerText": "Appointment Reminder",
"body": ["John", "Monday at 2:00 PM"],
"buttons": [
{
"type": "url",
"index": 0,
"value": "booking-123"
}
]
}
recipient.phoneUse this when the contact phone is available in the workflow payload.
"recipient": {
"phone": "{{contact.phone}}"
}
If this is not provided, WAsndr may try to resolve the phone from the GHL contact ID.
contactIdThe GHL contact ID. Recommended for tracking and conversation mapping.
"contactId": "{{contact.id}}"
conversationIdUse this if your workflow has access to the GHL conversation ID.
"conversationId": "{{conversation.id}}"
options.previewTextA local preview label that appears in WAsndr tracking.
"previewText": "Appointment reminder sent via WhatsApp"
options.phoneNumberIdUse this only if your subaccount has multiple WhatsApp Cloud API numbers and you need to select a specific one.
"phoneNumberId": "YOUR_WASTARTER_PHONE_NUMBER_ID"
GHL may retry webhooks. To prevent duplicate WhatsApp sends, every request must include a stable metadata.idempotencyKey.
Good examples:
{{contact.id}}-{{workflow.id}}-{{appointment.id}}
{{contact.id}}-payment-reminder-{{invoice.id}}
{{contact.id}}-missed-call-followup-{{workflow.execution_id}}
If the same idempotency key is sent again, WAsndr will not send the WhatsApp message a second time.
Example successful response:
{
"success": true,
"data": {
"idempotencyKey": "contact123-appointment-reminder-appointment456",
"messagePkId": 12345,
"providerMessageId": "wamid.xxx",
"status": "sent",
"templateName": "appointment_reminder",
"templateLanguage": "en_US"
}
}
If the same idempotency key was already processed, the webhook may return:
{
"success": true,
"duplicate": true,
"data": {
"idempotencyKey": "contact123-appointment-reminder-appointment456",
"status": "sent",
"templateName": "appointment_reminder",
"templateLanguage": "en_US"
}
}
This means the duplicate was safely ignored.
UNAUTHORIZED_TEMPLATE_WEBHOOKThe webhook secret is missing or invalid.
Check that your Custom Webhook action includes one of these headers:
Authorization: Bearer YOUR_WORKFLOW_WEBHOOK_SECRET
or:
X-DML-Workflow-Secret: YOUR_WORKFLOW_WEBHOOK_SECRET
TEMPLATE_WEBHOOK_DISABLEDThe workflow template webhook is not enabled for this GHL subaccount.
INVALID_LOCATION_IDThe payload is missing locationId.
INVALID_TEMPLATE_PAYLOADThe template name, language, or variables are invalid.
INVALID_IDEMPOTENCY_KEYmetadata.idempotencyKey is missing or longer than allowed.
IDEMPOTENCY_IN_PROGRESSA request with the same idempotency key is already being processed.
IDEMPOTENCY_PREVIOUSLY_FAILEDA previous request with the same idempotency key failed. Use a new idempotency key if you intentionally want to retry.
Use this webhook when a GHL Workflow needs to send an official WhatsApp Cloud API template message through WAsndr.
Use the regular GHL SMS/custom provider path for convenience messages. Use this dedicated webhook for reliable production automations that require template name, language, variables, idempotency, and better tracking.