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

# Webhooks API

> Create and manage webhook subscriptions from code or an app: the event catalog, scope rules, pausing and resuming, and the delivery log.

Everything you can do under **Settings → Integrations → Webhooks** is available here, for API keys and for [OAuth apps](https://docs.orbitforms.ai/apps/webhooks.md). Payloads, headers and retries are described in the [webhooks guide](https://docs.orbitforms.ai/developers/webhooks/overview.md).

## Scopes

| Scope | Allows |
| - | - |
| `webhooks:read` | List webhooks, read one, read its deliveries, read the event catalog |
| `webhooks:write` | Create, change and delete webhooks |
| The object's read scope | Subscribing to an object's events: `submissions:read`, `contacts:read`, `companies:read`, `deals:read`, `tasks:read`, `meetings:read` or `dialer:read` |

A request that subscribes to an event whose read scope the credential lacks gets `403 insufficient_scope` with `required_scopes` naming what is missing.

## Endpoints

| Method | Endpoint | Description |
| - | - | - |
| `GET` | `/api/v1/webhooks/events` | The event catalog, grouped by object, with `subscribable` per group for this credential |
| `GET` | `/api/v1/webhooks` | Your webhooks (an app sees the ones it registered in this workspace; an API key the workspace's own) |
| `POST` | `/api/v1/webhooks` | Create a webhook. The signing secret is returned once |
| `GET` | `/api/v1/webhooks/:id` | One webhook |
| `PATCH` | `/api/v1/webhooks/:id` | Change `url`, `events`, `form_id` or `is_active` |
| `DELETE` | `/api/v1/webhooks/:id` | Delete a webhook |
| `GET` | `/api/v1/webhooks/:id/deliveries` | Delivery attempts, newest first |

## Create a webhook

```bash theme={null}
curl -X POST https://orbitforms.ai/api/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4d3c2b1a-deal-sync" \
  -d '{
    "url": "https://partner.example/orbit/events",
    "events": ["deal.created", "deal.stage_changed", "contact.created", "call.analyzed"]
  }'
```

```json theme={null}
{
  "data": {
    "id": "f0e1d2c3-...",
    "url": "https://partner.example/orbit/events",
    "events": ["deal.created", "deal.stage_changed", "contact.created", "call.analyzed"],
    "is_active": true,
    "form_id": null,
    "created_at": "2026-10-27T14:00:00Z",
    "secret": "a1b2c3...64 hex characters"
  },
  "message": "Webhook created. Save the secret - it will only be shown once."
}
```

| Field | Type | Notes |
| - | - | - |
| `url` | string | HTTPS. Private and internal addresses are refused, at creation and again on every delivery |
| `events` | string\[] | One or more events from the catalog. Duplicates are dropped |
| `form_id` | uuid | Optional. Limits the webhook to one form's submission events. Refused when `events` contains anything other than `submission.*` events, which are workspace-wide |

Send an `Idempotency-Key` header to make retries of the create call safe; a replay returns the webhook without the secret.

Limits: 5 webhooks per workspace for an app in development, 100 once the app is approved; 500 per workspace in total. Creation is rate limited per app and per workspace.

## Change a webhook

```bash theme={null}
curl -X PATCH https://orbitforms.ai/api/v1/webhooks/f0e1d2c3-... \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "events": ["deal.created", "deal.stage_changed", "deal.deleted"], "is_active": true }'
```

Setting `is_active` to `true` on a webhook that was switched off after a failure streak clears `consecutive_failures` and `last_error`. Changing `form_id` re-checks the current events against the new filter.

## The delivery log

```bash theme={null}
curl "https://orbitforms.ai/api/v1/webhooks/f0e1d2c3-.../deliveries?status=failed&per_page=20" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{
  "data": [
    {
      "id": "0a1b2c3d-...",
      "event": "deal.stage_changed",
      "event_id": "5f0c1f2e-9c0a-5a3b-8d1e-6b2f0c9a4d11",
      "object_id": "9a1d3c4e-...",
      "success": false,
      "status_code": 503,
      "error_message": "HTTP 503: Service Unavailable",
      "attempt_number": 2,
      "is_retry": true,
      "attempted_at": "2026-10-27T14:32:10Z",
      "completed_at": "2026-10-27T14:32:11Z",
      "duration_ms": 812
    }
  ],
  "meta": { "total": 3, "page": 1, "per_page": 20, "total_pages": 1 }
}
```

Query parameters: `page`, `per_page` (max 100), `event` (one catalog event), `status` (`success` or `failed`). Payload data is not kept in the log; `event_id` and `object_id` let you look the record up.

## The webhook object

| Field | Meaning |
| - | - |
| `id`, `url`, `events`, `form_id`, `is_active` | What you set |
| `last_triggered_at`, `last_success_at`, `last_error` | The latest attempt, the latest success, and why the latest failure failed |
| `consecutive_failures` | Events in a row that failed every attempt; at 10 the webhook is switched off |
| `created_at`, `updated_at` | Timestamps |

The secret is only returned by `POST`. Store it; it is what you [verify signatures](https://docs.orbitforms.ai/developers/webhooks/security.md) with.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.