How to send webhooks from automation workflows
Intro
Use the sendWebhook action block in automation workflows to send real-time HTTP POST requests to external systems when contacts reach a specific point in a workflow. This enables integrations with CRMs, messaging platforms (Slack, WhatsApp), order management systems, or any custom API endpoint — without building a native integration.
See API reference: Automations
Prerequisites
- An API key with
automations.writescope (or an OAuth token with the same scope) - A paid Omnisend plan — webhooks in automations are not available on the free plan
- An HTTPS endpoint to receive the webhook (for testing, webhook.site provides a temporary URL)
Step 1: Create an automation with a webhook action
Create a disabled automation workflow that triggers on an event and sends an HTTP POST to your endpoint. The example below triggers when a contact subscribes to marketing, waits 5 minutes, then sends contact data to an external URL.
See endpoint: Create automation workflow
curl -X POST 'https://api.omnisend.com/api/automations' \
-H 'Authorization: Omnisend-API-Key YOUR-API-KEY' \
-H 'Omnisend-Version: 2026-03-15' \
-H 'Content-Type: application/json' \
-d '{
"name": "New Subscriber Webhook",
"trigger": {
"condition": {
"event": "subscribed to marketing"
}
},
"blocks": [
{
"temporaryID": "wait-5m",
"type": "delay",
"delay": {
"mode": "duration",
"duration": {
"amount": 5,
"units": "m"
}
}
},
{
"temporaryID": "webhook-notify",
"type": "action",
"action": {
"type": "sendWebhook",
"sendWebhook": {
"callbackUrl": "https://example.com/webhooks/omnisend",
"headers": [
{"key": "X-Webhook-Secret", "value": "my-shared-secret"}
],
"body": "{\"contactEmail\": \"[[contact.email]]\", \"contactName\": \"[[contact.first_name]]\", \"event\": \"subscribed\"}"
}
}
}
]
}'The response returns the automation in disabled state with an id and the resolved blocks array (each block now has a server-assigned id replacing temporaryID).
Note: The
callbackUrlmust be HTTPS. IP literals, non-443 ports, non-FQDN hosts, and internal/private IP addresses are rejected. Thebodymust be valid JSON and supports personalization tags like[[contact.email]],[[contact.first_name]], and[[event.raw.<property>]]. Thetrigger.condition.eventvalue must be a valid event for your brand — some events (e.g.,"placed order") require anoriginfield specifying the source platform.
Step 2: Verify the webhook configuration
Retrieve the automation to confirm the webhook block was created correctly.
See endpoint: Get automation workflow
curl -X GET 'https://api.omnisend.com/api/automations/AUTOMATION_ID' \
-H 'Authorization: Omnisend-API-Key YOUR-API-KEY' \
-H 'Omnisend-Version: 2026-03-15'In the response, look for the blocks array. The webhook block will contain:
{
"type": "action",
"action": {
"type": "sendWebhook",
"sendWebhook": {
"callbackUrl": "https://example.com/webhooks/omnisend",
"headers": [
{"key": "X-Webhook-Secret", "value": "my-shared-secret"}
],
"body": "{\"contactEmail\": \"[[contact.email]]\", ...}"
}
}
}Step 3: Update the webhook (optional)
To change the webhook URL, body, or headers on an existing automation, use PATCH. Only provided fields are updated.
See endpoint: Patch automation workflow
curl -X PATCH 'https://api.omnisend.com/api/automations/AUTOMATION_ID' \
-H 'Authorization: Omnisend-API-Key YOUR-API-KEY' \
-H 'Omnisend-Version: 2026-03-15' \
-H 'Content-Type: application/json' \
-d '{
"blocks": [
{
"id": "WEBHOOK_BLOCK_ID",
"type": "action",
"action": {
"type": "sendWebhook",
"sendWebhook": {
"callbackUrl": "https://example.com/webhooks/omnisend/v2",
"body": "{\"contactEmail\": \"[[contact.email]]\", \"contactPhone\": \"[[contact.phone_number]]\", \"event\": \"subscribed\"}"
}
}
}
]
}'Note: The automation must be disabled before replacing blocks. If it is currently enabled, disable it first via
POST /automations/{id}/disable, replace blocks, then re-enable.
Webhook configuration reference
| Field | Required | Description |
|---|---|---|
callbackUrl | Yes | HTTPS URL that receives the POST request (max 2,000 characters) |
body | Yes | JSON body template with optional personalization tags (max 64 KB) |
headers | No | Up to 15 custom HTTP headers (key max 256 chars, value max 10,000 chars) |
URL restrictions
- Must use
https://scheme - Must be a fully-qualified domain name (no IP literals)
- Only port 443 is allowed (or omit port entirely)
- Internal/private IP addresses are blocked (DNS is resolved externally)
- TLS certificate must be valid
Forbidden header keys
The following headers cannot be set as custom headers (they are managed by the system): Host, Content-Length, Transfer-Encoding, Connection, Keep-Alive, Upgrade, TE, Trailer, Proxy-Authorization, Proxy-Authenticate, Expect, User-Agent, Content-Type.
Personalization tags
The webhook body supports the same personalization variables available in automation messages:
[[contact.email]],[[contact.first_name]],[[contact.last_name]],[[contact.phone_number]][[event.raw.<property>]]— access properties from the triggering event
For a full list of available personalization tags, see Use personalization in Omnisend.
Receiving and verifying webhooks
When the automation fires, Omnisend sends an HTTP POST to your callbackUrl with:
- Content-Type:
application/json - Your custom headers (as configured)
- The JSON body with personalization tags resolved to actual contact/event values
To verify the request comes from Omnisend:
- Shared secret header — include a secret value in a custom header (e.g.,
X-Webhook-Secret) and validate it on your server. - IP whitelisting — Omnisend sends webhook requests from these static IPs:
34.170.162.11,34.56.62.59,34.56.108.215,34.133.59.36,34.58.66.146,35.184.130.5,35.232.14.89.
Testing with webhook.site
For quick testing without setting up your own server:
- Go to webhook.site and copy your unique URL
- Use that URL as the
callbackUrlwhen creating the automation - Enable the automation and trigger the event — the request will appear on webhook.site
Troubleshooting
- 400 on create/patch with
callbackUrlerror: The URL is not HTTPS, uses an IP literal, or resolves to an internal address. Use a public HTTPS domain. - 400 with body validation error: The
bodyfield must contain valid JSON. Validate your JSON before submitting. - 409 Conflict on PATCH: The automation is currently enabled. Disable it first via
POST /automations/{id}/disable, make changes, then re-enable. - Webhook not reaching your endpoint: Ensure your server accepts POST requests, has a valid TLS certificate, and is not blocking Omnisend's IP addresses.
- Duplicate headers rejected: Header keys are case-insensitive and must be unique.