Documentation / Monitoring and security
Notifications
Get alerted about downtime, server issues, and service events via Slack, Teams, or webhooks.
calmo.cloud can send real-time notifications to your team when something needs attention — whether a site goes down, a server metric exceeds a threshold, or an Odoo service changes state.
Supported destinations #
| Type | Description |
|---|---|
| Slack | Posts rich messages with colored status bars and action buttons to a Slack channel |
| Microsoft Teams | Sends adaptive card messages to a Teams channel |
| Webhook | Sends signed JSON payloads to any https:// URL — see Webhook payload |
Setting up a destination #
1. Go to Notification Destinations #
Navigate to Settings > Notification Destinations and click New Destination.
2. Configure the destination #
Provide:
- Name — A friendly name (e.g., "Ops Slack Channel")
- Type — Slack, Teams, or Webhook
- Webhook URL — The incoming webhook URL from your messaging platform, or the address of your own endpoint. It has to start with
https://and be reachable from the internet (see Blocked addresses).
For Slack, create an incoming webhook in your Slack workspace settings under Apps > Incoming Webhooks.
3. Test the connection #
After saving, click Send Test on the destination's edit page. The test is sent right away, and the result tells you what happened: the HTTP status your endpoint answered with and how long it took, or why the delivery failed. You can send up to five tests per minute and destination. Each test shows up under Logs like any other delivery.
Event subscriptions #
Each destination can subscribe to specific event types. Go to the Subscriptions section on a destination's edit page to configure which events trigger notifications. Add Subscriptions lets you pick several events at once.
Available events #
| Category | Event | Description |
|---|---|---|
| Uptime & Certificates | Uptime Check Failed | A monitored URL is not responding |
| Uptime Check Recovered | A previously down URL is back up | |
| Certificate Check Failed | An SSL certificate is invalid | |
| Certificate Expires Soon | An SSL certificate is expiring soon | |
| Server Metrics | CPU Usage High | CPU usage exceeded the threshold |
| Memory Usage High | Memory usage exceeded the threshold | |
| Disk Usage High | Disk usage exceeded the threshold | |
| Server Events | Server Powered On | A server was powered on |
| Server Powered Off | A server was powered off | |
| Odoo Events | Odoo Service Stopped | An Odoo service has stopped |
| Odoo Service Redeployed | An Odoo service was redeployed |
Metric thresholds #
For server metric events (CPU, Memory, Disk), you can set a custom threshold percentage (1–100%). The default threshold is 90%. Notifications are sent when the current value exceeds your configured threshold.
Coding agents #
Notification destinations do not receive coding-agent events. To have your own system told when an agent needs a decision, finishes its turn or opens a pull request, create a webhook with the source Agent sessions on the Webhook page — see Webhooks.
The person who started a session in the panel is told in the panel's notification bell when the agent needs a decision or a reply, and when the session fails or cannot start. This needs no destination and no webhook. For sessions started through the API, there is nobody to tell.
Slack message format #
Slack notifications include:
- A colored sidebar indicating severity (red for failures, green for recoveries, orange for warnings)
- A header with the event title
- Details about the affected resource
- A View Details button linking directly to the resource in calmo.cloud
- A timestamp footer
Webhook payload #
A Webhook destination receives every notification as a POST with a JSON body and these headers:
| Header | Content |
|---|---|
content-type |
application/json |
user-agent |
calmo.cloud-Webhooks/1 |
calmo-event-type |
The event type, the same as event_type in the body |
webhook-id |
The ID of the notification, the same as id in the body. It stays the same on every retry. |
webhook-timestamp |
When this attempt was sent, in Unix seconds |
webhook-signature |
The signature — see Webhook signatures |
The body has these keys:
| Key | Content |
|---|---|
title |
A short headline |
body |
One or two sentences |
event_type |
The event, e.g. odoo_service_stopped. Send Test uses test. |
url |
Where to look at it in the panel, or null |
metadata |
A few facts as strings; the keys depend on the event |
id |
Unique ID of the notification. Use it to recognise repeats. |
version |
Version of the body format, currently 1 |
occurred_at |
When the notification was raised, ISO 8601 in UTC with microseconds |
For example, when an Odoo service stops:
{
"title": "Odoo service ACME Live stopped",
"body": "Odoo service ACME Live on prod-1 was stopped.",
"event_type": "odoo_service_stopped",
"url": "https://calmo.cloud/admin/acme/service-odoos/42/edit",
"metadata": {
"service": "ACME Live",
"server": "prod-1"
},
"id": "5f1c9a3e-2b7d-4e8f-a1c6-9d0b3e4f5a27",
"version": 1,
"occurred_at": "2026-09-25T09:41:07.318204Z"
}
New keys can be added at any time. Ignore the ones you do not know.
Webhook signatures #
Every request to a Webhook destination is signed following the Standard Webhooks specification, so your endpoint can check that it really comes from calmo.cloud and has not been changed on the way. Each webhook destination has its own Signing secret, shown (hidden until revealed) on its edit page once it is saved. It starts with whsec_.
To check a request, calculate the HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{raw body} with the Base64-decoded part of the secret after whsec_ as the key, and compare its Base64 encoding with the value after v1, in webhook-signature. Examples in PHP and Node.js are in Webhooks; they work unchanged with a destination's signing secret. Notification destinations do not send the Signature header described there.
If the secret has leaked, click Regenerate signing secret. Requests are signed with the new secret right away, so update your endpoint at the same time — until then it rejects them.
Requests to Slack and Microsoft Teams are not signed: their incoming webhook URLs are secret and act as the credential, so treat them like a password.
Delivery and retries #
A notification counts as delivered when your endpoint answers with a 2xx status within 10 seconds. When it does not, calmo.cloud tries again: 10 seconds after the first failed attempt, then after 1 minute, 5 minutes and 30 minutes — five attempts over about 36 minutes.
It retries when the endpoint cannot be reached, its host name cannot be looked up, the connection or TLS handshake fails, the request times out, or the endpoint answers with HTTP 408, 425, 429 or any 5xx status. Any other answer ends the delivery right away:
- Other 4xx statuses — the endpoint refused the notification, and sending it again would not change that
- Redirects (3xx) — calmo.cloud does not follow them; enter the final URL instead
- Blocked addresses — see below
Retries can deliver the same notification more than once, for example when your endpoint processed it but answered too late. Webhook payloads carry an id that stays the same on every attempt (it is also sent as webhook-id), so your endpoint can ignore repeats.
A notification that is waiting for its next attempt is dropped when you disable or delete the destination in the meantime.
Blocked addresses #
Notifications are sent from calmo.cloud's own network, so a destination has to be a public address on the internet. calmo.cloud refuses:
- URLs that do not start with
https://, or that contain a user name or password - Ports other than 443, 8443 or 1024–65535
localhostand host names ending in.localhost,.localor.internal- Host names that do not resolve to any address when you save the destination
- Host names with any address in a private, loopback, link-local or other reserved range
- calmo.cloud itself, and servers that belong to another team (your own team's servers are fine)
The URL is checked when you save the destination and again before every delivery, and each delivery goes to exactly the address that was checked. A delivery to an address that is not allowed — for example from an existing destination with an http:// URL, or one whose host name now points into a private network — is not retried and is listed as blocked in the log until you change the URL. A host name that cannot be looked up at delivery time — for example because a DNS server did not answer — is not blocked: the attempt fails with DNS lookup failed and is retried like an endpoint that cannot be reached.
Delivery log #
The Logs section of a destination lists every delivery attempt: the event, the status (success, failed or blocked), the HTTP status your endpoint answered with, the attempt number, the event ID and the payload that was sent. A failed attempt shows a short reason such as HTTP 500, Timed out, TLS error, Connection failed, DNS lookup failed or Redirect not followed.
What your endpoint answers with is not stored — only its HTTP status. Log entries are deleted after 30 days.
Enabling and disabling #
You can enable or disable notifications at two levels:
- Destination level — Toggle the entire destination on or off
- Subscription level — Toggle individual event subscriptions on or off
Disabled destinations or subscriptions will not receive any notifications. Send Test still works on a disabled destination, so you can check a destination before switching it on.