# Grain webhook trigger

> Run a workflow on Grain recording, highlight, and story events: setup, the event catalog, the delivery envelope, and URL-token authentication.

Source: https://cotera.co/docs/reference/webhooks/grain

---

Run a workflow when something changes in Grain — a recording is added, a highlight is clipped, a story is updated. The event envelope becomes the workflow's input.

For behavior shared by every provider (the delivery URL, testing), see the [webhook triggers reference](https://cotera.co/docs/reference/webhooks.md).

## Prerequisites

Grain webhooks are **auto-registered**: Cotera creates the hook for you over Grain's API, reusing your connected **Grain** tool connection (its API token). Without a connected Grain token, install fails with `grain_credentials_missing`.

## Setup

1. Add a **From an app** trigger and pick **Grain**.
2. Choose the event (see below).
3. Save. Cotera registers a Grain hook for that type, pointed at the trigger's unique URL with an authentication token embedded in it.

Deleting the trigger removes the hook from Grain.

## Events

| Event key           | Fires when                                         |
| ------------------- | -------------------------------------------------- |
| `recording_added`   | A new recording is added to the workspace          |
| `recording_updated` | A recording is updated (title, participants, tags) |
| `recording_deleted` | A recording is deleted                             |
| `highlight_added`   | A highlight/clip is created on a recording         |
| `highlight_updated` | A highlight/clip is updated                        |
| `highlight_deleted` | A highlight/clip is deleted                        |
| `story_added`       | A story is created                                 |
| `story_updated`     | A story is updated                                 |
| `story_deleted`     | A story is deleted                                 |
| `upload_status`     | A recording's upload/processing status changes     |

## Payload

Grain delivers a `{ type, user_id, data }` envelope, passed straight through as the workflow input. `type` is the event key; `data` holds the affected object. A `recording_added` delivery looks like:

```json
{
  "type": "recording_added",
  "user_id": "user_01EXAMPLE0000000000000000",
  "data": {
    "id": "zzzz6666-aa77-bb88-cc99-dddd00000000",
    "title": "Acme <> Cotera Weekly Sync",
    "url": "https://grain.com/share/recording/zzzz6666-aa77-bb88-cc99-dddd00000000",
    "start_datetime": "2023-07-15T10:30:00Z",
    "end_datetime": "2023-07-15T11:00:00Z"
  }
}
```

| Field     | Description                                                                                                                                                                     |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`    | The event key — matches the trigger's selected event.                                                                                                                           |
| `user_id` | The Grain user the hook belongs to.                                                                                                                                             |
| `data`    | The affected object. Recordings carry `id`, `title`, `url`, and timing; highlights add `recording_id`, `text`, and a `timestamp`; delete events carry just the identifying ids. |

## Authentication

Grain doesn't sign deliveries. Cotera embeds a generated token in the hook URL registered with Grain (`?cotera_webhook_token=…`) and timing-safe compares it against the trigger's secret on every delivery, rejecting anything that doesn't match.

