Uptime, SSL and domain monitoring

This module needs no integration: it works without adding code to your application, just by giving it an address.

Monitor types

Type What it checks
Http Status code, response time, redirect chain
Ssl TLS handshake, chain validation, hostname match, self-signed, notAfter
Domain Domain expiry date and registrar (RDAP, falling back to WHOIS)
TcpPort Whether the given port is open
Ping ICMP echo reply and round-trip time (routers, servers and other devices without HTTP)
Dns Whether a DNS record resolves and, optionally, shows the expected value
LinkScan Broken links on the site and http:// resources on HTTPS pages (mixed content)
Heartbeat Whether the ping you send arrives within the expected interval
curl -X POST https://api.errorbird.com/api/v1/monitors \
  -H "Authorization: Bearer <access-token>" \
  -H "content-type: application/json" \
  -d '{
        "projectId": "<project-id>",
        "type": "Http",
        "name": "Main site",
        "target": "https://example.com",
        "intervalSeconds": 60,
        "expectedStatusCodes": "2xx,3xx"
      }'

expectedStatusCodes can be written one by one (200,204) or as a class (2xx).

Ping and DNS

A Ping target is a domain name or an IP address (203.0.113.10). Each round sends at most three echo requests and stops at the first reply: a single lost packet is not an outage.

A Dns monitor queries a record. An HTTP check tests DNS only indirectly and only for the A record; a hijacked domain can point to another server and still return 200, and a deleted MX record silently stops email.

curl -X POST https://api.errorbird.com/api/v1/monitors \
  -H "Authorization: Bearer <access-token>" -H "content-type: application/json" \
  -d '{ "projectId": "<project-id>", "type": "Dns", "name": "Email",
        "target": "example.com", "dnsRecordType": "MX",
        "dnsExpectedValue": "mx.example.com" }'
Field Description
dnsRecordType A, AAAA, CNAME, MX, TXT, NS (default A)
dnsExpectedValue Optional. If empty, the record only has to exist; if set, one of the returned records must be this value. IP addresses are compared regardless of notation, domain names ignoring the trailing dot and letter case; for TXT, part of the text is enough.

Queries bypass caches: a change shows up in the next round without waiting for the record's TTL.

A LinkScan monitor crawls your site's pages and reports two problems:

curl -X POST https://api.errorbird.com/api/v1/monitors \
  -H "Authorization: Bearer <access-token>" -H "content-type: application/json" \
  -d '{ "projectId": "<project-id>", "type": "LinkScan", "name": "Corporate site",
        "target": "https://example.com/" }'
curl "https://api.errorbird.com/api/v1/monitors/<monitor-id>/link-scans/latest" \
  -H "X-API-KEY: ebrd_pub_…"
# { "brokenCount": 2, "mixedContentCount": 1, "pagesCrawled": 37, "findings": [
#   { "kind": "Broken", "url": "https://example.com/campaign-2024", "statusCode": 404,
#     "foundOn": "https://example.com/", "element": "a", "occurrences": 3 }, … ] }

The scan's requests identify themselves as Errorbird-LinkCheck/1.0. It does not connect to internal addresses (see below); leftover links such as http://localhost:3000 on your site are reported as broken.

Internal addresses are not monitored

Checks run from Errorbird's servers. That is why targets resolving to internal or reserved addresses are refused: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 (localhost), 169.254.0.0/16 (cloud metadata service), 100.64.0.0/10 and their IPv6 equivalents. The check fails with the error that the target "resolves to an internal or reserved address".

The check runs after the address is resolved, on the IP that will actually be connected to, and is repeated at every redirect step. A public name resolving to an internal address, or a public page redirecting to one, is refused as well.

To monitor your internal services, install the probe in your own network and allow internal addresses (Errorbird:Probe:AllowPrivateTargets=true). In a single-tenant Errorbird installed on your own server, the same setting exists as Errorbird:Monitoring:AllowPrivateTargets.

The no-false-alarm rule

Checks run from more than one location, and an outage record (Incident) is not opened until at least two locations confirm it.

Situation Monitor Incident Alert
All locations succeed Up — —
Some fail Degraded not opened not sent
All fail Down opened sent when the threshold is reached

If a location's probe cannot be reached, that location's result is ignored; the probe's own failure is not your outage.

The alert is sent not when the incident opens but when consecutive failures reach the rule's threshold (2 rounds by default). A one-off network blip produces no notification. Location setup is described in the repository's docs/coklu-lokasyon.md document (in Turkish).

Heartbeat (cron checks)

Noticing that a nightly job did not run matters more than seeing that it did. Send a ping at the end of your job:

curl -X POST https://api.errorbird.com/api/v1/monitors/<monitor-id>/heartbeat \
  -H "X-API-KEY: ebrd_secret_…"

If no ping arrives within the expected interval plus the grace period, an incident is opened automatically.

SSL warnings

Staged warnings go out 30 / 14 / 7 / 1 days before the certificate expires. A missing chain, a hostname mismatch and a self-signed certificate are a separate warning type: the certificate may not have expired, yet the browser still shows an error.

.tr domains

The expiry date of .tr domains is data most foreign services cannot read. Errorbird tries RDAP first, then falls back to the TRABİS WHOIS server and parses its Turkish and English field labels.

curl "https://api.errorbird.com/api/v1/monitors/<monitor-id>/domain-status" \
  -H "X-API-KEY: ebrd_pub_…"
# { "domain": "metu.edu.tr", "expiresAt": "2027-03-10T00:00:00Z", "daysRemaining": 568, … }

Status page

A public status page can be published for every project:

GET /status/<slug>

It needs no authentication and uses its own rate limit pool: the load on the status page during an outage does not affect your API.

The same address responds in two forms:

Request Response
Opened in a browser (Accept: text/html) A ready-made page for your visitors: overall status, active incidents, scheduled maintenance and the last 90 days of every service
fetch, SDK, curl (Accept: application/json or */*) The same data as JSON — to show it with your own design

?format=json or ?format=html pins the format, ?lang=tr or ?lang=en the language. The page refreshes itself every minute and shows times in the visitor's time zone.

Because it is public, the response is deliberately narrow: the monitored address, the raw error message and location details are not shown. The incident description is a generic text; the raw error ("could not connect to port 10.0.0.5:5432") could leak your internal infrastructure.

The daily history goes back as far as your plan's retention period; days older than that are shown as "no data".

Incident history and notes

The page lists incidents closed in the last 14 days, grouped by day: which service, when, and for how long. In the JSON response this is the pastIncidents field.

You can write a public note for every incident in the dashboard (monitor details → incident history → Add note):

Only the Admin role can write notes, and every change is recorded in the audit trail. The technical error text in the cause column is never published; the note is kept separate from it.

curl -X PUT "https://api.errorbird.com/api/v1/incidents/<incident-id>/public-note" \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{ "note": "The database server was restarted; a permanent fix follows tomorrow." }'
# To remove the note: { "note": null }

Status badge

You can add an SVG badge showing the page's current status to your site, README or help center:

[![Status](https://app.errorbird.com/status/<slug>/badge.svg?lang=en)](https://app.errorbird.com/status/<slug>?lang=en)
Address Shows
/status/<slug>/badge.svg Current status: operational, partial outage, outage, maintenance
/status/<slug>/badge.svg?type=uptime Uptime percentage over the last 30 days (rounded down; "no data" if there is none)

The badge is Turkish by default; ?lang=en makes it English. The language does not follow the visitor: CDNs and GitHub's image proxy cache the badge by address. It is cached for one minute. The badge contains neither the page title nor service names. In the dashboard, the Add a status badge to your site section of the status page card gives you ready-made Markdown and HTML.

Email subscriptions

Visitors can subscribe with their email address using the form at the bottom of the page. Subscribers receive an email when an incident opens and when it closes. These emails are independent of your organization's notification channels; subscribers are notified even if you have defined no channels at all.

To collect subscribers from your own interface, you can send JSON to the same address:

curl -X POST "https://app.errorbird.com/status/<slug>/subscribe" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]", "language": "en" }'
# 202 { "status": "pending_confirmation" }

The response does not reveal whether the address was already subscribed. No second confirmation email is sent to the same address within 10 minutes, and at most 10 requests per hour are accepted from the same IP.

Notification channels

Email, Slack/Discord webhooks, Telegram bots, Microsoft Teams, PagerDuty, Opsgenie / Jira Service Management, signed generic webhooks and an on-call schedule channel that reaches whoever is on call are supported. Repeated notifications for the same event are suppressed (deduplication); when the monitor recovers, the suppression markers are reset and the alert goes out again at the next outage.

Microsoft Teams: in Teams, create a webhook address from the channel's ••• menu with Workflows → the "Send webhook alerts to a channel" template, and add it as a channel. Notifications go out as Adaptive Cards. The old Office 365 connector addresses (webhook.office.com) were retired in May 2026 and are not accepted.

PagerDuty and Opsgenie / Jira Service Management

These two channels differ from chat channels: they are stateful. An outage or log alert opens an incident (or alert) on the platform and runs your on-call schedule; when the monitor recovers or the log rule's condition clears, the same incident closes itself. Matching uses a fixed key per situation (errorbird:incident:…, errorbird:log-rule:…, errorbird:issue:…); a repeated alert for the same outage does not open a new incident. SSL and domain warnings are tied to a single incident per monitor (the warnings at 30, 14 and 7 days update the same incident).

Channel What you need Region
PagerDuty The Integration Key of the service's Events API V2 integration (32 characters) us (default) or eu
Opsgenie / JSM The key of an API integration (GUID format) jsm (default), us or eu
curl -X POST https://api.errorbird.com/api/v1/notification-channels \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{ "organizationId": "<org-id>", "type": "PagerDuty", "name": "On-call",
        "secretToken": "<integration-key>", "region": "eu" }'

Quiet hours

Each channel can have a daily quiet hours window (e.g. 22:00–08:00, in the channel's time zone). Set it with the Quiet hours button on the channel row in the dashboard.

Notification During quiet hours
Outage, missed heartbeat, invalid certificate, recovery Sent immediately
Certificate / domain expiry warning, new or reopened error, error threshold Sent when the window ends

Non-urgent notifications are deferred, not dropped: an SSL warning produced once per threshold would never arrive again if it were dropped. A deferred notification comes with a note giving the real time of the event. For channels watched only during business hours, there is an option to "hold outage and recovery notifications too". A test notification sent from the dashboard ignores quiet hours.

curl -X PUT "https://api.errorbird.com/api/v1/notification-channels/<channel-id>/quiet-hours" \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{ "quietHours": { "start": "22:00", "end": "08:00", "timeZone": "Europe/Istanbul" } }'
# To remove: { "quietHours": null }