Alerts
Send Referalli's failure signals (failing webhooks, held payments, failed payouts and credits, expiring keys) to Watchtower, Slack or your own signed webhook.
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 |
|---|---|---|
| warning | An endpoint has failed every delivery for 1 hour, and again at 24 hours |
| critical | An endpoint was disabled after 72 hours of failures |
| warning | A payment was held for review (over the limit, dated ahead, unknown currency) |
| critical | The payout provider refused or failed a payout |
| warning | A Stripe balance credit or debit gave up |
| 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 | |
|---|---|
|
|
| Watchtower: your edge's base URL. Slack: the incoming-webhook URL (on |
| Watchtower only: an ingestion key with scope |
|
|
| Only these topics; leave it out for all. |
| 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.
In Watchtower, create an ingestion key with scope
ingest:alertsfor the project that should own these alerts.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"}'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.