Filtered Webhooks

The optional filterExpression field mentioned in the setup guide allows specifying further conditions that restrict the Webhook applicability.

The filterExpression field needs to be a valid JMESPath expression and it will be evaluated on the payload entry of the data about to be sent to the url (see previous example). If the result is "falsy" (i.e. 0, "", null, false or []) then the Webhook will not be sent.

Only filter conditions referring to the target entity after the event has occurred are acceptable. It is not possible to filter based on the entity state before the event. In other words, filter conditions such as "field X has value Y" are acceptable, but "field X has changed" are not.

We recommend using [node queries](/docs/guides/GraphQL Basics/node.md) when using Filtered Webhooks in order to make the JMESPath expression much simpler than it would have to be when using a [connection query](/docs/guides/GraphQL Basics/connections.md).

For help testing your filter expressions use the JMESPath playground

Filtering and Rate Limits

A filter only controls whether a Webhook is delivered — it does not reduce the processing load a triggered event generates. To decide whether an event matches the filter, the system must first fetch and assemble the full payload, and that preparation is exactly the work that rate limiting protects each instance from.

As a result, every triggered event consumes rate-limit allowance, including events that do not match the filter. A filter that delivers only a small subset of a large burst of events will still incur throttling, retries, and — if enough failures accumulate — automatic deactivation based on the full volume of triggered events. See Rate Limits for details on what counts toward the limits and how exceeding them is handled.

Example

The following is an example node query that is going to be used Filtered "Contact Updated" Webhook:

Example

query ContactNodes{
  node(id: "UGVyc29uOjI=") {
    id
    ... on Contact {
      personalName {
        firstName
        lastName
      }
      emailAddress
    }
  }
}

The following mutation sets up a Webhook that only triggers when the Contact's email address contains @getadministrate.com by means of the above query and a filterExpression set to contains(node.emailAddress, '@getadministrate.com')

Example

mutation createFilteredContactUpdate {
  webhooks {
    create(
      input: {
        name: "Send Contact Updates for Administrate staff"
        webhookTypeId: "V2ViaG9va1R5cGU6Y29udGFjdF91cGRhdGVk"
        url: "<ENDPOINT_URL>"
        query: "query node($objectid: ID!) { node(id: $objectid) { id ... on Contact { personalName { firstName lastName } emailAddress } } }"
        notificationEmails: ["an@example.com"]
        filterExpression: "contains(node.emailAddress, '@getadministrate.com')"
      }
    ) {
      webhook {
        id
      }
      errors {
        label
        message
        value
      }
    }
  }
}

Logs

To inspect the execution of Filtered Webhooks, the logs can be queried for the matchesFilter field (more on this on our Logs documentation):

Example

query logs{
  webhookLogs(filters:[
    {field: webhookId, operation: eq, value: "T3V0Ym91bmRIb29rOjYy"}
  ]){
    edges{
      node{
        id
        webhook{
          filterExpression
        }
        matchesFilter
        objectId
        payload
        status
        success
        triggeredAt
      }
    }
  }
}

The example response below shows that

  • the first item was sent to the url (status is success) because matchesFilter is true, as the payload matched the filter
  • the second item was not sent to the url (status is notSent) because matchesFilter is false, as the webhook payload did not match the filter
{
  "data": {
    "webhookLogs": {
      "edges": [
        {
          "node": {
            "id": "T3V0Ym91bmRIb29rc0xvZzozODg=",
            "webhook": {
              "filterExpression": "contains(node.emailAddress, '@getadministrate.com')"
            },
            "matchesFilter": true,
            "objectId": "UGVyc29uOjI4MQ==",
            "payload": "{\"metadata\": {\"user\": {\"email\": null}, \"instance\": \"https://myinstance.administrateapp.com\", \"triggered_at\": \"2023-04-04T11:42:42.000000Z\", \"context\": \"graphql\", \"webhook_id\": \"T3V0Ym91bmRIb29rOjYy\", \"is_retry\": false}, \"payload\": {\"node\": {\"id\": \"UGVyc29uOjI4MQ==\", \"personalName\": {\"firstName\": \"Fiona\", \"lastName\": \"Brown\"}, \"emailAddress\": \"fiona@getadministrate.com\"}}}",
            "status": "success",
            "success": true,
            "triggeredAt": "2023-04-04T11:42:42Z"
          }
        },
        {
          "node": {
            "id": "T3V0Ym91bmRIb29rc0xvZzozODk=",
            "webhook": {
              "filterExpression": "contains(node.emailAddress, '@getadministrate.com')"
            },
            "matchesFilter": false,
            "objectId": "UGVyc29uOjM5",
            "payload": "{\"metadata\": {\"user\": {\"email\": null}, \"instance\": \"https://myinstance.administrateapp.com\", \"triggered_at\": \"2023-04-04T11:46:03.000000Z\", \"context\": \"graphql\", \"webhook_id\": \"T3V0Ym91bmRIb29rOjYy\", \"is_retry\": false}, \"payload\": {\"node\": {\"id\": \"UGVyc29uOjM5\", \"personalName\": {\"firstName\": \"Fergus\", \"lastName\": \"White\"}, \"emailAddress\": \"fergus@example.com\"}}}",
            "status": "notSent",
            "success": false,
            "triggeredAt": "2023-04-04T11:46:03Z"
          }
        }
      ]
    }
  }
}