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 delivers every alert to a URL of your choice, in addition to e-mail. Available on the Pro plan.

Setting it up

  1. Go to Settings → Webhooks.
  2. Paste the URL — your own endpoint, or the incoming-webhook URL from Slack or Teams.
  3. Choose the payload format that matches where the URL points (see below).
  4. Click Send test. A sample alert in the chosen format arrives at the URL, so you can see exactly what a real alert will look like.
  5. Save.

To stop webhooks, clear the URL and save.

Slack and Microsoft Teams

Pick the format that matches the URL:

  • Slack: a Slack message, colour-coded by severity, with the domain, status, days remaining and issuer as fields. Use with a Slack incoming webhook URL.
  • Microsoft Teams: a card with the same information. Use with a Teams incoming webhook URL.
  • CertWatchr JSON: the plain format below, for your own endpoint.

The format matters: Slack and Teams silently ignore fields they do not recognise, so sending the plain JSON to a Slack URL produces an empty message rather than an error. If the test arrives blank, the format is wrong.

The JSON payload

One POST per alert with a JSON body:

{
  "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 is one of:

event Meaning
expiry_warning The certificate is inside your alert threshold.
expiry_critical 7 days or fewer left.
cert_changed The certificate served changed since the last check.
check_error The host could not be checked, even after retries.
ct_new_cert A new certificate for the hostname appeared in a CT log — see CT log monitoring.

Delivery

  • The same once-per-day spacing as e-mail: one alert of each kind per domain per 24 hours.
  • Requests carry Content-Type: application/json and no authentication headers. They are not signed. If you need to make sure requests really come from CertWatchr, put a long random token in the URL path (https://hooks.example.com/cw/<random-32-chars>) and check it on your side.
  • Your endpoint has 10 seconds to answer. Reply with a 2xx status quickly and do any further work afterwards.
  • A failed delivery is not retried. It is recorded on the Alerts page with the HTTP status and error it got, so a broken endpoint is visible rather than silently missed.

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

One webhook per account

The URL and format are set once per account, not per domain. Every payload includes the hostname, so if you route alerts for different customers to different places, do the routing at your endpoint by hostname. See Monitoring certificates for customers and projects.