Alerts via Slack, Teams and webhook

Besides email, failure and recovery alerts can go to Slack, Microsoft Teams or your own system (webhook). Each channel gets exactly the same alerts as email: one when something starts failing and another once it has been working for several minutes in a row. Failures that come back within that window are part of the same alert.

Available on the Basic and Professional plans (up to 5 active channels). A company administrator sets them up in the Channels tab, with two-factor authentication turned on.

Slack

  1. In Slack, create an app (or use an existing one) and turn on Incoming Webhooks.
  2. Pick the channel and copy the address, which starts with https://hooks.slack.com/services/.
  3. In Channels, choose Slack, give it a name, paste the address and press Add. Use Send test to check it.

Microsoft Teams

Teams receives alerts through the Workflows app (the old Microsoft 365 connectors are being retired).

  1. In the Teams channel, open More options (…) → Workflows and choose the Send webhook alerts to a channel template.
  2. Save the flow and copy the address it gives you.
  3. In Channels, choose Microsoft Teams and paste the address.

Webhook to your system

We send a POST with a JSON body to an https address of yours. Only public addresses are accepted, we don't follow redirects and we wait up to 10 seconds for the response. Reply with any 2xx code. If you reply 408, 429 or 5xx, or don't reply in time, we retry with increasing waits for 2 hours; any other 4xx stops the retries for that alert.

Headers:

  • Content-Type: application/json
  • X-Tecdex-Event: the event type.
  • X-Tecdex-Delivery: the alert identifier. It's the same on every retry: use it to avoid processing the same alert twice.
  • X-Tecdex-Signature: the signature, in the form t=<unix seconds>,v1=<signature>.

Example failure alert:

{
  "version": 1,
  "id": "6f1c2d1e-9a7b-4c51-8f0e-2b1f5c3a9d10",
  "event": "failure",
  "account": "acme",
  "system": "Website",
  "title": "ACME · Website is failing",
  "text": "ACME · Website is failing\nWhat is failing: Availability\nSince: 26-10-2026 09:00 (UTC-03:00)\n…",
  "language": "en",
  "url": "https://monitoreo.tecdex.net/c/acme/incidentes",
  "sent_at": "2026-10-26T12:00:05Z",
  "since": "2026-10-26T12:00:00Z",
  "recovery_wait_minutes": 10,
  "checks": [{"name": "Availability", "detail": "No response within the time limit"}]
}

Fields:

  • version: format version (currently 1). We may add new fields without changing the version; we never change or remove existing ones.
  • id: unique alert identifier (same as the X-Tecdex-Delivery header).
  • event: failure (started failing), failure_update (another check of the same system is failing too), recovery (back to normal) or test (test message).
  • account: your company identifier. system: the monitored system (empty for test).
  • title and text: the alert written in the channel's language (language: es, en or pt).
  • url: where to see the details in the portal (may be null). sent_at: when we sent it (UTC).
  • For failure and failure_update: since (failing since, UTC), recovery_wait_minutes (minutes in a row working before the recovery alert) and checks with name and detail (may be null).
  • For recovery: started_at and ended_at (failure period, UTC), duration_seconds, failures (failures in the period) and checks with name and failures.

Verifying the signature

When you create the webhook we show its secret only once (it starts with whsec_). Store it in your system. If you lose it or it leaks, use Rotate secret: the old one stops working.

Compute HMAC-SHA256 with the secret over the text <t>.<body> (the body exactly as received, without re-serializing the JSON) and compare it with v1. Also reject alerts whose t differs from your clock by more than 5 minutes, so nobody can replay an old one. Python example:

import hashlib
import hmac
import time


def verify(secret, header, body, ahora=None, tolerance=300):
    """secret: the whsec_… · header: X-Tecdex-Signature · body: bytes exactly as received."""
    try:
        parts = dict(p.split("=", 1) for p in header.split(","))
        t = int(parts["t"])
    except (ValueError, KeyError):
        return False
    if abs((ahora or time.time()) - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Sign in · Create account