Webhook Step

Integrate external services with Workflow webhook actions.

Send HTTP POST requests to external services.

In Workflow, webhooks are configured as an ACTION step with actionType: WEBHOOK rather than a separate step type.

Configuration

  • Webhook URL: HTTP(S) endpoint to call
  • Webhook Secret: Optional secret for authentication
  • Payload: Optional JSON object nested under payload on the request body. It is never merged onto the top-level fields.
  • Body format: json (default) or form. Use form for endpoints that expect email=... form data, such as a Deadline Funnel custom webhook.
{
  "id": "notify-crm",
  "type": "ACTION",
  "config": {
    "actionType": "WEBHOOK",
    "url": "https://example.com/webhooks/lumail",
    "secret": "optional-secret",
    "payload": { "source": "welcome-workflow" },
    "bodyFormat": "json"
  }
}

get_workflow / get_workflow_draft replace config.secret with the literal [REDACTED]. That is a read placeholder, not a keep-token.

  • First configure, or any new webhook step: write the real secret or omit it. Never paste a redacted get_workflow payload as the first draft.
  • Later graph updates may send "secret": "[REDACTED]" only if draft already stores a real secret. restoreRedactedWorkflowSecrets then copies the stored value.
  • If the stored value is missing, empty, or already [REDACTED], sending [REDACTED] is rejected. The placeholder is never stored.
  • After configure/publish, get_workflow still shows [REDACTED]. That does not mean the stored secret is valid.

Request

POST with content-type: application/json. When a secret is set, the same value is sent as x-lumail-signature and as body.secret.

{
  "subscriberId": "sub_123",
  "subscriberEmail": "[email protected]",
  "organizationId": "org_xyz",
  "workflowId": "wf_789",
  "stepId": "step_456",
  "secret": "your-secret",
  "payload": { "source": "welcome-workflow" }
}

secret is omitted when the step has no secret. payload is always an object ({} when unset). Do not flatten payload onto the top level.

get_workflow always redacts config.secret. Do not treat that as proof the stored secret is real. See the rules above.

Form body

With "bodyFormat": "form", the request is sent as content-type: application/x-www-form-urlencoded:

email=user%40example.com&subscriberId=sub_123&organizationId=org_xyz&workflowId=wf_789&stepId=step_456&secret=your-secret&payload%5Bsource%5D=welcome-workflow

The subscriber email is sent as email. Each payload key becomes payload[key]: strings are sent as-is, and other values are JSON-encoded. A payload key never overrides a top-level field. secret is omitted when unset and is still sent as x-lumail-signature.

Deadline Funnel

  1. In your Deadline Funnel campaign, set the email platform to Other and copy the custom webhook URL from the Emails tab.
  2. Add a Webhook action where the deadline should start, paste the URL, and set Body format to Form.
  3. In your Lumail emails, replace the Deadline Funnel email placeholder with {{email}} in email links (https://dfl0.us/s/abc123?em={{email}}) and in the email timer image URL. Copy a fresh timer URL for each email.

Response

  • Success: HTTP status < 400, workflow continues
  • Timeout: Lumail waits up to 30 seconds for a response. A timed-out request is retried, so the same call can reach your endpoint more than once: make it idempotent on subscriberId + workflowId + stepId
  • Failure: HTTP 4xx (except 408/429) fails the run immediately and shows the status plus response body. 5xx, 408, 429, and timeouts retry then fail. The FAILED run event stores HTTP status, a truncated response body, and the URL host
  • Dry run: Records a skipped-action timeline event and makes no HTTP request