> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baanx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Types & Payload Structure

> Available event types, subscription patterns, and the standard webhook payload

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           |

<Note>
  Additional event types will be added as the platform grows. Contact your account team to discuss events relevant to your integration.
</Note>

## 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

```json theme={null} theme={null}
{
  "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.

```json theme={null} theme={null}
{
  "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.

<Note>
  `user_external_id` is set once during user onboarding and remains consistent across all webhooks for that user.
</Note>

## 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]`:

```json theme={null} theme={null}
"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:

```javascript theme={null} theme={null}
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

<CardGroup cols={2}>
  <Card title="KYC Events" icon="id-card" href="/guides/webhooks/kyc-events">
    Full reference for KYC status payloads, status values, and failure codes
  </Card>

  <Card title="Security & Verification" icon="lock" href="/guides/webhooks/security">
    How to verify the signature on every incoming webhook
  </Card>
</CardGroup>
