Skip to main content

Verify and secure webhooks in Formalize workflows

Answer webhook challenge requests with verification rules, filter out test events, check a shared secret and restrict sender IP addresses in Formalize workflows.

Overview

In Formalize, you can verify your webhook URL with external systems that require it, filter out test events such as GitHub pings, make your workflow act only on events that contain a shared secret, and limit which IP addresses can send events with an IP allowlist. To set up your first webhook workflow, see Trigger workflows from external systems with webhooks.


Before you start

To use these webhook settings in Formalize, you need:

  • A saved and enabled Formalize webhook workflow. See Trigger workflows from external systems with webhooks.

  • The Automations feature enabled for your company. To enable it, contact your Formalize account manager, or Formalize support if you don't have one.

  • A company account that isn't a trial account. The webhook trigger isn't available on trial accounts.

  • Automations access rights (see Access Roles Management). Go to Settings > Users & Access > Access Roles, click Edit next to the role, expand Automations by clicking the arrow (>), select the following permissions, then click Save:

    • Automations sidebar to show Workflows in the navigation.

    • View automations to open workflows.

    • Edit automations to change the webhook settings.


Verify your webhook URL

Before some external systems send events, they send a test request to check your webhook URL. This is called a challenge request. To answer it, add a verification rule under Endpoint Verification in the Trigger node.

When a challenge request matches a rule, Formalize sends the response and doesn't start a run or log the request in Inbound events. When it doesn't match, Formalize treats it like a normal event.

💡 Note: Create the verification rule before you paste the webhook URL into the external system. Many external systems send the challenge request right away, and verification fails if no rule exists yet.

How verification rules work

A verification rule in Formalize has three parts:

Part

What it does

Request fields

Reads a value from the request, so that conditions can check it and the response can return it. Each field has a Name and a Source: Body, Headers or Query. Request fields are separate from trigger variables, so define every value the rule uses here.

Conditions

Recognize the challenge request. Each condition checks that its Path (the method or a request field) Equals a Value. A request must match all conditions.

Responds with

The status code, body and optional headers that Formalize sends back. To return a value from the request, such as a challenge value that changes with every request, type {{ and select its request field.

⚠️ Important: A rule that only checks the method answers every request with that method, including normal events. Those events then don't start a run or appear in the Inbound events tab.
If the external system sends normal events with the same method as its challenge request, add a condition on a request field that only the challenge request has.
A method-only rule is safe for Trello (HEAD), Dropbox and Meta (GET), because all three send normal events with POST.

Create a verification rule

  1. Go to Operations > Workflows, open the workflow and click Builder.

  2. Click the Trigger node.

  3. If the rule needs to check or return a header, a query parameter or a body field (for example, Slack's body challenge or Dropbox's query challenge), go to Endpoint Verification > Request fields and click Add field.

  4. Enter a Name for the field. Under Source, select Body, Headers or Query, then enter the body field, header name or query parameter to read.

  5. Under Conditions, click Add condition.

  6. Select the Path: Method or the name of a request field. Then enter the Value it must equal. For Method, select HEAD, GET or POST. The Operation is always Equals. To add another condition, click Add condition again.

  7. Under Responds with, set the status code, body and any headers. To return a request field, type {{ and select it.

  8. Click Save.

For example, Slack sends a POST challenge request with type set to url_verification, and expects the challenge value back:
​

Slack verification rule: request fields type and challenge with Source set to Body, a condition where type Equals url_verification, and a response with status 200 and the challenge field in the body.

Trello checks the URL with a HEAD request and expects the status 200 with an empty body:
​

Verification rule with the condition Method Equals HEAD, responding with status 200 and an empty body.

Verification rules for common external systems

To verify your Formalize webhook URL with one of these external systems, create the verification rule shown in the table. Define every value the rule checks or returns as a request field first.

External system

Conditions

Responds with

Trello

Method Equals HEAD

Status 200 and an empty body.

Slack (Events API)

A request field with Source set to Body and type, and a condition where that field Equals url_verification

Status 200. Body: the value of the challenge field from the request body.

Dropbox

Method Equals GET

Status 200. Body: the value of the challenge query parameter. Headers: Content-Type: text/plain and X-Content-Type-Options: nosniff.

Meta (WhatsApp, Instagram)

Method Equals GET

Status 200. Body: the value of the hub.challenge query parameter.

GitHub, Stripe, Shopify and Twilio don't send a challenge request, so they don't need a rule.

Filter out GitHub ping events

GitHub sends a ping event when you create the webhook. It starts a run like any other event. To stop that run before any action:

  1. Go to Operations > Workflows, open the workflow and click Builder.

  2. Click the Trigger node.

  3. Under Trigger variables, click Add variable, then set Name to githubEvent, Source to Headers and Path to x-github-event.

    Trigger variable githubEvent with Source set to Headers and Path set to x-github-event.

  4. Click + below the trigger, select Condition, then select True / False and set githubEvent Equals ping.

  5. Leave the True branch empty and build the workflow in the False branch.
    ​

    Condition step githubEvent = ping, with an empty True branch and the rest of the workflow in the False branch.

  6. Click Save.

When a verification rule can't answer the challenge request (Zoom, AWS SNS)

A verification rule can't answer the challenge request when:

  • The external system expects a computed response, for example Zoom's hashed challenge.

  • The external system expects a callback, such as following AWS SNS's SubscribeURL. Endpoint Verification can't make callbacks.

💡 Workaround for AWS SNS: SNS sends the subscription confirmation as a normal event, so it starts a run and appears in the Inbound events tab. Click Payload on that event, copy the SubscribeURL value from the body, and open it in your browser. To stop confirmation events from running the workflow, add a trigger variable for the x-amz-sns-message-type header and a Condition step that continues only when it isn't SubscriptionConfirmation.


Protect your webhook with a shared secret

Anyone who knows your webhook URL can send events to it. To make sure your workflow acts only on events from your external system, have the external system send a shared secret in a header with each event, and check that secret in your workflow.

The check doesn't block events. Formalize still logs each event and starts a run, unless the IP allowlist rejects it or a verification rule answers it. When the secret is wrong, the check stops the run before any action.

This method only works if your external system lets you add a custom header to its webhooks. If it doesn't, use an IP allowlist instead.

⚠️ Important: Don't use Endpoint Verification to check the shared secret. A request that matches a verification rule gets a response but doesn't run the workflow, so verification rules are only for challenge requests. Use a trigger variable and a Condition step, as in the steps below.

  1. Create a shared secret: a long, random value, for example generated by a password manager.

  2. In the external system's webhook settings, add a header named x-api-key with your shared secret as its value.

  3. Go to Operations > Workflows, open the workflow and click Builder.

  4. Click the Trigger node.

  5. Under Trigger variables, click Add variable, then set Name to apiKey, Source to Headers and Path to x-api-key.

    Trigger variable apiKey with Source set to Headers and Path set to x-api-key.

  6. Click + below the trigger, select Condition, then select True / False and set apiKey Equals your shared secret.
    ​

    Condition step checking that apiKey equals the shared secret

  7. Build the rest of the workflow in the True branch. Leave the False branch empty, so runs started by events without the right secret stop before any action.

  8. Click Save.

⚠️ Warning: The shared secret is visible to anyone who can view this workflow, and in each event's headers in the Inbound events tab. Limit who has View automations, use the secret only for this webhook, and change it if it leaks. This secret isn't a Formalize Public API key.


Restrict senders with an IP allowlist

In Formalize, each webhook workflow has its own IP allowlist, which limits which IP addresses can send events to that workflow's webhook. It's separate from the login IP restriction (Whitelisted IP addresses for login) in Security Settings.

  1. Go to Operations > Workflows, open the workflow and click Builder.

  2. Click the Trigger node.

  3. Under IP allowlist, click Add IP or CIDR and enter an IP address or CIDR range, for example 203.0.113.0/24. Repeat for each address or range.
    ​

    IP allowlist with the range 203.0.113.0/24

  4. Click Save.

If the list is empty, Formalize accepts events from any IP address. An event from an address that isn't on the list doesn't start a run, and appears in the Inbound events tab with the status Rejected · IP.

💡 Note: The IP allowlist doesn't block challenge requests that match a verification rule. Formalize answers them before it checks the allowlist.

To find the addresses to add, check the external system's documentation. Many external systems publish the IP ranges they send events from, for example GitHub and Stripe. Some, such as Twilio, don't use fixed IP addresses, so you can’t restrict them with an IP allowlist.


Troubleshooting

The external system can't verify the webhook URL

The external system sent a challenge request and didn't get the response it expects. Check these in order:

  1. Check that a verification rule exists. If not, create one, then paste the URL into the external system again.

  2. Check that the rule's conditions match the challenge request.

  3. Check that the status code, body and headers match the external system's documentation.

A challenge request that no rule matched can appear in the Inbound events tab. Compare the method in its row with your rule's conditions. To see its headers, query parameters and body, click Payload.
​

Inbound events tab with an unmatched Slack challenge request and its Payload: body, headers and query parameters.

Events don't start a run and don't appear in Inbound events

A verification rule may be matching normal events as well as the challenge request. Open the Trigger node and check the conditions under Endpoint Verification. If a rule only checks the method, and the external system sends normal events with that method, narrow its conditions: add a request field for a header, query parameter or body field that only the challenge request has, and a condition on that field.

Runs stop at the shared-secret Condition step

The event didn't contain the right secret in the header you check. In the Inbound events tab, click Payload on the event and check that the x-api-key header is present and its value exactly matches the value in the Condition step. Also check that the trigger variable's Path contains the correct header name. Header names aren't case-sensitive, but the secret value must match exactly. To see which branch the run took, click View run on the event.
​

Workflow run details for an event with the wrong secret: the Condition step has the status False.

An event has the status Rejected · IP

The sender's IP address isn't on the IP allowlist. Compare the event's source IP in the Inbound events tab with your allowlist, and add the external system's address or range, then send the event again.
​

Inbound events tab with an event in the status Rejected · IP, method POST and a source IP.
Did this answer your question?