Skip to main content

How to Use Webhooks with WaveRez

How to set up a receiving webhook URL in WaveRez admin, what each event sends, and how to connect waiver data to bookings.

Written by Amber Dudley

WaveRez can automatically send booking and waiver information to another system, like Zapier, Make, or your own CRM, whenever something important happens. This is done using a webhook, which lets two systems talk to each other automatically without anyone clicking a button.


Setting Up Your Webhook

  1. Go to the Admin Area of WaveRez

  2. Click Integrations > Webhooks

  3. Check the box for Use Webhook

  4. Paste your Zapier, Make, or custom URL into the Webhook Endpoint field

That's it. WaveRez will begin sending data to that URL as events occur.


What Triggers a Webhook

Every webhook includes a type field (what kind of record it is) and an event field (what happened to it). Check both before processing.

type

event

Fires when

Booking

created

A new booking is made — through WaveRez, or through a connected channel like TripShock, Viator, or GetYourGuide

Booking

updated

An existing booking changes — date, time, guest count, guest details, payment status

Booking

cancelled

A booking is cancelled

Waiver

completed

A guest finishes and signs a waiver

Actions that don't change the booking itself — sending an SMS or email, a declined payment attempt — do not trigger a webhook.

Coming soon: crew management webhooks, which will fire when a crew member is assigned to a trip and again when they accept or decline.


How Delivery Works

A few things to understand before you build an automation:

Webhooks send the current state, not a list of changes. An updated event contains the booking as it exists right now. It does not tell you which fields changed. If you need to detect what changed, store the previous version on your side and compare.

Rapid changes are batched. If a booking is edited several times in quick succession, you'll typically receive one webhook reflecting the final state rather than one per edit. This keeps your integration from being flooded, but it means the webhook is a snapshot, not an audit trail.


Booking Webhooks

Fields you'll use most

Field

Notes

booking.id

The stable, permanent booking ID. Use this as your record key.

booking.format_id

The human-readable confirmation code shown to guests (e.g. EJH-1368612)

booking.state

confirmed, paid, cancelled, etc.

booking.customer_first_name / customer_last_name / customer_email / customer_phone

The lead booker's details

booking.net_total

Total value of the booking

booking.made

When the booking was created

booking.detailsURL

Direct link to the order in WaveRez admin - great for CRM records

booking.access_link

Guest-facing link to manage the booking

booking.new

true on creation, false afterward

booking.cancelled

true when the booking has been cancelled

booking.timezone

UTC offset for your business location

Fields inside each cart item

A booking contains one or more cart_items — each one is a trip on a specific date and time.

Field

Notes

id

The stable line-item ID. Use this to match waivers (see below).

trip_id / trip_name

Which activity was booked

start_at / end_at

Full departure and return time with time zone offset, e.g. 2026-07-31T09:00:00-05:00. Use these rather than combining arrival_date and schedule.time yourself.

booked_format

Human-readable summary, e.g. 1 x $20.00 Child, 3 x $40.00 Adult

inventory.name

The boat, vehicle, or unit assigned

tickets[]

Each ticket type with its name, price, and booked quantity

calendar_id

Identifies the departure, not the booking — see the note below

cancel_reason

Populated when this item is cancelled

voucher_link

Guest voucher URL

About calendar_id

calendar_id identifies a specific departure slot, and every booking on that same departure shares the same value. It is not unique to a booking, and it changes if a booking is moved to a different date or time.

  • Don't use it as a record key, you'll merge two different customers into one record.

  • Do use it to group bookings by departure. It's the right key for automations like "text everyone on tomorrow morning's 9:00am boat."

Example: booking created

{
"type": "Booking",
"event": "created",
"booking": {
"id": "1368612",
"format_id": "EJH-1368612",
"user_id": "495016",
"is_direct": true,
"is_office": "0",
"payment_method": "cash",
"rs_agent": "Amber",
"ts_agent": null,
"accesskey": "rqnrgssy",
"made": "2026-07-24 13:38:20",
"state": "confirmed",
"was_refunded": "0",
"refund_amount": "0.00",
"gift_certificate": "",
"has_different_days": false,
"customer_last_name": "Sample",
"customer_first_name": "Jane",
"customer_email": "[email protected]",
"customer_phone": "+15550000000",
"customer_phone2": "",
"customer_address": "123 Example Street",
"customer_city": "Destin",
"customer_zip": "32541",
"customer_state_id": "0",
"customer_state_name": "FL",
"customer_country_id": "5",
"subtotal": 140,
"surcharges": 0,
"subtotal_tax": 0,
"subtotal_fee": 0,
"net_total": 140,
"transaction_info": [
{
"card_type": null,
"card_number": null,
"transaction_id": null
}
],
"discount": [],
"cart_items": [
{
"id": "5495980",
"is_integrated": "0",
"is_overnight": false,
"is_wr_aff": "0",
"state": "confirmed",
"is_gift": false,
"trip_activity": "",
"comment_guest": "",
"comment_partner": "",
"trip_id": "6100",
"trip_name": "Destin Dolphin Watching Cruise",
"trip_slug": "",
"trip_google_place_id": "",
"trip_tax": "0",
"trip_fee": "6",
"booking_fee": "0",
"trip_need_tickets_guests": "0",
"arrival_date": "2026-07-31",
"calendar_id": "151370299",
"booked_format": "1 x $20.00 Child, 3 x $40.00 Adult",
"subtotal": 140,
"surcharges": 0,
"subtotal_tax": 0,
"subtotal_fee": 0,
"net_total": "140.00",
"cancel_reason": "",
"invitation_sent": "0",
"voucher_link": "https://reservations.waverez.com/id0000000/voucher/1368612/5495980/example/",
"rate": [],
"inventory": {
"id": "2885",
"name": "Dolphin Cruise Boat 1",
"duration": "120",
"description": ""
},
"schedule": {
"id": "49564",
"time": "9:00am"
},
"tickets": [
{
"id": "44992",
"name": "Child",
"duration": "120",
"description": "Ages 2-15",
"rs_internal": "",
"is_overnight": false,
"booked": "1",
"price": "20",
"wholesale": "0",
"comission": "0",
"guests": [],
"barcodes": []
},
{
"id": "44993",
"name": "Adult",
"duration": "120",
"description": "Ages 16 and up",
"rs_internal": "",
"is_overnight": false,
"booked": "3",
"price": "40",
"wholesale": "0",
"comission": "0",
"guests": [],
"barcodes": []
}
],
"additional_options": [],
"has_multipass": null,
"start_at": "2026-07-31T09:00:00-05:00",
"end_at": "2026-07-31T11:00:00-05:00",
"clx_fee": 0
}
],
"access_link": "https://reservations.waverez.com/id0000000/access/1368612/user-example",
"clx_fee": 0,
"customer_country_name": "US",
"cancelled": false,
"new": false,
"timezone": "-0500",
"detailsURL": "https://admin-new.waverez.com/orders/1368612"
}
}

An updated event looks identical, with "event": "updated", "new": false, and the changed values in place.


Cancellations

A cancellation arrives as "event": "cancelled" with "cancelled": true, "state": "cancelled" at both the booking and cart-item level, and a cancel_reason on the affected cart item.

Monetary totals are zeroed on a cancelled booking. subtotal, net_total, subtotal_tax, subtotal_fee, and surcharges will all read 0.

If you need the original value of a cancelled booking for reporting, you have two options:

  1. Store it when the booking is created. This is the reliable approach, and another reason to build your automation as an upsert — you keep the value from the created event and simply flag the record as cancelled.

  2. Reconstruct it from the tickets. The tickets[] array keeps its price and booked values, and booked_format remains readable. In the example below, 2 × $20 + 7 × $40 gives you $320 in gross ticket revenue. Note that this excludes taxes, fees, and discounts.

Example: booking cancelled

{
"type": "Booking",
"event": "cancelled",
"booking": {
"id": "1368591",
"format_id": "EJH-1368591",
"made": "2026-07-24 13:28:12",
"state": "cancelled",
"was_refunded": "0",
"refund_amount": "0.00",
"customer_last_name": "Sample",
"customer_first_name": "Jane",
"customer_email": "[email protected]",
"customer_phone": "+15550000000",
"subtotal": 0,
"surcharges": 0,
"subtotal_tax": 0,
"subtotal_fee": 0,
"net_total": 0,
"discount": {
"total": 0,
"total_with_tax": 0
},
"cart_items": [
{
"id": "5495949",
"state": "cancelled",
"trip_id": "6100",
"trip_name": "Destin Dolphin Watching Cruise",
"arrival_date": "2026-07-31",
"calendar_id": "151370299",
"booked_format": "2 x $20.00 Child, 7 x $40.00 Adult",
"subtotal": 0,
"net_total": "0.00",
"cancel_reason": "Guest requested",
"inventory": {
"id": "2885",
"name": "Dolphin Cruise Boat 1",
"duration": "120"
},
"schedule": {
"id": "49564",
"time": "9:00am"
},
"tickets": [
{
"id": "44992",
"name": "Child",
"booked": "2",
"price": "20"
},
{
"id": "44993",
"name": "Adult",
"booked": "7",
"price": "40"
}
],
"start_at": "2026-07-31T09:00:00-05:00",
"end_at": "2026-07-31T11:00:00-05:00"
}
],
"cancelled": true,
"new": false,
"timezone": "-0500",
"detailsURL": "https://admin-new.waverez.com/orders/1368591"
}
}

Trimmed for readability — the full payload carries the same fields as a created event.


Waiver Webhooks

This is new. When a guest completes a waiver, WaveRez sends that guest's information to your webhook. Previously you only received data for the lead booker; now you receive a separate webhook for every guest who signs.

This is what makes it possible to:

  • Send review invitations to everyone on the trip, not just the person who paid

  • Build birthday and trip-anniversary campaigns from real guest data

  • Maintain an accurate marketing list with per-guest consent

Structure

Waiver payloads use "type": "Waiver" and put everything under a data object — not booking. Branch on type first in your automation.

Field

Notes

fillId

Unique per signer. The suffix increments for each guest on the same booking (..._0, ..._1, ..._2). Use this as your deduplication key.

firstName / lastName / email / phoneNumber

The individual guest's details

getEmailPromotions / getPhonePromotions

"1" if the guest opted in to marketing, "0" if not. Honor these.

birthDate

Format is MM/DD/YYYY

waiverTitle / waiverId

Which waiver was signed

completed

"1" when the waiver is finished

signatureMetadata

When and on what device the waiver was signed

field_XXXX

Custom questions you added to your waiver. The key is generated from the question and will differ per waiver.

Example: waiver completed

{
"type": "Waiver",
"event": "completed",
"data": {
"arrivalAt": "2026-07-31 09:00:00",
"fillId": "example12345678-abcd-efgh1_0",
"inventory": "Dolphin Cruise Boat 1",
"orderId": "1368612",
"productId": "6100",
"productTitle": "Destin Dolphin Watching Cruise",
"purchaseId": "5495980",
"sellerId": "0000000",
"waiverId": "0000000_r_xxxx",
"waiverTitle": "Standard Liability Waiver",
"firstName": "Jane",
"lastName": "Sample",
"birthDate": "11/28/1985",
"phoneNumber": "+15550000000",
"getPhonePromotions": "1",
"email": "[email protected]",
"getEmailPromotions": "1",
"address": "123 Example Street",
"zip": "32541",
"gender": "female",
"hasMinors": "no",
"isDriver": "yes",
"medicalConditions": "no",
"emergencyFullName": "Alex Sample",
"emergencyContactPhone": "+15550000001",
"emergencyContactAddress": "123 Example Street",
"emergencyContactZip": "32541",
"heardAboutUs": "returnCustomer",
"field_4i-t__ack-1": "1",
"completed": "1",
"waiverVersion": "0000000_r_xxxx",
"signatureMetadata": {
"signedAt": "2026-07-24T18:40:06.400Z",
"device": "macOS Chrome",
"timezone": "America/Chicago",
"platform": "macOS"
}
}
}

Connecting Waivers to Bookings

Waiver payloads carry the IDs you need to match a signed waiver back to the booking it belongs to:

Waiver field

Matches

orderId

booking.id

purchaseId

cart_items[].id

productId

cart_items[].trip_id

sellerId

Your WaveRez account

Three things to keep in mind:

Deduplicate on fillId, not email. Guests frequently enter the lead booker's email address for the whole party, and some enter nothing meaningful at all. fillId is the only value guaranteed unique per signer.

For trip date and time, always read the booking — not the waiver. The waiver's arrivalAt reflects the trip date at the moment it was signed. If the booking is later rescheduled, the waiver record is not resent and its date will be out of date. Join on orderId and take the date from the booking's start_at.

There is no "everyone has signed" event. Each waiver arrives on its own, and minors typically don't sign, so ticket counts won't reliably tell you how many waivers to expect. Treat each waiver as an independent record.


Reading the Data Correctly

A few conventions worth knowing before you build:

Dates come in several formats. Use start_at and end_at when you need a precise, time zone-aware moment. arrival_date is date-only. arrivalAt on a waiver has no time zone attached. signedAt is UTC. birthDate is MM/DD/YYYY.

Many yes/no values are sent as text. You'll see "0" and "1" as strings alongside true JSON booleans like is_direct: true. Zapier and Make treat "0" as a non-empty text value, so test your filters — a check for "is true" won't behave the way you expect on these fields.

Use booking.id and cart_items[].id as your keys. They're permanent. calendar_id, schedule.id, and format_id can all change or repeat.


Security and Privacy

Waiver payloads can contain sensitive personal information, depending on how you've configured your waiver — dates of birth, driver's license numbers, emergency contacts, and any medical or health questions you've added.

That data is sent to whatever URL you enter, and it will be stored by whatever service receives it, including in task and run histories.

Before you turn on waiver webhooks:

  • Treat your endpoint URL as a secret. Anyone with the URL can receive or spoof data. Don't share it or paste it into public forums.

  • Use a service you'd be comfortable storing guest ID data in, and confirm who on your team can view its logs.

  • Only collect what you need. If your waiver asks for a driver's license number or health details you don't actually use, remove the question rather than routing it to a third-party system.

  • Honor getEmailPromotions and getPhonePromotions. A signed waiver is not marketing consent on its own.

If you're unsure whether your setup is appropriate for the data you collect, reach out to WaveRez support before enabling waiver webhooks.


Need Help?

Did this answer your question?