Webhooks
Send certificate alerts to any URL you control, as JSON or formatted natively for Slack and Microsoft Teams. Expiry, health and CT alerts all included.
A webhook sends every alert to a URL of your choice, in addition to e-mail — a Slack channel, a Teams chat, or your own system. Available on Pro, with up to 10 webhooks, and on Team, with up to 25.
Set one up
- Go to Settings → Webhooks.
- Under Add a webhook, paste the URL and, if you like, give it a name.
- Choose the payload format that matches the URL (see below).
- Click Add webhook.
- Click Send test. A sample alert arrives, so you see exactly what a real one looks like.
Repeat for every place alerts should go. Next to each webhook:
- Edit changes its URL, format or name.
- Switching off Send alerts to this webhook pauses it and keeps its settings.
- Delete removes it.
The delivery log below shows which webhook received each alert and what it answered.
Slack, Teams, Discord, Mattermost and Telegram
| Format | Use with |
|---|---|
| Slack | A Slack incoming webhook URL. Colour-coded message with domain, status, days left and issuer. |
| Microsoft Teams | A Teams incoming webhook URL. A card with the same information. |
| Discord | A channel webhook (Server settings → Integrations → Webhooks). |
| Mattermost | A Mattermost incoming webhook. |
| Telegram | https://api.telegram.org/bot<token>/sendMessage?chat_id=<chat id> for a bot created with @BotFather and added to the chat. The URL contains the bot token — keep it secret. |
| CertWatchr JSON | Your own endpoint. Format below. |
The test arrives empty? The format is wrong. Slack, Teams and the others silently ignore fields they do not know, so plain JSON sent to a Slack URL shows up as an empty message.
The JSON payload
One POST per alert:
{
"event": "expiry_warning",
"hostname": "example.com",
"days_remaining": 12,
"cert_expiry": "2026-07-31T23:59:59",
"status": "warning"
}
A ct_new_cert alert adds a ct object with new_count and a certs list (up to ten), each
with serial_number, issuer, common_name, not_before, not_after and observed_at.
event |
Meaning |
|---|---|
expiry_warning |
Inside your alert threshold. |
expiry_critical |
7 days or fewer left. |
renewal_overdue |
A short-lived certificate is well past its automatic-renewal point. |
caa_blocks_issuer |
DNS CAA would refuse the certificate's CA at the next renewal. |
registration_expiring |
The domain expires at its registrar within 30 days. |
endpoint_mismatch |
The servers behind the hostname serve different certificates. |
cert_changed |
A different certificate is served than at the last check. |
check_error |
The host could not be checked, even after retries. |
ct_new_cert |
A new certificate appeared in a CT log — see CT log monitoring. |
Delivery
- Same spacing as e-mail: one alert of each kind per domain per 24 hours; per week for
renewal_overdueandcaa_blocks_issuer. - 10 seconds to answer. Reply with a 2xx status straight away and do the work afterwards.
- No retries. A failed delivery is listed under Alerts with its HTTP status and error.
- No signature. Requests carry
Content-Type: application/jsonand no authentication. To make sure a request comes from CertWatchr, put a long random token in the URL path (https://hooks.example.com/cw/<random-32-chars>) and check it.
A minimal receiver
from flask import Flask, request
app = Flask(__name__)
@app.post("/cw")
def receive():
data = request.json
if data["event"] in ("expiry_warning", "expiry_critical"):
notify_team(data["hostname"], data["days_remaining"])
return "", 200
Several webhooks
- Every webhook gets every alert, each in its own format.
- A webhook that was down when an alert went out gets it at the next check. One that already has it does not get it twice.
- Webhooks belong to the account, not to a domain. To send each customer's alerts somewhere else, route them in your own endpoint by hostname. See Monitoring certificates for customers and projects.
- On a team, the owner's webhooks receive the team's alerts. Members get them by e-mail.
- Leaving Pro or Team: webhooks stop receiving alerts but stay saved, ready for when you upgrade. Moving from Team to Pro keeps the oldest 10 working.