# Integrations API

The Integrations API manages the notification destinations on your account: Slack, PagerDuty, Discord, Microsoft Teams, Google Chat, Lark, Opsgenie, Splunk On-Call, Datadog On-Call, Telegram, and custom webhooks. These are the same integrations you configure under **Settings → Integrations**.

## Authentication

The Integrations API requires API key authentication with integration scopes:
- **`integration:read`** - Required to list integrations (including `?service=` / `?label=` filters) and list available services
- **`integration:write`** - Required to create or delete an integration

Create an API key with the appropriate scopes from the [API Settings](https://cronitor.io/app/settings/api) page. SDK Integration keys include both scopes. Session tokens issued to users who cannot [manage integrations](https://cronitor.io/docs/integrations.md) have `integration:read` only. For more information about API authentication and scopes, see the [API documentation](https://cronitor.io/docs/api.md).

Include the key as an HTTP Basic username (leave the password empty):

```bash
curl https://cronitor.io/api/integrations \
  -u API_KEY:
```

---

## Quick Start

### List available services
```bash
curl https://cronitor.io/api/integrations/services \
  -u API_KEY:
```

### Create a Discord integration
Use a placeholder URL — never commit a real webhook secret.

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "service": "discord",
        "name": "Eng alerts",
        "fields": { "key": "https://discord.com/api/webhooks/EXAMPLE/TOKEN" }
    }' \
    https://cronitor.io/api/integrations
```

### List integrations
```bash
curl https://cronitor.io/api/integrations -u API_KEY:
```

Responses never include secrets. Slack and PagerDuty `identifier` is always `null`. Webhook URLs, PagerDuty routing keys, and API tokens are omitted from `metadata`.

---

## List Integrations

#### Endpoint
```bash
GET https://cronitor.io/api/integrations
```

Returns every visible integration on the account whose service is in the current catalogue. Unconnected catalogue services are not included here — use [List Available Services](#list-available-services).

#### Query Parameters
- **`service`** (string) - Restrict results to one service key (for example `?service=discord`)
- **`label`** (string) - Restrict results to this display name (for example `?label=Eng+alerts`)
- **`page`** (integer) - Page number, starting at `1`. Default: `1`
- **`pageSize`** (integer) - Results per page. Default: `50`

#### Example
```bash
curl "https://cronitor.io/api/integrations?service=discord&pageSize=50" \
  -u API_KEY:
```

#### Response
```json
{
  "integrations": [
    {
      "id": "Eng alerts",
      "service": "discord",
      "service_name": "Discord",
      "method": "apikey",
      "name": "Eng alerts",
      "label": "Eng alerts",
      "identifier": "eng-alerts",
      "available": true,
      "metadata": null,
      "created": "2026-09-03T12:00:00Z"
    }
  ],
  "page": 1,
  "page_size": 50,
  "total_integration_count": 1
}
```

---

## Get Integration

There is no path-key Get. Filter the list by `service` and `label`:

```bash
curl "https://cronitor.io/api/integrations?service=discord&label=Eng+alerts" \
  -u API_KEY:
```

`200` with the usual list envelope. The `integrations` array is empty when no visible row matches. Labels are unique per service for visible rows.

---

## Create Integration

#### Endpoint
```bash
POST https://cronitor.io/api/integrations
```

Creates an API-key integration.

Slack and PagerDuty cannot be created with this endpoint (`{"error": "connect_required"}`). Start a browser session with `POST /api/integrations/connect` or connect them from [Settings → Integrations](https://cronitor.io/app/settings/integrations).

Telegram cannot be created here either; link the Cronitor bot first (`{"error": "link_required", "url": "/docs/using-telegram-with-cronitor"}`).

#### Request Body

**`service`** (string] **required**)
Service key. Must be an API-key provider: `discord`, `microsoft-teams`, `gchat`, `larksuite`, `opsgenie`, `victorops`, `datadog-on-call`, or `webhook`.

**`name`** (string] **required**)
Display name, 1–250 characters after trimming. Blank names are rejected.

**`identifier`** (string)
Optional slug, 1–250 characters. Defaults to a slug of `name` (`[^a-zA-Z0-9-]` → `-`, lowercased).

**`fields`** (object)
Provider-specific fields. See [Create fields](#create-fields). Keys must be a subset of the catalogue `fields` for that service.

**`key`** (string)
Shorthand for `fields.key`. Ignored when `fields.key` is also set.

### Create fields

Most API-key services take a single `key` field (the webhook URL, API key, or notification URL). Extra fields are listed in `GET /api/integrations/services`.

| Service | Required `fields` | Optional | Notes |
| --- | --- | --- | --- |
| `discord` | `key` | | Discord incoming webhook URL |
| `microsoft-teams` | `key` | | Teams incoming webhook URL |
| `gchat` | `key` | | Google Chat incoming webhook URL |
| `larksuite` | `key` | | Lark / Feishu webhook URL |
| `opsgenie` | `key` | | Opsgenie API key |
| `victorops` | `key` | | Splunk On-Call notification URL |
| `webhook` | `key` | | Destination URL. Never returned |
| `datadog-on-call` | `webhook_url`, `api_key`, `oncall_team` | | Cronitor builds the delivery URL; `api_key` is never returned |

Do not send secrets in documentation, tickets, or version control. The examples below use placeholders.

#### Example
```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "service": "opsgenie",
        "name": "Primary",
        "identifier": "primary",
        "fields": { "key": "YOUR_OPSGENIE_API_KEY" }
    }' \
    https://cronitor.io/api/integrations
```

Custom webhook:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "service": "webhook",
        "name": "Internal relay",
        "fields": { "key": "https://example.com/hooks/cronitor" }
    }' \
    https://cronitor.io/api/integrations
```

#### Response
`201` with the created [Integration Object](#integration-object). Secrets from `fields` are not echoed back.

| Status | Body | When |
| --- | --- | --- |
| `400` | `{"error": "unknown_service"}` | `service` is not a known key |
| `400` | `{"error": "connect_required"}` | Slack or PagerDuty — use [Connect Slack or PagerDuty](#connect-slack-or-pagerduty) |
| `400` | `{"error": "link_required", "url": "/docs/using-telegram-with-cronitor"}` | Telegram must be linked from the bot |
| `400` | `{"error": "name_required"}` | Missing or blank `name` |
| `400` | `{"error": "key_required"}` | Missing `fields.key` (or the Datadog equivalent) |
| `400` | `{"error": "webhook_url_required"}` | Datadog On-Call is missing `fields.webhook_url` |
| `400` | `{"error": "webhook_url_invalid"}` | Datadog On-Call webhook URL is not a valid HTTPS intake host |
| `400` | `{"error": "webhook_url_credentials"}` | Datadog On-Call webhook URL includes credentials |
| `400` | `{"error": "webhook_url_query"}` | Datadog On-Call webhook URL includes a query or fragment |
| `400` | `{"error": "api_key_required"}` | Datadog On-Call is missing `fields.api_key` |
| `400` | `{"error": "oncall_team_required"}` | Datadog On-Call is missing `fields.oncall_team` |
| `400` | `{"error": "invalid_fields", "fields": ["…"]}` | A field is not in the catalogue for that service |
| `403` | `{"error": "plan_required"}` | Plan does not allow the service |
| `403` | `{"error": "forbidden"}` | Caller cannot manage integrations |
| `400` | `{"identifier": ["identifier must be unique"]}` | Duplicate `identifier` for that service |
| `400` | `{"name": ["name must be unique"]}` | Duplicate `name` / label for that service (same validation error shape as monitors) |
| `503` | | Account is in read-only mode |

---

## Connect Slack or PagerDuty

Slack and PagerDuty connect through a browser authorization. Start a session, give the `authorize_url` to a person who is signed in to Cronitor in the same organization, then poll until the session ends.

#### Endpoints

```
POST https://cronitor.io/api/integrations/connect
GET  https://cronitor.io/api/integrations/connect/:token
```

#### Start a session

```bash
curl -X POST "https://cronitor.io/api/integrations/connect" \
  -u API_KEY: \
  -H "Content-Type: application/json" \
  -d '{"service": "slack"}'
```

```json
{
  "token": "Hk3...",
  "authorize_url": "https://cronitor.io/integrations/connect/Hk3...",
  "expires_at": "2026-10-01T12:15:00Z",
  "poll_interval": 3,
  "status": "pending"
}
```

The session expires 15 minutes after it starts. An organization can have at most 10 pending sessions.

#### Poll the session

```bash
curl "https://cronitor.io/api/integrations/connect/Hk3..." -u API_KEY:
```

Wait at least `poll_interval` seconds between requests. `status` is `pending`, `complete`, `failed`, or `expired`. When it is `complete`, `integration` holds the new [Integration Object](#integration-object); add its `label` to a notification list to route alerts there.

#### Errors

| Status | Body | Meaning |
|--------|------|---------|
| `400` | `{"error": "connect_required"}` | The service does not use browser authorization; use [Create Integration](#create-integration) |
| `403` | `{"error": "plan_required"}` | Plan does not allow the service |
| `429` | `{"error": "too_many_pending"}` | Ten sessions are already pending |
| `404` | — | Unknown token, or a token from another organization |

## Delete Integration

#### Endpoint
```bash
DELETE https://cronitor.io/api/integrations
```

Soft-deletes the integration (`visible=false`). Identify the row with `service` and `label` query parameters — the same filters as List. By default the request fails if any notification list or monitor still references it.

#### Query Parameters
- **`service`** (string, required) - Catalogue service key
- **`label`** (string, required) - Display name
- **`force`** (string) - Set `force=1` to delete anyway. Cronitor detaches the integration from notification lists and monitors, then hides it.

#### Example
```bash
curl -X DELETE "https://cronitor.io/api/integrations?service=discord&label=Eng+alerts" \
  -u API_KEY:
```

Force delete:

```bash
curl -X DELETE "https://cronitor.io/api/integrations?service=discord&label=Eng+alerts&force=1" \
  -u API_KEY:
```

#### Response
`200` with `{"deleted": true}` when the integration is not in use.

If the integration is still in use and `force` is not set, `409`:

```json
{
  "error": "in_use",
  "notification_lists": ["oncall"],
  "monitors": ["important-job"]
}
```

With `force=1`:

```json
{
  "deleted": true,
  "detached_from": {
    "notification_lists": ["oncall"],
    "monitors": ["important-job"]
  }
}
```

`404` if no visible row matches `service` + `label`. `400` if either query parameter is missing.

---

## List Available Services

#### Endpoint
```bash
GET https://cronitor.io/api/integrations/services
```

Returns the integration catalogue for this account. `available` is `true` when the current plan includes the service. SMS is not part of this catalogue.

#### Example
```bash
curl https://cronitor.io/api/integrations/services \
  -u API_KEY:
```

#### Response
```json
{
  "services": [
    {
      "service": "slack",
      "service_name": "Slack",
      "type": "Messaging",
      "method": "oauth",
      "fields": {},
      "available": true
    },
    {
      "service": "webhook",
      "service_name": "Webhook",
      "type": "Messaging",
      "method": "apikey",
      "fields": {
        "key": "URL"
      },
      "available": true
    },
    {
      "service": "telegram",
      "service_name": "Telegram",
      "type": "Messaging",
      "method": "link",
      "fields": {
        "type": "Type"
      },
      "available": true
    }
  ]
}
```

---

## Integration Object

```json
{
  "id": "Eng alerts",
  "service": "discord",
  "service_name": "Discord",
  "method": "apikey",
  "name": "Eng alerts",
  "label": "Eng alerts",
  "identifier": "eng-alerts",
  "available": true,
  "metadata": null,
  "created": "2026-09-03T12:00:00Z"
}
```

**`id`** (string)
Public identity. Same value as `label`. Unique with `service` among visible rows. Autoincrement primary keys are never returned.

**`service`** (string)
Service key. One of `slack`, `pagerduty`, `discord`, `microsoft-teams`, `gchat`, `larksuite`, `opsgenie`, `victorops`, `datadog-on-call`, `webhook`, `telegram`.

**`service_name`** (string)
Human-readable provider name (for example `Microsoft Teams`).

**`method`** (string)
How the service is connected: `oauth` (Slack, PagerDuty), `apikey` (webhook / token providers), or `link` (Telegram).

**`name`** (string)
Display name shown in the dashboard. Same value as `label`.

**`label`** (string)
Stable name used when attaching this integration to a [notification list](https://cronitor.io/docs/notifications-api.md).

**`identifier`** (string)
Customer-supplied slug for API-key services. Always `null` for Slack and PagerDuty — those destinations are not addressable by a customer-supplied slug.

**`available`** (boolean)
`true` when the current plan includes this service. Existing rows remain listed after a plan downgrade so they can still be deleted (`available: false`).

**`metadata`** (object)
Non-secret provider details (for example `oncall_team`). Keys named `api_key`, `password`, `webhook_url`, or `key` are never returned. `null` when empty.

**`created`** (timestamp)
ISO-8601 timestamp when the integration was created.

---

## Related

- [Integrations](https://cronitor.io/docs/integrations.md) — dashboard overview and provider guides
- [Notifications API](https://cronitor.io/docs/notifications-api.md) — attach integrations to notification lists
- [Cronitor CLI](https://cronitor.io/docs/using-cronitor-cli.md) — account resource commands
- [API Keys](https://cronitor.io/docs/api-keys.md) — scopes and key types
- [Using Slack](https://cronitor.io/docs/using-slack-with-cronitor.md) · [Using PagerDuty](https://cronitor.io/docs/using-pagerduty-with-cronitor.md)
