Webhook Events
All webhook event types, payloads, and signature verification for Clearmargin integrations.
Webhooks send real-time HTTP POST notifications to your server when events occur in Clearmargin. Use them to trigger workflows, sync data, and build custom integrations.
Configuration
Configure webhooks in your dashboard at Settings > Webhooks. For each endpoint you can:
- Set a destination URL that receives POST requests
- Choose which events to subscribe to
- Add a signing secret for HMAC-SHA256 signature verification
- Enable or disable the endpoint without deleting it
Signature Verification
When you configure a signing secret, Clearmargin includes two headers on every delivery for verification:
| Header | Description |
|---|---|
X-Signature | HMAC-SHA256 signature in the format sha256=hex_digest |
X-Webhook-Timestamp | Unix timestamp (seconds) for replay protection |
The signature is computed over {timestamp}.{body}. Verify it like this:
const crypto = require('crypto')
function verifyWebhookSignature(body, timestamp, signature, secret) {
// Reject old timestamps (> 5 minutes) to prevent replay attacks
const now = Math.floor(Date.now() / 1000)
if (Math.abs(now - parseInt(timestamp)) > 300) {
throw new Error('Timestamp too old')
}
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex')
const received = signature.replace('sha256=', '')
if (
!crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(received, 'hex'),
)
) {
throw new Error('Invalid signature')
}
}Always verify signatures
If you configure a signing secret, always verify the signature before processing the payload. Use timingSafeEqual to prevent timing attacks.
Event Types
Proposal Events
| Event | Description |
|---|---|
proposal_sent | A proposal has been sent to a client |
proposal_expiring | A proposal is about to expire |
proposal_accepted | A client has accepted a proposal |
proposal_rejected | A client has rejected a proposal |
proposal_expired | A proposal has passed its expiration date |
Invoice & Payment Events
| Event | Description |
|---|---|
invoice_sent | An invoice has been sent to a client |
invoice_due_soon | An invoice is approaching its due date |
invoice_overdue | An invoice has passed its due date |
invoice_paid | A client has paid an invoice |
invoice_refunded | A payment has been refunded |
invoice_payment_failed | A payment attempt has failed |
invoice_disputed | A payment has been disputed |
Receipt Events
| Event | Description |
|---|---|
receipt_sent | A receipt has been sent to a client |
Payment Schedule Events
| Event | Description |
|---|---|
schedule_deposit_ready | A deposit invoice is ready |
schedule_installment_due | An installment payment is due |
schedule_installment_paid | An installment payment has been received |
schedule_completed | All payments in a schedule are complete |
Document Intelligence Events
| Event | Description |
|---|---|
document_processed | A document has finished AI processing |
document_needs_review | A document has actions requiring manual review |
document_failed | Document processing has failed |
document_action_created | An AI-proposed action is awaiting approval |
document_action_approved | An AI-proposed action has been approved |
Entity Events
| Event | Description |
|---|---|
client_created | A new client has been created |
project_created | A new project has been created |
time_entry_created | A new time entry has been logged |
Inquiry Events
| Event | Description |
|---|---|
inquiry_created | A new inquiry has been submitted |
inquiry_status_changed | An inquiry's status has changed |
inquiry_converted | An inquiry has been converted to a client and project |
Payload Format
All webhook deliveries use this envelope format:
{
"event": "invoice_paid",
"timestamp": "2026-03-14T15:30:00.000Z",
"data": {
"documentType": "invoice",
"documentId": "inv_abc123",
"documentNumber": "INV-0042",
"amount": 2500.00,
"client": {
"id": "client_xyz",
"name": "Acme Corp"
},
"contact": {
"id": "contact_123",
"name": "Jane Smith",
"email": "jane@acme.com"
}
}
}The data object varies by event type but always includes identifiers for the relevant resources. Payment-related events include additional fields:
{
"event": "invoice_paid",
"timestamp": "2026-03-14T15:30:00.000Z",
"data": {
"documentType": "invoice",
"documentId": "inv_abc123",
"documentNumber": "INV-0042",
"amount": 2500.00,
"client": {
"id": "client_xyz",
"name": "Acme Corp"
},
"contact": {
"id": "contact_123",
"name": "Jane Smith",
"email": "jane@acme.com"
},
"paymentMethod": "card",
"last4": "4242",
"brand": "visa"
}
}Retry Policy
Delivery retries
Failed deliveries are retried up to 3 times with exponential backoff (30 seconds, 2 minutes, 10 minutes). Your endpoint must respond with a 200 status code within 10 seconds or the delivery is considered failed. Ensure your handler returns quickly --- offload heavy processing to a background job.