Webhooks
Receive signed HTTP POST requests for every change event and pipe them into any system.
Org Webhooks (Business plan) let you receive a signed HTTP POST to your own endpoint every time a change is confirmed in your organization. Configure them in Dashboard → Webhooks.
Setup
Go to Dashboard → Webhooks and click New Webhook. Requires a Business plan.
| Field | Required | Notes |
|---|---|---|
| Name | Yes | Label shown in the dashboard |
| Endpoint URL | Yes | Publicly reachable HTTPS URL |
| Active | — | Toggle delivery on/off without deleting |
| Filter event types | — | Leave empty to receive all events |
localhost won't work in production. Use ngrok or a similar tunnel for local testing.
The payload
ArtistGuard POSTs application/json:
{
"id": "chg_abc123",
"organization_id": "org_xyz",
"monitored_artist_id": "art_...",
"artist_name": "Sophie",
"change_type": "TRACKS_WENT_OFFLINE",
"severity": "critical",
"summary": "2 track(s) on \"Album Name\" went offline",
"confirmed": true,
"detected_at": "2026-03-07T10:32:00Z",
"confirmed_at": "2026-03-07T10:37:00Z",
"payload": { }
}
severity is always "critical", "warning", or "info".
Request headers
| Header | Value |
|---|---|
Content-Type |
application/json |
X-ArtistGuard-Signature |
sha256=<hmac> |
X-ArtistGuard-Event |
change |
Signature verification
The signature is HMAC-SHA256 of the raw JSON body keyed with your signing secret. Always verify it before processing.
Node.js
import { createHmac } from 'crypto'
app.post('/webhook', express.json(), (req, res) => {
const sig = req.headers['x-artistguard-signature']
const expected = 'sha256=' + createHmac('sha256', process.env.SECRET)
.update(JSON.stringify(req.body))
.digest('hex')
if (sig !== expected) return res.status(401).end()
const { artist_name, change_type, summary } = req.body
console.log(`[${change_type}] ${artist_name}: ${summary}`)
res.sendStatus(200)
})
Python
import hmac, hashlib, json
def verify(body: bytes, secret: str, header: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)
Rotating the secret
Click Rotate on a webhook card in the dashboard. The new secret is shown once, so copy it before closing the modal. The old secret is invalidated immediately.
Responding
Return any 2xx within 10 seconds. There are no automatic retries. Queue events on receipt and process asynchronously if your handler might be slow.
All change_type values
Availability
| Value | Severity | What happened |
|---|---|---|
RELEASE_WENT_OFFLINE |
critical | Entire release is no longer playable |
RELEASE_CAME_ONLINE |
info | Release is playable again |
TRACKS_WENT_OFFLINE |
critical | Multiple tracks on a release went offline (grouped) |
TRACKS_CAME_ONLINE |
info | Multiple tracks came back online (grouped) |
TRACK_WENT_OFFLINE |
critical | A single track became unplayable |
TRACK_CAME_ONLINE |
info | A single track is playable again |
Releases & Tracks
| Value | Severity | What happened |
|---|---|---|
NEW_RELEASE_DETECTED |
warning | New album, EP, or single appeared |
NEW_TRACKS_DETECTED |
warning | Multiple tracks added to a release (grouped) |
NEW_TRACK_DETECTED |
info | A single track added to an existing release |
RELEASE_LABEL_CHANGED |
warning | Label name changed on a release |
RELEASE_ARTISTS_CHANGED |
warning | Artist credits on a release changed |
RELEASE_IMAGE_CHANGED |
info | Cover art updated |
Track metadata (grouped)
Grouped changes are emitted when the same change affects multiple tracks in one monitoring cycle, one event per release instead of one per track.
| Value | Severity | What happened |
|---|---|---|
TRACKS_ARTISTS_CHANGED |
warning | Artist credits changed on multiple tracks |
TRACKS_ISRC_CHANGED |
warning | ISRC replaced on multiple tracks |
TRACKS_EXPLICIT_CHANGED |
info | Explicit flag changed on multiple tracks |
Track metadata (per-track)
| Value | Severity | What happened |
|---|---|---|
TRACK_ARTISTS_CHANGED |
warning | Artist credits changed on a single track |
TRACK_ISRC_CHANGED |
warning | ISRC replaced on a single track |
TRACK_EXPLICIT_CHANGED |
info | Explicit flag toggled on a single track |
Artist stats
| Value | Severity | What happened |
|---|---|---|
FOLLOWERS_CHANGED |
info | Follower count shifted |
MONTHLY_LISTENERS_CHANGED |
info | Monthly listeners shifted |
WORLD_RANK_CHANGED |
info | World rank moved |
ARTIST_IMAGE_CHANGED |
info | Artist profile picture updated |
ARTIST_VERIFIED_CHANGED |
info | Verified badge added or removed |
ARTIST_BIOGRAPHY_CHANGED |
info | Biography edited |
TOP_CITIES_CHANGED |
info | Top listener cities shifted |
TOP_TRACKS_CHANGED |
info | Top tracks ranking changed |
Testing locally
Your endpoint must be publicly reachable. Two easy options for local development:
ngrok
ngrok http 3000
Cloudflare Tunnel (free, no account required for quick tunnels)
cloudflared tunnel --url http://localhost:3000
Both give you a public HTTPS URL to paste as the endpoint. Use the Test button on a webhook card to send a signed delivery immediately. Pick an event type to preview the sample payload before firing.
Replace with a real URL before going to production.