Webhooks
Receive real-time notifications when events occur in Moneyline.
Setup
Create a Webhook Endpoint
curl -X POST https://api.moneyline.dev/v1/webhooks \
-H "Authorization: Bearer ml_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/moneyline",
"events": ["submission.processed", "document.parsed", "policy.evaluated"]
}'Dashboard
Navigate to Webhooks in the dashboard to create, edit, and monitor webhook endpoints.
Events
| Event | Description |
|---|---|
submission.created | New submission created |
submission.processing | Processing workflow started |
submission.processed | All documents processed, analytics ready |
submission.failed | Processing failed |
document.uploaded | Document uploaded |
document.classified | Document auto-classified |
document.parsed | Document parsing complete |
document.failed | Document processing failed |
transactions.enriched | Transaction enrichment complete |
policy.evaluated | Policy evaluation complete |
Payload Format
{
"id": "evt_abc123",
"type": "submission.processed",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"id": "sub_xyz789",
"external_id": "deal-001",
"status": "processed",
"documents_count": 3,
"transactions_count": 245
}
}Verification
All webhook requests include an HMAC-SHA256 signature in the X-Moneyline-Signature header.
Verify in Node.js
import crypto from 'crypto';
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}Verify in Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, f"sha256={expected}")Retry Logic
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
After 6 failed attempts, the webhook is disabled and an email notification is sent.
Best Practices
- Return 2xx quickly — Process webhook payloads asynchronously
- Use idempotency — Events may be delivered more than once; use
event.idto deduplicate - Verify signatures — Always validate the
X-Moneyline-Signatureheader - Monitor delivery logs — Check the dashboard for failed deliveries