Install
openclaw skills install travai-search-book-pay-flights-staysSearch flights and stays, then create card or crypto payments via TravAI. Use when the user asks about flights, travel planning, booking trips, finding accom...
openclaw skills install travai-search-book-pay-flights-staysUse this skill when the user wants to search travel options or book a flight/stay through TravAI.
If this skill conflicts with backend behavior, check the live OpenAPI schema:
https://api.travai.tech/openapi.json
Follow the live schema over this doc.
search_id (not search_hash)method ("CRYPTO" or "CARD") — not typeEXPIRED_OFFER, re-run the search and use fresh IDsoffer_id values from the new results; old offer IDs will not work with the new search_idhttps://api.travai.tech
All endpoints (except sign-in and sign-up) require:
Authorization: Bearer {access_token}
Three ways to authenticate:
POST /auth/signin
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret"
}
Response (200):
{
"access_token": "eyJhbG...",
"token_type": "bearer"
}
POST /auth/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret",
"first_name": "Jane",
"last_name": "Doe"
}
Response (200):
{
"message": "User created successfully",
"access_token": "eyJhbG...",
"token_type": "bearer"
}
GET /auth/me
Authorization: Bearer {access_token}
Response (200):
{
"user_id": 1,
"email": "user@example.com",
"role": "customer",
"is_active": true,
"created_at": "2025-01-01T00:00:00",
"currency": "USD"
}
Any failed request may return:
{ "detail": "Human-readable error description" }
Always check for detail in non-2xx responses and surface the message to the user.
POST /searches
Authorization: Bearer {access_token}
Content-Type: application/json
{
"type": "FLIGHT",
"origin_iata": "LON",
"destination_iata": "LIS",
"departure_date": "2025-10-10",
"return_date": "2025-10-17",
"customers": [
{ "type": "ADULT" },
{ "type": "CHILD", "age": 10 }
]
}
| Field | Required | Notes |
|---|---|---|
type | yes | "FLIGHT" |
origin_iata | yes | 3-letter IATA code (uppercase, e.g. "LON") |
destination_iata | yes | 3-letter IATA code (uppercase, e.g. "LIS") |
departure_date | yes | YYYY-MM-DD |
return_date | no | omit for one-way |
customers | yes | array of CustomerAge (see below) |
CustomerAge object:
| Field | Required | Notes |
|---|---|---|
type | no (default: ADULT) | ADULT, CHILD, SENIOR, HELD_INFANT |
age | required for CHILD and HELD_INFANT | integer |
Customer type rules:
HELD_INFANT — under 2 years oldCHILD — between 2 and 18 years oldADULT — defaultSENIOR — senior travellerResponse (200):
{
"search_id": "abc123",
"total_offers": 42,
"best_offers": [ /* top 3 offers */ ]
}
| Field | Description |
|---|---|
search_id | Identifier for this search — use in all subsequent calls |
total_offers | Total number of offers matching the search |
best_offers | The 3 best offers (sorted by price/quality) |
Direct-flight filtering: To find direct flights, only keep offers where the total segment count across all slices equals the number of slices (i.e. 1 segment per slice = no stops).
POST /searches
Authorization: Bearer {access_token}
Content-Type: application/json
{
"type": "STAY",
"location_anchor": {
"value": "Abu Dhabi",
"type": "city"
},
"check_in_date": "2026-03-05",
"check_out_date": "2026-03-10",
"rooms": 1,
"guests": 1,
"radius_km": 50
}
| Field | Required | Default | Notes |
|---|---|---|---|
type | yes | — | "STAY" |
location_anchor | yes | — | see StaysSearchAnchor below |
check_in_date | yes | — | YYYY-MM-DD |
check_out_date | yes | — | YYYY-MM-DD |
rooms | no | 1 | 1–8 |
guests | no | 1 | number of adult guests |
radius_km | no | 50 | search radius in km around the anchor |
StaysSearchAnchor object:
| Field | Required | Description |
|---|---|---|
value | yes | Reference point text, e.g. "Milano", "London center", "LHR" |
type | yes | One of: airport, city, city_center, region, address, place, others |
Anchor type guide:
airport — 3-letter IATA code (e.g. "LHR")city — city name (e.g. "Abu Dhabi")city_center — city centre / downtown (e.g. "London center")region — neighbourhood / district (e.g. "Manhattan")address — street address (e.g. "Via G.B. Pioda 10")place — landmark / point of interest (e.g. "Parque Edgar Sampaio Fontes")others — anything elseResponse (200):
{
"search_id": "def456",
"total_offers": 15,
"best_offers": [ /* top 3 offers */ ]
}
| Field | Description |
|---|---|
search_id | Identifier for this search — use in all subsequent calls |
total_offers | Total number of offers matching the search |
best_offers | The 3 best offers |
GET /searches/{search_id}/offers?limit=20&offset=0
Authorization: Bearer {access_token}
| Parameter | Required | Default | Description |
|---|---|---|---|
search_id | yes (path) | — | search_id from POST /searches |
limit | no (query) | 20 | Max offers to return (1–200) |
offset | no (query) | 0 | Offset for pagination |
Response (200):
{
"total_offers": 42,
"offers": [ /* paginated list of offer summaries */ ]
}
| Field | Description |
|---|---|
total_offers | Total number of offers for this search |
offers | Paginated slice of offer summaries |
GET /searches/{search_id}/offers/{offer_id}
Authorization: Bearer {access_token}
Response (200): a single offer summary object.
GET /searches/{search_id}/offers/{offer_id}/pricing
Authorization: Bearer {access_token}
| Path parameter | Description |
|---|---|
search_id | search_id from POST /searches |
offer_id | Offer ID from best_offers or the paginated offers list |
Response (200): a flat offer summary dict with full pricing details (final price, segments, airlines, etc.).
Error (410): returns { "detail": "EXPIRED_OFFER" } if the offer is no longer available. When this happens, inform the user and re-run the search.
GET /searches/{search_id}/offers/{offer_id}/rates
Authorization: Bearer {access_token}
| Path parameter | Description |
|---|---|
search_id | search_id from POST /searches |
offer_id | Hotel offer ID from best_offers or the paginated offers list |
Response (200): a list of room objects, each containing room details (name, description, size, max occupancy, photos, beds) and nested rates array with pricing, payment type, and cancellation conditions.
GET /searches/{search_id}/offers/{offer_id}/rates/{rate_id}/pricing
Authorization: Bearer {access_token}
| Path parameter | Description |
|---|---|
search_id | search_id from POST /searches |
offer_id | Hotel offer ID |
rate_id | Rate ID from the rates list |
Response (200): a stay summary with full pricing details (total amount, currency, accommodation info, room breakdown).
Error (410): returns { "detail": "EXPIRED_OFFER" } if the rate is no longer available. When this happens, inform the user and re-run the search.
GET /searches/{search_id}/offers/{offer_id}/photos
No authentication required. Returns an interactive HTML photo gallery for the hotel.
| Path parameter | Description |
|---|---|
search_id | search_id from POST /searches |
offer_id | Hotel offer ID |
How to present to the user: Build the full gallery URL and share it as a clickable link:
https://api.travai.tech/searches/{search_id}/offers/{offer_id}/photos
Do not share individual photo URLs from the API response — they are CDN-signed and will not load in the browser. Always use the gallery link instead.
The gallery features keyboard navigation (arrow keys), click-to-advance, and a thumbnail strip.
Individual photos can also be accessed at /searches/{search_id}/offers/{offer_id}/photos/{index} (0-based) if needed for embedding.
GET /tokens
Authorization: Bearer {access_token}
Response (200): a flat list of supported crypto tokens. Each item represents one asset on one blockchain.
[
{ "assetId": "nep141:wrap.near", "blockchain": "NEAR", "symbol": "NEAR" },
{ "assetId": "nep141:usdc.near", "blockchain": "NEAR", "symbol": "USDC" },
{ "assetId": "eth:0xabc...", "blockchain": "Ethereum", "symbol": "USDC" }
]
| Field | Description |
|---|---|
assetId | Token identifier — use as origin_asset in a CRYPTO payment |
blockchain | Blockchain network the token lives on |
symbol | Token symbol (e.g. "NEAR", "USDC") |
Token selection guidance:
blockchain when presenting to the usersymbol + blockchainassetId before proceedingAfter the user picks a token, ask for their wallet address on that token's blockchain to use as refund_to.
POST /payments
Authorization: Bearer {access_token}
Content-Type: application/json
Payment methods: CRYPTO or CARD. Use the method field (not type).
{
"method": "CRYPTO",
"origin_asset": "{assetId from GET /tokens}",
"refund_to": "{user wallet address}",
"offers": [ /* see offer object below */ ]
}
| Field | Required | Description |
|---|---|---|
method | yes | "CRYPTO" |
origin_asset | yes | assetId of the token selected by the user from GET /tokens |
refund_to | yes | User's wallet address on the selected token's network — refunds go here in the same currency |
offers | yes | Selected offers to pay for |
Response (200):
{
"payment_id": 123,
"itinerary_id": 456,
"deposit_address": "travai.near",
"amount_in": "1000000",
"amount_in_formatted": "1.00",
"total_amount": 100,
"currency": "USD"
}
| Field | Description |
|---|---|
payment_id | Payment identifier (use to check status via GET /payments/{payment_id}) |
itinerary_id | Itinerary identifier (use to get confirmations via GET /itineraries/{itinerary_id}/offers) |
deposit_address | Address to send tokens to |
amount_in_formatted | Exact amount of tokens to send — must match exactly, do not round |
amount_in | Raw token amount (integer string) |
total_amount | Fiat equivalent in smallest unit (cents) |
currency | ISO currency code (e.g. "USD") |
{
"method": "CARD",
"success_url": "https://app.travai.tech/?payment=success",
"cancel_url": "https://app.travai.tech/?payment=cancel",
"offers": [ /* see offer object below */ ]
}
| Field | Required | Description |
|---|---|---|
method | yes | "CARD" |
success_url | yes | Always "https://app.travai.tech/?payment=success" |
cancel_url | yes | Always "https://app.travai.tech/?payment=cancel" |
offers | yes | Selected offers to pay for |
Response (200):
{
"payment_id": 123,
"itinerary_id": 456,
"payment_url": "https://checkout.stripe.com/...",
"total_amount": 100,
"currency": "USD"
}
| Field | Description |
|---|---|
payment_id | Payment identifier (use to check status via GET /payments/{payment_id}) |
itinerary_id | Itinerary identifier (use to get confirmations via GET /itineraries/{itinerary_id}/offers) |
payment_url | URL for the user to complete card payment |
total_amount | Amount in smallest currency unit (cents) |
currency | ISO currency code (e.g. "USD") |
GET /payments/{payment_id}
Authorization: Bearer {access_token}
| Path parameter | Description |
|---|---|
payment_id | Payment ID from the POST /payments response |
Response (200): full payment record including status, fiat_amount, fiat_currency, token_amount, token_asset, created_at, updated_at.
Payment statuses: PENDING, PENDING_DEPOSIT, INCOMPLETE_DEPOSIT, KNOWN_DEPOSIT_TX, PROCESSING, SUCCESS, REFUNDED, FAILED, ERROR.
GET /itineraries/{itinerary_id}/offers
Authorization: Bearer {access_token}
| Path parameter | Description |
|---|---|
itinerary_id | Itinerary ID from the POST /payments response |
Response (200): list of itinerary offer items, each with itinerary_offer_id, type, provider, price, currency, status, booking_summary, created_at.
offers array in payment request){
"type": "FLIGHT",
"search_id": "abc123",
"offer_id": "offer_xyz",
"customers": [
{
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"date_of_birth": "1989-04-23",
"gender": "FEMALE",
"nationality": "UA",
"country_code": "380",
"phone_number": "632816433",
"title": "MS"
}
]
}
| Field | Required | Description |
|---|---|---|
type | yes | "FLIGHT" or "STAY" |
search_id | yes | search_id from the search step |
offer_id | yes (FLIGHT) | Offer ID from the search results |
rate_id | yes (STAY) | Rate ID from the hotel rates step |
customers | yes | Traveller details (see below) |
customers array)| Field | Required | Example |
|---|---|---|
first_name | yes | Jane |
last_name | yes | Doe |
title | yes | MR, MRS, MS, DR |
gender | yes | MALE or FEMALE |
date_of_birth | yes | 1989-04-23 (YYYY-MM-DD) |
nationality | yes | UA, GB, US (ISO 3166-1 alpha-2) |
country_code | yes | 380 (phone country code, no +) |
phone_number | yes | 632816433 (without country code) |
email | yes | jane@example.com |
IMPORTANT: first_name, last_name, and date_of_birth must exactly match the traveller's international passport. Always remind the user of this before collecting their details.
POST /searches with type: "FLIGHT"GET /searches/{search_id}/offers/{offer_id}/pricingGET /tokens, present grouped by blockchain (prefer stablecoins), collect wallet address for refundPOST /payments with method fieldamount_in_formatted + token + deposit_address + refund walletpayment_urlGET /payments/{payment_id}POST /searches with type: "STAY"https://api.travai.tech/searches/{search_id}/offers/{offer_id}/photos — do NOT share raw photo URLsGET /searches/{search_id}/offers/{offer_id}/rates when user picks a hotelGET .../rates/{rate_id}/pricing when user picks a raterate_id in offer object, use method fieldGET /payments/{payment_id}Many cities are served by multiple airports. Always check this mapping before searching — run parallel searches for all relevant airports and compare results.
If a city is not listed below but the user mentions a country or region, use your knowledge of IATA codes to identify all relevant airports.
| City / Country | Airports (IATA) |
|---|---|
| London / UK | LHR, LGW, STN, LTN, LCY |
| Manchester / UK | MAN, LPL (Liverpool, 1h) |
| Edinburgh / UK | EDI, GLA (Glasgow, 1h) |
| Paris / France | CDG, ORY, BVA |
| Nice / France | NCE |
| Lyon / France | LYS |
| Amsterdam / Netherlands | AMS, EIN (Eindhoven), RTM (Rotterdam) |
| Brussels / Belgium | BRU, CRL (Charleroi) |
| Berlin / Germany | BER |
| Frankfurt / Germany | FRA, HHN (Hahn) |
| Munich / Germany | MUC, NUE (Nuremberg, nearby) |
| Düsseldorf / Germany | DUS, CGN (Cologne, 1h), DTM (Dortmund) |
| Hamburg / Germany | HAM |
| Zurich / Switzerland | ZRH, BSL (Basel), BRN (Bern) |
| Geneva / Switzerland | GVA |
| Vienna / Austria | VIE, BTS (Bratislava, 80km) |
| Dublin / Ireland | DUB, SNN (Shannon), ORK (Cork) |
| City / Country | Airports (IATA) |
|---|---|
| Milan / Italy | MXP, LIN, BGY (Bergamo) |
| Rome / Italy | FCO, CIA (Ciampino) |
| Naples / Italy | NAP |
| Venice / Italy | VCE, TSF (Treviso) |
| Florence / Italy | FLR, PSA (Pisa, 1h) |
| Bologna / Italy | BLQ |
| Catania / Sicily | CTA, PMO (Palermo) |
| Madrid / Spain | MAD |
| Barcelona / Spain | BCN, GRO (Girona), REU (Reus) |
| Malaga / Spain | AGP |
| Valencia / Spain | VLC |
| Seville / Spain | SVQ |
| Alicante / Spain | ALC |
| Palma de Mallorca / Spain | PMI |
| Lisbon / Portugal | LIS |
| Porto / Portugal | OPO |
| Athens / Greece | ATH |
| Thessaloniki / Greece | SKG |
| Istanbul / Turkey | IST, SAW (Sabiha Gökçen) |
| Antalya / Turkey | AYT |
| City / Country | Airports (IATA) |
|---|---|
| Stockholm / Sweden | ARN, BMA, NYO (Skavsta), VST (Västerås) |
| Copenhagen / Denmark | CPH, BLL (Billund), AAR (Aarhus) |
| Oslo / Norway | OSL, TRF (Torp), RYG (Rygge) |
| Helsinki / Finland | HEL, TMP (Tampere) |
| Reykjavik / Iceland | KEF, RKV |
| City / Country | Airports (IATA) |
|---|---|
| Prague / Czech Republic | PRG |
| Warsaw / Poland | WAW, WMI (Modlin) |
| Krakow / Poland | KRK |
| Gdansk / Poland | GDN |
| Wroclaw / Poland | WRO |
| Budapest / Hungary | BUD |
| Bucharest / Romania | OTP, BBU (Băneasa) |
| Sofia / Bulgaria | SOF |
| Kyiv / Ukraine | KBP, IEV (Zhuliany) |
| Belgrade / Serbia | BEG |
| Zagreb / Croatia | ZAG |
| Split / Croatia | SPU |
| Dubrovnik / Croatia | DBV |
| Riga / Latvia | RIX |
| Vilnius / Lithuania | VNO |
| Tallinn / Estonia | TLL |
| Bratislava / Slovakia | BTS, VIE (Vienna, 60km) |
| City / Country | Airports (IATA) |
|---|---|
| Dubai / UAE | DXB, DWC (Al Maktoum) |
| Abu Dhabi / UAE | AUH |
| Doha / Qatar | DOH |
| Riyadh / Saudi Arabia | RUH |
| Jeddah / Saudi Arabia | JED |
| Muscat / Oman | MCT |
| Bahrain | BAH |
| Kuwait | KWI |
| Amman / Jordan | AMM |
| Tel Aviv / Israel | TLV |
| Beirut / Lebanon | BEY |
| Cairo / Egypt | CAI |
| Sharm El Sheikh / Egypt | SSH |
| Hurghada / Egypt | HRG |
| City / Country | Airports (IATA) |
|---|---|
| Tokyo / Japan | NRT (Narita), HND (Haneda) |
| Osaka / Japan | KIX (Kansai), ITM (Itami) |
| Seoul / South Korea | ICN (Incheon), GMP (Gimpo) |
| Beijing / China | PEK (Capital), PKX (Daxing) |
| Shanghai / China | PVG (Pudong), SHA (Hongqiao) |
| Guangzhou / China | CAN |
| Shenzhen / China | SZX |
| Hong Kong | HKG |
| Taipei / Taiwan | TPE (Taoyuan), TSA (Songshan) |
| City / Country | Airports (IATA) |
|---|---|
| Bangkok / Thailand | BKK (Suvarnabhumi), DMK (Don Mueang) |
| Phuket / Thailand | HKT |
| Chiang Mai / Thailand | CNX |
| Singapore | SIN |
| Kuala Lumpur / Malaysia | KUL, SZB (Sultan Abdul Aziz Shah) |
| Jakarta / Indonesia | CGK (Soekarno-Hatta) |
| Bali / Indonesia | DPS |
| Manila / Philippines | MNL |
| Ho Chi Minh City / Vietnam | SGN |
| Hanoi / Vietnam | HAN |
| Phnom Penh / Cambodia | PNH |
| Siem Reap / Cambodia | REP |
| Yangon / Myanmar | RGN |
| City / Country | Airports (IATA) |
|---|---|
| Delhi / India | DEL |
| Mumbai / India | BOM |
| Bangalore / India | BLR |
| Colombo / Sri Lanka | CMB |
| Maldives | MLE |
| Kathmandu / Nepal | KTM |
| Almaty / Kazakhstan | ALA |
| Tbilisi / Georgia | TBS |
| Yerevan / Armenia | EVN |
| Baku / Azerbaijan | GYD |
| Tashkent / Uzbekistan | TAS |
| City / Country | Airports (IATA) |
|---|---|
| Johannesburg / South Africa | JNB, HLA (Lanseria) |
| Cape Town / South Africa | CPT |
| Nairobi / Kenya | NBO |
| Addis Ababa / Ethiopia | ADD |
| Casablanca / Morocco | CMN |
| Marrakech / Morocco | RAK |
| Tunis / Tunisia | TUN |
| Lagos / Nigeria | LOS |
| Accra / Ghana | ACC |
| Dar es Salaam / Tanzania | DAR |
| Zanzibar / Tanzania | ZNZ |
| Mauritius | MRU |
| City / Country | Airports (IATA) |
|---|---|
| New York / USA | JFK, EWR (Newark), LGA (LaGuardia) |
| Los Angeles / USA | LAX, BUR (Burbank), LGB (Long Beach), ONT, SNA (Orange County) |
| San Francisco / USA | SFO, OAK (Oakland), SJC (San Jose) |
| Chicago / USA | ORD (O'Hare), MDW (Midway) |
| Miami / USA | MIA, FLL (Fort Lauderdale), PBI (West Palm Beach) |
| Washington DC / USA | IAD (Dulles), DCA (Reagan), BWI (Baltimore) |
| Dallas / USA | DFW, DAL (Love Field) |
| Houston / USA | IAH, HOU (Hobby) |
| Boston / USA | BOS |
| Seattle / USA | SEA |
| Atlanta / USA | ATL |
| Denver / USA | DEN |
| Las Vegas / USA | LAS |
| Honolulu / USA | HNL |
| Toronto / Canada | YYZ, YTZ (Billy Bishop) |
| Montreal / Canada | YUL |
| Vancouver / Canada | YVR |
| Mexico City / Mexico | MEX, NLU (Felipe Ángeles) |
| Cancun / Mexico | CUN |
| City / Country | Airports (IATA) |
|---|---|
| São Paulo / Brazil | GRU (Guarulhos), CGH (Congonhas), VCP (Campinas) |
| Rio de Janeiro / Brazil | GIG (Galeão), SDU (Santos Dumont) |
| Buenos Aires / Argentina | EZE (Ezeiza), AEP (Aeroparque) |
| Santiago / Chile | SCL |
| Lima / Peru | LIM |
| Bogota / Colombia | BOG |
| Medellin / Colombia | MDE, EOH (Olaya Herrera) |
| Cartagena / Colombia | CTG |
| City / Country | Airports (IATA) |
|---|---|
| Sydney / Australia | SYD |
| Melbourne / Australia | MEL, AVV (Avalon) |
| Brisbane / Australia | BNE |
| Perth / Australia | PER |
| Auckland / New Zealand | AKL |
| Queenstown / New Zealand | ZQN |
| Fiji | NAN |
This is the core of intelligent search. Always follow this protocol.
Before searching, check the Nearby Airports Database for both origin and destination. If the city has multiple airports, plan parallel searches.
Example: User says "London to Milan" — search LHR->MXP, LHR->LIN, LHR->BGY, LGW->MXP, LGW->LIN, LGW->BGY, STN->MXP, STN->BGY in parallel. Surface cheapest overall.
Always launch multiple searches simultaneously using parallel tool calls:
Flexible date matrix example (+/-1 day, 1 origin, 1 destination = 3 searches):
If user says they are flexible or "around that date", go +/-2 days (5 searches).
After collecting all results:
If a search returns 0 offers:
After showing results, if prices are high (>$300 for short-haul, >$700 for medium-haul), proactively say:
"Prices on this date are on the higher side. Would you like me to check +/-1-2 days to find a cheaper option?"
Act like an experienced travel agent, not a search engine.
Always start with a header containing route, dates, passengers, and total offers found.
Template:
## [City] [IATA] -> [City] [IATA] | [Departure date] - [Return date] | [N] adults
Found [X] offers. Here are the best:
All results are divided into three groups in strict order:
Group 1 — Cheapest
Header: ### Cheapest
Group 2 — Fastest
Header: ### Fastest
Group 3 — Other options
Header: ### Other options
Round trip:
| Route out | Departure -> Arrival (duration) | Class | Route back | Departure -> Arrival (duration) | Class | Price |
|---|
One way:
| Route | Departure -> Arrival (duration) | Class | Price |
|---|
Column rules:
Route (out / back):
Bangkok BKK -> Kuala Lumpur KUL -> Singapore SIN, Amsterdam AMS -> Barcelona BCN (direct)Departure -> Arrival (duration):
+1 after arrival time and include arrival date06/10 06:00 -> 11:25 (4h 25m)06/10 23:00 -> 06/11 03:30 +1 (4h 30m)Class: Economy / Business / First
Price:
After each table — one italic line with key details.
Key info to include per offer:
Caption template:
*[Airline] · baggage included · non-refundable · ...*
Next-day arrival — always use +1:
+1 goes right after arrival time in table cell05/11 23:25 -> 05/12 06:25 +1After all tables — a summary block with:
5.1 Cheapest
Cheapest: [Airline] — [price]. [pros]. But: [cons].
5.2 Best pick A reasoned recommendation — not always the cheapest. Consider: direct vs layover, refund, baggage, price difference.
Best pick: [Airline], [route] — [price] ([difference] more). [conditions]. [One-sentence rationale].
5.3 Warnings (if any) Each warning — separate line. Must mention:
5.4 Call to action Last line — always offer to proceed to booking:
Want to book? Name your preferred option and I'll request exact pricing and details.
Always respond in the language the user is writing in. The structure stays the same regardless of language.
| Error | Cause | Fix |
|---|---|---|
422 method field required | Sent type instead of method in payment request | Use "method": "CRYPTO" or "method": "CARD" |
Search ... not found | Search expired or invalid search_id | Re-run POST /searches and use fresh search_id |
404 Offer ... not found in search | Offer ID from a different/old search | Re-run search and use offer_id values from the new results |
410 EXPIRED_OFFER | Offer or rate is no longer available | Re-run search, pick a fresh offer, then retry |
| 422 on signup | Sent name instead of first_name/last_name | Use separate first_name and last_name fields |
| Unsupported token | Token/chain mismatch | Call GET /tokens and pick a valid assetId |
| Redirect loop on POST | Trailing-slash routing issue | Ensure POST goes to /payments (no trailing slash) |
Booking changes and cancellations cannot be done via the API. The user must contact support directly:
Response time: within 24 hours.
When a user asks to change or cancel a booking, do not attempt any API calls — instead, direct them to one of the support channels above.
For any technical issues, questions, or problems with the API:
We will reply within 24 hours.