> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withpersona.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withpersona.com/_mcp/server.

# Webhook Event Filters

> **Warning**
>
> #### Enterprise support
>
> This feature is restricted to customers on the Enterprise plan. Please reach out to your Account Team or [contact us](https://app.withpersona.com/dashboard/contact-us) if you are interested in enabling and setting up this feature.

![webhook-event-filters](https://assets.withpersona.com/f_auto,q_auto/developer-docs/images/webhook-event-filters.png)

## Description

Webhook Event Filters allow developers to be more selective with which [Events](/events) they receive. If the filter is a strict subset of the `payload` data for an Event, the Webhook Event will go through. Otherwise, the Webhook Event will change to `skipped`.

## Filter key casing

> **Warning**
>
> Filters are matched against the Event payload using **exact key comparison**. If a key in your filter does not match the payload character for character, **the filter will never match and every Event will be `skipped`**.

Two different rules decide which [key inflection](/api-key-inflection) a key appears in, so you need to check both.

### Attribute and relationship names

Attribute and relationship names — `reference-id`, `created-at`, `inquiry-template` — always follow the key inflection configured on the webhook, on every API version.

A webhook's key inflection is set on its **Overview** tab in the Dashboard, under **Key inflection**. It is **Kebab-case by default**, and is configured per webhook — it does not follow the key inflection on your API keys.

The same filter has to be written differently for each setting:

**`Kebab-case (default)`**

```json Kebab-case (default)
{
  "data": {
    "attributes": {
      "reference-id": "abc123"
    },
    "relationships": {
      "inquiry-template": {
        "data": {
          "id": "itmpl_abc123def456"
        }
      }
    }
  }
}
```

**`Camel-case`**

```json Camel-case
{
  "data": {
    "attributes": {
      "referenceId": "abc123"
    },
    "relationships": {
      "inquiryTemplate": {
        "data": {
          "id": "itmpl_abc123def456"
        }
      }
    }
  }
}
```

**`Snake-case`**

```json Snake-case
{
  "data": {
    "attributes": {
      "reference_id": "abc123"
    },
    "relationships": {
      "inquiry_template": {
        "data": {
          "id": "itmpl_abc123def456"
        }
      }
    }
  }
}
```

### Field names inside `fields`

Field names inside `data.attributes.fields` do **not** always follow the webhook's key inflection. Which inflection a field name appears in depends on the API version configured on the webhook:

* **API version `2025-10-27` and later** — field names are **never** key inflected. They appear exactly as they are configured on the Inquiry Template, Case Template, or Transaction Type, which is normally snake\_case (`address_country_code`).
* **API versions before `2025-10-27`** — field names on Inquiries, Cases, and Transactions **are** key inflected, so they follow the webhook's key inflection (`address-country-code` on a Kebab-case webhook).
* **Account field names are never key inflected**, on any API version.

The same field filter is therefore not portable across an API version change:

**`2025-10-27 and later (any key inflection)`**

```json 2025-10-27 and later (any key inflection)
{
  "data": {
    "attributes": {
      "fields": {
        "address_country_code": {
          "value": "US"
        }
      }
    }
  }
}
```

**`Before 2025-10-27 (Kebab-case webhook)`**

```json Before 2025-10-27 (Kebab-case webhook)
{
  "data": {
    "attributes": {
      "fields": {
        "address-country-code": {
          "value": "US"
        }
      }
    }
  }
}
```

> **Info**
>
> Rather than converting names by hand, copy the keys straight out of a real Event payload delivered by this webhook — it is already serialized with that webhook's key inflection and API version. See [Designing an event filter](#designing-an-event-filter) below.

## Examples

> **Info**
>
> Note that the examples below assume a webhook configured with Kebab-case key inflection!

Let's say that you only want `inquiry.completed` events that are related to specific Inquiry Templates.

On the Webhook create/edit modal, you'd put in JSON similar to the following:

**`json`**

```json json
{
  "data": {
    "relationships": {
      "inquiry-template": {
        "data": {
          "id": "itmpl_abc123def456"
        }
      }
    }
  }
}
```

This would permit an event like the following, ensuring only `inquiry.completed` payloads associated with the correct template are triggered.

**`json`**

```json json
{
  "data": {
    "type": "event",
    "id": "evt_Xzh192s6ZKyUVVf3L7Ynz82M",
    "attributes": {
      "name": "inquiry.completed",
      "payload": {
        "data": {
          "type": "inquiry",
          "id": "inq_J95Dw2iV4H9spDDCqCJNHS7b",
          "attributes": {
            "status": "completed",
            "...": "..."
          },
          "relationships": {
            "inquiry-template": {
              "data": {
                "type": "inquiry-template",
                "id": "itmpl_abc123def456"
              }
            },
            "...": "..."
          }
        }
      }
    }
  }
}
```

## Designing an event filter

> **Info**
>
> This walkthrough uses a payload from a Kebab-case webhook on an API version before `2025-10-27`, which is why the field name below reads `address-country-code`. Always start from a payload delivered by the webhook you are filtering — see [Filter key casing](#filter-key-casing).

If you look at the object in the `payload` of an [Event](/events), you might see something like this.

**`json`**

```json json
{
  "data": {
    "type": "inquiry",
    "id": "inq_XN8jxMoEhUeihzNypSaFKFfo",
    "attributes": {
      "status": "completed",
      "reference-id": null,
      "...": "...",
      "fields": {
        "address-country-code": {
          "type": "string",
          "value": "US"
        },
        "...": "..."
      }
    },
    "relationships": {
      "inquiry-template": {
        "data": {
          "type": "inquiry-template",
          "id": "itmpl_abc123def456"
        }
      },
      "...": "..."
    }
  }
}
```

Decide on a particular value that you're interested in. Let's say it's that the Inquiry had a country code of `US`. You'd then delete everything in the JSON object until only the key/value `"value": "US"` remained and all of the objects that contain it.

You would end up with the following filter for Inquiries with US addresses.

**`json`**

```json json
{
  "data": {
    "attributes": {
      "fields": {
        "address-country-code": {
          "value": "US"
        }
      }
    }
  }
}
```

## Matching against arrays

To match against an array in your payload, make an array with the elements you want to check for.

Matching on Inquiries with a tag named "INBOUND" would look like this.

**`json`**

```json json
{
  "data": {
    "attributes": {
      "tags": [
        "INBOUND"
      ]
    }
  }
}
```

If you wanted to only receive Events with an "INBOUND" and "CAMPAIGN ABC" tags, you'd make the following.

**`json`**

```json json
{
  "data": {
    "attributes": {
      "tags": [
        "INBOUND",
        "CAMPAIGN ABC"
      ]
    }
  }
}
```

## `$or` operator

To allow for multiple criteria, use the `$or` operator. It can be used on a list of values or JSON object literals.

Value example:

**`json`**

```json json
{
  "data": {
    "relationships": {
      "inquiry-template": {
        "data": {
          "id": {
            "$or": [
              "itmpl_abc123def456",
              "itmpl_ghi789jkl012"
            ]
          }
        }
      }
    }
  }
}
```

Hash example:

**`json`**

```json json
{
  "data": {
    "relationships": {
      "$or": [
        {
          "inquiry-template": {
            "data": {
              "id": "itmpl_abc123def456"
            }
          }
        },
        {
          "template": {
            "data": {
              "id": "tmpl_foo999bar888"
            }
          }
        }
      ]
    }
  }
}
```

### `$or` on array fields

When using `$or` to match against a field whose value is an array (like `tags`), the `$or` must be placed at the **parent level**, not directly on the array field. Each option should wrap the array field with the value(s) to match.

This will **not** work — `$or` directly on an array field:

**`json`**

```json json
{
  "data": {
    "attributes": {
      "tags": { "$or": ["US 1", "CAN 1"] }
    }
  }
}
```

Instead, place `$or` at the parent level with each option wrapping the array:

**`json`**

```json json
{
  "data": {
    "attributes": {
      "$or": [
        { "tags": ["US 1"] },
        { "tags": ["CAN 1"] }
      ]
    }
  }
}
```

This also composes with other filters using AND semantics. For example, to match (tag "US 1" OR tag "CAN 1") AND (one of several templates):

**`json`**

```json json
{
  "data": {
    "attributes": {
      "$or": [
        { "tags": ["US 1"] },
        { "tags": ["CAN 1"] }
      ]
    },
    "relationships": {
      "inquiry-template": {
        "data": {
          "id": {
            "$or": ["itmpl_aaa", "itmpl_bbb", "itmpl_ccc"]
          }
        }
      }
    }
  }
}
```

> **Info**
>
> **Key rules for `$or`:**
>
> * `$or` on a **scalar field** (like `id`, `status`) works directly: `{ "id": { "$or": ["a", "b"] } }`
> * `$or` on an **array field** (like `tags`) must be lifted to the parent object, with each option as `{ "field": ["value"] }`
> * Multiple array values in a single option use AND semantics: `{ "tags": ["US 1", "CAN 1"] }` means "has both tags"
> * Sibling keys at the same level are ANDed together