Every failed TripWorks API request returns a stable error code you can branch on, and some requests return a decision instead of an error: a question TripWorks needs you to answer before it can finish. Reading the code, not the message, keeps your integration working when wording changes. Answering a decision means sending the same request again with your answer attached. This article explains both, with a worked example.
In this article
In this article
- What an error response contains and which codes to expect
- What a decision is, and why TripWorks asks instead of guessing
- How to answer a decision and resend the request
- How to check a request with a dry run before it saves anything
What does an error response look like?
A response always includes a success boolean. When a request fails, it also includes a human-readable message and a structured error object with a stable code. The message is written for people and its wording can change. The code is written for your software and stays the same, so branch on the code.
{
"success": false,
"message": "The key doesn't have the scope this request needs.",
"error": { "code": "insufficient_scope" }
}
Which error codes will you meet?
These are the codes you will see most often, and what to do about each:
-
insufficient_scope(status403). The key is valid but doesn't have permission for this object. Edit the key's permissions. See How API keys and access work. -
too_many_requests(status429). The key or the account went over an allowance. Pause, retry with a growing delay, and fetch less. See Build integrations that stay fast and efficient. -
validation_failed(status422). A value in your request isn't valid. The response lists each field that failed, so you can show or fix each one. -
unknown_fields. The request includes a field TripWorks doesn't accept for that operation. Remove it, or check its spelling in the API reference. -
not_found. The record you asked for doesn't exist. -
decision_required(status409). TripWorks needs a choice from you before it can finish. This is covered next.
Two more cases return a status without a record-level code: a request with no key or a malformed key gets a 401, and a filter or sort the object doesn't support gets a 422.
When you are tracking down a failure, note the request ID. Every request has one, and it's how you and TripWorks support find exactly what was sent and returned.
What is a decision?
Some actions can't be finished without a choice that only you can make. Adding guests to a trip that already has voucher codes is the clear example: should the voucher be reapplied to the new bookings, or should they be left as they are? TripWorks doesn't guess. It stops, tells you what it needs to know, and waits for your answer.
This is deliberate. The question is part of the response, so a program can answer it the same way a person does, and nothing is saved until every question has an answer. People adding guests in TripWorks see the same question as a prompt, and an AI assistant asks you.
How do you answer a decision?
You answer by sending the same request again, with your answers attached in a decisions object, keyed by each decision's kind.
-
Send the request as usual, for example
POST /rest/bookingsto add guests to a trip. -
Read the reply. If a choice is needed, the status is
409, the error code isdecision_required, anderror.decisionslists each decision with itskindand acontextthat describes the choice. - Choose an answer for each decision, by policy in your code or by asking your user.
-
Send the same request again with your answers in
decisions, keyed bykind. - Repeat if asked again. Answering one decision can reveal another, so keep going until the request succeeds.
The reply to step 2 looks like this:
{
"success": false,
"message": "A decision is needed before these bookings can be added.",
"error": {
"code": "decision_required",
"decisions": [
{ "kind": "voucher_reapply", "context": { } }
]
}
}
And the resend in step 4 carries the answer. For a voucher decision, the strategy must be exactly REAPPLY or KEEP_AS_IS, and tripVoucherCodeId must be a voucher code on that trip:
{
"trip_order_id": "0a1b2c3d-4e5f-8a7b-9c0d-1e2f3a4b5c6d",
"experience_customer_type_id": "9d8c7b6a-5e4f-8d3c-9b2a-1f0e9d8c7b6a",
"decisions": {
"voucher_reapply": { "strategy": "REAPPLY", "tripVoucherCodeId": 42 }
}
}
An answer that isn't recognized is refused with a 422 validation_failed before anything is created, and the response points at the field that failed, such as decisions.voucher_reapply.strategy.
Handling it in code
A small loop covers every case, however many questions come up:
let decisions = {};
for (let round = 0; round < 5; round++) {
const res = await post('/rest/bookings', { ...booking, decisions });
if (res.status !== 409) return res;
for (const d of res.body.error.decisions) {
decisions[d.kind] = await choose(d);
}
}
Your choose function is where your policy lives. It might always reapply vouchers, or it might ask a person. Keep a cap on the number of rounds, and log the request ID of each so you can follow the exchange later.
How do you check a request before it saves anything?
Add "dry_run": true to the request. TripWorks runs every check it would run for real, including whether the trip is locked, whether there is capacity, whether the ticket type is valid, and whether your decisions are valid. It saves nothing. A successful dry run replies with 200 and a summary of what would happen, such as how many bookings would be added.
Use a dry run to preview a booking before you commit, to find out which decisions will come up, and to test your handling of decisions without creating records.
Examples
-
Branch on the code. When a request fails, read
error.code. Pause ontoo_many_requests, fix permissions oninsufficient_scope, and show the field errors onvalidation_failed. -
Preview a booking. Send
POST /rest/bookingswithdry_runset totrue, read the checks that ran, and only then send it for real. -
Reapply a voucher. When you get
decision_requiredwith avoucher_reapplydecision, resend the request withdecisions.voucher_reapplyset toREAPPLYand the trip's voucher code. -
Leave bookings as they are. Answer the same decision with
KEEP_AS_ISto add the guests without touching the voucher. - Ask a person. In a back-office tool, show the decision's context to your agent and send their choice as the answer.
- Keep a trail. Log the request ID with every failure so support can find the exact request quickly.
Frequently asked questions
Why does the API return 409 instead of doing the action?
Because the action needs a choice that only you can make, such as whether to reapply a voucher. Returning 409 with decision_required means nothing is changed until you answer. It is the API equivalent of the question TripWorks would ask a person doing the same thing in the app.
Should my code read the message or the error code?
The error code. The message is written for people and its wording can change over time. The code is stable, so it is safe to branch on. Show the message to a person if you like, but let the code decide what your software does.
What is a decision kind?
A kind names the question being asked, such as voucher_reapply. You answer by putting your answer under that same name in decisions. The context next to each kind describes the choice, so your code or your user can answer it.
Is it safe to resend a request after a decision?
Yes. Nothing is saved until every decision has an answer, so resending doesn't create duplicates. TripWorks checks the request again from scratch each time, which is also why answering one decision can reveal another.
What does dry_run do?
It runs every check the real request would run, including capacity, the trip's lock state, the ticket type, and your decisions, and saves nothing. A successful dry run replies 200 with a summary of what would happen. Use it to preview an action or to test your decision handling.
What happens if I send an answer TripWorks doesn't recognize?
The request is refused with a 422 and the code validation_failed before anything is created. The response names the field that failed, such as decisions.voucher_reapply.strategy, so you can correct it and resend.
Where do I find the request ID for a failed request?
Every request has an ID that you can look up on the Logs tab of the REST API card, by pasting it into Find by request ID. Quote it when you contact TripWorks support so they can see exactly what was sent and returned.
Related
- Get started with the REST API
- Build integrations that stay fast and efficient
- How API keys and access work
- Does TripWorks have an API?
Built for attractions, tours & activities
Build on top of your booking data
See how attractions, tours, and activities connect their booking data to the tools they already use with TripWorks. Book a demo and make the switch.