---
title: "Services"
description: "List, create, update and delete the services of your organization (service and asset inventory with criticality and responsible team), and how incidents are assigned to them."
---

`GET /api/im/services` · `POST /api/im/services` · `GET /api/im/services/:id` · `PATCH /api/im/services/:id` · `DELETE /api/im/services/:id`

A **service** is something your organization runs and that an incident can affect, for example "Web shop", "Customer portal" or "Mail". Each service has a **criticality** (`critical`, `high`, `medium`, `low`), an optional responsible [team](/api/incident-management/teams), and the monitors it consists of. This is the service and asset inventory NIS-2 asks for (Implementing Regulation (EU) 2024/2690, Annex 12 and 5.2).

Every incident carries `serviceId` (`null` = no service). It is set in two ways:

- **Automatically** when an incident is opened by the built-in monitoring bridge and its monitor belongs to **exactly one** service of your organization. A monitor in two services is ambiguous and leaves `serviceId` empty; alerts from any other source (webhooks, email, API) are never assigned automatically.
- **By hand** with [Set Incident Service](/api/incident-management/incident-service).

`:id` accepts the numeric `id` or the `publicId`.

## Authentication

Reading is open to every IM-eligible role (`admin`, `editor`, `responder`) and to organization-wide API tokens, see [Authentication](/api/incident-management#authentication). **Creating, updating and deleting** require the organization `admin` role; an API token runs with the role of the user who created it. Other roles get `403` `imServiceWriteDenied`.

## The service object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Internal ID. |
| `publicId` | string (UUID) | Public ID. |
| `organizationId` | integer | Owning organization. |
| `name` | string | 1 to 200 characters, unique per organization regardless of case. |
| `description` | string \| null | At most 2000 characters. |
| `criticality` | string | `critical`, `high`, `medium` or `low`. |
| `ownerTeamId` | integer \| null | Responsible team; becomes `null` when the team is deleted. |
| `monitors` | object[] | `{ monitorType, monitorId, monitorName }`, sorted by name. A monitor that was deleted no longer appears. |
| `createdAt` / `updatedAt` | string (ISO 8601) | |

`monitorType` is one of `website` (also heartbeat and browser monitors), `dns`, `icmp`, `smtp`, `ssh`, `tcp`, `ftp`, `imap_pop`. Each type has its own IDs, so a monitor is always identified by the pair `monitorType` + `monitorId`.

## List services

`GET /api/im/services`

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | `1` | Page number. |
| `perPage` | integer | `50` | Page size, at most `200`. |

```bash
curl "$BASE_URL/api/im/services" \
  -H "Authorization: Bearer $TOKEN"
```

`200 OK`, sorted by name:

```json
{
  "items": [
    {
      "id": 4,
      "publicId": "0d7c6a0e-6f43-4d3b-9b1e-2a8f1d1c5e77",
      "organizationId": 1,
      "name": "Web shop",
      "description": "Storefront and checkout",
      "criticality": "critical",
      "ownerTeamId": 3,
      "monitors": [
        { "monitorType": "website", "monitorId": 46, "monitorName": "shop.example.com" },
        { "monitorType": "dns", "monitorId": 9, "monitorName": "example.com DNS" }
      ],
      "createdAt": "2026-10-25T08:00:00.000Z",
      "updatedAt": "2026-10-25T08:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "perPage": 50
}
```

## Get a service

`GET /api/im/services/:id` returns one service in the same shape.

## Create a service

`POST /api/im/services`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | 1 to 200 characters, unique per organization regardless of case (`409 imServiceNameTaken`). |
| `description` | string \| null | No | At most 2000 characters. |
| `criticality` | string | No | `critical`, `high`, `medium` (default) or `low`. |
| `ownerTeamId` | integer \| null | No | A team of your organization (`404 imTeamNotFound`). |
| `monitors` | object[] | No | `{ monitorType, monitorId }`, at most 500. Every monitor must belong to your organization (`404 imServiceMonitorNotFound`, `data.monitors` lists the ones that do not). |

Unknown fields are rejected with `400 invalidRequestBody`.

```bash
curl -X POST "$BASE_URL/api/im/services" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Web shop",
    "criticality": "critical",
    "ownerTeamId": 3,
    "monitors": [{ "monitorType": "website", "monitorId": 46 }]
  }'
```

`201 Created` with the service.

## Update a service

`PATCH /api/im/services/:id`

Merge patch: send only the fields to change (same fields and rules as on create). `null` clears `description` or `ownerTeamId`. `monitors` replaces the whole set; `[]` removes every monitor. Answers `200 OK` with the service.

```bash
curl -X PATCH "$BASE_URL/api/im/services/4" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "criticality": "high" }'
```

## Delete a service

`DELETE /api/im/services/:id` answers `204 No Content`. Its monitor assignments go with it; incidents stay and lose the reference (`serviceId` becomes `null`).

Every create, update and delete is written to the organization audit log with the changed fields.

## Common errors

- `401 Unauthorized` when not authenticated
- `403 Forbidden` (`imAccessDenied`, `imNotEnabled`) without IM access or when Incident Management is not enabled
- `403 Forbidden` (`imServiceWriteDenied`) when a non-admin creates, updates or deletes
- `400 Bad Request` (`invalidRequestBody`) for unknown fields or wrong types; (`invalidIdentifier`) when `:id` is neither an ID nor a UUID
- `404 Not Found` (`imServiceNotFound`) when the service does not exist or belongs to another organization
- `404 Not Found` (`imTeamNotFound`, `imServiceMonitorNotFound`) for a team or monitor outside your organization
- `409 Conflict` (`imServiceNameTaken`) when the name is already in use
