Webhooks

Receive real-time event notifications via HTTP callbacks.


Overview

Instead of polling for status updates, register webhook endpoints to receive push notifications when events occur. Overvoid delivers a signed HTTP POST to your endpoint for each event.

Register an Endpoint

curl -X POST https://api.overvoid.io/v1/developer/webhook-endpoints \
-H "Authorization: Bearer cusd_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/cusd",
"event_types": [
"alert.created",
"case.status_changed",
"sar.filed",
"compliance.status_changed"
]
}'

The response includes a signing_secret — save it to verify incoming webhooks.

Event Payload

Each webhook delivery sends a JSON payload:

{
"id": "evt_01HXYZ...",
"type": "alert.created",
"created_at": "2026-03-06T21:00:00Z",
"data": {
"alert_id": "alert_01HXYZ...",
"entity_id": "ent_01HXYZ...",
"rule_id": "rule_structuring",
"severity": "high",
"description": "Possible structuring: 5 transactions totaling $9,800 in 24h"
}
}

Verifying Signatures

Every webhook includes two headers for HMAC verification:

  • x-cusd-signature — HMAC-SHA256 hex digest
  • x-cusd-timestamp — Unix timestamp (seconds)

The signed payload is {timestamp}.{body}. Verify like this:

Python
import hmac, hashlib, time
def verify_webhook(body: bytes, signature: str, timestamp: str, secret: str) -> bool:
# Reject if timestamp is more than 5 minutes old
if abs(time.time() - int(timestamp)) > 300:
return False
expected = hmac.new(
secret.encode(),
f"{timestamp}.".encode() + body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
Node.js
import crypto from "crypto";
function verifyWebhook(body, signature, timestamp, secret) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return false;
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}

Event Types

CategoryEvents
Alertalert.created alert.disposition_set
Casecase.created case.status_changed case.assigned
Filingsar.filed ctr.filed
Entityentity.risk_score_changed
Screeningfincen_314a.match_found adverse_media.detected
Compliancecompliance.status_changed compliance.wallet_flagged compliance.entity_suspended
Transfertransfer.detected transfer.confirmed
Accountaccount.approved account.suspended account.kyb_expired

Example Payloads

Below are example payloads for key compliance event types.

alert.created

Fired when a monitoring rule triggers a new compliance alert.

alert.created
{
"id": "evt_01J8QXYZ...",
"type": "alert.created",
"created_at": "2026-03-24T14:30:00Z",
"data": {
"alert_id": "alert_01J8QXYZ...",
"entity_id": "ent_01HXYZ...",
"rule_id": "rule_structuring",
"rule_name": "Structuring Detection",
"severity": "high",
"description": "Possible structuring: 5 transactions totaling $9,800 in 24h",
"transaction_ids": ["tx_01A...", "tx_01B...", "tx_01C..."],
"created_at": "2026-03-24T14:30:00Z"
}
}

case.status_changed

Fired when an investigation case transitions status (e.g. open to under_review, under_review to escalated).

case.status_changed
{
"id": "evt_01J9ABCD...",
"type": "case.status_changed",
"created_at": "2026-03-24T16:45:00Z",
"data": {
"case_id": "case_01J9ABCD...",
"entity_id": "ent_01HXYZ...",
"previous_status": "open",
"new_status": "under_review",
"assigned_to": "analyst@example.com",
"alert_ids": ["alert_01J8QXYZ..."],
"updated_at": "2026-03-24T16:45:00Z"
}
}

sar.filed

Fired when a Suspicious Activity Report is marked as filed with FinCEN.

sar.filed
{
"id": "evt_01JAEFGH...",
"type": "sar.filed",
"created_at": "2026-03-24T18:00:00Z",
"data": {
"sar_id": "sar_01JAEFGH...",
"case_id": "case_01J9ABCD...",
"entity_id": "ent_01HXYZ...",
"bsa_id": "31000012345678",
"filing_date": "2026-03-24",
"amount_involved": 15000.00,
"activity_start_date": "2026-02-01",
"activity_end_date": "2026-03-20"
}
}

Retry Policy

Failed deliveries (non-2xx response or timeout) are retried with exponential backoff:

AttemptDelay
1st retry5 seconds
2nd retry30 seconds
3rd retry2 minutes
4th retry15 minutes
5th retry1 hour
6th retry4 hours

After 6 failed retries (~5.5 hours), the delivery is marked as permanently failed. You can manually retry from the Webhooks page or via the API.


Notifications (Polling Alternative)

Poll for compliance events instead of receiving webhooks. Useful for integrations that cannot receive inbound HTTP requests or need a simpler integration path.

curl "https://api.overvoid.io/v1/monitoring/notifications?unread_only=false&limit=20" \
-H "Authorization: Bearer cusd_test_YOUR_KEY"

Query parameters:

ParamTypeDescription
unread_onlybooleanIf true, only return unread notifications (default false)
limitintMax results (default 20, max 100)

Response:

{
"notifications": [
{
"id": "ntf_01HXYZ...",
"type": "alert_created",
"title": "CRITICAL: Risk Tier Threshold Breach",
"body": "Phantom Shell Ltd triggered a critical alert...",
"read": false,
"entity_id": "ent_01HXYZ...",
"alert_id": "alt_01HXYZ...",
"case_id": null,
"created_at": "2026-03-25T19:00:00Z"
}
],
"unread_count": 4
}

Notification types:

TypeDescription
alert_createdA monitoring rule triggered a new alert
case_assignedA case was assigned to an investigator
sla_breachAn alert or case exceeded its SLA deadline
sar_filedA SAR was filed with FinCEN
entity_status_changedAn entity's compliance status changed
screening_completeA sanctions or adverse media screening finished

Mark Notification as Read

Mark a single notification as read to track which events have been processed by your integration.

curl -X POST https://api.overvoid.io/v1/monitoring/notifications/{notification_id}/read \
-H "Authorization: Bearer cusd_test_YOUR_KEY"

Best Practices

  • Always verify signatures — Never trust webhook payloads without HMAC verification.
  • Respond quickly — Return a 2xx within 5 seconds. Process the event asynchronously if needed.
  • Handle duplicates — Use the id field for idempotency. The same event may be delivered more than once.
  • Use HTTPS — Webhook URLs must use HTTPS. HTTP endpoints are rejected.