/alerts/config - Save the caller's alerting layer

POST /alerts/config

Saves the CALLER's alerting configuration layer (one row per organization in alert_config_layers). The body is an AlertingConfigLayer: three channel toggles plus three recipient lists. Each recipient carries its own severities[]; email recipients additionally carry language and format.

After save, the effective per-tenant Mimir YAML is recomputed server-side (merge of all layers walking up to the Owner) and pushed to every tenant in the caller's hierarchy with bounded concurrency. Per-tenant push failures are returned in warnings[]; the caller's layer is saved regardless of push outcome (Mimir can be reconciled by saving again).

Additive-only contract: descendants can ADD recipients but cannot disable channels enabled by ancestors. The server normalises any explicit false in enabled.{email,webhook,telegram} from non-Owner layers to null on storage.

Save+propagate is serialised per-organization (in-process mutex) to prevent two concurrent saves from racing at the Mimir push step. Body is capped at 1 MiB; oversized payloads are rejected with 413.

Requires manage:alerts permission.

application/json

Body Required

  • enabled object

    Per-layer enable/disable for each notification channel. Each value is tri-state:

    • true — explicitly enabled at this layer
    • false — explicitly disabled (Owner only; non-Owner false is normalised to null on save)
    • null — no opinion at this layer; effective state inherits from the merge of any ancestor layer that took a position. If no layer in the chain enables a channel, the channel stays off.
    Hide enabled attributes Show enabled attributes object
    • email boolean | null
    • webhook boolean | null
    • telegram boolean | null
  • email_recipients array[object]

    Not more than 50 elements.

    Hide email_recipients attributes Show email_recipients attributes object
    • address string(email) Required

      Maximum length is 320.

    • severities array[string]

      Severity scope for this recipient. Empty (or omitted) means "all severities" — the recipient lands on every per-severity receiver. A non-empty subset narrows delivery to those severities.

      Not more than 3 elements. Values are critical, warning, or info.

    • language string

      Rendering language for this recipient's email body and subject. Defaults to en when omitted.

      Values are en or it.

    • format string

      Body format preference. html (default) emits multipart with an html primary body and text alternative; plain emits only a text body.

      Values are html or plain.

  • webhook_recipients array[object]

    Not more than 20 elements.

    Hide webhook_recipients attributes Show webhook_recipients attributes object
    • name string Required

      Descriptive label for the webhook target (UI only).

      Maximum length is 100.

    • url string(uri) Required

      Webhook URL. Validation rejects non-public destinations (loopback, RFC1918, RFC6598 CGNAT, link-local, multicast, cloud metadata) and requires a canonical hostname (containing at least one letter) or a valid IP literal. Only http/https schemes are accepted; URLs cannot embed userinfo.

      Maximum length is 2048.

    • severities array[string]

      Not more than 3 elements. Values are critical, warning, or info.

  • telegram_recipients array[object]

    Not more than 20 elements.

    Hide telegram_recipients attributes Show telegram_recipients attributes object
    • bot_token string Required

      Maximum length is 256.

    • chat_id integer(int64) Required
    • severities array[string]

      Not more than 3 elements. Values are critical, warning, or info.

Responses

  • 200 application/json

    Layer saved (and propagation attempted)

    Hide response attributes Show response attributes object
    • code integer
    • message string
    • data object
      Hide data attributes Show data attributes object
      • affected_tenants integer

        Number of tenants in caller's hierarchy whose effective config was recomputed

      • propagated_to integer

        Of affected_tenants, how many were successfully pushed to Mimir

      • warnings array[string]

        Per-tenant push errors. Always present; empty when every push succeeded. Each entry: org <logto_id>: <error>.

  • 400 application/json

    Bad request - validation error

    Hide response attributes Show response attributes object
    • code integer

      HTTP error code

    • message string

      Error message

    • data object
      Hide data attributes Show data attributes object
      • type string

        Type of error

        Values are validation_error or external_api_error.

      • errors array[object]
        Hide errors attributes Show errors attributes object
        • key string

          Field name that failed validation

        • message string

          Error code or message

        • value string

          Value that failed validation

      • details

        Additional error details

  • 401 application/json

    Unauthorized - invalid or missing token

    Hide response attributes Show response attributes object
    • code integer
    • message string
    • data object | null
  • 403 application/json

    Forbidden - insufficient permissions

    Hide response attributes Show response attributes object
    • code integer
    • message string
    • data object | null
  • 413 application/json

    Request body exceeds the configured maximum (1 MiB).

    Hide response attributes Show response attributes object
    • code integer
    • message string
  • 500 application/json

    Internal server error

    Hide response attributes Show response attributes object
    • code integer
    • message string
    • data object | null
POST /alerts/config
curl \
 --request POST 'https://api.your-domain.com/api/alerts/config' \
 --header "Authorization: Bearer $ACCESS_TOKEN" \
 --header "Content-Type: application/json" \
 --data '{"enabled":{"email":true,"webhook":true,"telegram":false},"email_recipients":[{"address":"noc@msp.example","severities":[],"language":"it","format":"html"}],"webhook_recipients":[{"name":"central-siem","url":"https://siem.example/api/alerts","severities":[]}],"telegram_recipients":[]}'
Request examples
Owner enables email + webhook globally, sets a NOC recipient on all severities in Italian HTML, plus a SIEM webhook on every severity. Every descendant inherits.
{
  "enabled": {
    "email": true,
    "webhook": true,
    "telegram": false
  },
  "email_recipients": [
    {
      "address": "noc@msp.example",
      "severities": [],
      "language": "it",
      "format": "html"
    }
  ],
  "webhook_recipients": [
    {
      "name": "central-siem",
      "url": "https://siem.example/api/alerts",
      "severities": []
    }
  ],
  "telegram_recipients": []
}
Reseller does NOT touch channel toggles (null = "no opinion"); it just adds a local NOC mailbox in English for critical+warning. Merged with Owner's recipients.
{
  "enabled": {
    "email": null,
    "webhook": null,
    "telegram": null
  },
  "email_recipients": [
    {
      "address": "noc@reseller.example",
      "severities": [
        "critical",
        "warning"
      ],
      "language": "en",
      "format": "html"
    }
  ],
  "webhook_recipients": [],
  "telegram_recipients": []
}
Different recipients can request different bodies. The on-call inbox wants plain text (alerts piped into a ticketing tool); the manager wants HTML in Italian.
{
  "enabled": {
    "email": null,
    "webhook": null,
    "telegram": null
  },
  "email_recipients": [
    {
      "address": "oncall@customer.example",
      "severities": [
        "critical"
      ],
      "language": "en",
      "format": "plain"
    },
    {
      "address": "manager@customer.example",
      "severities": [],
      "language": "it",
      "format": "html"
    }
  ],
  "webhook_recipients": [],
  "telegram_recipients": []
}
Customer adds a Slack webhook scoped to critical. The rendered Alertmanager route puts this webhook only on the critical receiver.
{
  "enabled": {
    "email": null,
    "webhook": null,
    "telegram": null
  },
  "email_recipients": [],
  "webhook_recipients": [
    {
      "name": "ops-slack",
      "url": "https://hooks.slack.com/services/T000/B000/XXX",
      "severities": [
        "critical"
      ]
    }
  ],
  "telegram_recipients": []
}
Single Telegram bot pushing to a channel for every severity (severities=[]). Telegram messages are currently always rendered in English.
{
  "enabled": {
    "email": null,
    "webhook": null,
    "telegram": true
  },
  "email_recipients": [],
  "webhook_recipients": [],
  "telegram_recipients": [
    {
      "bot_token": "123456:ABC-DEF1234ghIkl",
      "chat_id": -1001234567890,
      "severities": []
    }
  ]
}
Saving an empty layer is meaningful: it just records audit metadata (who/when) without contributing recipients or toggles.
{
  "enabled": {
    "email": null,
    "webhook": null,
    "telegram": null
  },
  "email_recipients": [],
  "webhook_recipients": [],
  "telegram_recipients": []
}
Response examples (200)
{
  "code": 200,
  "message": "alerting configuration updated successfully",
  "data": {
    "affected_tenants": 42,
    "propagated_to": 42,
    "warnings": [
      "string"
    ]
  }
}
Response examples (400)
{
  "code": 400,
  "message": "validation failed",
  "data": {
    "type": "validation_error",
    "errors": [
      {
        "key": "username",
        "message": "required",
        "value": "string"
      }
    ]
  }
}
Response examples (401)
{
  "code": 401,
  "message": "invalid token",
  "data": {}
}
Response examples (403)
{
  "code": 403,
  "message": "insufficient permissions",
  "data": {}
}
Response examples (413)
{
  "code": 413,
  "message": "request body exceeds the configured maximum"
}
Response examples (500)
{
  "code": 500,
  "message": "internal server error",
  "data": {}
}