Appearance
Handshake: Leads Enrichment Webhooks
Sequence (end-to-end)
webhook payloads
Headers
X-Webhook-Signature: HMAC-SHA256 over bodyContent-Type:application/json
Shared fields
json
{
"event": "SUCCESS-SAVE | BATCH | COMPLETE_BATCH",
"brandId": "string",
"userId": "string",
"jobId": "string",
"requestId": "string",
"timestamp": "2025-09-04T15:32:10.123Z"
}1) SUCCESS-SAVE
fired once raw rows are stored so the user can see uploaded leads immediately (no loading limbo)
json
{
"event": "SUCCESS-SAVE",
"brandId": "br_123",
"userId": "usr_789",
"jobId": "job_456",
"requestId": "req_001",
"timestamp": "2025-09-04T15:32:10.123Z",
"stats": {
"rowsAccepted": 542,
"rowsRejected": 7
},
"preview": {
"sampleLeadIds": ["lead_101", "lead_102", "lead_103"]
}
}Core app behavior: immediately render the uploaded (raw) leads list.
2) BATCH
progress heartbeat per processed batch
json
{
"event": "BATCH",
"brandId": "br_123",
"userId": "usr_789",
"jobId": "job_456",
"requestId": "req_132",
"timestamp": "2025-09-04T15:33:40.555Z",
"batch": {
"batchId": "b_05",
"batchIndex": 5,
"totalBatches": 12,
"itemsProcessed": 50,
"itemsSucceeded": 47,
"itemsFailed": 3,
"errorSamples": [
{ "row": 221, "code": "VALIDATION_EMAIL", "message": "Invalid email" },
{ "row": 229, "code": "RATE_LIMIT", "message": "Provider throttled" }
]
},
"cumulative": {
"processed": 250,
"succeeded": 234,
"failed": 16
}
}Core app behavior: update progress bar & counts in real time.
3) COMPLETE-BATCH (your “COMPLETE_BATCH”)
final summary; safe to notify the user
json
{
"event": "COMPLETE_BATCH",
"brandId": "br_123",
"userId": "usr_789",
"jobId": "job_456",
"requestId": "req_199",
"timestamp": "2025-09-04T15:40:22.900Z",
"result": {
"totalRows": 549,
"totalBatches": 12,
"succeeded": 520,
"failed": 29,
"durationMs": 492_000
}
}Core app behavior: mark job complete and send user notification (push/email).
minimal payload compatible with your original spec
If you want the lean version exactly as you wrote:
json
{
"event": "SUCCESS-SAVE | BATCH | COMPLETE_BATCH",
"brandId": "string",
"userId": "string"
}