# Slack alerts: add a channel

> The alert_settings.slack_channels.create operation: POST /api/v1/alert_settings/slack_channels in the REST API and the alert_settings_slack_channels_create tool in the MCP server.

Adds a Slack incoming webhook (or any Slack-compatible one) to the organization's alerts in the mode of the
request. Critical alerts are posted to every channel; high alerts to channels whose `minSeverity` is `high`
(the default). Info alerts stay in the inbox.

The webhook URL is stored encrypted and never shown again (`urlHint` shows its host and last characters).
Every message is signed like a webhook (`Ownrecord-Signature`, spec §26) with the channel's own
`signingSecret`, returned once here, so a relay in front of Slack can check that it came from Ownrecord.
URLs must be https on a public host (spec §6.8). Test the channel with
`alert_settings.slack_channels.test`. At most 10 channels per mode.

Needs `org:write` (owners and admins). Security tier: it sends alert messages outside Ownrecord.

## At a glance

- **REST:** `POST /api/v1/alert_settings/slack_channels`
- **MCP tool:** `alert_settings_slack_channels_create`
- **Operation ID:** `alert_settings.slack_channels.create`
- **Scope:** Organization
- **Permission:** `org:write`
- **Risk tier:** `security`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `org:write` scope
- **Since:** M7

## Request

### Body

A JSON object (`Content-Type: application/json`):

| Field         | Type                      | Required | Description |
| ------------- | ------------------------- | -------- | ----------- |
| `name`        | string                    | Yes      |             |
| `webhookUrl`  | string                    | Yes      |             |
| `minSeverity` | one of `high`, `critical` | No       |             |

## Response

`201 Created` with a JSON object:

| Field           | Type                      | Description |
| --------------- | ------------------------- | ----------- |
| `id`            | `sec_…` ID                |             |
| `object`        | `"slack_channel"`         |             |
| `name`          | string                    |             |
| `urlHint`       | string                    |             |
| `minSeverity`   | one of `high`, `critical` |             |
| `createdAt`     | date-time                 |             |
| `createdBy`     | string                    |             |
| `lastTest`      | object or null            |             |
| `signingSecret` | string                    |             |

## Examples

### Post tax alerts to #tax-alerts

```http
POST /api/v1/alert_settings/slack_channels
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "name": "#tax-alerts",
  "webhookUrl": "https://hooks.slack.com/services/T00000000/B00000000/ExampleOnlyx7Qa",
  "minSeverity": "high"
}
```

Response (`201`):

```json
{
  "id": "sec_01m4ttwd3veh9apbewxheq5xad",
  "object": "slack_channel",
  "name": "#tax-alerts",
  "urlHint": "hooks.slack.com …x7Qa",
  "minSeverity": "high",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "lastTest": null,
  "signingSecret": "whsec_ExampleOnlyNotARealSigningSecret0000000"
}
```

## Errors

| Code                                                                                                       | HTTP status | When                                                     | What to do                                                                                                                                                     |
| ---------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                     | 400         | The name or URL is missing or too long.                  | Fix the fields listed in `details.issues` and send the request again.                                                                                          |
| [`slack_webhook_url_invalid`](https://ownrecord.io/docs/reference/errors.md#slack_webhook_url_invalid)     | 400         | The URL isn't https on a public host (`details.reason`). | Use the incoming-webhook URL Slack (or a Slack-compatible tool) gave you: https, a public host, no user name or password. `details.reason` says what is wrong. |
| [`slack_channel_limit_reached`](https://ownrecord.io/docs/reference/errors.md#slack_channel_limit_reached) | 409         | The organization has 10 channels in this mode.           | Remove a channel you no longer use (`details.limit` is the maximum), then add the new one.                                                                     |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                       | 409         | The settings changed at the same moment; retry.          | Fetch the object again and retry the change.                                                                                                                   |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                     | 403         | You can't change this organization's settings.           | Ask an organization owner or admin for a role or scope that allows this action.                                                                                |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
