Docs
This is a push integration: you register an HTTPS endpoint, we deliver signed JSON to it. There is no polling/pull API in v1 — you receive alerts as they happen.
1. Setup
Open Settings → Notification channels in the dashboard.
Add a Webhook channel and paste your HTTPS receiver URL.
We generate a signing secret and show it once — copy it now; it is never displayed again (rotate it later if you lose it).
Click Send test to deliver a synthetic payload and confirm your receiver parses + verifies it before any real alert fires.
Requirements for your URL:
Must be
https://(plainhttpis rejected — payloads carry account cost data and must not cross the wire in clear text).Should be a stable URL: we send with
allow_redirects=false, so a 3xx is treated as a failure, not followed (this prevents us from being bounced into an internal endpoint).
2. The request
We POST a JSON body to your URL with these headers:
Header
Example
Meaning
Content-Type
application/json
Body is JSON, UTF-8.
User-Agent
watchmy.cloud-notifier/1
Identifies our notifier.
X-Watchmycloud-Api-Version
v1
Contract version (see §6). Mirrors schema_version in the body.
X-Watchmycloud-Timestamp
1748674800
Unix seconds when we signed the request. Use it to reject replays.
X-Watchmycloud-Signature
v1=9f86d08...
HMAC-SHA256 signature (see §4).
Body — schema_version: 1
Request body
Copy lines below
Field reference
Field
Type
Notes
schema_version
integer
Contract major version. 1 today.
alert_event_id
string (UUID)
Unique per triggered alert. Use it for idempotency — retries reuse the same value.
channel_id
string (UUID)
The webhook channel that received this delivery.
alert.rule_type
string
Stable machine code of the rule. One of: daily_anomaly, hourly_spike, daily_threshold, absolute_cap, percent_increase. Branch on this, not on message.
alert.current_value
string
The measured value that tripped the rule. Sent as a string to preserve exact decimals (no float rounding). Parse as a decimal.
alert.threshold_value
string
The configured threshold, same units. String for the same reason.
alert.currency
string
ISO-4217 code. USD today.
alert.triggered_at
string
ISO-8601 UTC (…Z) timestamp of when the alert fired.
alert.message
string
Human-readable, localised to the account owner's language. For display only — do not parse.
alert.account.id
string
The cloud account identifier that tripped the rule (AWS Account ID today).
alert.account.label
string
Display name of that account, or the id if no label was set.
Stability promise: within schema_version: 1 we only ever add fields. Treat unknown fields as forward-compatible and ignore them. We never remove or retype a field without bumping to schema_version: 2 (see §6).
3. How you should respond
Your response
What we do
2xx
Treated as acknowledged. Done.
4xx
Permanent failure — we do not retry (auth/validation is on you). The error surfaces in your Notification History.
5xx, timeout, connection error
Transient — we retry with backoff.
3xx
Treated as failure (we do not follow redirects).
Return 2xx fast. We use a 10-second timeout. If your processing is slow, return 202 immediately and do the work asynchronously on your side.
Retry schedule
Backoff is 1m → 5m → 30m → 2h (4 retries after the first try), up to 5 attempts total / ~2h36m, then the delivery is marked failed and stops. Every attempt reuses the same alert_event_id, so deduplicate on it.
4. Verifying the signature
We sign "<timestamp>.<raw-body>" with HMAC-SHA256 keyed on your channel's signing secret. The header value is v1=<lowercase hex digest>. The format mirrors Stripe/Svix, so off-the-shelf verifiers adapt with one line.
Always verify before trusting a payload, and reject stale timestamps to defeat replays (we recommend a ±5-minute tolerance).
Python
Python
Copy code
Node.js
Node.js
Copy code
Verify against the raw request body bytes, before any JSON re-encoding — re-serialising changes whitespace/key order and breaks the HMAC.
Rotating the secret
If your secret leaks, rotate it from the channel's settings (or POST /api/v1/notification-channels/{id}/rotate-secret). The new secret is shown once; deliveries sign with it immediately, so update your receiver in the same change window.
5. Security model
HTTPS only, enforced at channel creation.
No redirects followed (
allow_redirects=false) — publish a stable URL.We never echo your receiver's response body back into the dashboard or logs (only the status code + reason), so a misconfigured receiver can't be turned into a data-exfiltration channel.
Egress is from our notifier worker, not from an untrusted browser — so we do not block private-IP /
localhostreceiver URLs. You can legitimately point a webhook at internal infra reached over VPN or a tunnel (ngrok, etc.).
6. Versioning policy
Two coordinated axes, one number to track:
Body:
schema_version(integer) + headerX-Watchmycloud-Api-Version.Our URLs: the developer API and machine spec are path-versioned (
/api/v1/…). The human-readable docs live at/docs/apiand always describe the current contract.
Rule: the API path version equals the body schema_version. /api/v1/… ⇔ "schema_version": 1.
What bumps the version: only breaking changes — removing a field, renaming one, or changing its type. Those ship as schema_version: 2 under a new /api/v2/… path, and v1 keeps working through a published deprecation window.
What does NOT bump it: purely additive changes (new optional fields). Build your receiver to ignore unknown fields and you stay compatible across all minor evolutions of a major version.
The receiver URL itself is yours — we cannot encode our contract version in it. That is exactly why the version travels in the body (schema_version) and the X-Watchmycloud-Api-Version header instead.
Changelog
v1 (2026-05) — initial contract:
schema_version 1, HMAC-SHA256 signatures, thealertobject documented above.