> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.thepennyinc.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.thepennyinc.com/_mcp/server.

# Event Types and Payloads

An asynchronous webhook event reports something that has already happened. Penny sends a `POST` with a stable envelope. Use `type` to identify the specific occurrence and `category` to choose the shape of `data`. The complete set of client-subscribable `type` values is in the [Event Catalog](/platform/webhooks-event-catalog).

## Envelope

| Field             | Meaning                                                                                                               |
| ----------------- | --------------------------------------------------------------------------------------------------------------------- |
| `event_id`        | Unique event identifier. Use it to deduplicate retries.                                                               |
| `business_id`     | Business the event belongs to. When the event originated from a different business, this is the originating business. |
| `type`            | Event identifier such as `transaction.updated` or `account.low_balance_alert`.                                        |
| `category`        | Payload family, such as `object` or `alert`.                                                                          |
| `schema_version`  | Wire format version, currently `1.0`.                                                                                 |
| `event_time`      | When Penny generated the notification.                                                                                |
| `occurrence_time` | When the underlying event occurred.                                                                                   |
| `data`            | Category-specific event data.                                                                                         |

`data` repeats the top-level `category`. Handle unknown fields safely so new optional data can be added without breaking your receiver.

## Object changes

Most public event identifiers have `category: "object"`. `data.operation` is `created`, `updated`, or `deleted`; the event identifier can be more specific, such as `card.issued`. `data.subject` identifies the object. `data.object` is the current projected object when included, and `data.previous_attributes` contains prior values when available. Neither is guaranteed for every event.

**`Object event data (abridged)`**

```jsonc title="Object event data (abridged)"
{
  "category": "object",
  "operation": "updated",
  "subject": { "object_type": "card", "object_id": "card_019375de-a2a0-7f31-884a-a2a0f3184abd" },
  "object": {
    "card_id": "card_019375de-a2a0-7f31-884a-a2a0f3184abd"
    // … the rest of the card object
  },
  "previous_attributes": null
}
```

Use `subject` and the object ID to retrieve the latest resource when you need current state; event delivery order is not guaranteed.

## Alerts

`account.low_balance_alert` is currently the public `alert` event. Its `data` carries `severity`, `subject`, a human-readable `message`, and optional `details`. It reports a threshold or derived condition, not a CRUD mutation, so do not expect `operation` or `object`.

## Messages

`message` events carry a platform notice rather than an object change. Their `data` has `topic`, `title`, optional `body`, `severity`, and optional `subject` and `details`. `message` events have no fixed `type` identifiers in the Event Catalog, so handle them by `category`.

## Verification challenges

When you verify an endpoint, Penny sends a challenge with `type: "endpoint.verification.pending"` and `category: "verification"`. Its `data.token` is the code you submit to complete verification. This is part of endpoint setup, not an event you subscribe to with `trigger_events`. See [Subscribing to Webhooks](/platform/subscribing-to-webhooks#3-verify-the-endpoint).

For the synchronous `transaction.authorization` request, use [Events vs. Decision Exchanges](/platform/webhook-events-vs-decisions) and [Live Decisioning](/platform/webhooks-live-decisioning).