Notification Groups — API v1 and MCP Tools | Nodea
A notification group aggregates three kinds of members — websites (website_ids), heartbeats (heartbeat_ids) and integrations (integration_ids) — into one named set through which alerts are routed. This article covers the API v1 REST interface and the full set of 5 MCP tools for managing groups. All endpoints live under the base path /api/v1 (full example host: https://app.nodea.io/api/v1).
Authentication and requirements
Authentication uses a Passport personal access token passed in the Authorization: Bearer <token> header. A rate limit of throttle:api-v1 = 240 requests/min applies. Notification-group routes require the Pennant feature flag notification_groups to be enabled — without it the API returns a feature-unavailable error.
The required token scopes depend on the operation:
- read —
alerting:read - write (create and update) —
alerting:write - delete —
alerting:delete
Every access is scoped to the owner (user_id equal to the token user). A group owned by another user, or an unknown id, consistently returns 404 — the API never confirms that a foreign id exists.
API v1
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /notification-groups | alerting:read | List the user's groups with member counts (pagination, search, usage filter). |
| GET | /notification-groups/{notificationGroup} | alerting:read | A single group with its member id lists. |
| POST | /notification-groups | alerting:write | Create a group and (optionally) attach members. |
| PATCH / PUT | /notification-groups/{notificationGroup} | alerting:write | Partial update — only the submitted member lists are re-synced. |
| DELETE | /notification-groups/{notificationGroup} | alerting:delete | Permanently delete the group (members are only detached, never deleted). |
GET /notification-groups — list
Query parameters (all optional): search (string, max 255 — matched against the name), usage (filter, see below), sort (name or created_at), direction (asc / desc; defaults to desc for created_at, asc otherwise), per_page (1–100, default 20), page (from 1). An invalid value for any parameter returns 422.
Allowed usage filter values: all (default), with_integrations, without_integrations, with_websites, without_websites. They mirror the web index predicates — filtering groups that respectively have or lack integrations or websites.
Each list item carries the counts integrations_count, websites_count, heartbeats_count and — thanks to a permanent eager-load — the integration_ids field. The response is wrapped in the Laravel Resource data / links / meta envelope.
curl -s "https://app.nodea.io/api/v1/notification-groups?usage=with_websites&per_page=20" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"GET /notification-groups/{notificationGroup} — detail
Returns a single group with its member id lists: integration_ids, website_ids, heartbeat_ids plus the *_count counters. Only member ids are serialized — full integration rows (and their secret config) never reach the response. A foreign or unknown group → 404.
curl -s "https://app.nodea.io/api/v1/notification-groups/42" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"POST /notification-groups — create
Request body: name (required, string, max 255) and the optional arrays integration_ids, website_ids, heartbeat_ids. Every member id must belong to the token user. Passing an integration, website or heartbeat id you do not own fails validation with 422 — rather than being silently dropped as it is in the web flow. Website ownership is checked against the sharables pivot (OWNER type) — a FOREIGN share is not enough, because attaching a website routes its alerts through this group's integrations.
Success returns 201 and the full group shape with member id lists and counts.
curl -s -X POST "https://app.nodea.io/api/v1/notification-groups" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "On-call team",
"integration_ids": [3, 7],
"website_ids": ["9f1c2d34-..."],
"heartbeat_ids": []
}'PATCH / PUT /notification-groups/{notificationGroup} — update
The update is partial. name is optional (sometimes). Member keys are re-synced only when submitted in the request body: omitting website_ids leaves the websites unchanged, whereas passing an empty array detaches all members of that type. Same ownership rules as on create — a foreign member id → 422. A missing or foreign group → 404.
curl -s -X PATCH "https://app.nodea.io/api/v1/notification-groups/42" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "name": "New name", "integration_ids": [3] }'DELETE /notification-groups/{notificationGroup} — delete
Permanently deletes the group. Attached members (websites, integrations, heartbeats) are only detached by the pivots' cascades — the member resources themselves are untouched. Success returns 204 No Content. A foreign or unknown group → 404.
curl -s -X DELETE "https://app.nodea.io/api/v1/notification-groups/42" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Error codes
| Code | Meaning |
|---|---|
| 401 | Missing or invalid token in the Authorization header. |
| 403 | The token lacks the required scope (e.g. alerting:write for writes). |
| 404 | The group does not exist or belongs to another user. |
| 422 | Validation error — { "message": ..., "errors": { ... } }. Includes passing a member id (website / heartbeat / integration) you do not own. |
MCP
The same set of operations is exposed as MCP tools. Each tool requires the notification_groups flag to be enabled and the appropriate token scope. The read/write tools reject foreign member ids outright (instead of silently trimming them like the web flow), and the delete tool is destructive and requires an explicit confirm: true.
| Name | Scope | Mode | Description |
|---|---|---|---|
notification-groups-list | alerting:read | read-only | Paginated list of groups with member counts (no id lists). Parameters: search, page, per_page (max 50). |
notification-group-get | alerting:read | read-only | A single group with full member lists: websites (id, name), integrations (id, name, type), heartbeats (id, name). Parameter: id. |
notification-group-create | alerting:write | write | Create a group with optional website_ids, heartbeat_ids, integration_ids. A foreign id → rejected, nothing is created. |
notification-group-update | alerting:write | write | Partial update; a submitted list replaces that member set (empty array detaches all), an omitted one stays unchanged. |
notification-group-delete | alerting:delete | destructive | Permanently delete the group. Requires confirm: true. Members are only detached. |
Call examples (JSON-RPC tools/call)
List groups that have websites:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "notification-groups-list",
"arguments": { "search": "on-call", "per_page": 20 }
}
}Create a group with members:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "notification-group-create",
"arguments": {
"name": "On-call team",
"integration_ids": [3, 7],
"website_ids": ["9f1c2d34-..."]
}
}
}Delete a group — confirm: true is required:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "notification-group-delete",
"arguments": { "id": 42, "confirm": true }
}
}Related: Integrations and Escalation policies.
