# How to Send xCloud Notifications to Discord or a Webhook

> Get xCloud server, site and team alerts in a Discord channel, or as signed JSON on your own HTTPS endpoint, and verify every webhook delivery.

[**xCloud**](/) can send your server, site and team alerts to a **Discord** channel, or as signed JSON to **your own HTTPS endpoint (a webhook)**. Both work alongside Slack, Telegram and WhatsApp, and they carry the same alerts as Telegram: server reboots and unavailable servers, SSL certificate and deployment problems, team account actions and more.

Use **Discord** when your team already talks there. Use a **webhook** to feed alerts into your own tools: an incident system, a chat bot, a ticket queue or an automation platform.

## Before you start

-   Notification integrations need a paid plan. On the Free plan, the page sends you to **Bills & Payment**.
-   You need a team role that can manage notifications.
-   A team can connect **one Discord** and **one webhook** integration. To change one, disconnect it and add it again.
-   Discord and webhook integrations are not available on white-label dashboards, and not yet in the Public API or MCP.

## Step 1: Open the notification integrations

From the xCloud dashboard, go to **Settings → Integrations → Notification** and click **Add New Notification**. Choose **Discord** or **Webhook**.

![The Add Notification window in xCloud with Slack, WhatsApp, Telegram, Discord and Webhook options](/_landing/docs/send-xcloud-notifications-to-discord-or-a-webhook-add-notification.png)

## Step 2a: Connect Discord

First, create a webhook for the Discord channel that should receive the alerts. In Discord, open **Server Settings → Integrations → Webhooks**, click **New Webhook**, pick the channel and click **Copy Webhook URL**.

Then, in xCloud, select **Discord**, paste the URL into **Discord Webhook URL** and click **Connect Discord**.

![The Discord option selected in xCloud, with the Discord Webhook URL field and the Connect Discord button](/_landing/docs/send-xcloud-notifications-to-discord-or-a-webhook-discord-form.png)

The URL must be a Discord channel webhook, such as `https://discord.com/api/webhooks/{id}/{token}`. Addresses on `discordapp.com`, `canary.discord.com` and `ptb.discord.com` are accepted too. Any other address, or a URL without its token, shows an error under the field.

Each alert arrives in the channel as a bold title, the alert text and an **Open** link to the server or site in xCloud. xCloud never mentions anyone in the channel.

## Step 2b: Connect a webhook

Select **Webhook**, enter your endpoint in **Webhook URL** and click **Add Webhook**. The URL must use `https://`.

![The Webhook option selected in xCloud, with the Webhook URL field and the Add Webhook button](/_landing/docs/send-xcloud-notifications-to-discord-or-a-webhook-webhook-form.png)

xCloud then shows the **webhook signing secret** (it starts with `whsec_`). **Copy it now: it is shown only once.** Store it where your endpoint can read it, then click **I have saved the secret**. To get a new secret later, disconnect the webhook and add it again.

### What xCloud sends

Every alert is a `POST` with a JSON body:

```json
{
  "id": "0d6c1f0e-5f3a-4a49-9a51-3c3f1d6a1b7e",
  "event": "ssl_renewed_failure",
  "occurred_at": "2026-10-19T10:00:00Z",
  "team": {"uuid": "..."},
  "resources": {"server": "...", "site": "..."},
  "title": "SSL Renewed Failure",
  "message": "This is to notify you that we were unable to renew SSL for example.com. ...",
  "url": "https://app.xcloud.host/..."
}
```

`resources` holds only the keys that apply to the alert. The request also carries these headers:

| Header | Value |
| --- | --- |
| `X-xCloud-Event` | The alert type, the same as `event` (`test` for a test notification) |
| `X-xCloud-Delivery` | The alert's unique id, the same as `id` |
| `X-xCloud-Timestamp` | When this delivery was signed, in Unix seconds |
| `X-xCloud-Signature` | `t=<timestamp>,v1=<signature>` |

### Verify each delivery

Check the signature before you trust a delivery. The `v1` value is the hex HMAC-SHA256 of `<X-xCloud-Timestamp>.<raw request body>`, signed with your secret. Use the raw body bytes from before JSON parsing, compare in constant time, and reject timestamps older than five minutes.

PHP:

```php
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_XCLOUD_TIMESTAMP'] ?? '';
preg_match('/v1=([0-9a-f]{64})/', $_SERVER['HTTP_X_XCLOUD_SIGNATURE'] ?? '', $m);
$expected = hash_hmac('sha256', $timestamp.'.'.$body, getenv('XCLOUD_WEBHOOK_SECRET'));

if (!$m || !hash_equals($expected, $m[1]) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit;
}
```

Node.js:

```js
const crypto = require('crypto');

function verify(rawBody, headers, secret) {
  const timestamp = headers['x-xcloud-timestamp'];
  const match = /v1=([0-9a-f]{64})/.exec(headers['x-xcloud-signature'] || '');
  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  return Boolean(match)
    && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(match[1]))
    && Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
}
```

If xCloud retries an alert, the retry keeps the same `id` and `X-xCloud-Delivery` but is signed again with a new timestamp. Use the id to ignore a delivery you already processed.

## Step 3: Send a test notification

In the list of notifications, click **Send test notification** on the new integration. Discord shows a test message in the channel, and your endpoint receives a delivery with `"event": "test"`. xCloud shows the result, such as the HTTP status your endpoint returned, or why it refused the address.

## Step 4: Choose which alerts to send

Go to **Settings → Notifications**. In **Server Notifications**, **Site Notifications** and **Other Notifications**, tick **Discord** or **Webhook** for each group you want, then click **Save Changes**.

![The xCloud Notifications settings page with Discord and Webhook options for server, site and other notifications](/_landing/docs/send-xcloud-notifications-to-discord-or-a-webhook-notification-settings.png)

The Discord and Webhook options stay greyed out until that integration is connected. They are separate from the Telegram options: turning on Telegram for a group does not send it to Discord, and the other way round.

## How xCloud handles your endpoint

-   **HTTPS and public addresses only.** xCloud refuses URLs that point at a private, internal or reserved address, such as `127.0.0.1`, `10.x.x.x` or `169.254.169.254`. It checks the address when you add the integration and again before every delivery.
-   **No redirects.** A `3xx` answer counts as a failed delivery, so point the integration at the final URL.
-   **Five-second timeout.** Answer quickly with a `2xx` status and do slow work after you respond.
-   **Retries.** A timeout, a `5xx` answer or a `429` (too many requests) is retried. For a `429`, xCloud waits for the `Retry-After` time you send.
-   **Removed destinations.** A `404` or `410` answer means the destination is gone, so xCloud removes the integration. This also happens when you delete the webhook in Discord. Add it again to resume alerts.
-   **Stored encrypted.** The Discord URL, the webhook URL and the signing secret are stored encrypted and are never shown again after you add them.

To stop alerts, click **Disconnect** on the integration in **Settings → Integrations → Notification**.

Still stuck? Contact our [**support team**](https://support.xcloud.host/) for any of your queries.
