Creating a Webhooks Filter

We now know what a Webhooks filter is, and we even have some idea regarding the kinds of information that can be included in a filter. But that still leaves one very important question unanswered: how exactly do we create a Webhooks subscription?

Funny you should ask. As it turns out, filters are specified by the optional filter claim in a Webhooks subscription. Prior to the release of Webhooks v3 filtering, the events returned by a given subscription were determined solely by the event type; for example, if the events claim specified the entityDeleted event then a notification would be issued only for entityDeleted events. Any other event type – e.g., entityCreated or entityDeleted – would be ignored.

That's still true today: your subscriptions still need to include an events claim that references the event types you're interested in. In addition to that, however, you can now use the filter claim to further restrict the events actually sent to your listener endpoint. For example, you can set the filter to a specific client ID; in that case, event notifications are issued only if they involve, say, an entityDeleted event and they involve the specified client. 

Note. We should emphasize that event filtering is optional: you can add a filter to a Webhooks subscription or you can choose not to add a filter to a Webhooks subscription. For example, suppose we chose not to add a filter to our entityDeleted subscription. In that case, we’ll get notifications for each and every entityDeleted event (as opposed to only being notified of entityDeleted events involving a specific client or a specific entity type).

To explain how you can create your own filters, let's start by taking a quick look at the properties of a Webhooks subscription that’s been assigned a filter. In this case, it’s a simple filter that checks each entityUpdated event to see if the captureApplicationId is equal to zzyn9gy9r8xdy5zkru4y54syk6. If that's true (i.e., if the captureApplicationId is equal to zzyn9gy9r8xdy5zkru4y54syk6), the event is forwarded to your listener endpoint. If it isn’t true, the event is ignored.

Note. OK: ignored by this subscription. If you have another subscription that monitoresfor instances of the entityUpdated event that subscription might not ignore the event. See this article for more information.

Here’s the subscription, and the filter:

{
    "subscriptionId": "a8b515a5-4de5-4b9b-aa02-f8a7005e5fac",
    "customerId": "e0a70b4f-1eef-4856-bcdb-f050fee66aae",
    "createdAt": "2021-04-15T18:03:56.287851Z",
    "updatedAt": "2021-08-16T20:29:00.145422Z",
    "title": "Sample Webhooks Subscription",
    "endpoint": "https://webhook.site/46ff3c5e-ae95-43df-b32d-d07bb84746b4",
    "events": [
        "entityUpdated"
    ],
    "enabled": true,
    "filter": {
      "$schema": "http://json-schema.org/draft-07/schema",
      "properties": {
        "entityUpdated": {
           "properties": {
             "captureApplicationId": {
               "const": "zzyn9gy9r8xdy5zkru4y54syk6"
          }
        }
      }
    }
  },
  "additionalProperties": false,
  "_links": {
      "self": {
          "href": "/e0a70b4f-1eef-4856-bcdb-f050fee66aae/webhooks/subscriptions/a8b515a5-4de5-4b9b-aa02-f8a7005e5fac"
      }
    }
  }

And here’s an example of a Curl command that adds the filter to a webhook subscription:

curl -L -X PATCH \
  'https://v1.api.us.janrain.com/e0a70b4f-1eef-4856-bcdb-f050fee66aae/webhooks/subscriptions/a8b515a5-4de5-4b9b-aa02-f8a7005e5fac' \
  -H 'Content-Type: application/merge-patch+json' \
  -H 'Authorization: Bearer fpt1bOe_0ZaZqQ3JqGIK-SjD2VpQYL0sxLPA2vEqsE_4SaytcB7v9hTaH1nCwItz' \
--data-raw '{
    "endpoint": "https://webhook.site/eeb46d8c-9fc0-42e4-b2d1-c0dc917031b1",
    "events": [
        "entityUpdated"
    ],
    "enabled": true,
    "filter": {
      "$schema": "http://json-schema.org/draft-07/schema",
        "properties": {
          "entityUpdated": {
            "properties": {
              "captureApplicationId": {
                "const": "zzyn9gy9r8xdy5zkru4y54syk6"
            }
          }
        }
      }
    },
    "additionalProperties": false
  }

Note. For more information on making changes to a webhook subscription see the article Modify a Webhook Subscription.

Admittedly, the code for adding that “simple” filter to a Webhooks subscription doesn’t look very simple. So let’s walk you through the filter syntax, line-by-line.

Our application ID filter starts out the way all webhook filters start out: by using the filter claim and specifying the schema used to create and validate the filter:

"filter": {
    "$schema": "http://json-schema.org/draft-07/schema",

From there we begin to burrow our way into the event details. We’re interested in the entityUpdated event, as indicated in the events claim. Because of that, we indicate our interest with this portion of the filter:

"filter": {
    "$schema": "http://json-schema.org/draft-07/schema",
      "properties": {
        "entityUpdated": {
Note. Yes, the event type listed under properties (in this case, entityUpdated) needs to match one of the events specified in the events claim. Why? Well, suppose you create a filter for the eventUpdated event, but the subscription itself only monitors for the eventDeleted event. In that case, the filter will never be used: after all, it filters on an event type that the subscription completely ignores. As a result, there won't be anything for the filter to filter on.

Of course, our interest doesn’t stop with the event type: we’re also interested in the application ID associated with that event; more specifically, we're interested in the value assigned to the captureApplicationId claim (a property of the entityUpdated event). Because of that, we use the properties claim a second time, and indicate that captureApplicationId is the subproperty we want to validate on:

"filter": {
  "$schema": "http://json-schema.org/draft-07/schema",
    "properties": {
    "entityUpdated": {
      "properties": {
        "captureApplicationId": {

But, again, it’s not just the captureApplicationId claim we care about; we also care about the value of the claim. More specifically, we care whether the value of that claim is zzyn9gy9r8xdy5zkru4y54syk6. If it is, then we want the event forwarded to us. If it isn’t, then the event can be ignored.

That’s why we include the const keyword and the value zzyn9gy9r8xdy5zkru4y54syk6:

"filter": {
  "$schema": "http://json-schema.org/draft-07/schema",
    "properties": {
    "entityUpdated": {
      "properties": {
        "captureApplicationId": {
          "const": "zzyn9gy9r8xdy5zkru4y54syk6"
        }

The const keyword lets us specify the ID of interest; in. this case, zzyn9gy9r8xdy5zkru4y54syk6. It’s probably worth noting that const is not the only keyword available to us when writing Webhook filters. (For a detailed list of keywords and what they’re used for, see the article Supported Filter Keywords.) For example, suppose we have two different applications (zzyn9gy9r8xdy5zkru4y54syk6 and nmub5w3rru9k6rzupqaeb7bbwv6jn658), and we want to see events involving either one of these applications. That's fine; we can reference multiple application IDs by using the enum keyword:

"properties": {
   "captureApplicationId": {
       "enum": ["zzyn9gy9r8xdy5zkru4y54syk6"], "nmub5w3rru9k6rzupqaeb7bbwv6jn658"]
  }

The enum keyword allows us to specify an array of values (note the square brackets that enclose the two comma-separated IDs). In this case, our array consists of the allowed application IDs. If the captureApplicationId for an event matches either of the values in the array then the filter passes validation and the event is forwarded. If the application ID isn’t in the enum array, the event is ignored.

Note. What if you have multiple applications but you don’t want to filter out any of them: instead, you want to receive events from all your applications. You probably already know the answer to that: don’t include captureApplicationId in your filter. Likewise, if you want to receive all your events, without filtering any of them, then don’t include the filter claim in your Webhook subscription. Remember, filters are optional: if you have no use for them, don’t use them.

Admittedly that’s a very brief, and a very rudimentary, overview of filter writing. But if that's not enough information for you, don't despair; instead, see this article for additional details, and a to view a number of sample filters that can readily be adapted for your own use.



A Note About the additionalProperties Keyword

In our sample filter you might have noticed this line immediately following the filter definition:

"additionalProperties": false

This line isn't required: your filter will probably work just fine without it. Nevertheless, we recommend that you include the additionalProperties keyword whenever you write a filter, and that you always set the value to false.

Why? The additionalProperties keyword helps ensure that unwanted events aren't inadvertently forwarded to you. For example, suppose you have a webhooks subscription that monitors for both the entityUpdated and the entityCreated events. Your subscription includes a filter that limits entityUpdated events to events from application zzyn9gy9r8xdy5zkru4y54syk6;: only entityUpdated events from that application are forwarded to you. 

However, that same subscription doesn't include a filter for the entityCreated events: that means that all entityCreated events are forwarded, regardless of the event's captureApplicationId. Adding additionalProperties ensures that no entityCreated events (that is, no unfiltered events) are forwarded. If you really do want entityCreated events then you'll need to add a filter for that event type.