A webhook is a mechanism for receiving updates about resources without
periodically requesting the resources (known as polling). This solves
important real-time issues, like having the most recent availability
and prices to display to your customers and avoids unnecessarily consuming
To support webhooks, the developer must register an endpoint (URL) with the
Application. This URL will be sent POST requests from the G Adventures API.
Webhooks can be registered by editing the Application and updating the
NOTE: Currently a webhook registration is for ALL event types (i.e. all or
nothing approach). If you would like specific subscriptions, please let us know.
- Maintaining accurate departure data (availability and prices)
- Updating tour_dossier descriptions and images
- Finding out immediately when a new promotion is available
- booking-related updates from G Adventures
- Knowing when voucher documents are available
- Knowing when a refund has been issued
- Knowing when customer information has been changed
- Keeping meta-resources such as country, state, or nationality in sync
A webhook is fired whenever the representation of a resource changes. Thus, if a
resource changes, it will not necessarily deliver the “parent” resource,
tour_dossiers unless the change
in that departure caused a change in the
Additionally, below is a table containing common business needs, and the
collection of webhooks you’d want to consider. Be aware that this is a subset
and is only meant to provide an example of how you’d approach capturing
|Displaying a tour, its itineraries & dates. The price shown is a single
“lead” price and no specific dates and availability are displayed.
|Displaying a tour/itinerary as above, but includes departure dates including availability, pricing per departure
||tour_dossiers, itineraries, departures
|As above, but includes promotional pricing
||tour_dossiers, itineraries, departures (All Promotion data is displayed in the departure)
|Collecting customer data and displaying it in reports, booking systems, etc.
|Collecting any booking data attributed to your agency
||customers, bookings, departure_services (And all other *_services as you need), payments
|Creating a form for your customers to checkout and purchase a G Adventures tour
||countries, states, nationalities (Form meta data)
Common Webhook Patterns
Below, a table containing a realistic use-case, along with the webhooks you’d
ideally listen to.
Receiving a Webhook
Configuring your server to receive a new webhook is no different than creating
any other page on your site. With most frameworks, you’d add a new route with
the desired URL and parse the request’s data.
Webhook data is sent as JSON in the request’s body. We advise you to guard
against replay-attacks by recording which events you receive and never
processing them twice.
An example webhook view using the popular Flask framework for Python might look
something like this:
# .. do something with request.json
return "OK", 200
Registering a Webhook
When editing your Application, you have the option to enter a Webhook URL. In
order to verify the URL, you must respond to the POST request accordingly.
First, the response from the URL must be of
200 OK status code. Next, you must
include a header named
X-Application-SHA256 containing the SHA256 hex
digest of your live application key. To get this value, you can simply visit
your application dashboard, or compute it using your language’s hash functions.
When entering a new webhook, you have the option to pick your preferred
representation format. All webhooks to this callback will be sent to the
Responding to a Webhook
Your application, if it successfully handles the POST, must reply with a
status code. If it does not, the post will be retried for a maximum of 72 hours
before the job is abandoned.
Verifying a Webhook
Webhooks delivered by the G API can be verified by inspecting the
X-Gapi-Signature that is part of each web hook request. To verify the request came
from the G API, compute the HMAC digest according to the following algorithm and
compare it to the value in the
X-Gapi-Signature header. If they match, you
can be sure the web hook came from the G API.
Here’s an example of how one might do that in Python:
from flask import request
APPLICATION_KEY = 'MY_SECRET_APPLICATION_KEY'
data = request.get_data()
calculated_hmac = hmac.new(APPLICATION_KEY, data, hashlib.sha256).hexdigest()
if calculated_hmac == request.headers.get('X-Gapi-Signature'):
return "OK", 200
# Signatures did not match, raise an exception or don't use the web hook..
Most languages and frameworks you work with should have the ability to compute
an HMAC using the sha256 algorithm. This process is of course optional but can
help you sleep better at night in ensuring no unverified requests are making it
to your web hook receiver.
The POST containing:
event_type – a string describing the event
resource - identifies the resource that triggered the event
created - the datetime of when the event was created. (See dates-time)
data - a collection of data, containing the unique reference (
id) and URL (
href) to the specific object of concern.
event_type string is for identifying what operation has occurred on a
resource and has a consistent pattern across all resources,
RESOURCE_TYPE.EVENT_NAME. The resource matches the existing resources
available in the API and the event name can be either
We used to provide
created event types, but this has been discontinued since March 2017.
You can safely use the
updated event to capture any scenario where new, or existing data is relevant to you.
Webhook events contain a list of items since many actions affect multiple
resources. For example, adding (CREATE) a departure service to a booking will
cause a Webhook event with the following list of items:
- bookings.updated (e.g. booking.amount_owing is increased, as well as other fields)
- invoices.updated (new invoice for every action that affects invoice amounts)
- documents.updated (there is a new document for every invoice)
Cancelling that same departure service would have a similar Webhook event
generated, with multiple items.
XML Schema: events.xsd
For security and privacy, the full object is not included in the body.
href references allow you to retrieve the resource securely
from the API using your API Key. To secure your endpoint, only retrieve
data from rest.gadventures.com.
Example Event Body
Example XML Event Body
Initial Load & Lifecycle
We recommend using Webhooks with the following process to keep a local copy of API data:
- write a script to iterate through the list view of a resource, following all links to other resources
- store a copy of each relevant resource in a local database, cache, etc…
- use Webhook events as a notification to
delete your local copy of each resource
- the Webhook events can also be used to trigger internal processes, for example
- a change in value for a particular resource field may trigger the execution of a piece of code
- sending an email
- adding a task to a queue
- repeat for each relevant resource list view
Following this best practice has the following benefits:
- the script to iterate through all resources only needs to be executed once
- no need for polling of updates to resources, minimizing calls to the API and maximizing internal efficiency
- real-time updates and synchronization of your website, backend systems, etc…
We DO NOT rate limit outgoing Webhook POSTs. This means that it is important that your receiver add all incoming requests to a queue to be processed (i.e. make a subsequent API call the the resource/ID identified in the Webhook) according to your own system limits. If you try to process all Webhook events immediately upon receiving them, this could lead to problems.
For example, at certain times of the year we bulk update all of our departure pricing. This bulk operation causes thousands (if not tens of thousands) of Webhook events to be generated in a very short period of time. Trying to process all of these Webhook events in real-time will likely cause problems, leading to your server to return a 500 or 504 error code.
You will also want to have your Webhook queue processing integrated with whatever throttling mechanism you are using to avoid hitting our rate-limits. This is another good reason for using a queue to process Webhook events.
If we receive 500 non 200 responses to outgoing Webhook events in a one hour time frame, we will send a warning email to the email address associated with that API account. If we receive an additional 250 non 200 responses to outgoing Webhook events in that same one hour time frame, we will disable the Webhooks for that API application. An automated email will be sent to the email address associated with that API account, indicating that we have disabled the Webhook for that application. You will need to email support to have the Webhook re-enabled.
The best ways to test webhooks:
- Webhooks Playground
- In the Dashboard, monitoring the last 500 request/response events that were sent to your application Webhook URL
- Writing to booking-related resources, and capturing the resulting webhooks which are fired through your changes.
Using the Webhooks Playground is the best way to
first test your server’s webhook listener to ensure you are receiving the
event and correctly processing the data contained within the body of the
webhook. These tests only contain a single item in the webhook body list of
Writing to resources
Testing of booking-related resources can be done via WRITE actions using
your test api key. A CREATE or UPDATE action on a resource will cause a
Webhook to be fired with events for one or more resources. This is a good way
to test making bookings, cancelling services, updating customers, etc…