Set up webhooks

Send booking, payment, waiver, and customer data to any third-party system in real time — endpoints, events, JSON payloads, delivery, and security.

Written By Melanie Gannone (Super Administrator)

Updated at July 21st, 2026

Webhooks push data out of TripWorks the moment something happens — a trip is booked, a payment lands, a waiver is signed. TripWorks sends an HTTP POST with a JSON body to any URL you choose, so you can wire bookings into a CRM, accounting tool, data warehouse, or a custom workflow without polling for changes.

Prefer no-code? The Zapier integration fires on the same events and connects to thousands of apps without writing anything. Webhooks are the raw-JSON path for your own systems — pick one path per event so a booking isn't processed twice downstream.

Before you begin. Setting up webhooks requires the Setup integrations catalog permission. It comes with the built-in Owner group, or any custom permission group that includes it. If Integrations doesn't appear under Setup, you don't have that permission yet, so ask an account admin to grant it to you.
Learn how to manage permission groups

Add a webhook endpoint

  1. Go to Setup Tools Integrations, find the Webhooks card, and select Activate (or Configure if it's already on).
  2. Turn on Enable Webhooks — the master switch. Nothing is sent unless this is on.
  3. For each event you care about, paste your destination URL. Use + Add another endpoint to send the same event to more than one place.
    Setting up webhooks in TripWorks — the Webhook Events list with a URL field per event, Add another endpoint, and an Example JSON Payload preview
    Add a URL for each event you want. Preview the shape under Example JSON Payload.
  4. Select Save. Webhooks take effect immediately.

Webhooks fire on real actions in your account — there's no separate test mode or "send test event" button. To try one out, point the URL at a free inspector like webhook.site (the Try It Now button links to it), then perform the real action.

Available events

The value in the payload's event field is the exact identifier below (an underscore slug — the setup page shows friendlier labels).

Event (event value) Fires when
trip_created A new trip is created — including drafts and abandoned carts
trip_reserved A trip moves to Reserved (typically the first payment)
trip_updated A trip changes — date/time, guest count, custom fields, notes
trip_cancelled A trip is cancelled
booking_updated A guest booking within a trip changes — check-in, status, guest added/removed
addon_updated An add-on or product is added to or removed from a booking
payment_created Any payment or refund is processed (fires per payment, including partials)
waiver_signed A customer signs a waiver
payout A payout is sent to your bank account
customer_create A new customer profile (with an email) is created
customer_update A customer profile is modified

The payload

Each webhook is a POST with Content-Type: application/json. There's no envelope — the object's fields sit at the top level with an event key merged in, like { "event": "trip_created", "id": …, "customer": {…}, "tripOrders": [ … ] }:

{ "event": "trip_created", "id": 2088161, "confirmation_code": "BOOK-1234" }

A few things to know when you parse it:

  • Money is in integer cents. "total": 24200 means $242.00.
  • Trip events nest everythingtripOrders[] (one per activity) each contain bookings[] (one per guest), with customer, ticket type, custom fields, add-ons, and status.
  • customer_portal_url — a login-free link to the guest's portal — is included on every trip-related event. trip_created, trip_reserved, trip_updated, trip_cancelled, and payment_created (nested under trip) include it at all three levels: the root trip, each trip order, and each booking. booking_updated and addon_updated include it once, at the booking level only. It's not included on waiver_signed, payout, customer_create, or customer_update.

customer_create

Webhook payload example
{
  "event": "customer_create",
  "id": 5567611,
  "code": "CUST-1A2B",
  "first_name": "Jordan",
  "last_name": "Avery",
  "full_name": "Jordan Avery",
  "email": "jordan@example.com",
  "phone": "+15550100",
  "phone_format_intl": "+1 555-0100",
  "phone_format_164": "+15550100",
  "phone_country_code": "US",
  "mappable_position": "Denver Colorado United States",
  "location_description": "Denver, CO, USA",
  "country": {},
  "is_vip": false,
  "is_opt_in": true,
  "timezone": "America/Denver",
  "created_at": "2026-02-26T00:20:59+00:00"
}

trip_created

trip_reserved, trip_updated, and trip_cancelled share this same trip shape — the event, trip_status, and amounts differ.

Webhook payload example
{
  "event": "trip_created",
  "id": 2088161,
  "confirmation_code": "BOOK-1234",
  "display_name": "Jordan Avery",
  "created_at": "2026-02-26T00:05:34+00:00",
  "reserved_at": "2026-02-26T00:05:57+00:00",
  "sale_date": "2026-02-28T00:00:00+00:00",
  "trip_status": { "slug": "draft", "name": "Draft" },
  "trip_method": { "name": "Walkup", "color": "#3498eb", "slug": "walkup" },
  "customer_portal_url": "https://portal.tripworks.com/t/abc123",
  "customer": {
    "first_name": "Jordan",
    "last_name": "Avery",
    "full_name": "Jordan Avery",
    "email": "jordan@example.com",
    "phone": "+15550100",
    "phone_format_intl": "+1 555-0100"
  },
  "tripOrders": [
    {
      "created_at": "2026-02-26T00:05:34+00:00",
      "experience_total": 20000,
      "addons_total": 3000,
      "products_total": 0,
      "pickup_total": 0,
      "promo_code_total": 0,
      "voucher_code_discount": 0,
      "total_sales": 23000,
      "customer_portal_url": "https://portal.tripworks.com/t/abc123?order=1",
      "experience": {
        "id": 105,
        "name": "Hiking",
        "internal_name": "Hiking",
        "duration": 480,
        "color": "#4e5dc1"
      },
      "experience_timeslot": {
        "label": "11:00 AM",
        "start_time": "2026-02-28T11:00:00+00:00",
        "end_time": "2026-02-28T19:00:00+00:00"
      },
      "status": { "id": 1, "slug": "booked", "name": "Booked" },
      "pax_count": 1,
      "addons_cnt": 2,
      "bookings": [
        {
          "id": 5201661,
          "created_at": "2026-02-26T00:05:34+00:00",
          "status": { "id": 15, "name": "Not Checked In" },
          "is_complimentary": false,
          "customer_portal_url": "https://portal.tripworks.com/t/abc123?booking=1",
          "customer": {
            "first_name": "Jordan",
            "last_name": "Avery",
            "full_name": "Jordan Avery",
            "email": "jordan@example.com",
            "phone": "+15550100"
          },
          "experience_customer_type": {
            "id": 195,
            "name": "Adult",
            "description": "price per person"
          },
          "custom_field_values": [],
          "booking_resources": [],
          "addons": [
            { "id": 371942, "name": "Ultimate Hike", "price": 25000, "experience_addon": { "id": 31, "title": "Ultimate Hike" } },
            { "id": 371943, "name": "Lunch", "price": 1000, "experience_addon": { "id": 6515, "title": "Lunch" } }
          ]
        }
      ]
    }
  ],
  "custom_field_values": [
    { "custom_field": { "internal_name": "Team Color" } },
    { "custom_field": { "internal_name": "Shirts Needed" } }
  ],
  "payments": [],
  "subtotal": 23000,
  "total": 24200,
  "paid": 0,
  "due": 24200,
  "total_sales": 24200,
  "products_cnt": 0,
  "addons_cnt": 2,
  "reseller_commission": 0,
  "session_ip_address": "203.0.113.10",
  "session_browser_name": "Chrome",
  "session_browser_os": "Apple",
  "session_location_label": "Denver, Colorado US",
  "created_by_user_avatar": {
    "id": 58,
    "first_name": "Taylor",
    "last_name": "Reed",
    "full_name": "Taylor Reed",
    "abbrev_name": "Taylor R."
  }
}

payment_created

A payment (or refund) wraps the trip under a trip key.

Webhook payload example
{
  "event": "payment_created",
  "id": 1277113,
  "created_at": "2026-02-26T00:20:05+00:00",
  "amount": 5000,
  "net_amount": 5000,
  "tip": 0,
  "currency": "USD",
  "is_card_present": false,
  "status": { "id": 6, "name": "Successful" },
  "type": { "id": 1, "name": "Cash" },
  "direction": { "id": 1, "name": "Payment" },
  "owner": {
    "id": 58,
    "first_name": "Taylor",
    "last_name": "Reed",
    "full_name": "Taylor Reed"
  },
  "trip": {
    "id": 2086899,
    "confirmation_code": "BOOK-5678",
    "trip_status": { "slug": "reserved", "name": "Reserved" },
    "customer_portal_url": "https://portal.tripworks.com/t/def456",
    "subtotal": 96000,
    "total": 106377,
    "paid": 5000,
    "due": 101377
  }
}

waiver_signed

Webhook payload example
{
  "event": "waiver_signed",
  "id": 1081786,
  "created_at": "2026-02-26T00:27:50+00:00",
  "full_name": "Jordan Avery",
  "first_name": "Jordan",
  "last_name": "Avery",
  "email": "jordan@example.com",
  "phone": "+15550100",
  "dob": "2006-04-14T00:00:00+00:00",
  "is_waiver_adult": true,
  "experience_name": "Swimming",
  "waiver_type": { "id": 2, "name": "Liability Waiver" },
  "signer_category": "lead",
  "booking_id": 5197289,
  "signed_pdf": "https://cdn-images.tripworks.com/tenants/profiles/example/waivers/example.pdf"
}

Example: what people build with this

  • Sync every new booking into a CRM — subscribe to trip_created and trip_reserved, and push each guest into your CRM the moment they book, instead of exporting a list by hand at the end of the day.
  • Post a Slack alert on a big payment — subscribe to payment_created, and have your receiver check amount (remember, it's in integer cents) before deciding whether to notify the team.
  • Feed a data warehouse for reporting — subscribe to all 11 events and land the raw JSON in a table per event type, so you can build reports TripWorks doesn't offer natively.
  • Trigger a welcome email from your own system — subscribe to customer_create and kick off a sequence in your own email tool the moment a new guest's profile is created.
  • Cancel a linked reservation in another system — subscribe to trip_cancelled so a booking cancelled in TripWorks also cancels whatever downstream record (a shuttle reservation, an equipment hold) depends on it.

How delivery works

  • TripWorks sends one POST per event to each endpoint you listed, a few seconds after the event.
  • Your endpoint must return HTTP 200. Any other final status, including 201 or 202, is treated as a failure. A redirect is followed automatically and judged by whatever status the redirect target returns, so a 301/302 to a URL that itself returns 200 counts as a success.
  • The request times out after 15 seconds — respond quickly and do heavy work asynchronously on your side.
  • There are no automatic retries and no auto-disable. If your endpoint is down, that event is simply missed — TripWorks won't resend it, and the endpoint keeps trying on future events. Build your receiver to be reliable (return 200 fast, queue the work).

Security

TripWorks does not sign webhook requests — there's no shared secret or signature header, so a receiver can't cryptographically verify that a request truly came from TripWorks. Protect your endpoint accordingly:

  • Treat the URL as a secret. Use a long, unguessable path (the setup field even suggests a random-looking URL), and don't share it.
  • Require HTTPS so the payload is encrypted in transit.
  • Add your own check if you need one — for example, include a secret token in the URL's path or query string and reject requests that don't carry it.

Test and troubleshoot

There's no "send test event" button — webhooks fire on real actions — so you test by triggering the real thing:

  1. Set a webhook.site URL (or your own inspector) as the endpoint for an event.
  2. Perform that action in your live account — e.g. create a trip for trip_created.
  3. Watch the POST arrive and inspect the JSON.

If nothing arrives: confirm Enable Webhooks is on, the URL is correct and returns 200, and that you actually performed an action that fires the event you subscribed to.

Frequently asked questions

Does TripWorks retry a failed delivery?

No. Each event is a single POST to each endpoint. If your server is down or returns a non-200 status, that event is missed and isn't resent. Make your receiver return 200 quickly and do the real work in the background.

How do I verify a request really came from TripWorks?

Requests aren't signed, so there's no signature to verify. Keep the endpoint URL secret, require HTTPS, and if you need stronger assurance, include a secret token in the URL and check for it.

Why isn't my webhook firing when I test it?

There's no test mode — webhooks only fire on real actions. Trigger the actual event (for example, create a booking for trip_created) with an inspector URL like webhook.site set as the endpoint, and check that Enable Webhooks is on and the URL returns 200.

Should I use webhooks or Zapier?

They fire on the same events. Use Zapier for no-code connections to thousands of apps; use webhooks to POST raw JSON to your own system. Pick one path per event so downstream processing doesn't happen twice.

Why is a money amount ten or a hundred times too big?

Amounts are in integer cents. Divide by 100 — 24200 is $242.00.

Built for attractions, tours & activities

Wire TripWorks into everything else

See how attractions, tours, and activities push bookings, payments, and waivers into their own systems in real time on TripWorks. Book a demo and make the switch.

Can't find what you're looking for?

Our team is here to help. Reach out and we'll get back to you as soon as possible.

Contact us →