Alerts

Send Referalli's failure signals (failing webhooks, held payments, failed payouts and credits, expiring keys) to Watchtower, Slack or your own signed webhook.

Embed
Preview of the card
<a href="https://referalli.com/docs/alerts"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/f1872b9d-14d1-4923-821b-6df3f3d2b5c2/social-card.svg?v=3e7ae33eec8b" alt="Alerts" width="600" height="315"></a>

Some events mean something needs fixing: a webhook endpoint keeps failing, a payment was held, a payout failed. The org owner gets these by email and your webhook endpoints can subscribe to them. Alert destinations send them to the place your team watches as well: Watchtower (or any PagerDuty-compatible pager), a Slack channel, or your own signed webhook.

Topic

Severity

When

webhook_endpoint.failing

warning

An endpoint has failed every delivery for 1 hour, and again at 24 hours

webhook_endpoint.disabled

critical

An endpoint was disabled after 72 hours of failures

billing_event.held

warning

A payment was held for review (over the limit, dated ahead, unknown currency)

payout.failed

critical

The payout provider refused or failed a payout

stripe_credit.failed

warning

A Stripe balance credit or debit gave up

api_key.expiring

info (warning on the last day)

An API key expires in 30, 7 or 1 days

Each destination has a minSeverity (default warning) and, optionally, topics to receive only some of them. Alerts go out once, as the event happens; the outcome is stored on the destination (lastSentAt, lastError). Owners and admins manage them.

Add a destination

curl -X POST https://referalli.com/v1/org/alerts \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"type": "slack", "url": "https://hooks.slack.com/services/T000/B000/XXXX", "minSeverity": "warning"}'

Field

type

watchtower, slack or webhook.

url

Watchtower: your edge's base URL. Slack: the incoming-webhook URL (on hooks.slack.com). Webhook: your endpoint. Must be a public https URL.

routingKey

Watchtower only: an ingestion key with scope ingest:alerts (wti_…).

minSeverity

info, warning or critical.

topics

Only these topics; leave it out for all.

name

A label for the dashboard.

URLs and keys are write-only: GET /v1/org/alerts shows each destination's host, filters and last outcome, plus the alerting topics with their severities. PATCH /v1/org/alerts/{id} changes name, minSeverity, topics or enabled; to change a URL or key, delete the destination and add it again. POST /v1/org/alerts/{id}/test sends one alert.test and answers { "ok": true } or the error.

Watchtower

Watchtower's edge takes PagerDuty Events API v2 at POST /v2/enqueue, and Referalli sends exactly that, so a Referalli alert becomes an external Watchtower alert that goes through your routing tree, muting and on-call like any other.

  1. In Watchtower, create an ingestion key with scope ingest:alerts for the project that should own these alerts.

  2. Add the destination:

    curl -X POST https://referalli.com/v1/org/alerts \
      -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
      -d '{"type": "watchtower", "url": "https://watchtower.example.com", "routingKey": "wti_...", "minSeverity": "warning", "name": "On-call"}'
  3. POST /v1/org/alerts/{id}/test, and check the alert in Watchtower.

Each alert is {"routing_key", "event_action": "trigger", "dedup_key", "payload": {"summary", "source": "referalli", "severity", "component", "class", "timestamp", "custom_details"}}. class is the topic and custom_details the event's data. dedup_key is referalli:<org>:<topic>:<subject> (the endpoint, payment, payout, credit or key id), so the 1-hour and 24-hour webhook_endpoint.failing signals for one endpoint fold into one Watchtower alert. Referalli sends no resolve; resolve it in Watchtower once fixed (a webhook_endpoint.recovered webhook tells you when deliveries succeed again).

Also monitor /v1/integration-health

Alerts cover what happens. To also catch what is wrong but quiet (a billing connection that never received an event, no payout connection), point a Watchtower HTTP monitor at the health endpoint, which answers ok: false while any check fails:

{
  "kind": "http",
  "name": "Referalli integration health",
  "interval_s": 300,
  "config": {
    "url": "https://referalli.com/v1/integration-health",
    "method": "GET",
    "headers": { "Authorization": "Bearer {{secret.REFERALLI_API_KEY}}" }
  },
  "assertions": ["[STATUS] == 200", "[BODY].ok == true"]
}

Use a read-only API key and store it as a Watchtower secret. The failing check's fix text in the response says what to do.

Slack

Create an incoming webhook for the channel (Slack app → Incoming Webhooks) and add it as type: "slack". Each alert is one message: severity, summary and topic.

Your own webhook

type: "webhook" POSTs JSON to your URL:

{
  "type": "alert",
  "topic": "payout.failed",
  "severity": "critical",
  "summary": "Payout pay_01J… failed: account closed",
  "dedupKey": "referalli:org_01J…:payout.failed:pay_01J…",
  "orgId": "org_01J…",
  "eventId": "evt_01J…",
  "createdAt": "2026-11-11T10:00:00.000Z",
  "data": { "payoutId": "pay_01J…", "partnerId": "ptr_01J…", "reason": "account closed" }
}

The response to POST /v1/org/alerts carries signingSecret (whsec_…) once. Deliveries are signed like webhook endpoints: verify webhook-signature over webhook-id.webhook-timestamp.body with any Standard Webhooks library. Answer 2xx; an alert is not retried.

Updated