webhooks-guide
Webhooks Integration Guide
Overview
eFICA supports outgoing webhooks so external systems can receive notifications when key events occur.
Design goals:
- At-least-once delivery (retries on transient failures)
- Receiver-side idempotency via a stable
eventId(duplicates are possible) - Full audit trail of deliveries and attempts
Webhook configuration (public API)
Webhook subscriptions are managed via the public API (admin-only):
Each subscription is scoped to the authenticated partner and contains:
- url: destination webhook endpoint (HTTPS recommended; required in production environments)
- eventTypes: which event types to receive
- enabled: toggle deliveries on/off
- secret: shared signing secret (stored encrypted at rest; only returned on creation)
Receiver endpoint expectations
All webhook deliveries are sent as:
- Method:
POST - Content-Type:
application/json - Body: a JSON object (see event payloads below)
Headers and signature (HMAC SHA-256)
Required headers:
Content-Type: application/jsonX-Efica-Signature: t=<unixSeconds>,v1=<hexHmac>
Additional headers:
X-Efica-Event-Id: <eventId>X-Efica-Event-Type: <eventType>User-Agent: efica-webhooks/2.0
Signature computation:
- Use the exact JSON string sent as the request body (no pretty-print / extra whitespace).
- Compute:
signedPayload = "<unixSeconds>.<rawBody>"v1 = HMAC_SHA256_HEX(secret, signedPayload)
- Send:
X-Efica-Signature: t=<unixSeconds>,v1=<hexHmac>
Receiver verification recommendations:
- Reject if timestamp skew is too large (e.g. > 5 minutes) to mitigate replay.
- Compare HMAC in constant time.
- Store and dedupe on
eventId(at-least-once delivery can produce duplicates).
Event types
1) customer_portal.application_status_changed
Emitted when a Customer Portal onboarding application changes status (e.g. created, emailed, opened, completed, expired).
Example payload (schemaVersion 1):
{
"schemaVersion": 1,
"eventId": "d46b29c4-0e39-4a86-8b4b-7f1a0e9b8d2c",
"eventType": "customer_portal.application_status_changed",
"occurredAt": "2026-01-25T12:00:00.000Z",
"sourceEntity": "onboarding",
"sourceId": "123",
"externalID": "889912d3-xxxx-xxxx-xxx-b77bb61231c1",
"onboardingId": "123",
"status": "Client Pending",
"previousStatus": "Email Sent",
"eficaIndividualGuid": null,
"data": {
"reason": "optional",
"expiresAt": "2026-02-01T12:00:00.000Z"
}
}
2) individual.updated
Emitted when the eFICA Individual record changes in a way that may cause drift in external systems.
Routing requirement:
externalIDandonboardingIdare always included so external systems can route updates correctly even when a person has multiple applications.
Example payload (schemaVersion 1):
{
"schemaVersion": 1,
"eventId": "7c44c2c1-2a0f-4c6f-bc47-3f8a9e5a1f2b",
"eventType": "individual.updated",
"occurredAt": "2026-01-25T12:05:00.000Z",
"sourceEntity": "ficaIndividual",
"sourceId": "123e4567-e89b-12d3-a456-426614174000",
"externalID": "889912d3-xxxx-xxxx-xxx-b77bb61231c1",
"onboardingId": "123",
"eficaIndividualGuid": "123e4567-e89b-12d3-a456-426614174000",
"data": {
"ficaStatus": "Approved",
"riskDescription": "High Risk",
"amlScreeningResults": { "any": "json" },
"clientValidationResults": { "any": "json" },
"changedFields": [
"riskDescription",
"memberCheckResults",
"kycResult"
]
}
}
Response codes and retries
- Success: Any
2xxresponse marks the delivery as successful. - Retryable failures: Network errors/timeouts, and HTTP
408,429,500–599. - Non-retryable failures: HTTP
400–499excluding408/429(treated as permanent until configuration is fixed).
Retry schedule (example):
- attempt 1: immediate
- attempt 2: +1 minute
- attempt 3: +5 minutes
- attempt 4: +15 minutes
- attempt 5: +60 minutes
- attempt 6: +6 hours
- attempt 7: +24 hours
- then: mark failed-final and stop retrying