Skip to content

Feature status messages

Feature status messages let back-office operators publish short, time-bound notices for a feature — for example planned maintenance, incidents or general information. Applications (e.g. via the PlatformSDK) read the currently-active messages for a feature and surface them as a banner.

Each message has a severity, a title and body, and an active window (startDateexpiringDate). A message either belongs to a single feature or is global (client-less).

Global (client-less) messages

A message may be created without a feature (a null client). Such a global message applies to every feature/client — for example a platform-wide maintenance notice. Global messages are:

  • created, updated and deleted through their own id-less endpoints (/api/admin/feature/message, see below) and, like all messages, can only be written by BackOffice users;
  • merged into every per-feature listing — a caller listing /api/admin/feature/{id}/message receives that feature's own messages plus all global messages;
  • returned with featureId: null in the response.

Permissions

Operation Scope Who
Read (list) platform_iam_featurestatus_read BackOffice → any feature; otherwise only the caller's own feature (the token client_id must equal the requested feature id). Global messages are visible to every caller.
Create / update / delete platform_iam_featurestatus_readwrite BackOffice

Message severity

type is one of:

Value Meaning
Critical Highest severity (e.g. outage)
Warning Warning (e.g. upcoming maintenance)
Informational General information

Message content

  • title — plain text (max 255 characters). Any markup is stripped server-side.
  • body — may contain a limited safe HTML subset: b, strong, i, em, u, br, p, ul, ol, li, and a with an href (schemes https, mailto). Max 4 000 characters.

On write, the server sanitizes both fields: scripts, event handlers (e.g. onclick), unsafe URL schemes (e.g. javascript:) and any non-allowlisted tags/attributes are removed before the message is stored.

Rendering responsibility

Server-side sanitization is defense-in-depth, not a substitute for safe rendering. Consumers that display the body in a web page must still render it through a framework that escapes/sanitizes by default (React/Angular do this for text bindings) and should serve a restrictive Content-Security-Policy. Avoid raw HTML sinks (innerHTML, dangerouslySetInnerHTML, bypassSecurityTrustHtml) unless the value is re-sanitized client-side.

List messages for a feature

GET/api/admin/feature/{id}/message

{id} is the feature id (for example Platform). Results are cursor-paginated.

The response contains the feature's own messages and all global (client-less) messages, since global messages apply to every feature. Global messages appear with featureId: null.

Callers in the BackOffice role may read any feature. Any other caller may only read the feature their access token was issued for — the token's client_id must equal {id}, otherwise the request is rejected with 403 Forbidden. This lets an application read its own feature's banner (including any global messages) without granting it access to other features' messages.

Query parameters:

Parameter Default Description
expiry NonExpired Which messages to return: All, NonExpired (active now), Expired (already ended), Pending (not started yet).
sortBy StartDate Sort property: StartDate or ExpiringDate.
sortOrder Desc Asc or Desc.
cursor Cursor returned by a previous page.
limit Maximum number of items to return.

The default (NonExpired) returns only the messages that are currently active — this is what an application banner typically requests.

Click to show example shell script
featureid="Platform"
openapikey="<replacewithopenapikey>"

# expiry is optional: All, NonExpired, Expired, Pending
curl -k -X GET \
  "https://api.mike-cloud-dev.com/api/admin/feature/$featureid/message?expiry=NonExpired" \
  -H 'Content-Type: application/json' \
  -H "dhi-open-api-key: $openapikey"
Click to show example response
{
    "data": [
        {
            "id": 42,
            "featureId": "Platform",
            "type": "Warning",
            "title": "Scheduled maintenance",
            "body": "The platform will be unavailable on Saturday between 02:00 and 04:00 UTC.",
            "startDate": "2026-06-20T00:00:00Z",
            "expiringDate": "2026-06-21T00:00:00Z"
        }
    ],
    "cursor": null
}

Create a message

POST/api/admin/feature/{id}/message

The feature is taken from the route ({id}); any featureId sent in the body is ignored. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role.

Click to show example shell script
featureid="Platform"
openapikey="<replacewithopenapikey>"

data='{
  "type": "Warning",
  "title": "Scheduled maintenance",
  "body": "The platform will be unavailable on Saturday between 02:00 and 04:00 UTC.",
  "startDate": "2026-06-20T00:00:00Z",
  "expiringDate": "2026-06-21T00:00:00Z"
}'

curl -k -X POST \
  "https://api.mike-cloud-dev.com/api/admin/feature/$featureid/message" \
  -H 'Content-Type: application/json' \
  -H "dhi-open-api-key: $openapikey" \
  -d "$data"
Click to show example response
{
    "id": 42,
    "featureId": "Platform",
    "type": "Warning",
    "title": "Scheduled maintenance",
    "body": "The platform will be unavailable on Saturday between 02:00 and 04:00 UTC.",
    "startDate": "2026-06-20T00:00:00Z",
    "expiringDate": "2026-06-21T00:00:00Z"
}

Update a message

PATCH/api/admin/feature/{id}/message/{messageId}

Updates a message that belongs to feature {id}. The feature association cannot be changed; a {messageId} that does not belong to {id} returns 404. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role.

Click to show example shell script
featureid="Platform"
messageid="42"
openapikey="<replacewithopenapikey>"

data='{
  "type": "Informational",
  "title": "Maintenance completed",
  "body": "The scheduled maintenance has finished.",
  "startDate": "2026-06-21T00:00:00Z",
  "expiringDate": "2026-06-22T00:00:00Z"
}'

curl -k -X PATCH \
  "https://api.mike-cloud-dev.com/api/admin/feature/$featureid/message/$messageid" \
  -H 'Content-Type: application/json' \
  -H "dhi-open-api-key: $openapikey" \
  -d "$data"
Click to show example response
{
    "id": 42,
    "featureId": "Platform",
    "type": "Informational",
    "title": "Maintenance completed",
    "body": "The scheduled maintenance has finished.",
    "startDate": "2026-06-21T00:00:00Z",
    "expiringDate": "2026-06-22T00:00:00Z"
}

Delete a message

DELETE/api/admin/feature/{id}/message/{messageId}

Deletes a message that belongs to feature {id}. A {messageId} that does not belong to {id} returns 404. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role. Returns 204 No Content.

Click to show example shell script
featureid="Platform"
messageid="42"
openapikey="<replacewithopenapikey>"

curl -k -X DELETE \
  "https://api.mike-cloud-dev.com/api/admin/feature/$featureid/message/$messageid" \
  -H 'Content-Type: application/json' \
  -H "dhi-open-api-key: $openapikey"

Global message endpoints

Global messages apply to every feature and are managed through id-less endpoints. They behave exactly like per-feature messages (same severity, content, sanitization and expiry rules) but are not tied to a feature. They are also merged into every per-feature listing (see above).

List global messages

GET/api/admin/feature/message

Lists only the global messages, in isolation from any feature. Requires the platform_iam_featurestatus_read scope. Accepts the same query parameters (expiry, sortBy, sortOrder, cursor, limit) as the per-feature listing.

Create a global message

POST/api/admin/feature/message

Creates a message that applies to every feature. There is no feature in the route and any featureId sent in the body is ignored — the message is always stored as global. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role.

Click to show example shell script
openapikey="<replacewithopenapikey>"

data='{
  "type": "Warning",
  "title": "Platform-wide maintenance",
  "body": "All applications will be unavailable on Saturday between 02:00 and 04:00 UTC.",
  "startDate": "2026-06-20T00:00:00Z",
  "expiringDate": "2026-06-21T00:00:00Z"
}'

curl -k -X POST \
  "https://api.mike-cloud-dev.com/api/admin/feature/message" \
  -H 'Content-Type: application/json' \
  -H "dhi-open-api-key: $openapikey" \
  -d "$data"
Click to show example response
{
    "id": 43,
    "featureId": null,
    "type": "Warning",
    "title": "Platform-wide maintenance",
    "body": "All applications will be unavailable on Saturday between 02:00 and 04:00 UTC.",
    "startDate": "2026-06-20T00:00:00Z",
    "expiringDate": "2026-06-21T00:00:00Z"
}

Update a global message

PATCH/api/admin/feature/message/{messageId}

Updates a global message. A {messageId} that does not belong to a global message (for example a feature-scoped one) returns 404. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role.

Delete a global message

DELETE/api/admin/feature/message/{messageId}

Deletes a global message. A {messageId} that does not belong to a global message returns 404. Requires the platform_iam_featurestatus_readwrite scope and the BackOffice role. Returns 204 No Content.

Note

Messages that expired more than a configured retention period ago (30 days by default) are automatically removed by a background cleanup job. This applies to global messages too.