Learn which events you can subscribe to, how to use subscription patterns, and what every webhook payload looks like.
Available Event Types
| Event Type | Description |
|---|
kyc.status.changed | KYC verification status has changed |
card.activated | A card has been activated |
transaction.cleared | A transaction has cleared |
Additional event types will be added as the platform grows. Contact your account team to discuss events relevant to your integration.
Subscription Patterns
When configuring your webhook, you can subscribe using three pattern styles:
| Pattern | Description | Example |
|---|
| Exact Match | Subscribe to one specific event | kyc.status.changed |
| Category Wildcard | Subscribe to all events in a category | card.* (matches card.activated) |
| Global Wildcard | Subscribe to all events | * |
Using card.* means your webhook will automatically receive new card event types as they are introduced, without needing to update your subscription.
Standard Payload Structure
Every webhook event, regardless of type, follows the same top-level structure:
| Field | Type | Required | Description |
|---|
api_version | string | Yes | API version — currently "v1" |
event_id | string (UUID) | Yes | Unique identifier for this event. Use for idempotency. |
event_category | string | Yes | Category of the event (e.g., "kyc", "card") |
event_type | string | Yes | Specific event type (e.g., "kyc.status.changed") |
user_id | string (UUID) | Yes | Baanx internal identifier for the user |
user_external_id | string | No | Your external user identifier, if provided during onboarding |
event_object_id | string | Yes | ID of the object that changed |
event_object_status | string | No | Current status of the changed object |
event_object_changes | object | No | Before/after values of the fields that changed |
event_object | object | Yes | Event-specific data — contents vary by event type |
occurred_at | string (ISO 8601) | Yes | Timestamp of when the event occurred |
Example Payload
{
"api_version": "v1",
"event_id": "c386021a-d7a5-4aff-8924-4374f1d1d84f",
"event_category": "kyc",
"event_type": "kyc.status.changed",
"user_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
"user_external_id": "your-user-id-123",
"event_object_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
"event_object_status": "completed",
"event_object_changes": {
"status": ["pending", "completed"]
},
"event_object": {
"status": "completed",
"failure_code": null,
"failure_reason": null
},
"occurred_at": "2026-01-06T14:50:31.481Z"
}
User Identification
Every webhook includes user_id, the Baanx internal identifier for the user. If you provided an external user ID when onboarding the user, it will also be present as user_external_id in all webhook events for that user.
{
"user_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
"user_external_id": "your-internal-user-id-456"
}
This lets you correlate webhook events with users in your own system without making additional API calls.
user_external_id is set once during user onboarding and remains consistent across all webhooks for that user.
The event_object_changes Field
When a field changes, event_object_changes provides a before/after snapshot as a two-element array — [previous_value, new_value]:
"event_object_changes": {
"status": ["pending", "completed"]
}
This makes it straightforward to see exactly what changed without needing to store previous state on your side.
Idempotency
Events may occasionally be delivered more than once due to retries or network conditions. Always use event_id as an idempotency key to avoid processing the same event twice:
async function handleWebhook(event) {
const alreadyProcessed = await db.findEvent(event.event_id);
if (alreadyProcessed) {
return { status: 204 }; // Acknowledge without reprocessing
}
await processEvent(event);
await db.markEventProcessed(event.event_id);
return { status: 204 };
}
Next Steps