Nodea — logo

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:

  • readalerting:read
  • write (create and update) — alerting:write
  • deletealerting: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

MethodPathScopeDescription
GET/notification-groupsalerting:readList the user's groups with member counts (pagination, search, usage filter).
GET/notification-groups/{notificationGroup}alerting:readA single group with its member id lists.
POST/notification-groupsalerting:writeCreate a group and (optionally) attach members.
PATCH / PUT/notification-groups/{notificationGroup}alerting:writePartial update — only the submitted member lists are re-synced.
DELETE/notification-groups/{notificationGroup}alerting:deletePermanently 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

CodeMeaning
401Missing or invalid token in the Authorization header.
403The token lacks the required scope (e.g. alerting:write for writes).
404The group does not exist or belongs to another user.
422Validation 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.

NameScopeModeDescription
notification-groups-listalerting:readread-onlyPaginated list of groups with member counts (no id lists). Parameters: search, page, per_page (max 50).
notification-group-getalerting:readread-onlyA single group with full member lists: websites (id, name), integrations (id, name, type), heartbeats (id, name). Parameter: id.
notification-group-createalerting:writewriteCreate a group with optional website_ids, heartbeat_ids, integration_ids. A foreign id → rejected, nothing is created.
notification-group-updatealerting:writewritePartial update; a submitted list replaces that member set (empty array detaches all), an omitted one stays unchanged.
notification-group-deletealerting:deletedestructivePermanently 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.