> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.withpersona.com/2020-05-18/webhook-event-filters/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 > Only have a subset of Events come through