---
id: webhooks-event-types
slug: /apis/webhooks/event-types
title: 'Event Types'
description: 'Describe exactly which asset changes should fire a webhook using type, from, attributes, filter, with, userContext and select.'
sidebar_label: 'Event Types'
sidebar_position: 2
tags: ['api', 'webhooks', 'events', 'filter']
---

The `eventTypes` array on a subscription is where you say what you actually want to hear about.
Each entry is an independent rule; a subscription fires if **any** of its event types matches.

## The Two Event Types

Agility records every asset creation and every asset change. Each event type entry declares
which of the two it is watching:

| `type`          | Fires when                        |
| --------------- | --------------------------------- |
| `AssetCreated`  | An asset of `from` is created.     |
| `AssetChanged`  | An asset of `from` is modified.    |

These are the only two accepted values. Anything else is rejected at subscription-create time
with a message naming both.

## Fields

| Field         | Type     | Required | Description                                                                                          |
| ------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `type`        | string   | yes      | `AssetCreated` or `AssetChanged`.                                                                     |
| `from`        | string   | yes      | The asset type to watch, e.g. `Story`. Use the system name — see [terminology](/apis/conventions/terminology#asset-type). |
| `attributes`  | string[] | no       | Restrict to changes touching these attributes. Omit to fire on **any** change.                        |
| `filter`      | string[] | no       | Filter expressions the asset must satisfy. The full [filter language](/reference/query-grammar/filter-expressions) — operators, value lists, unary existence and grouping all work here. |
| `with`        | object   | no       | [Filter context](/reference/query-grammar/filter-context) — variable bindings substituted into `filter`. |
| `userContext` | string   | no       | Oid token of a member whose visibility the event is evaluated under.                                  |
| `select`      | array    | no       | [Attribute paths](/reference/query-grammar/attribute-paths) to project into the delivered payload's `snapshot`.                                        |

## Building up an Event Type

### Every Change to a Story

```json
{
  "type": "AssetChanged",
  "from": "Story"
}
```

### Only When Status Changes

`attributes` narrows the rule to changes that touch the listed attributes. Without it you are
notified of *any* change to the asset, which on a busy instance is a lot of traffic.

```json
{
  "type": "AssetChanged",
  "from": "Story",
  "attributes": ["Status"]
}
```

### Only Stories in One Project

`filter` takes the same filter expressions as a query, and `with` binds the variables they
reference. Keeping the value in `with` rather than inlining it makes the subscription easy to
retarget later.

```json
{
  "type": "AssetChanged",
  "from": "Story",
  "attributes": ["Status"],
  "filter": ["Scope=$Scope"],
  "with": {
    "$Scope": "Scope:0"
  }
}
```

See the [query grammar reference](/reference/query-grammar) for the full filter syntax.

### Only What a Particular Member Can See

Most assets in Agility are secured by their relationship to a Scope (project). Without
`userContext`, an event type sees **everything** — assets in every project, regardless of who
can normally view them.

`userContext` runs the evaluation as a specific member, so only changes visible to that member
produce events:

```json
{
  "type": "AssetChanged",
  "from": "Story",
  "attributes": ["Status"],
  "userContext": "Member:1002"
}
```

:::warning `userContext` is a security control, not a convenience
An event type without `userContext` will happily deliver data from projects the subscription's
creator could not otherwise read, to whatever URL you named. If the receiving system is not
trusted with instance-wide data, set `userContext` to a member with appropriately limited
visibility.

Note also that the original reference material contained the literal value `"Memeber:1002"` in
two of its examples — a misspelling of `Member`. An unresolvable Oid token here does not fail
open in a useful way; use the correct spelling.
:::

### Include Asset Details in the Delivery

By default the delivered payload identifies the asset by Oid and lists what changed, but tells
you nothing else about it. `select` projects additional attributes into the payload's
`snapshot`, using the same projection syntax as a query — including dot traversal across
relations:

```json
{
  "type": "AssetChanged",
  "from": "Story",
  "attributes": ["Status"],
  "filter": ["Scope=$Scope"],
  "with": {
    "$Scope": "Scope:0"
  },
  "select": ["Name", "Owners.Name"]
}
```

The `snapshot` in the delivered payload then carries `Name` and each owner's `Name`, in the same
shape a [`query.v1`](/apis/query-v1) result would have. This is what lets a handler act on an
event without calling back into the API for context — worth doing, because it removes a
round-trip and avoids a race where the asset changes again before you read it.

:::note The field is `attributes`, plural
A common mistake is sending `attribute` (singular). Because `api/webhook` rejects unknown
keys instead of ignoring them, that produces a `400` rather than a silent misconfiguration.
See [unknown keys are rejected](/apis/webhooks/create) on the create page for the mechanism.
:::

## A Complete `eventTypes` Array

Event types compose. This subscription fires on new Defects anywhere, and on Story status
changes in one project:

```json
{
  "eventTypes": [
    {
      "type": "AssetCreated",
      "from": "Defect",
      "select": ["Name", "Number", "Priority"]
    },
    {
      "type": "AssetChanged",
      "from": "Story",
      "attributes": ["Status"],
      "filter": ["Scope=$Scope"],
      "with": { "$Scope": "Scope:0" },
      "select": ["Name", "Status.Name", "Owners.Name"]
    }
  ]
}
```

Each delivered event names the `eventType` that produced it, so a handler can dispatch on it.

## Related Topics

- [Create a subscription](/apis/webhooks/create)
- [Webhook payload](/apis/webhooks/payload) — what `select` produces
- [Query grammar reference](/reference/query-grammar) — filter and projection syntax
- [Attribute definition syntax](/apis/conventions/attribute-definitions)
