Instead of polling the API for changes, register an endpoint and Palm pushes events to you. Webhooks deliver events from all Palm products through a single integration — verification results, monitoring alerts, and compliance updates all arrive at the same endpoint.
Setting up a webhook
When you create a webhook in Palm Console, you specify:
- URL: The HTTPS endpoint that will receive events. HTTP is not supported.
- Events: The event types you want to subscribe to.
Palm generates a signing secret (format: whsec_...) at creation time. This secret is returned once — store it securely. You’ll need it to verify that incoming requests are from Palm.
All events follow a consistent naming pattern:
{resource}.{action}
{resource}.{sub_resource}.{action}
Every event payload contains:
| Field | Description |
|---|
id | Unique event identifier. Use for idempotency. |
object | Always event. |
type | The event type (e.g., user.verification.completed). |
created_at | ISO 8601 timestamp. |
data | Event-specific payload. Structure depends on event type. |
Event catalog
A note on naming: Webhook event types use user (e.g., user.verification.completed) while the rest of the docs refer to “person” for KYC verification. These are the same entity — the event names reflect the API resource name.
Verification events
Fired when verification processing completes. Payloads include the verification ID, risk level, match results, and — for business verification — associate results.
| Event | Description |
|---|
user.verification.completed | Person (KYC) verification finished. |
business.verification.completed | Business (KYB) verification finished. Includes associate-level results. |
Monitoring events
Fired by the monitoring system when Palm detects changes from external sources (Secretary of State filings). These events only fire for businesses enrolled in Monitor.
| Event | Description |
|---|
business.registration.updated | Registration data changed (status, name, address, officers, filings). Includes structured diffs with previous and current values. |
business.filing.due | A compliance filing deadline is approaching. |
business.filing.overdue | A compliance filing deadline has passed. |
Fired when entities are created or removed through the API.
| Event | Description |
|---|
user.created | A person was created. |
user.removed | A person was removed. |
business.created | A business was created. |
business.removed | A business was removed. |
session.completed | An onboarding session workflow completed. |
Example payloads
Verification completed
When a business verification completes, the payload includes risk assessment and field-level match results for the business and each associate:
{
"id": "evt_1704538200000_f6g7h8i9j0k1",
"object": "event",
"type": "business.verification.completed",
"created_at": "2025-01-06T10:30:00Z",
"data": {
"object": {
"business_id": "bus_987e6543-e21a-45b6-c789-012345678901",
"verification_id": "ver_456e7890-f12a-34b5-c678-901234567890",
"reference_id": "your_business_ref_789",
"risk": {
"level": "low",
"reasons": []
},
"match": {
"legal_name": "match",
"ein": "match",
"entity_type": "match",
"address_line_1": "match"
},
"associates": [
{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"verification_id": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"risk": { "level": "low", "reasons": [] },
"match": {
"first_name": "match",
"last_name": "match",
"date_of_birth": "match",
"ssn": "match"
},
"watchlist": { "status": "clear" }
}
]
}
}
}
Registration change (monitoring)
When registration data changes, the payload includes structured diffs so you can process changes programmatically:
{
"id": "evt_1704538200000_h8i9j0k1l2m3",
"object": "event",
"type": "business.registration.updated",
"created_at": "2026-01-12T10:30:00Z",
"data": {
"business": {
"id": "987e6543-e21a-45b6-c789-012345678901",
"palm_id": "A1B2-C3D4-E5F6",
"legal_name": "NEW NAME LLC",
"registration_jurisdiction_id": "US-NC"
},
"changes": [
{
"attribute": "name.legal",
"action": "updated",
"summary": "Legal name changed from OLD NAME LLC to NEW NAME LLC",
"previous_value": "OLD NAME LLC",
"current_value": "NEW NAME LLC"
},
{
"attribute": "address.principal",
"action": "updated",
"summary": "Principal address updated",
"previous_value": {
"street_line_1": "123 OLD ST",
"city": "RALEIGH",
"state": "NC",
"postal_code": "27601"
},
"current_value": {
"street_line_1": "456 NEW ST",
"city": "CHARLOTTE",
"state": "NC",
"postal_code": "28202"
}
}
]
}
}
The data.business object is a lightweight reference — enough to identify which business changed. Use GET /business/{id} to retrieve the full record.
Filing deadline (monitoring)
Filing events include the filing type and due date:
{
"id": "evt_1704538200000_l2m3n4o5p6q7",
"object": "event",
"type": "business.filing.due",
"created_at": "2026-02-26T12:00:00Z",
"data": {
"object": {
"type": {
"id": "annual_report",
"name": "Annual Report"
},
"frequency": "annual",
"due_date": "2026-04-15"
},
"business": {
"id": "987e6543-e21a-45b6-c789-012345678901",
"palm_id": "A1B2-C3D4-E5F6",
"legal_name": "ACME HOLDINGS LLC",
"registration_jurisdiction_id": "US-NC"
}
}
}
Verifying signatures
All webhook payloads are signed using HMAC-SHA256. Always verify signatures before processing events.
Palm sends three headers with every delivery:
| Header | Description |
|---|
Palm-Signature | Format: t={timestamp},v1={signature} |
Palm-Webhook-Id | The webhook endpoint receiving the event. |
Palm-Event-Type | The event type being delivered. |
To verify:
- Extract the timestamp (
t) and signature (v1) from the Palm-Signature header.
- Check the timestamp is within 5 minutes of current time. Reject stale timestamps to prevent replay attacks.
- Construct the signed payload:
{timestamp}.{raw_request_body}
- Compute HMAC-SHA256 using your webhook secret (
whsec_...).
- Compare your computed signature with the
v1 value using constant-time comparison.
Do not skip signature verification, even in development. Unsigned requests could come from anywhere.
Retries and delivery
Timeout: Your endpoint must respond within 10 seconds. Return a 2xx immediately, then process the event asynchronously.
Update event types: If your endpoint returns a non-2xx status or times out, Palm retries:
| Attempt | Delay |
|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
After 5 total attempts (1 original + 4 retries), the delivery is marked as failed.
Auto-disable: If a webhook accumulates 10 consecutive failed deliveries across any events, Palm automatically disables it. Check webhook execution logs in Palm Console to monitor delivery health.
Idempotency
Events may be delivered more than once. Use the event id field to deduplicate. If your processing is not naturally idempotent, store processed event IDs and skip duplicates.
Best practices
- Respond fast, process later: Return a 2xx immediately and handle the event asynchronously. Heavy processing that exceeds the 10-second timeout will trigger retries and eventually disable your webhook.
- Always verify signatures: Validate the
Palm-Signature header on every request. This is your only guarantee the event came from Palm.
- Deduplicate with event IDs: Network issues can cause duplicate deliveries. Store the
id from each processed event to avoid processing it twice.
- Subscribe selectively: Only subscribe to events you act on. If you don’t need
user.created events, don’t subscribe — it reduces noise and load on your endpoint.
- Monitor delivery health: Check webhook execution logs in Palm Console regularly. A pattern of failures usually means your endpoint is slow or returning errors.
Next steps