> For the complete documentation index, see [llms.txt](https://docs.statuspal.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.statuspal.io/next/developers/api-reference/services.md).

# Services

Manage services displayed on a status page

## GET /status\_pages/{subdomain}/services

> List services

```json
{"openapi":"3.1.0","info":{"title":"StatusPal Public API","version":"1"},"tags":[{"name":"Services","description":"Manage services displayed on a status page"}],"servers":[{"url":"https://next.statuspal.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Organization API key — issue one via the Rails console (management UI coming soon)"}},"schemas":{"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid","readOnly":true},"slug":{"type":"string","description":"URL-safe identifier. May be supplied on create/update; if omitted, it is\nauto-generated from `name`. Uniqueness is enforced per status page.\n"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"When `scope` is `containers`, the worst-case status across the containers this service is included in. When `scope` is `global`, the service's own status.\nRead-only here; change it with a notice update, from the operational dashboard, or by writing `status` / `container_statuses` on update.\n","readOnly":true},"order":{"type":"integer","description":"Display position on the status page. For an ungrouped service it is the position in the page's one sequence of groups and ungrouped services, shared with `ServiceGroup.order`. For a grouped service it only ranks the service among its group's members, and the group's own `order` places the group. To rebuild the page, sort groups and ungrouped services together by `order`, then list each group's members by theirs. Sorting services alone does not give the grouped layout, because moving a service into a group keeps its `order`.\n"},"group":{"type":["string","null"],"readOnly":true,"description":"Slug of the service group this service belongs to, or `null` when it is ungrouped. A service belongs to at most one group, and the group's `scope` always matches the service's.\nRead-only here; write the same slug on create or update to change it. Fetch the group's name, order and rollup status from `/service_groups/{service_group_slug}`.\n"},"scope":{"type":"string","readOnly":true,"enum":["global","containers"],"description":"Whether this service applies status-page-wide (`global`) or is scoped to specific containers (`containers`).\nDerived, not stored: a service with at least one container is `containers`, and a service with none is `global`. It is computed per response and cannot be written directly — set `containers` on create or update instead.\n"},"containers":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"Slugs of the containers this service is included in. Empty when `scope` is `global`. Containers on the status page that are absent from this list are ones the service is explicitly excluded from.\n"},"container_statuses":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Live status for each container this service is included in. Empty when `scope` is `global`. Containers the service is excluded from produce no entry.\n"},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}}},"ContainerStatusEntry":{"type":"object","required":["container_slug","status"],"properties":{"container_slug":{"type":"string"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"]}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AccountPendingActivation":{"description":"The account is pending activation. A newly created account must be activated by an administrator before its API key can be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/status_pages/{subdomain}/services":{"get":{"summary":"List services","operationId":"listServices","tags":["Services"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccountPendingActivation"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## POST /status\_pages/{subdomain}/services

> Create a service

```json
{"openapi":"3.1.0","info":{"title":"StatusPal Public API","version":"1"},"tags":[{"name":"Services","description":"Manage services displayed on a status page"}],"servers":[{"url":"https://next.statuspal.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Organization API key — issue one via the Rails console (management UI coming soon)"}},"schemas":{"ServiceCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"slug":{"type":"string","description":"Optional URL-safe identifier. If omitted, it is auto-generated from `name`.\nIf a non-unique value is provided, a numeric suffix is appended (e.g. `api-1`).\n"},"description":{"type":["string","null"]},"group":{"type":["string","null"],"description":"Slug of the service group to put this service in, or `null` for ungrouped. The group must already exist — create it with `POST /service_groups` first.\nIts `scope` must match the service's own, which `containers` determines in this same request: putting a Global Service in a container-scoped group, or the reverse, returns 422. An unknown slug returns 422.\n"},"containers":{"type":"array","items":{"type":"string"},"description":"The containers to include this service in, by slug. This is what determines the service scope: listing one or more containers creates a Container Service, while passing an explicit empty array creates a Global Service.\nOmitting the field defaults to every container on the status page, which keeps existing clients working unchanged. On a page with no containers that default is empty, so the service is Global. An unknown slug returns 422.\n"},"container_statuses":{"type":"array","items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Optional initial status per container, for a Container Service.\nEvery `container_slug` must be one this service is included in — otherwise 422. Included containers not listed default to `ok`. Ignored for a Global Service; use `status` there instead.\n"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"Optional initial status for a Global Service. Defaults to `ok`.\nIgnored for a Container Service; use `container_statuses` there instead.\n"}}},"ContainerStatusEntry":{"type":"object","required":["container_slug","status"],"properties":{"container_slug":{"type":"string"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"]}}},"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid","readOnly":true},"slug":{"type":"string","description":"URL-safe identifier. May be supplied on create/update; if omitted, it is\nauto-generated from `name`. Uniqueness is enforced per status page.\n"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"When `scope` is `containers`, the worst-case status across the containers this service is included in. When `scope` is `global`, the service's own status.\nRead-only here; change it with a notice update, from the operational dashboard, or by writing `status` / `container_statuses` on update.\n","readOnly":true},"order":{"type":"integer","description":"Display position on the status page. For an ungrouped service it is the position in the page's one sequence of groups and ungrouped services, shared with `ServiceGroup.order`. For a grouped service it only ranks the service among its group's members, and the group's own `order` places the group. To rebuild the page, sort groups and ungrouped services together by `order`, then list each group's members by theirs. Sorting services alone does not give the grouped layout, because moving a service into a group keeps its `order`.\n"},"group":{"type":["string","null"],"readOnly":true,"description":"Slug of the service group this service belongs to, or `null` when it is ungrouped. A service belongs to at most one group, and the group's `scope` always matches the service's.\nRead-only here; write the same slug on create or update to change it. Fetch the group's name, order and rollup status from `/service_groups/{service_group_slug}`.\n"},"scope":{"type":"string","readOnly":true,"enum":["global","containers"],"description":"Whether this service applies status-page-wide (`global`) or is scoped to specific containers (`containers`).\nDerived, not stored: a service with at least one container is `containers`, and a service with none is `global`. It is computed per response and cannot be written directly — set `containers` on create or update instead.\n"},"containers":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"Slugs of the containers this service is included in. Empty when `scope` is `global`. Containers on the status page that are absent from this list are ones the service is explicitly excluded from.\n"},"container_statuses":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Live status for each container this service is included in. Empty when `scope` is `global`. Containers the service is excluded from produce no entry.\n"},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AccountPendingActivation":{"description":"The account is pending activation. A newly created account must be activated by an administrator before its API key can be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/status_pages/{subdomain}/services":{"post":{"summary":"Create a service","operationId":"createService","tags":["Services"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Service"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccountPendingActivation"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}}}}
```

## GET /status\_pages/{subdomain}/services/{service\_slug}

> Get a service

```json
{"openapi":"3.1.0","info":{"title":"StatusPal Public API","version":"1"},"tags":[{"name":"Services","description":"Manage services displayed on a status page"}],"servers":[{"url":"https://next.statuspal.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Organization API key — issue one via the Rails console (management UI coming soon)"}},"schemas":{"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid","readOnly":true},"slug":{"type":"string","description":"URL-safe identifier. May be supplied on create/update; if omitted, it is\nauto-generated from `name`. Uniqueness is enforced per status page.\n"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"When `scope` is `containers`, the worst-case status across the containers this service is included in. When `scope` is `global`, the service's own status.\nRead-only here; change it with a notice update, from the operational dashboard, or by writing `status` / `container_statuses` on update.\n","readOnly":true},"order":{"type":"integer","description":"Display position on the status page. For an ungrouped service it is the position in the page's one sequence of groups and ungrouped services, shared with `ServiceGroup.order`. For a grouped service it only ranks the service among its group's members, and the group's own `order` places the group. To rebuild the page, sort groups and ungrouped services together by `order`, then list each group's members by theirs. Sorting services alone does not give the grouped layout, because moving a service into a group keeps its `order`.\n"},"group":{"type":["string","null"],"readOnly":true,"description":"Slug of the service group this service belongs to, or `null` when it is ungrouped. A service belongs to at most one group, and the group's `scope` always matches the service's.\nRead-only here; write the same slug on create or update to change it. Fetch the group's name, order and rollup status from `/service_groups/{service_group_slug}`.\n"},"scope":{"type":"string","readOnly":true,"enum":["global","containers"],"description":"Whether this service applies status-page-wide (`global`) or is scoped to specific containers (`containers`).\nDerived, not stored: a service with at least one container is `containers`, and a service with none is `global`. It is computed per response and cannot be written directly — set `containers` on create or update instead.\n"},"containers":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"Slugs of the containers this service is included in. Empty when `scope` is `global`. Containers on the status page that are absent from this list are ones the service is explicitly excluded from.\n"},"container_statuses":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Live status for each container this service is included in. Empty when `scope` is `global`. Containers the service is excluded from produce no entry.\n"},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}}},"ContainerStatusEntry":{"type":"object","required":["container_slug","status"],"properties":{"container_slug":{"type":"string"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"]}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AccountPendingActivation":{"description":"The account is pending activation. A newly created account must be activated by an administrator before its API key can be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/status_pages/{subdomain}/services/{service_slug}":{"get":{"summary":"Get a service","operationId":"getService","tags":["Services"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Service"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccountPendingActivation"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## DELETE /status\_pages/{subdomain}/services/{service\_slug}

> Delete a service

```json
{"openapi":"3.1.0","info":{"title":"StatusPal Public API","version":"1"},"tags":[{"name":"Services","description":"Manage services displayed on a status page"}],"servers":[{"url":"https://next.statuspal.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Organization API key — issue one via the Rails console (management UI coming soon)"}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AccountPendingActivation":{"description":"The account is pending activation. A newly created account must be activated by an administrator before its API key can be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}},"paths":{"/status_pages/{subdomain}/services/{service_slug}":{"delete":{"summary":"Delete a service","operationId":"deleteService","tags":["Services"],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccountPendingActivation"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## PATCH /status\_pages/{subdomain}/services/{service\_slug}

> Update a service

```json
{"openapi":"3.1.0","info":{"title":"StatusPal Public API","version":"1"},"tags":[{"name":"Services","description":"Manage services displayed on a status page"}],"servers":[{"url":"https://next.statuspal.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Organization API key — issue one via the Rails console (management UI coming soon)"}},"schemas":{"ServiceUpdate":{"type":"object","properties":{"name":{"type":"string","maxLength":255},"slug":{"type":"string","description":"Optional. If provided, replaces the URL slug (with uniqueness suffixing if needed)."},"description":{"type":["string","null"]},"order":{"type":"integer","description":"Position in the display list (1-indexed). Other services shift accordingly."},"group":{"type":["string","null"],"description":"Slug of the service group to move this service into, or `null` to ungroup it. The group must already exist and its `scope` must match the service's — otherwise 422.\nChanging `containers` so the service's scope flips removes it from its current group automatically, unless this field names a group of the new scope in the same request.\nMoving a group's last service out leaves the group standing, empty. To move a whole group between scopes, write `containers` on the group rather than on each service.\n"},"containers":{"type":"array","items":{"type":"string"},"description":"Replaces the set of containers this service is included in, by slug, and with it the service scope. Passing an empty array makes the service Global; passing one or more containers makes it a Container Service. An unknown slug returns 422.\nStatus is preserved across a scope change, never reset. Switching Global to Container-scoped gives every newly-included container the service current status. Switching Container-scoped to Global discards the per-container configuration and keeps the worst-case of the discarded statuses — the status the public page was already showing.\n"},"container_statuses":{"type":"array","items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Sets the live status of this service in the named containers — the API equivalent of the operational dashboard.\nEvery `container_slug` must be one this service is included in — otherwise 422. Containers not listed keep their current status. Rejected with 422 for a Global Service; use `status` there instead.\n"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"Sets the live status of a Global Service — the API equivalent of the operational dashboard.\nRejected with 422 for a Container Service; use `container_statuses` there instead.\nA status set this way does not notify subscribers and does not count toward uptime history, which is built from notices. To record an outage, post a notice or notice update with `service_statuses` instead.\n"}}},"ContainerStatusEntry":{"type":"object","required":["container_slug","status"],"properties":{"container_slug":{"type":"string"},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"]}}},"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid","readOnly":true},"slug":{"type":"string","description":"URL-safe identifier. May be supplied on create/update; if omitted, it is\nauto-generated from `name`. Uniqueness is enforced per status page.\n"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ok","minor","major","critical","maintenance"],"description":"When `scope` is `containers`, the worst-case status across the containers this service is included in. When `scope` is `global`, the service's own status.\nRead-only here; change it with a notice update, from the operational dashboard, or by writing `status` / `container_statuses` on update.\n","readOnly":true},"order":{"type":"integer","description":"Display position on the status page. For an ungrouped service it is the position in the page's one sequence of groups and ungrouped services, shared with `ServiceGroup.order`. For a grouped service it only ranks the service among its group's members, and the group's own `order` places the group. To rebuild the page, sort groups and ungrouped services together by `order`, then list each group's members by theirs. Sorting services alone does not give the grouped layout, because moving a service into a group keeps its `order`.\n"},"group":{"type":["string","null"],"readOnly":true,"description":"Slug of the service group this service belongs to, or `null` when it is ungrouped. A service belongs to at most one group, and the group's `scope` always matches the service's.\nRead-only here; write the same slug on create or update to change it. Fetch the group's name, order and rollup status from `/service_groups/{service_group_slug}`.\n"},"scope":{"type":"string","readOnly":true,"enum":["global","containers"],"description":"Whether this service applies status-page-wide (`global`) or is scoped to specific containers (`containers`).\nDerived, not stored: a service with at least one container is `containers`, and a service with none is `global`. It is computed per response and cannot be written directly — set `containers` on create or update instead.\n"},"containers":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"Slugs of the containers this service is included in. Empty when `scope` is `global`. Containers on the status page that are absent from this list are ones the service is explicitly excluded from.\n"},"container_statuses":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ContainerStatusEntry"},"description":"Live status for each container this service is included in. Empty when `scope` is `global`. Containers the service is excluded from produce no entry.\n"},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AccountPendingActivation":{"description":"The account is pending activation. A newly created account must be activated by an administrator before its API key can be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/status_pages/{subdomain}/services/{service_slug}":{"patch":{"summary":"Update a service","operationId":"updateService","tags":["Services"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Service"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccountPendingActivation"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}}}}
```
