Docs Navigation
Core Telemetry

Heartbeat Telemetry

Monitor can receive lightweight application-level heartbeats from your bot. This provides a second liveness signal alongside Discord presence and helps the dashboard identify processes that have stopped reporting.

Discord Presence

Shows whether the bot's Discord gateway connection is online. Presence alone cannot prove that your application code is still executing normally. Outage alerts and downtime records are driven by presence.

Monitor Heartbeat

Confirms that your bot is actively running application code and can make outbound HTTP requests to Monitor. Heartbeat telemetry is informational only.

Keep your token secret

The heartbeat token is a credential. Store it in your hosting environment and never commit it to GitHub or expose it in browser code.

Step 1 — Generate your token

  1. Open the Monitor dashboard for your server.
  2. Select your monitored bot.
  3. Locate the Heartbeat Telemetry section.
  4. Click Get Heartbeat Token and copy the token shown once.
  5. Store it as MONITOR_HEARTBEAT_TOKEN in the monitored bot's hosting environment.
  6. Restart or redeploy the monitored bot so it can read the new environment variable.
bash
MONITOR_HEARTBEAT_TOKEN=mb_live_your_secret_token_here

Step 2 — Install aiohttp

Monitor's example uses aiohttp for a lightweight async HTTP client.

bash
pip install -U aiohttp
Non-Sharded Bot

Step 3 — Basic Heartbeat Integration

For non-sharded bots, simply submit the required bot_id and interval_seconds. Non-sharded bots omit the shards field entirely.

python
import os
import logging

import aiohttp
from discord.ext import commands, tasks

log = logging.getLogger("monitor.heartbeat")

MONITOR_HEARTBEAT_URL = "https://botmonitor.pages.dev/api/public/heartbeat"


class MonitorHeartbeat(commands.Cog):
    """Basic heartbeat for non-sharded bots (omits the shards array)."""

    def __init__(self, bot: commands.Bot):
        self.bot = bot
        self.session: aiohttp.ClientSession | None = None

    async def cog_load(self):
        self.session = aiohttp.ClientSession(
            timeout=aiohttp.ClientTimeout(total=10)
        )
        self.heartbeat_loop.start()

    async def cog_unload(self):
        self.heartbeat_loop.cancel()
        if self.session and not self.session.closed:
            await self.session.close()

    @tasks.loop(seconds=60)
    async def heartbeat_loop(self):
        token = os.getenv("MONITOR_HEARTBEAT_TOKEN")
        if not token or not self.bot.user or not self.session:
            return

        payload = {
            "bot_id": str(self.bot.user.id),
            "interval_seconds": 60,
        }

        try:
            async with self.session.post(
                MONITOR_HEARTBEAT_URL,
                headers={"Authorization": f"Bearer {token}"},
                json=payload,
            ) as response:
                if response.status != 200:
                    log.warning(
                        "Monitor heartbeat failed: HTTP %s",
                        response.status,
                    )
        except Exception:
            log.exception("Monitor heartbeat request failed")

    @heartbeat_loop.before_loop
    async def before_heartbeat(self):
        await self.bot.wait_until_ready()


async def setup(bot: commands.Bot):
    await bot.add_cog(MonitorHeartbeat(bot))
Multi-Shard Bots

Shard Telemetry

Automatic Detection

Shard telemetry does not have its own dashboard toggle. It becomes available automatically when your bot includes the optional shards array in its heartbeat request.

Monitor does not determine shard health independently; the monitored bot reports the shard state. Shard telemetry is informational only and will not trigger the normal bot offline/outage system, downtime records, grace periods, or outage pings.

Exact Shard JSON Payload

When reporting shards, include the shards array inside your standard heartbeat payload:

json
{
  "bot_id": "123456789012345678",
  "interval_seconds": 60,
  "shards": [
    {
      "shard_id": 0,
      "status": "healthy",
      "latency_ms": 42,
      "guild_count": 150
    },
    {
      "shard_id": 1,
      "status": "healthy",
      "latency_ms": 51,
      "guild_count": 147
    }
  ]
}

Supported Shard Fields

FieldTypeRequiredDescription & Validation
shard_idintegerRequiredZero-indexed shard ID (integer from 0 to 1000). Must be unique within the payload.
statusstringRequiredMust be one of: healthy, degraded, or disconnected.
latency_msinteger | nullOptionalRound-trip WebSocket latency in milliseconds (0 to 10000). Pass null if disconnected.
guild_countinteger | nullOptionalNumber of Discord servers assigned to this shard (0 to 10,000,000). Pass null if unknown.

Supported Shard Statuses

healthy

Shard WebSocket is fully connected, authenticated, and handling Discord gateway heartbeats normally.

degraded

Shard is experiencing high latency, reconnect loops, backoff delays, or partial packet loss.

disconnected

Shard gateway connection dropped, failed to resume, or is currently closed.

Sharded Bot

AutoShardedBot discord.py Example

Unlike basic heartbeats, this sharded example inspects bot.latencies and guild assignments to build and transmit the shards array.

python
import os
import logging

import aiohttp
from discord.ext import commands, tasks

log = logging.getLogger("monitor.heartbeat")

MONITOR_HEARTBEAT_URL = "https://botmonitor.pages.dev/api/public/heartbeat"


class MonitorShardedHeartbeat(commands.Cog):
    """Sharded heartbeat for AutoShardedBot (includes the optional shards array)."""

    def __init__(self, bot: commands.AutoShardedBot):
        self.bot = bot
        self.session: aiohttp.ClientSession | None = None

    async def cog_load(self):
        self.session = aiohttp.ClientSession(
            timeout=aiohttp.ClientTimeout(total=10)
        )
        self.heartbeat_loop.start()

    async def cog_unload(self):
        self.heartbeat_loop.cancel()
        if self.session and not self.session.closed:
            await self.session.close()

    @tasks.loop(seconds=60)
    async def heartbeat_loop(self):
        token = os.getenv("MONITOR_HEARTBEAT_TOKEN")
        if not token or not self.bot.user or not self.session:
            return

        shards = []
        for shard_id, latency in self.bot.latencies:
            shard_guilds = sum(
                1
                for guild in self.bot.guilds
                if guild.shard_id == shard_id
            )

            status = "healthy" if latency >= 0 else "disconnected"
            latency_ms = round(latency * 1000) if latency >= 0 else None

            shards.append(
                {
                    "shard_id": shard_id,
                    "status": status,
                    "latency_ms": latency_ms,
                    "guild_count": shard_guilds,
                }
            )

        payload = {
            "bot_id": str(self.bot.user.id),
            "interval_seconds": 60,
            "shards": shards,
        }

        try:
            async with self.session.post(
                MONITOR_HEARTBEAT_URL,
                headers={"Authorization": f"Bearer {token}"},
                json=payload,
            ) as response:
                if response.status != 200:
                    log.warning(
                        "Monitor heartbeat failed: HTTP %s",
                        response.status,
                    )
        except Exception:
            log.exception("Monitor heartbeat request failed")

    @heartbeat_loop.before_loop
    async def before_heartbeat(self):
        await self.bot.wait_until_ready()


async def setup(bot: commands.AutoShardedBot):
    await bot.add_cog(MonitorShardedHeartbeat(bot))

Endpoint Specification & Response Codes

The public heartbeat endpoint accepts POST requests with JSON bodies.

POST https://botmonitor.pages.dev/api/public/heartbeat

Authorization: Bearer <MONITOR_HEARTBEAT_TOKEN>

Content-Type: application/json

StatusResponse PayloadDescription
200 OK{"ok": true, "bot_id": "...", "status": "healthy", "timestamp": "..."}Heartbeat and shard telemetry successfully recorded.
400 Bad Request{"error": "<validation message>"}Invalid JSON body, missing bot_id, interval outside 10–300s, or invalid shard fields.
401 Unauthorized{"error": "Missing heartbeat authentication token"}Missing or malformed Authorization header or token.
403 Forbidden{"error": "Heartbeat token is invalid or has been revoked"}Token hash not found in database, token was revoked, or token belongs to a different bot_id.
429 Too Many Requests{"error": "Rate limit exceeded. Heartbeat interval should be 30-60s."}Heartbeat sent faster than the 3-second rate limit threshold.

Health States

HEALTHY

Heartbeat age is at or below 1.5× the configured interval. A token exists and the bot is reporting normally.

LATE

Heartbeat age is above 1.5× but at or below 2.5× the configured interval.

ZOMBIE

Heartbeat age has exceeded 2.5× the configured interval. Zombie status is dashboard telemetry only; it does not create an outage, downtime record, or public outage ping by itself.