> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withleaf.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Alerts API

> Manage webhooks for real-time notifications about field boundary changes, file processing, field operations, credential expiration, and satellite images.

Use these endpoints to create and manage webhooks that notify your application when field boundaries change, machine files finish processing, field operations are created, provider credentials expire, or satellite images are ready.

For conceptual background, see [Alerts Overview](/alerts/overview).

## Base URL

```
https://api.withleaf.io/services/alerts/api/alerts
```

## Endpoints

| Description                           | Method                                                             | Path                     |
| ------------------------------------- | ------------------------------------------------------------------ | ------------------------ |
| [Create a webhook](#create-a-webhook) | <span style={{fontWeight: 'bold', color: '#eab308'}}>POST</span>   | `/webhooks`              |
| [Get a webhook](#get-a-webhook)       | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>    | `/webhooks/{id}`         |
| [Get all webhooks](#get-all-webhooks) | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>    | `/webhooks`              |
| [Get failed calls](#get-failed-calls) | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>    | `/webhooks/failed-calls` |
| [Delete a webhook](#delete-a-webhook) | <span style={{fontWeight: 'bold', color: '#dc2626'}}>DELETE</span> | `/webhooks/{id}`         |

<Info>
  You cannot update a webhook. To change an existing webhook, delete it and create a new one. Keep the previous URL running until the new webhook is confirmed.
</Info>

<Note>
  On delivery failure, Leaf retries at **1, 30, 60, and 240 minutes** after the initial attempt.
</Note>

***

## Available events

You can subscribe to any combination of these events:

**Credentials**

| Event                          | Description                                       |
| ------------------------------ | ------------------------------------------------- |
| `credentialsLimitedPermission` | Provider credentials have limited permissions.    |
| `credentialsUnauthenticated`   | Provider credentials are no longer authenticated. |

**Fields and boundaries**

| Event                  | Description                  |
| ---------------------- | ---------------------------- |
| `fieldCreated`         | A field is created.          |
| `fieldUpdated`         | A field is updated.          |
| `fieldBoundaryCreated` | A field boundary is created. |
| `fieldBoundaryUpdated` | A field boundary is updated. |
| `fieldBoundaryDeleted` | A field boundary is deleted. |
| `mergedFieldCreated`   | A merged field is created.   |
| `mergedFieldUpdated`   | A merged field is updated.   |

**Machine files**

| Event                              | Description                                              |
| ---------------------------------- | -------------------------------------------------------- |
| `uploadedFileProcessingFinished`   | An uploaded machine file finishes processing.            |
| `uploadedFileProcessingFailed`     | An uploaded machine file fails processing.               |
| `providerFileProcessingFinished`   | A provider-synced machine file finishes processing.      |
| `providerFileProcessingFailed`     | A provider-synced machine file fails processing.         |
| `mergedFileProcessingFinished`     | A merged file finishes processing.                       |
| `mergedFileProcessingFailed`       | A merged file fails processing.                          |
| `automergedFileProcessingFinished` | An auto-merged field operation file finishes processing. |
| `automergedFileProcessingFailed`   | An auto-merged field operation file fails processing.    |
| `batchUploadProcessingFinished`    | All files in a batch upload have finished processing.    |

**Field operations**

| Event                         | Description                            |
| ----------------------------- | -------------------------------------- |
| `operationCreated`            | A field operation is created.          |
| `operationUpdated`            | A field operation is updated.          |
| `operationProcessingFinished` | A field operation finishes processing. |
| `operationProcessingFailed`   | A field operation fails processing.    |

**Satellite imagery**

| Event                         | Description                         |
| ----------------------------- | ----------------------------------- |
| `newSatelliteImage`           | A new satellite image is available. |
| `satelliteSubscriptionFailed` | A satellite subscription fails.     |

**Assets (Beta)**

| Event              | Description              |
| ------------------ | ------------------------ |
| `machineCreated`   | A machine is created.    |
| `machineUpdated`   | A machine is updated.    |
| `machineDeleted`   | A machine is deleted.    |
| `implementCreated` | An implement is created. |
| `implementUpdated` | An implement is updated. |
| `operatorCreated`  | An operator is created.  |
| `operatorUpdated`  | An operator is updated.  |

**Workflows (Beta)**

| Event                        | Description                     |
| ---------------------------- | ------------------------------- |
| `workflowProcessingFinished` | A workflow finishes processing. |
| `workflowProcessingFailed`   | A workflow fails processing.    |

**Irrigation**

| Event                        | Description                                   |
| ---------------------------- | --------------------------------------------- |
| `newIrrigationActivity`      | A new irrigation activity is available.       |
| `newFieldIrrigationActivity` | A new field irrigation activity is available. |

**Provider organizations**

| Event                         | Description                         |
| ----------------------------- | ----------------------------------- |
| `providerOrganizationCreated` | A provider organization is created. |
| `providerOrganizationBlocked` | A provider organization is blocked. |
| `providerOrganizationRemoved` | A provider organization is removed. |

<Warning>
  You cannot register two webhooks that listen to the same event. Attempting to do so returns a `400` response with error `eventRegisteredTwice`.
</Warning>

***

## Create a webhook

<span style={{fontWeight: 'bold', color: '#eab308'}}>POST</span> `/webhooks`

Creates a webhook and begins delivering matching events immediately.

### Request body

| Parameter | Type      | Required | Description                                                               |
| --------- | --------- | -------- | ------------------------------------------------------------------------- |
| events    | string\[] | Yes      | Array of event names from the [available events](#available-events) list. |
| name      | string    | Yes      | Display name for the webhook.                                             |
| secret    | string    | Yes      | Secret used for HMAC signature verification of payloads.                  |
| url       | string    | Yes      | A valid HTTP(S) URL where Leaf delivers event payloads.                   |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    -d '{
      "events": ["fieldCreated", "operationCreated"],
      "name": "Field and operation listener",
      "secret": "your-hmac-secret",
      "url": "https://example.com/webhooks/leaf"
    }' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  payload = {
      "events": ["fieldCreated", "operationCreated"],
      "name": "Field and operation listener",
      "secret": "your-hmac-secret",
      "url": "https://example.com/webhooks/leaf",
  }

  response = requests.post(
      "https://api.withleaf.io/services/alerts/api/alerts/webhooks",
      headers=headers,
      json=payload,
  )
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  const TOKEN = "YOUR_TOKEN";
  const headers = { Authorization: `Bearer ${TOKEN}` };

  const payload = {
    events: ["fieldCreated", "operationCreated"],
    name: "Field and operation listener",
    secret: "your-hmac-secret",
    url: "https://example.com/webhooks/leaf",
  };

  axios
    .post("https://api.withleaf.io/services/alerts/api/alerts/webhooks", payload, { headers })
    .then((res) => console.log(res.data))
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "uuid",
  "events": ["fieldCreated", "operationCreated"],
  "name": "Field and operation listener",
  "secret": "your-hmac-secret",
  "url": "https://example.com/webhooks/leaf"
}
```

***

## Get a webhook

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/webhooks/{id}`

Returns a single webhook by its ID.

### Parameters

| Parameter | Type   | Location | Required | Description          |
| --------- | ------ | -------- | -------- | -------------------- |
| id        | string | path     | Yes      | UUID of the webhook. |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks/{id}'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  webhook_id = "WEBHOOK_UUID"
  response = requests.get(
      f"https://api.withleaf.io/services/alerts/api/alerts/webhooks/{webhook_id}",
      headers=headers,
  )
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  const TOKEN = "YOUR_TOKEN";
  const headers = { Authorization: `Bearer ${TOKEN}` };

  const webhookId = "WEBHOOK_UUID";
  axios
    .get(`https://api.withleaf.io/services/alerts/api/alerts/webhooks/${webhookId}`, { headers })
    .then((res) => console.log(res.data))
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "uuid",
  "events": ["fieldCreated", "operationCreated"],
  "name": "Field and operation listener",
  "secret": "your-hmac-secret",
  "url": "https://example.com/webhooks/leaf"
}
```

***

## Get all webhooks

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/webhooks`

Returns all webhooks registered for the API owner.

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  response = requests.get(
      "https://api.withleaf.io/services/alerts/api/alerts/webhooks",
      headers=headers,
  )
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  const TOKEN = "YOUR_TOKEN";
  const headers = { Authorization: `Bearer ${TOKEN}` };

  axios
    .get("https://api.withleaf.io/services/alerts/api/alerts/webhooks", { headers })
    .then((res) => console.log(res.data))
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
[
  {
    "id": "uuid",
    "events": ["fieldCreated", "operationCreated"],
    "name": "Field and operation listener",
    "secret": "your-hmac-secret",
    "url": "https://example.com/webhooks/leaf"
  }
]
```

***

## Get failed calls

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/webhooks/failed-calls`

Returns a paginated list of failed webhook delivery attempts when any exist. Use the `nextPageToken` value from a response to fetch the next page. If no failed calls are available for the API owner, this endpoint returns `404`.

### Parameters

| Parameter     | Type   | Location | Required | Description                                                        |
| ------------- | ------ | -------- | -------- | ------------------------------------------------------------------ |
| nextPageToken | string | query    | No       | Token returned in the previous response to retrieve the next page. |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks/failed-calls'

  # To fetch the next page:
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks/failed-calls?nextPageToken=TOKEN_VALUE'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  response = requests.get(
      "https://api.withleaf.io/services/alerts/api/alerts/webhooks/failed-calls",
      headers=headers,
  )
  data = response.json()
  print(data)

  # Fetch next page if available
  next_token = data.get("nextPageToken")
  if next_token:
      response = requests.get(
          "https://api.withleaf.io/services/alerts/api/alerts/webhooks/failed-calls",
          headers=headers,
          params={"nextPageToken": next_token},
      )
      print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  const TOKEN = "YOUR_TOKEN";
  const headers = { Authorization: `Bearer ${TOKEN}` };

  axios
    .get("https://api.withleaf.io/services/alerts/api/alerts/webhooks/failed-calls", { headers })
    .then((res) => {
      console.log(res.data);
      // Use res.data.nextPageToken for the next page
    })
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "items": [
    {
      "apiOwner": "your-api-owner",
      "createdAt": "2026-01-15T12:16:30Z",
      "url": "https://example.com/webhooks/leaf",
      "status": 502,
      "response": "Bad Gateway",
      "requestBody": "{\"leafUserId\":\"uuid\",\"fileId\":\"uuid\",\"type\":\"automergedFileProcessingFinished\",\"timestamp\":\"2026-01-15T12:16:27Z\"}"
    },
    {
      "apiOwner": "your-api-owner",
      "createdAt": "2026-01-15T14:10:05Z",
      "url": "https://example.com/webhooks/leaf",
      "connectionError": "ConnectionError: Remote end closed connection without response"
    }
  ],
  "nextPageToken": "eyJsYXN0..."
}
```

***

## Delete a webhook

<span style={{fontWeight: 'bold', color: '#ef4444'}}>DELETE</span> `/webhooks/{id}`

Deletes a webhook. Returns `204 No Content` on success. Leaf stops delivering events for this webhook immediately.

### Parameters

| Parameter | Type   | Location | Required | Description          |
| --------- | ------ | -------- | -------- | -------------------- |
| id        | string | path     | Yes      | UUID of the webhook. |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/alerts/api/alerts/webhooks/{id}'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  webhook_id = "WEBHOOK_UUID"
  response = requests.delete(
      f"https://api.withleaf.io/services/alerts/api/alerts/webhooks/{webhook_id}",
      headers=headers,
  )
  print(response.status_code)  # 204
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  const TOKEN = "YOUR_TOKEN";
  const headers = { Authorization: `Bearer ${TOKEN}` };

  const webhookId = "WEBHOOK_UUID";
  axios
    .delete(`https://api.withleaf.io/services/alerts/api/alerts/webhooks/${webhookId}`, { headers })
    .then((res) => console.log(res.status))  // 204
    .catch(console.error);
  ```
</CodeGroup>

***
