---
title: Filtering and options
description: Narrow and shape what a webhook delivers with its options, from filter and on-field-change to load-only, chunk_size and skip_keys.
canonical: https://developers.coevera.com/webhooks/filtering-and-options/
section: webhooks
verification: behaviour
lastVerified: 2026-10-15
publisher: Coevera
---

# Filtering and options

**Answer:** A webhook's options object narrows and shapes deliveries. Per entity, filter and filter-op choose which records are delivered, on-field-change delivers only when listed fields changed, and load-only and expand shape the record. At the top level, chunk_size (1 to 100), chunk_delay, skip_keys and custom_entity_api_name apply. Option keys use load-only with a hyphen; load_only is refused.

`options` is optional: without it, a webhook delivers every record of the events it subscribes
to, as the REST API returns it. The API checks every key when you create or update a webhook and
refuses one it does not know with a 400, naming the key ([errors](/concepts/errors/)). The
options follow the REST API's list parameters, which [querying](/concepts/querying/) describes,
and are a field of the [Webhooks entity](/reference/rest/webhooks/#fields).

This update sets a per-entity `load-only` and two top-level options, `chunk_size` and
`skip_keys`:

**REST · cURL**

```bash
# The events and options you send replace the webhook's: send the whole list and object.
curl -sS --fail-with-body -X PATCH "$COEVERA_BASE/entities/Webhooks/$WEBHOOK_ID" \
  -u "$COEVERA_API_KEY:$COEVERA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d @- <<JSON
{
  "events": ["Opportunity.Won", "Opportunity.Lost", "Opportunity.Move"],
  "options": {
    "entity": {
      "Opportunity": { "load-only": ["id", "name", "owner_id", "step_id", "value", "revision"] }
    },
    "chunk_size": 50,
    "skip_keys": ["northwind-sync"]
  }
}
JSON
echo
```

**REST · TypeScript**

```ts
// The events and options you send replace the webhook's: send the whole list and object.
await coevera("PATCH", `/entities/Webhooks/${webhookId}`, {
  events: ["Opportunity.Won", "Opportunity.Lost", "Opportunity.Move"],
  options: {
    entity: { Opportunity: { "load-only": ["id", "name", "owner_id", "step_id", "value", "revision"] } },
    chunk_size: 50,
    skip_keys: ["northwind-sync"],
  },
});
```

**REST · Python**

```python
# The events and options you send replace the webhook's: send the whole list and object.
coevera(
    "PATCH",
    f"/entities/Webhooks/{webhook_id}",
    json={
        "events": ["Opportunity.Won", "Opportunity.Lost", "Opportunity.Move"],
        "options": {
            "entity": {"Opportunity": {"load-only": ["id", "name", "owner_id", "step_id", "value", "revision"]}},
            "chunk_size": 50,
            "skip_keys": ["northwind-sync"],
        },
    },
)
```

**GraphQL · cURL**

```bash
# The events and options you send replace the webhook's: send the whole list and object.
jq -n --arg id "$WEBHOOK_ID" '{
  query: "mutation UpdateWebhook($input: UpdateWebhookInput!) { updateWebhook(input: $input) { webhook { id events options } } }",
  variables: {
    input: {
      id: $id,
      events: ["OpportunityWon", "OpportunityLost", "OpportunityMove"],
      options: ({
        entity: { Opportunity: { "load-only": ["id", "name", "owner_id", "step_id", "value", "revision"] } },
        chunk_size: 50,
        skip_keys: ["northwind-sync"]
      } | tojson)
    }
  }
}' | gql
```

**GraphQL · TypeScript**

```ts
// The events and options you send replace the webhook's: send the whole list and object.
await gql(
  `mutation UpdateWebhook($input: UpdateWebhookInput!) {
    updateWebhook(input: $input) { webhook { id events options } }
  }`,
  {
    input: {
      id: webhookId,
      events: ["OpportunityWon", "OpportunityLost", "OpportunityMove"],
      options: JSON.stringify({
        entity: { Opportunity: { "load-only": ["id", "name", "owner_id", "step_id", "value", "revision"] } },
        chunk_size: 50,
        skip_keys: ["northwind-sync"],
      }),
    },
  },
);
```

**GraphQL · Python**

```python
# The events and options you send replace the webhook's: send the whole list and object.
gql(
    """
    mutation UpdateWebhook($input: UpdateWebhookInput!) {
      updateWebhook(input: $input) { webhook { id events options } }
    }
    """,
    {
        "input": {
            "id": webhook_id,
            "events": ["OpportunityWon", "OpportunityLost", "OpportunityMove"],
            "options": json.dumps({
                "entity": {"Opportunity": {"load-only": ["id", "name", "owner_id", "step_id", "value", "revision"]}},
                "chunk_size": 50,
                "skip_keys": ["northwind-sync"],
            }),
        }
    },
)
```

## Top-level options

| Key                      | What it does                                                                                                                                                      |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entity`                 | Options for the record in `entity`, per entity: `{"Opportunity": {…}}`. The keys are entity names as the events use them; an unknown entity is refused.           |
| `related_entity`         | `expand` for the document in `related_entity` of a `DocumentLinked` event, per event entity: `{"Opportunity": {"expand": ["owner"]}}`. Only `expand` is accepted. |
| `chunk_size`             | How many events one delivery carries: 1 to 100, 100 by default. 0 or 101 is refused.                                                                              |
| `chunk_delay`            | Seconds between chunks: chunk N, counted from 0, is sent N × `chunk_delay` seconds later. 0 by default; a negative value is refused.                              |
| `skip_keys`              | Keys that stop this webhook firing: a write sent with the header `WebHook-Skip-Key` set to one of them does not fire it.                                          |
| `custom_entity_api_name` | For `CustomEntity` events, the custom entities that fire this webhook, such as `["CE_Shipment"]`. All of them by default.                                         |

## Per-entity options

Under `entity.<Entity>`, five keys are accepted, in the REST API's names (snake case for fields,
a hyphen in `load-only`, `filter-op` and `on-field-change`):

| Key               | What it does                                                                                                                                                                                                 |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `filter`          | Deliver only the records that match, in the REST API's [filter](/concepts/querying/#filter) form: `{"name": "Northwind"}`, or a list of values for any of them. A field the entity does not have is refused. |
| `filter-op`       | The operator per filtered field, such as `{"name": "contains"}`; `eq` by default. The [operators](/concepts/querying/#operators) are those of the REST API.                                                  |
| `on-field-change` | Deliver a record only if one of these fields changed in that change: `["step_id", "value"]`. An unknown field name is refused.                                                                               |
| `load-only`       | The fields the record in the delivery carries. Keep `id` and `revision` if you deduplicate on them.                                                                                                          |
| `expand`          | Related records to include in full, such as `["owner"]`.                                                                                                                                                     |

> **Warning:** `load-only` takes a hyphen. `load_only`, which some REST query parameters tolerate, is refused
> here with a 400.

### on-field-change narrows every event

`on-field-change` applies to every event of that entity the webhook subscribes to, not only to
`Update`: a `Create`, a `Won` or a `Delete` is delivered only if one of the listed fields changed
in that same change. Subscribe to `Opportunity.Won` with `on-field-change: ["value"]`, and a win
that leaves the value alone is not delivered. Use a second webhook for events that must always
arrive.

It works for these entities: Account, Contact, Lead, Opportunity, Task, Appointment, product line
items (`OpptyProductRelation`), Project, Quote, Email, and custom entities. `OnlineForm` is
accepted but has no events. Any other entity, such as `Product`, `Memo` or `Note`, is refused.

## Reading and changing options

- An update replaces `options` as a whole: send every key you want to keep, as the sample above
  does.
- List webhooks without `load-only`: with it, `options` comes back as `{}`.
- In GraphQL, `options` is a JSON string, in the input and in the response.
- `options: null` is refused; send `{}` to clear them.

## Limits

- `related_entity` takes only `expand`; there is no `load-only` or `filter` for the document.
- The filter applies to the record as it is when the delivery is built, not when it changed.
- Records the webhook's owner cannot read are left out of deliveries. A webhook registered with an
  API application's credentials has no owner, so it delivers every record.
