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.
Set Up a Webhook Endpoint and Choose Which Events It Receives
A webhook sends WaveRez booking, waiver, crew, and cart data to another system automatically as events happen, without anyone clicking a button. You configure webhooks in WaveRez Admin, and you can send different events to different destinations by adding more than one endpoint URL.
Step 1 — Go to the Admin Area of WaveRez, then click Integrations, then click Webhooks. WaveRez Admin opens the Webhooks page and displays a description of the available events above the configuration panel.
Step 2 — Tick the Use Webhook checkbox. WaveRez enables webhook sending for your account. No data is sent until you also enter an endpoint URL.
Step 3 — Paste your destination address into the Webhook Endpoint field. This is the URL provided by Zapier, Make, your CRM, or your own server. WaveRez sends event data to whatever address you enter here.
Step 4 — Click the Trigger for dropdown next to that endpoint and tick the events you want this endpoint to receive. The available events are New Bookings, Updated Bookings, Cancelled Bookings, Waiver Completed, Crew Assignment Updated, and Cart Abandoned. Tick Select all to send every event to this endpoint. The dropdown displays a count of how many events you have selected, for example "6 selected."
Step 5 — Add more endpoints if you want different events going to different systems. Click Add new url, paste the second destination address into the new Webhook Endpoint field, and choose that endpoint's events in its own Trigger for dropdown. Each endpoint has an independent event selection, so you can send booking events to your CRM and waiver events to a marketing tool without sending everything to both. Click the red trash icon beside an endpoint to remove it.
Step 6 — Tick Include Dynamic Waiver URL if you want waiver links included in the data WaveRez sends. This option applies to WaveRez Waivers only. Trips that still use a Wherewolf waiver do not produce a waiver URL in the payload regardless of this setting.
Step 7 — Click Save. WaveRez saves your endpoints and event selections and begins sending data as those events occur. Send a test event from your destination system to confirm the connection before you build automations on it.
💡 Waiver, crew, and cart events are not selected by default. When you add a webhook endpoint, the Trigger for dropdown starts with New Bookings, Updated Bookings, and Cancelled Bookings selected. Waiver Completed, Crew Assignment Updated, and Cart Abandoned are newer events and are not selected automatically. If you want any of those three, open the Trigger for dropdown on your endpoint, tick them, and click Save. WaveRez begins sending those events immediately.
What Triggers a Webhook and Which Events WaveRez Sends
Every webhook includes a type field identifying what kind of record it is, and an event field identifying what happened to it. Check both before processing, because the shape of the data differs by type. Booking events put their data under a booking object, while waiver, crew, and cart events use a data object.
WaveRez sends six event types. In plain terms: three cover the life of a booking, one fires each time a guest signs a waiver, one fires as crew assignments move through assignment and response, and one fires when a customer leaves your booking portal without finishing.
|
| Setting name | Fires when |
|
| New Bookings | A new booking is made, through WaveRez or through a connected channel like TripShock, Viator, or GetYourGuide |
|
| Updated Bookings | An existing booking changes: date, time, guest count, guest details, or payment status |
|
| Cancelled Bookings | A booking is cancelled |
|
| Waiver Completed | A guest finishes and signs a waiver |
|
| Crew Assignment Updated | A crew member is assigned to a trip |
|
| Crew Assignment Updated | An assigned crew member accepts the assignment |
|
| Crew Assignment Updated | An assigned crew member declines the assignment |
The Crew Assignment Updated setting in the Trigger for dropdown is a single option that covers all three crew events. Ticking it sends assigned, accepted, and declined. There is no way to receive only one of the three.
Actions that do not change the underlying record do not trigger a webhook. Sending an SMS or email, or a declined payment attempt, produces no event.
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 |
| The stable, permanent booking ID. Use this as your record key. |
| The human-readable confirmation code shown to guests (e.g. |
|
|
| The lead booker's details |
| Total value of the booking |
| When the booking was created |
| Direct link to the order in WaveRez admin - great for CRM records |
| Guest-facing link to manage the booking |
|
|
|
|
| UTC offset for your business location |
| Guest-facing link to complete the waiver for this order. Only present when Include Dynamic Waiver URL is ticked and the trip uses a WaveRez waiver. One URL per order, not per trip. |
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 |
| The stable line-item ID. Use this to match waivers (see below). |
| Which activity was booked |
| Full departure and return time with time zone offset, e.g. |
| Human-readable summary, e.g. |
| The boat, vehicle, or unit assigned |
| Each ticket type with its |
| Identifies the departure, not the booking — see the note below |
| Populated when this item is cancelled |
| Guest voucher URL |
| Options the customer selected on this trip. Each entry has an |
Options selected by the customer appear twice in a booking payload. The additional_options array on each cart item holds them in structured form, with the option's question in name and the customer's selection inside the value object. The same selections are also appended to the human-readable booked_format string, which for a booking with two options reads something like 1 x $100.00 1 Hour Rental + No, thanks. ($0) + Born On or After January 1st, 1988 ($0). Use additional_options when you need to act on a specific selection, and booked_format when you just need a readable summary for a CRM note or a message to staff.
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.
Include the Waiver Link in Booking Webhooks with Dynamic Waiver URL
When the Include Dynamic Waiver URL checkbox is ticked under Integrations > Webhooks, WaveRez adds a waiverURL field to every booking webhook payload. The waiverURL is the customer-facing link that lets guests on that order complete their waiver, the same link customers receive by email and SMS and the same link staff see in the Signed in Guests section of the order Overview.
The waiverURL sits at the top level of the booking object alongside detailsURL and access_link, not inside cart_items. There is one waiver URL per order, not one per trip. If an order contains several trips that use different waivers, the single waiverURL still covers the whole order, and the page it opens presents each required waiver to the guest.
A waiverURL value looks like https://waivers.waverez.com/order/b0b8bc1fb379f4c4-rhbm.
This is the field that makes waiver reminder automations possible. Send the waiverURL to a Zapier or Make scenario alongside customer_email and start_at, and you can text or email guests who have not signed as their trip date approaches.
⚠️ Dynamic Waiver URL applies to WaveRez Waivers only. The tooltip beside the Include Dynamic Waiver URL checkbox states "WaveRez waivers only." Trips that still use a Wherewolf waiver do not produce a waiverURL in the booking payload, regardless of whether the checkbox is ticked. If a booking payload arrives with no waiverURL field, check whether the trip on that booking is assigned a WaveRez waiver or a Wherewolf waiver.
⚠️ The waiver URL is unguarded. Anyone holding a waiverURL can open the waiver page for that order. Treat it as you would the access_link: do not post it publicly, and be deliberate about which systems receive it and who can read their logs.
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:
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
createdevent and simply flag the record as cancelled.Reconstruct it from the tickets. The
tickets[]array keeps itspriceandbookedvalues, andbooked_formatremains 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 |
| Unique per signer. The suffix increments for each guest on the same booking ( |
| The individual guest's details |
|
|
| Format is |
| Which waiver was signed |
|
|
| An object recording when, where, and on what device the waiver was signed. Contains |
| Custom questions you added to your waiver. The key is generated from the question and will differ per waiver. |
| The guest's driver's license number, sent when the guest indicated they are driving and your waiver collects the license number. Sensitive personal data. Omitted when the guest is not a driver or your waiver does not ask for it. |
In plain terms, a waiver webhook gives you one guest's identity and contact details, their marketing consent choices, which waiver they signed, and an audit trail of the signature itself. The fillId is unique per signer and is the value to deduplicate on. The firstName, lastName, email, and phoneNumber fields hold that individual guest's details rather than the lead booker's. The getEmailPromotions and getPhonePromotions fields hold that guest's marketing consent as the string "1" for opted in or "0" for not. The birthDate field is formatted MM/DD/YYYY. The driverLicenseNumber field appears only when the guest indicated they are driving and your waiver collects that number, and it is sensitive personal data. The waiverTitle and waiverId fields identify which waiver was signed, completed reads "1" when the waiver is finished, and signatureMetadata is an object holding the full signing audit trail. Any field with a field_ prefix is a custom question you added to your own waiver, and those keys are generated per waiver so they differ between waivers.
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",
"driverLicenseNumber": "000000000",
"medicalConditions": "no",
"emergencyFullName": "Alex Sample",
"emergencyContactPhone": "+15550000001",
"emergencyContactAddress": "123 Example Street",
"emergencyContactZip": "32541",
"heardAboutUs": "returnCustomer",
"field_DaAJ": "1",
"field_4i-t__ack-1": "1",
"field_4i-t__bszj": "1",
"completed": "1",
"waiverVersion": "0000000_r_xxxx",
"signatureMetadata": {
"signedAt": "2026-07-24T18:40:06.400Z",
"device": "macOS Chrome",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
"userAgentData": {
"brands": [
{ "brand": "Not=A?Brand", "version": "99" },
{ "brand": "Google Chrome", "version": "151" },
{ "brand": "Chromium", "version": "151" }
],
"mobile": "0",
"platform": "macOS"
},
"timezone": "America/Chicago",
"platform": "macOS"
}
}
}The example above shows three custom fields, field_DaAJ, field_4i-t__ack-1, and field_4i-t__bszj. The number of custom fields and the exact key names depend entirely on how you built your own waiver, so do not hard-code these keys in an automation. Read them dynamically or map them once against a test payload from your own waiver.
What the Signature Metadata Records About a Signed Waiver
Every waiver webhook includes a signatureMetadata object recording the circumstances under which the guest signed. This is the closest thing WaveRez sends to an audit trail for a signature, and it is the part of the payload that matters if a guest later disputes that they signed or claims someone else signed for them.
The signedAt value is the exact moment the guest completed the waiver, given in UTC with millisecond precision, for example 2026-07-24T18:40:06.400Z. Convert from UTC when comparing against times shown elsewhere in WaveRez.
The timezone value is the guest's own device time zone, such as America/Chicago, which is not necessarily your business's time zone.
The device value is a short human-readable summary such as macOS Chrome. The platform value is the operating system on its own, such as macOS.
The userAgent value is the full raw browser user agent string. The userAgentData object breaks the same information into structured parts: a brands array listing the browser engines and their versions, a mobile value of "1" when the guest signed on a mobile device or "0" when they did not, and a platform value repeating the operating system.
If you store waiver data in a CRM or spreadsheet, signedAt and device are the two values worth keeping. The full userAgent string is verbose and rarely useful on its own, but preserving it costs nothing and it is the value that carries the most weight in a dispute.
⚠️ Signature metadata is not stored anywhere you can retrieve later. WaveRez sends signatureMetadata in the waiver webhook payload, and the on-screen completed waiver view and the saved PDF do not display the user agent, device, or time zone details. If you want that audit trail available for a future dispute, your receiving system has to capture and keep it at the moment the webhook arrives. There is no way to go back and pull it for a waiver that was signed before you enabled waiver webhooks.
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 |
|
|
|
|
|
|
| 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.
Crew Webhooks: Assignment, Acceptance, and Decline Events
Crew webhooks fire as a crew assignment moves through its lifecycle. A crew member is a WaveRez user whose Type is set to Crew member under General > System setup > Users. When you assign that person to a trip, they receive an email notification and can accept or decline it. Each of those three moments sends a webhook.
Crew payloads use "type": "Crew" and place everything under a data object, not booking. The event field is assigned, accepted, or declined.
The data.crew array contains the crew member's firstName, lastName, email, phone, and status. The status value mirrors the event value, so an accepted event carries "status": "accepted". The phone field is empty when the crew member has no phone number saved on their user record.
The data.trip object contains the trip's name, the equipmentName of the boat or unit assigned, the scheduleTime and endTime as clock times, and the duration in minutes.
Example: crew assignment sent
{
"type": "Crew",
"event": "assigned",
"data": {
"crew": [
{
"firstName": "Tammy",
"lastName": "D",
"email": "[email protected]",
"phone": "",
"status": "pending"
}
],
"trip": {
"name": "Jet Ski Rental",
"equipmentName": "Jet Ski 1",
"scheduleTime": "4:00pm",
"endTime": "5:00pm",
"duration": "60"
}
}
}⚠️ Crew webhooks do not include a trip date or a booking reference. The data.trip object carries only the trip name, equipment name, and clock times. It does not contain the date of the trip, an order ID, a trip ID, or a calendar ID. This means a crew webhook cannot tell you which day the assignment is for, and it cannot be matched back to a specific booking. Automations that depend on the trip date, such as sending a crew member a reminder the morning of their trip, are not possible from the crew webhook alone. Use the crew webhook to track that an assignment was made or answered, and read the trip date from your WaveRez calendar.
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 configured your waiver. Depending on your form, a single payload may include the guest's full name, date of birth, home address and ZIP code, gender, email address, phone number, emergency contact name, phone, address, and ZIP code, their answer to any medical or health question you added, their driver's license number in the driverLicenseNumber field, and the full browser user agent string and device time zone in the signatureMetadata object.
That data is sent to whatever URL you enter, and it will be stored by whatever service receives it, including in that service's 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.
Driver's license numbers leave WaveRez in plain text. The
driverLicenseNumberfield is sent as an ordinary value in the payload with no masking or encryption beyond the connection itself. If your waiver collects license numbers and you enable waiver webhooks, those numbers will sit in your receiving service's logs. Confirm that service is appropriate for storing government ID numbers, or remove the license number question from your waiver.Signature metadata identifies the guest's device. The
signatureMetadataobject includes the full browser user agent string and the guest's device time zone. This is useful as an audit trail and is also identifying information about an individual. Treat it with the same care as the rest of the payload.Honor
getEmailPromotionsandgetPhonePromotions. 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.
