The Versa protocol supports several primary event types — receipts, bookings, invoices, and itineraries — all sharing the same underlying schema. Beyond these, additional secondary event types can be registered by the same mechanism for real-time updates and status changes.
Register a specific type of event and filter receivers by the specific transaction data you are prepared to send.
This allows senders to target only receivers that are interested in specific event types (receipts, itineraries, etc.) and are prepared to handle the corresponding data payload.
This is an advanced usecase and your account must be opted in to enable this feature. Contact support@versa.org if interested.
The transaction event filtering feature allows you to specify which type of event data you will send when registering a receipt with the Versa Registry. This ensures that only receivers configured to handle your specific event type will receive the webhook delivery, which is recommended best practice.
Currently supported event types:
Primary event types — all use the Versa schema:
receipt - A completed transaction with payment. The most common event type.
booking - A pre-purchase reservation; payment not required. Valid for travel itemization types only (car_rental, flight, lodging, transit_route).
invoice - An outstanding bill where payment is still due (header.paid < header.total).
itinerary - Travel information without financial data. See the Itinerary Schema.
Secondary (update) event types — reference an existing transaction:
transit_route.status.updated - Updates to the status of a transit route
transit_route.location.updated - Updates to the location of a transit route
Include the event_type parameter in your receipt registration request to filter receivers by event type:
curl -X POST https://registry.versa.org/register \
-H 'Authorization: Basic CLIENT_ID:CLIENT_SECRET' \
-H 'Content-Type: application/json' \
-d '{
"event_type": "receipt",
"schema_version": "2.3.0",
"handles": {
"customer_email_domain": "acme.co"
}
}'
| Field | Type | Description |
|---|
event_type | string | Optional. Filter receivers by transaction event type. Valid values: "receipt", "booking", "invoice", "itinerary" |
When event_type is provided, only receivers that have registered to handle the specified event type will be included in the response.
If the event_type parameter is not specified, it is assumed that a fully qualified receipt is available according to the Versa schema,
and all receivers will be included regardless of their event type configuration.
{
"mode": "prod",
"receipt_id": "rcpt_abc123def456",
"transaction_id": "txn_789xyz012",
"receivers": [
{
"org_id": "org_expense_tracker",
"endpoint_url": "https://api.expensetracker.com/webhooks/versa",
"secret": "whsec_abc123...",
"event_type": "receipt"
}
],
"encryption_key": "pk_live_abc123..."
}
Note that receivers in the response will only include those configured to handle the specified event_type type.
Beyond receipts, events can be leveraged to send additional or alternative representations of the data.
When a reservation is confirmed but payment has not yet been collected (e.g., a hotel hold), send a booking event. Bookings are valid for travel itemization types only: car_rental, flight, lodging, or transit_route.
{
"event_type": "booking",
"schema_version": "2.3.0",
"handles": {
"customer_email": "traveler@company.com"
}
}
When payment is outstanding — e.g., a net-30 bill — send an invoice event. The schema is identical to a receipt, but header.paid will be less than header.total.
{
"event_type": "invoice",
"schema_version": "2.3.0",
"handles": {
"customer_email": "client@company.com"
}
}
When sending travel confirmations without payment details attached, you can register an itinerary event. See the Itinerary Schema for the reduced field set.
{
"event_type": "itinerary",
"schema_version": "2.3.0",
"handles": {
"customer_email": "traveler@company.com",
"merchant_group_code": "ABC789Z1"
}
}
This targets only travel management and duty-of-care receivers, excluding general expense tools that don't handle travel data.
After an initial primary event (receipt, booking, invoice, or itinerary) has been posted, you can send update events.
{
"event_type": "transit_route.status.updated",
"schema_version": "2.3.0",
"transaction_id": "txn_6b408ba7406b4754b7b7d287e0ca9fdf"
}
A transaction_id is required for updates; update events will not be accepted without one.
At this time, all update events are organized hierarchically by itemization type.
As with receipt registration, update event data payloads are not included with the event registration.
Instead, the payload is encrypted and sent as part of the webhook delivery to receivers returned by the registry.
Transit Route Status Updated
Event Type: transit_route.status.updated
{
"status": "departed",
"transit_route_item_index": 0,
"updated_at": 1713196492
}
| Field | Type | Description |
|---|
status | enum | One of departed or arrived. |
transit_route_item_index | nullable integer | The ordered index of the route item to be updated from the original event. If left null, it should be interpreted as 0. |
updated_at | integer | The timestamp of the update, measured in seconds since the Unix epoch. |
Transit Route Location Updated
Event Type: transit_route.location.updated
{
"lat": 37.7749,
"lon": -122.4194,
"transit_route_item_index": 0,
"updated_at": 1713196492
}
| Field | Type | Description |
|---|
lat | number | Latitude, represented as a floating point number. |
lon | number | Longitude, represented as a floating point number. |
transit_route_item_index | nullable integer | The ordered index of the route item to be updated from the original event. If left null, it should be interpreted as 0. |
updated_at | integer | The timestamp of the update, measured in seconds since the Unix epoch. |