Trawish APIdeveloper docs

Odaduu · DMC QuoteOps · v1

Odaduu Public API

A small, stable HTTP surface for B2B partners to pull booking status, browse our reference catalogs, and submit new RFQs into the Odaduu sales pipeline. Bearer-authenticated, JSON in and JSON out, cursor-paginated where it matters.

Base URLhttps://builder.trawish.com/api/public/v1

Overview

Every request is bearer-authenticated. Keys are minted per partner and (optionally) bound to a specific agent. An agent-scoped key can only see its own bookings and can only submit RFQs on that agent’s behalf. Reference catalogs are always org-wide.

Default rate limit is 1000 requests per rolling hour per key. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining headers.

Authentication

Send your key in the Authorization header:

Authorization: Bearer odaduu_live_ab12CDEfghIJKlmnOP34

We show the full secret once at mint time. If you lose it, revoke the key and mint a fresh one; we cannot recover the plaintext. The first 20 characters (the odaduu_live_ab12CDEf prefix) stay visible in our admin so we can identify a key without seeing the secret.

Response envelope

All successful responses wrap the payload:

{
  "ok": true,
  "data": { "id": "cl...", "ref": "OD-2026-4132", "status": "quote_sent", ... },
  "meta": { "count": 10, "nextCursor": "eyJ0Ijoi..." }
}

Errors follow the same shape:

{
  "ok": false,
  "error": { "code": "unauthorized", "message": "invalid API key" }
}

List endpoints return an opaque meta.nextCursor. Pass it back on the next request to page forward; its value is stable across concurrent writes.

Endpoints

Health

GET/health

Verifies your key and echoes the tenant it resolves to. Hit this first after mint.

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/health

Response

{
  "ok": true,
  "data": {
    "status": "ok",
    "orgId": "cmq11ccnc0000f9mjwylvqaxs",
    "agentId": null,
    "rateLimitPerHour": 1000,
    "apiVersion": "v1",
    "serverTime": "2026-09-03T09:20:00.000Z"
  }
}

Bookings

GET/bookings

List bookings visible to your key, newest first. Agent-scoped keys see only their own bookings.

ParamTypeNotes
statusstring, optionaldraft_quote, quote_sent, client_confirmed, ops_in_progress, travel_completed, cancelled
limitint, optionalPage size (default 25, max 100)
cursorstring, optionalOpaque token from a prior response's meta.nextCursor

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/bookings?limit=10&status=quote_sent

Response

{
  "ok": true,
  "data": [
    {
      "id": "cmtl0q9b20004j39gdw0wk6q7",
      "ref": "OD-2026-7245",
      "status": "draft_quote",
      "guestName": null,
      "arrivalDate": "2026-11-05",
      "departureDate": "2026-11-14",
      "pax": { "adults": 2, "children": 1, "infants": 0 },
      "destination": { "code": "JP", "name": "Japan" },
      "agentId": null,
      "createdAt": "2026-09-02T06:54:08.327Z",
      "updatedAt": "2026-09-02T06:54:08.327Z"
    }
  ],
  "meta": {
    "count": 1,
    "nextCursor": "eyJ0IjoiMjAyNi0wOS0wMlQwNjo1NDowOC4zMjdaIiwiaSI6ImNtdGpxcTdyZDA5OGF3ZnVjMG96YTU5ZW4ifQ"
  }
}

meta.nextCursor is null when the last page has been reached.

GET/bookings/{id}

Full detail for one booking, including the currently active quote's total in your sell currency.

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/bookings/cmtl0q9b20004j39gdw0wk6q7

Response

{
  "ok": true,
  "data": {
    "id": "cmtl0q9b20004j39gdw0wk6q7",
    "ref": "OD-2026-7245",
    "status": "draft_quote",
    "guestName": "Mr. Sharma",
    "arrivalDate": "2026-11-05",
    "departureDate": "2026-11-14",
    "pax": { "adults": 2, "children": 1, "infants": 0 },
    "destination": { "code": "JP", "name": "Japan" },
    "agentId": null,
    "focPax": 0,
    "childAges": [8],
    "leadSource": "email",
    "agentRequirements": "Vegetarian meals",
    "activeQuote": {
      "version": 1,
      "variant": "standard",
      "label": null,
      "totalSell": 0,
      "sellCurrency": "INR",
      "marginPercent": 0,
      "validUntil": null,
      "sentAt": null,
      "sentChannel": null
    },
    "createdAt": "2026-09-02T06:54:08.327Z",
    "updatedAt": "2026-09-02T06:54:08.327Z"
  }
}
POST/quotes

Submit an RFQ. Creates a DRAFT_QUOTE booking on our side; sales picks it up in the internal dashboard and builds the itinerary.

Request

curl -X POST -H 'Authorization: Bearer odaduu_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
  "destinationCode": "JP",
  "guestName": "Mr. Sharma",
  "arrivalDate": "2026-11-05",
  "departureDate": "2026-11-14",
  "pax": { "adults": 2, "children": 1, "infants": 0 },
  "childAges": [8],
  "leadSource": "EMAIL",
  "agentRequirements": "Vegetarian meals"
}' \
  https://builder.trawish.com/api/public/v1/quotes

Response

{
  "ok": true,
  "data": {
    "id": "cmtl9a3fk00046i2aupcodp19",
    "ref": "OD-2026-4761",
    "status": "draft_quote",
    "guestName": "Mr. Sharma",
    "arrivalDate": "2026-11-05",
    "departureDate": "2026-11-14",
    "pax": { "adults": 2, "children": 1, "infants": 0 },
    "destination": { "code": "JP", "name": "Japan" },
    "agentId": null,
    "focPax": 0,
    "childAges": [8],
    "leadSource": "email",
    "agentRequirements": "Vegetarian meals",
    "activeQuote": {
      "version": 1,
      "variant": "standard",
      "label": null,
      "totalSell": 0,
      "sellCurrency": "INR",
      "marginPercent": 0,
      "validUntil": null,
      "sentAt": null,
      "sentChannel": null
    },
    "createdAt": "2026-09-03T08:20:00.000Z",
    "updatedAt": "2026-09-03T08:20:00.000Z"
  }
}

Returns the created BookingDetail. activeQuote is present but empty (days[] and hotels[] are []) until sales fills it in.

Reference catalogs

GET/destinations

Destinations available for RFQ submission.

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/destinations

Response

{
  "ok": true,
  "data": [
    {
      "id": "cmq11cczg0002f9mjcvsmp9gf",
      "code": "JP",
      "name": "Japan",
      "currency": "JPY"
    }
  ],
  "meta": { "count": 1 }
}
GET/hotels

Hotel catalog. Filter by city or free-text name match.

ParamTypeNotes
citystring, optionalCase-insensitive equality
qstring, optionalSubstring match on name
limitint, optionalDefault 100, max 200

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/hotels?city=Tokyo&limit=10

Response

{
  "ok": true,
  "data": [
    {
      "id": "hot_abc123",
      "name": "Park Hyatt Tokyo",
      "city": "Tokyo",
      "country": "Japan",
      "address": "3-7-1-2 Nishi Shinjuku"
    }
  ],
  "meta": { "count": 1 }
}
GET/sightseeings

Paid activities and free stops.

ParamTypeNotes
citystring, optionalMatches against the catalog's cities[] array
categorystring, optionalPAID or FREE
kindstring, optionalSIGHTSEEING, ACTIVITY, SHOPPING, WALKING
qstring, optionalSubstring match on name

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/sightseeings?city=Kyoto&category=PAID

Response

{
  "ok": true,
  "data": [
    {
      "id": "sight_xyz789",
      "name": "Fushimi Inari Shrine Tour",
      "category": "PAID",
      "kind": "SIGHTSEEING",
      "cities": ["Kyoto"],
      "description": "Guided walking tour of the iconic torii gates.",
      "adultPrice": 4500,
      "childPrice": 2500,
      "childAgeFrom": 4,
      "childAgeTo": 11,
      "durationMinutes": 120,
      "openingHours": { "from": "08:00", "to": "17:00" },
      "closedDaysOfWeek": [],
      "timedEntry": false,
      "mustSee": true
    }
  ],
  "meta": { "count": 1 }
}
GET/fullday-tours

Private full-day tour catalog with per-seater JPY pricing.

ParamTypeNotes
citystring, optionalMatches against the catalog's cities[] array
qstring, optionalSubstring match on name

Request

curl -H 'Authorization: Bearer odaduu_live_...' \
  https://builder.trawish.com/api/public/v1/fullday-tours?city=Hiroshima

Response

{
  "ok": true,
  "data": [
    {
      "id": "mig_2026_08_14_ft_hiroshima_local_751cfe93b7e9",
      "name": "Hiroshima day tour with local guide",
      "cities": ["Hiroshima"],
      "description": "Peace Memorial, Miyajima ferry, local lunch included.",
      "pricePerSeater": {
        "seater6": 85000,
        "seater9": 110000,
        "seater13": 140000,
        "seater22": 210000,
        "seater29": null,
        "seater45": null
      }
    }
  ],
  "meta": { "count": 1 }
}

Error codes

Codes are stable across versions. Messages may drift; match on codes.

CodeHTTPWhen it fires
unauthorized401Missing, malformed, unknown, or expired key.
forbidden403Key is valid but not permitted for this resource.
not_found404Booking (or similar) does not exist or is out of scope for your key.
bad_request400Payload validation failed. `error.details` carries specifics.
rate_limited429Hourly quota exceeded. `error.details.retryAfterSec` tells you when to retry.
conflict409Reserved for write endpoints; not raised by v1 today.
internal_error500Server-side fault. Please report with `error.details.hint`.
not_implemented501Public API is disabled on this deployment.

Troubleshooting 401

Almost every failed integration lands here first. Work down the list; nine times out of ten it is one of the top three.

  1. Prefix mismatch. Your key must start with odaduu_live_. Anything else returns "malformed API key" before we even look it up.
  2. Missing scheme. The header value must be Bearer <key> with the literal word Bearer and one space. No Token, no Basic.
  3. Wrong host. The base URL is exactly https://builder.trawish.com/api/public/v1. Any other host will 404 or 401 depending on how your DNS resolves.
  4. Key revoked or expired. "invalid API key" means the shape passed but the DB lookup failed. Ask your account manager to check Settings, API keys for the row.
  5. Copy-paste artifact. A trailing newline, a zero-width space, or a curly quote around the key sneaks through Notion and Slack. Retype it, or paste through echo -n "$KEY" | wc -c to check the length: a valid key is 39 characters.

Still stuck? Send your keyPrefix (never the full secret) plus the timestamp of a failing request to [email protected] and we can look up the specific 401 in our logs.

Getting started

  1. Ask your Odaduu account manager at [email protected] to mint you a key. They will send you the full secret once; store it in your secret manager immediately.
  2. Run the /health call in the Endpoints section to confirm auth is wired up and your key resolves to the right tenant.
  3. Poll /bookings to pull your live pipeline, or hit /quotes to open new RFQs directly from your system.
  4. Watch X-RateLimit-Remaining on every response and back off when it approaches zero. A 429 comes with retryAfterSec in the error details.