{"openapi":"3.1.0","info":{"title":"Apiq — Unified Travel API","version":"2026-09-30","x-contract-version":"1.64.0","description":"One API for searching, pricing, holding, booking and servicing travel across every product: flights, hotels, cars, transfers, experiences, cruises, packages and more. Every route carries the /v1 prefix. Authenticate with your API key in the X-Api-Key header (mo_test_ keys call the sandbox, mo_live_ keys production), or exchange the key for a short-lived Bearer token at POST /v1/oauth/token. Travel routes take the envelope { context, message: { intent } } and answer { context, message }, with the result directly under message. Errors are a non-2xx ApiErrorResponse: branch on its code and honour its retryable flag, never on the HTTP status alone. Versioning is date-based: the version string is the contract date, and a breaking change ships as a new date, with the old one kept for 12 months behind Deprecation and Sunset headers.\n","contact":{"name":"Apiq API support","email":"connect-api@mondee.com"}},"servers":[{"url":"https://api.apiq.example","description":"Production. The production host is issued to each tenant at go-live; this URL is a placeholder until then. All routes carry the /v1 prefix in the path."},{"url":"https://apiq-sandbox.tripproagencysites.com","description":"Sandbox. Call it with a sandbox key (mo_test_) in the X-Api-Key header; nothing here reaches production."}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]},{"oauthClientCredentials":[]}],"tags":[{"name":"identity","description":"Sign in, refresh and end sessions, read the signed-in caller, and create, rotate and revoke API keys; these routes take and answer plain JSON, not the travel envelope."},{"name":"discovery","description":"The public discovery documents an agent reads before it holds any credential: the signed onboarding manifest and the key set that verifies it."},{"name":"devices","description":"Register and remove the push-notification devices of a signed-in person's mobile apps; a human session only, never an API key."},{"name":"shop","description":"Search every product, poll for results, and read offer detail, schedules, seat maps, fare rules and ancillaries."},{"name":"price","description":"Confirm an offer's current price and hold it for a while before booking."},{"name":"config","description":"Your commercial configuration: the markup rules applied to every price your customers see, versioned and simulated before you publish them."},{"name":"orders","description":"Book an offer, then read, change and cancel the order."},{"name":"enquiries","description":"Send a lead to a supplier that takes enquiries instead of bookings; nothing is priced, held or paid."},{"name":"payments","description":"Pay for an order with a card tokenized by the payment provider, and confirm the payment."},{"name":"wallets","description":"Your prepaid wallet, one per settlement currency: its balance, its movements and top-ups."},{"name":"reference","description":"Reference data and static content: places, airports and other lookups, and what each product supports today."},{"name":"events","description":"Subscribe to webhooks and replay the events your account has received."},{"name":"agents","description":"Proposals an AI agent submits for a signed-in person to approve or reject; an agent proposes, a person commits."},{"name":"enrichment","description":"Read-only travel data such as flight emissions and status, ratings, weather, holidays and entry requirements; it never blocks a booking."},{"name":"supplier","description":"The supplier's own sign-in and the bookings routed to that supplier, to confirm, decline and fulfil; a supplier session only."},{"name":"admin","description":"The customer console's own API: team, audit, queues, reports, statements, rules, usage, branding and credentials; a console session only, never an API key."},{"name":"operator","description":"The platform operators' API: tenant lifecycle, entitlements, two-person approvals and kill switches; an operator session only."},{"name":"dining-partner","description":"The restaurant-partner side of dining: guests, point of sale, reservation sync, menus, profiles, reviews and onboarding, for the restaurants a tenant has linked."}],"paths":{"/v1/search":{"post":{"operationId":"search","x-mcp":{"expose":"tool","toolset":"public","name":"v1_search","class":"read","autonomy":"L0","tasks":["search_trips"],"example":"{\"domain\":\"FLT\",\"intent\":{\"slices\":[{\"origin\":\"GRU\",\"destination\":\"MIA\",\"date\":\"2026-11-10\"}],\"travelers\":[{\"type\":\"ADT\"},{\"type\":\"CHD\",\"age\":9}],\"cabin\":\"ECONOMY\"}}"},"tags":["shop"],"summary":"Fan-out search entry for every product (03 §3 #1)","description":"Absorbs /v1/calendar via intent.resultShape OFFERS | CALENDAR. Async-by-default products (FLT, CRU, PKG, DPKG, TOUR) and budget-exceeded fan-outs return the 202 form (03 §5, ADR-C04). Typed intents (IMP-P2-075): message.intent is validated against the schema the domain selects — FlightSearchIntent (FLT), StaySearchIntent (HTL, VR, DAYUSE), GroundSearchIntent (XFR, TXI, CAR), ExperienceSearchIntent (EXP, ATT, TOUR, EVT, MOV), InsuranceSearchIntent (INS), AirportServiceSearchIntent (APTSVC, FASTTRK), LoungeSearchIntent (LOUNGE), VisaSearchIntent (VISA), DiningSearchIntent (FNB, v1.7.0), BusSearchIntent (BUS, v1.15.0), CheckinSearchIntent (CHK, v1.21.0; code CHK since v1.22.0), GiftSearchIntent (GIFT, v1.31.0), EsimSearchIntent (ESIM, v1.33.0), CruiseSearchIntent (CRU, v1.35.0), PackageSearchIntent (PKG, v1.35.0), AicontSearchIntent (AICONT, v1.39.0), ParkingSearchIntent (PARK, v1.55.0). One request example per vertical below; FNB carries three (by destination, by restaurantIds, with experiences) and FLT two (the default COMPLETE journey, and flightOutboundFirst). Two-step round trip (v1.27.0): FlightSearchIntent.journeyMode OUTBOUND_FIRST answers outbound journeys (extension.air.journey) whose return is chosen afterwards through POST /v1/flights/returns; see that route's description. A supplier that cannot build them answers COMPLETE offers with a context.warnings[] entry JOURNEY_MODE_UNSUPPORTED. Flight search filters (v1.37.0): FlightSearchIntent carriers, alliance, refundableOnly, changeableOnly, excludeBasicEconomy, slices[].cabin and fareTypes are sent to the supplier; one a supplier cannot apply is left out of its search with a context.warnings[] entry SEARCH_FILTER_UNSUPPORTED (example flightFiltered).\n","parameters":[{"$ref":"#/components/parameters/SandboxTest"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"},"examples":{"flight":{"summary":"FLT — FlightSearchIntent (API-0001 §8)","value":{"context":{"domain":"FLT","currency":"USD"},"message":{"intent":{"slices":[{"origin":"GRU","destination":"MIA","date":"2026-11-10"}],"travelers":[{"type":"ADT"},{"type":"CHD","age":9}],"cabin":"ECONOMY"}}}},"flightOutboundFirst":{"summary":"FLT — FlightSearchIntent, a round trip in journeyMode OUTBOUND_FIRST (v1.27.0)","description":"Synthetic values. A round trip searched outbound first: each offer is an outbound journey and its return is chosen afterwards through POST /v1/flights/returns.","value":{"context":{"domain":"FLT","currency":"USD"},"message":{"intent":{"slices":[{"origin":"SFO","destination":"BOM","date":"2026-12-18"},{"origin":"BOM","destination":"SFO","date":"2027-01-06"}],"travelers":[{"type":"ADT"},{"type":"ADT"}],"cabin":"ECONOMY","journeyMode":"OUTBOUND_FIRST"}}}},"flightFiltered":{"summary":"FLT — FlightSearchIntent with search filters (v1.37.0)","description":"Synthetic values. A round trip on two named airlines, refundable fares only, no basic economy, published fares only, with business class on the return slice. A filter the supplier cannot apply comes back as a context.warnings[] entry SEARCH_FILTER_UNSUPPORTED.","value":{"context":{"domain":"FLT","currency":"USD"},"message":{"intent":{"slices":[{"origin":"JFK","destination":"LHR","date":"2026-12-02"},{"origin":"LHR","destination":"JFK","date":"2026-12-09","cabin":"BUSINESS"}],"travelers":[{"type":"ADT"}],"cabin":"ECONOMY","carriers":["BA","AA"],"refundableOnly":true,"excludeBasicEconomy":true,"fareTypes":["PUBLISHED"]}}}},"stay":{"summary":"VR — StaySearchIntent, whole property, pets, child ages","value":{"context":{"domain":"VR","currency":"USD"},"message":{"intent":{"destination":{"placeId":"mondee:place:miami"},"stay":{"checkIn":"2026-11-10","checkOut":"2026-11-14"},"rooms":[{"adults":2,"childAges":[4],"pets":1}],"unitType":"WHOLE_PROPERTY","petsAllowed":true}}}},"ground":{"summary":"XFR — GroundSearchIntent, flight-linked pickup, luggage, child seat","value":{"context":{"domain":"XFR","currency":"USD"},"message":{"intent":{"waypoints":[{"type":"PICKUP","location":{"iata":"GRU"},"flightLink":{"carrier":"LA","flightNumber":"8084","date":"2026-11-10"}},{"type":"DROPOFF","location":{"placeId":"ChIJ0WGkg4FEzpQRrlsz_whLqZs"}}],"party":{"adults":2,"childAges":[4]},"luggage":{"checked":3},"serviceType":"TRANSFER","shared":"PRIVATE","childSeats":[{"childAge":4}]}}}},"experience":{"summary":"EXP — ExperienceSearchIntent with a pickup point and guide language","value":{"context":{"domain":"EXP","currency":"EUR"},"message":{"intent":{"destination":{"placeId":"mondee:place:rome"},"dates":{"from":"2026-11-12","to":"2026-11-12"},"party":{"adults":2,"childAges":[9]},"pickup":{"mode":"PICKUP","point":{"placeId":"mondee:place:rome-termini"}},"guideLanguage":"en"}}}},"event":{"summary":"EVT — ExperienceSearchIntent with event filters (section, quantity)","value":{"context":{"domain":"EVT","currency":"USD"},"message":{"intent":{"dates":{"from":"2026-11-20","to":"2026-11-20"},"party":{"adults":2},"event":{"eventId":"mondee:event:tevo-2211907","quantity":2,"section":"114"}}}}},"insurance":{"summary":"INS — InsuranceSearchIntent, overseas single trip","value":{"context":{"domain":"INS","currency":"INR"},"message":{"intent":{"trip":{"start":"2026-11-10","end":"2026-11-20","origin":"IN","destinations":["US"],"tripType":"SINGLE"},"insured":[{"travelerIndex":0,"ageAtDeparture":34},{"travelerIndex":1,"ageAtDeparture":9}],"categories":["OVERSEAS"]}}}},"airportService":{"summary":"APTSVC — AirportServiceSearchIntent, keyed to the flight","value":{"context":{"domain":"APTSVC","currency":"INR"},"message":{"intent":{"flightLink":{"carrier":"AI","flightNumber":"131","date":"2026-11-10","origin":"BOM","destination":"LHR","departureTerminal":"2"},"direction":"DEPARTURE","categories":["MEET_ASSIST","PORTER"],"party":{"adults":2,"childAges":[6]}}}}},"lounge":{"summary":"LOUNGE — LoungeSearchIntent, voucher at a station","value":{"context":{"domain":"LOUNGE","currency":"INR"},"message":{"intent":{"station":{"airport":"DEL","terminal":"3","terminalType":"INTERNATIONAL","direction":"DEPARTURE"},"visitAt":"2026-11-10T20:00:00+05:30","party":{"adults":2},"entitlementModels":["VOUCHER"]}}}},"visa":{"summary":"VISA — VisaSearchIntent, corridor IN → AE","value":{"context":{"domain":"VISA","currency":"INR"},"message":{"intent":{"corridor":{"nationality":"IN","residence":"IN","destinations":["AE"],"purpose":"TOURISM"},"travel":{"from":"2026-12-01","to":"2026-12-10"},"party":{"adults":2}}}}},"bus":{"summary":"BUS — BusSearchIntent, city to city on a date (v1.15.0)","value":{"context":{"domain":"BUS","currency":"USD"},"message":{"intent":{"origin":{"text":"Montreal"},"destination":{"text":"Ottawa"},"departureDate":"2026-12-10","party":{"adults":1,"childAges":[10],"seniorAges":[70]}}}}},"checkin":{"summary":"CHK — CheckinSearchIntent, automated check-in on a booked flight (v1.21.0; code CHK since v1.22.0)","value":{"context":{"domain":"CHK","currency":"USD"},"message":{"intent":{"flightLink":{"carrier":"BA","flightNumber":"117","date":"2026-11-10","origin":"LHR","destination":"JFK"},"departureTime":"08:25","party":{"adults":2}}}}},"gift":{"summary":"GIFT — GiftSearchIntent, digital gift cards redeemable in the US, one face value (v1.31.0)","value":{"context":{"domain":"GIFT","currency":"USD"},"message":{"intent":{"country":"US","category":"beauty","faceValue":{"amount":1000,"currency":"USD"}}}}},"esim":{"summary":"ESIM — EsimSearchIntent, eSIM data plans for a week in Japan, 3 GB or more (v1.33.0)","value":{"context":{"domain":"ESIM","currency":"USD"},"message":{"intent":{"esim":{"country":"JP","minDataGb":3},"dates":{"from":"2026-11-10","to":"2026-11-16"},"party":{"adults":1}}}}},"parking":{"summary":"PARK — ParkingSearchIntent, airport parking at LAX from 1 to 3 June 2027, lot-local times (v1.55.0)","value":{"context":{"domain":"PARK","currency":"USD"},"message":{"intent":{"location":{"iata":"LAX"},"entryAt":"2027-06-01T09:00","exitAt":"2027-06-03T18:00","filters":{"covered":true}}}}},"cruise":{"summary":"CRU — CruiseSearchIntent, ocean cruises of 5 to 9 nights in the first quarter of 2027 (v1.35.0)","value":{"context":{"domain":"CRU","currency":"USD"},"message":{"intent":{"cruiseType":"OCEAN","departureWindow":{"from":"2027-01-01","to":"2027-03-31"},"nights":{"min":5,"max":9},"regions":["mondee:cruise-region:8f3k2q"],"party":{"adults":2,"childAges":[]}}}}},"package":{"summary":"PKG — PackageSearchIntent, a hotel + flight package for two adults and a child (v1.35.0)","value":{"context":{"domain":"PKG","currency":"BRL"},"message":{"intent":{"packageType":"HOTEL_FLIGHT","origin":{"placeId":"mondee:place:sao-paulo-br"},"destination":{"placeId":"mondee:place:fortaleza-br"},"departureDate":"2027-02-10","returnDate":"2027-02-17","party":{"adults":2,"childAges":[7]}}}}},"aicont":{"summary":"AICONT — AicontSearchIntent, the enriched attractions of one city, a free catalogue read (v1.39.0)","value":{"context":{"domain":"AICONT","currency":"INR"},"message":{"intent":{"operation":"list_services","arguments":{"city":"Jaipur","category":"attractions","limit":20}}}}},"diningByDestination":{"summary":"FNB — DiningSearchIntent by destination (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"destination":{"text":"San Francisco","countryCode":"US"},"date":"2026-11-12","time":"19:00","windowMinutes":90,"party":{"adults":2},"seating":["default"]}}}},"diningByRestaurantIds":{"summary":"FNB — DiningSearchIntent for named restaurants, REST alias (v1.7.0)","value":{"context":{"domain":"REST","currency":"USD"},"message":{"intent":{"restaurantIds":["prov:dining:100123","prov:dining:100456"],"date":"2026-11-13","time":"20:00","party":{"adults":4,"children":2}}}}},"diningWithExperiences":{"summary":"FNB — DiningSearchIntent with experiences offered beside table slots (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"destination":{"text":"San Francisco","countryCode":"US"},"date":"2026-11-12","time":"19:00","party":{"adults":2},"includeExperiences":true}}}},"diningExperienceSelection":{"summary":"FNB — slots for one experience with a per-diner price split (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"restaurantIds":["prov:dining:100123"],"date":"2026-11-12","time":"19:00","party":{"adults":3},"includeExperiences":true,"experienceSelection":{"experienceId":900101,"version":3,"prices":[{"priceId":1,"count":2},{"priceId":2,"count":1}]}}}}}}}}},"responses":{"200":{"description":"COMPLETE or PARTIAL result set (06 §2 partials are the normal case)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"},"examples":{"partial":{"summary":"200 PARTIAL — 2 of 4 suppliers in, 1 failed (03 §5)","description":"THE partial-state fixture. suppliers {done:2, pending:1, failed:1} is what a console reads to say '2 of 4 suppliers'; the failed one is named in context.warnings[] (03 §2.2, §5).","value":{"context":{"domain":"HTL","action":"on_search","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":812,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-H-51ab77c3","ranking":"DEFAULT_V3","warnings":[{"code":"SUPPLIER_DROPPED","message":"circuit open","supplier":"EXAMPLEHOTELS3_RAPID"},{"code":"CURRENCY_UNAVAILABLE","message":"offers not priceable in USD were dropped","supplier":"EXAMPLEHOTELS2","dropped":12}],"page":{"nextCursor":"c2Vjb25kLXBhZ2U","total":2}},"message":{"status":"PARTIAL","retryAfterMs":900,"suppliers":{"done":2,"pending":1,"failed":1},"offers":[{"offerRef":"OF-H-8c1f4a2e","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"},{"field":"traveler[0].passportNumber","when":"international"}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}},{"offerRef":"OF-H-2d70b915","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":91500,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":false,"holdable":false,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":false,"names":"NONE","partialCancel":false}},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"}],"fareSource":{"supplier":"EXAMPLEHOTELS2","credentialSet":"tenant-acme-tgx-01","moneyModel":"PASS_THROUGH"}}]}}},"partialUnattributed":{"summary":"200 PARTIAL — 2 failed, only 1 attributable (OD-CONSOLE2-02 / OD-101)","description":"The conforming response that breaks a client which pairs the two lists. 06 §2 obliges a server to describe each failed supplier in context.warnings[], and the schema cannot enforce it (see SupplierProgress' own description), so this is legal: suppliers.failed is 2 and exactly one warning names a supplier. A console must render \"2 failed\" and name the one it can attribute — never derive the count from the array, and never drop the count because the array is shorter. It also carries CREDENTIAL_DEGRADED with `fellBackTo` (08 §4 rule 3), so the offers below are tagged by their ACTUAL source (08 §4 E2).\n","value":{"context":{"domain":"HTL","action":"on_search","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_7c1b40","took_ms":1140,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-H-51ab77c3","ranking":"DEFAULT_V3","warnings":[{"code":"SUPPLIER_DROPPED","message":"circuit open","supplier":"EXAMPLEHOTELS3_RAPID"},{"code":"CREDENTIAL_DEGRADED","message":"own-key bucket exhausted mid-search; the remainder used the platform's own credentials","supplier":"EXAMPLEACTIVITIES","fellBackTo":"MONDEE"}]},"message":{"status":"PARTIAL","retryAfterMs":900,"suppliers":{"done":1,"pending":1,"failed":2},"offers":[{"offerRef":"OF-H-8c1f4a2e","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":true,"holdable":true,"holdTtlSeconds":1800,"instantConfirm":true,"confirmSlaSeconds":0},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}]}}},"complete":{"summary":"200 COMPLETE — the whole fan-out answered","value":{"context":{"domain":"HTL","action":"on_search","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":1904,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-H-51ab77c3","ranking":"DEFAULT_V3","warnings":[]},"message":{"status":"COMPLETE","suppliers":{"done":4,"pending":0,"failed":0},"offers":[{"offerRef":"OF-H-8c1f4a2e","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"},{"field":"traveler[0].passportNumber","when":"international"}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}},{"offerRef":"OF-H-2d70b915","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":91500,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":false,"holdable":false,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":false,"names":"NONE","partialCancel":false}},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"}],"fareSource":{"supplier":"EXAMPLEHOTELS2","credentialSet":"tenant-acme-tgx-01","moneyModel":"PASS_THROUGH"}}]}}},"flightRoundTrip":{"summary":"200 COMPLETE — FLT round trip: slices, rich segments, baggage, breakdown, brand fares (v1.23.0), fare rules and meals (v1.24.0)","description":"Synthetic values. Two adults, GRU-MIA-GRU, one nonstop segment per slice. extension.air.slices groups segments by index and carries each slice's baggage per passenger; price.breakdown has one BASE and one TAX line for the adult passengers (quantity 2, unitAmount per passenger, amount the total); fare.brandFares lists the brands the carrier sells on the same flights, priced for the party, and is informational (the offer is priced at the selected brand). Since v1.24.0 the example also carries extension.air.fareRules (the whole itinerary), a slice's own fareRules and meals, the segment's connecting and operatedByText, and the newer fare keys (validatingCarrierName, fare flags, milesEarned, cardFee, brandFares tier and meals).","value":{"context":{"domain":"FLT","action":"on_search","version":"2026-09-30","timestamp":"2026-10-16T17:04:11Z","message_id":"msg_f1r7t0","took_ms":2410,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-F-2c84e1b7","warnings":[]},"message":{"status":"COMPLETE","suppliers":{"done":1,"pending":0,"failed":0},"offers":[{"offerRef":"OF-F-9a0e5c21","expiresAt":"2026-10-16T17:34:11Z","price":{"amount":256800,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-16T17:00Z-01","breakdown":[{"type":"BASE","amount":220000,"label":"Adult base fare","quantity":2,"unitAmount":110000},{"type":"TAX","amount":36800,"label":"Adult taxes and carrier fees","quantity":2,"unitAmount":18400}]},"conditions":{"refundable":true,"cancellation":{"penalties":[{"from":"2026-10-16","amount":{"amount":30000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"fareSource":{"supplier":"SUP-AIR-EXAMPLE","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"extension":{"air":{"segments":[{"marketingCarrier":"LA","marketingCarrierName":"LATAM Airlines","operatingCarrier":"LA","operatingCarrierName":"LATAM Airlines","flightNumber":"8084","origin":"GRU","originName":"Sao Paulo Guarulhos International Airport","originCity":"Sao Paulo","destination":"MIA","destinationName":"Miami International Airport","destinationCity":"Miami","departure":"2026-11-10T23:50:00-03:00","arrival":"2026-11-11T07:10:00-05:00","durationMinutes":560,"departureTerminal":"3","arrivalTerminal":"N","cabin":"ECONOMY","bookingClass":"Q","equipment":"789","fareBasis":"QL7X0NB1","brandName":"LIGHT","redEye":true,"selfTransfer":false,"bagsRecheckRequired":false,"longLayover":false,"seatsRemaining":4,"connecting":false,"operatedByText":"Operated by LATAM Airlines Brasil"},{"marketingCarrier":"LA","marketingCarrierName":"LATAM Airlines","operatingCarrier":"LA","operatingCarrierName":"LATAM Airlines","flightNumber":"8085","origin":"MIA","originName":"Miami International Airport","originCity":"Miami","destination":"GRU","destinationName":"Sao Paulo Guarulhos International Airport","destinationCity":"Sao Paulo","departure":"2026-11-20T20:30:00-05:00","arrival":"2026-11-21T06:40:00-03:00","durationMinutes":490,"departureTerminal":"N","arrivalTerminal":"3","cabin":"ECONOMY","bookingClass":"Q","equipment":"789","fareBasis":"QL7X0NB1","brandName":"LIGHT","redEye":true,"selfTransfer":false,"bagsRecheckRequired":false,"longLayover":false,"seatsRemaining":7,"connecting":false,"operatedByText":"Operated by LATAM Airlines Brasil"}],"slices":[{"index":0,"origin":"GRU","destination":"MIA","departure":"2026-11-10T23:50:00-03:00","arrival":"2026-11-11T07:10:00-05:00","durationMinutes":560,"stops":0,"segmentIndexes":[0],"baggage":{"checked":{"pieces":0,"included":false,"charge":{"amount":6000,"currency":"USD"},"description":"First checked bag for a fee, up to 23 kg"},"carryOn":{"pieces":1,"weightKg":10,"included":true},"personalItem":{"pieces":1,"included":true}},"meals":["Dinner","Breakfast"],"fareRules":{"changeBeforeDeparture":{"allowed":true,"amount":{"amount":15000,"currency":"USD"},"description":"Change before departure of this journey for a fee, plus any fare difference"},"changeAfterDeparture":{"allowed":false}}},{"index":1,"origin":"MIA","destination":"GRU","departure":"2026-11-20T20:30:00-05:00","arrival":"2026-11-21T06:40:00-03:00","durationMinutes":490,"stops":0,"segmentIndexes":[1],"baggage":{"checked":{"pieces":0,"included":false,"charge":{"amount":6000,"currency":"USD"},"description":"First checked bag for a fee, up to 23 kg"},"carryOn":{"pieces":1,"weightKg":10,"included":true},"personalItem":{"pieces":1,"included":true}},"meals":["Dinner"]}],"fareRules":{"refundBeforeDeparture":{"allowed":true,"amount":{"amount":30000,"currency":"USD"},"description":"Refund before departure less a penalty per passenger"},"refundAfterDeparture":{"allowed":false},"changeBeforeDeparture":{"allowed":true,"percentBasisPoints":1000,"description":"Change before departure for 10% of the fare, plus any fare difference"},"changeAfterDeparture":{"allowed":false}},"fare":{"fareBasis":"QL7X0NB1","brandName":"LIGHT","sourceType":"GDS","validatingCarrier":"LA","validatingCarrierName":"LATAM Airlines","fareType":"PUB","basicEconomy":false,"nonRefundable":false,"gdsType":"EXAMPLEGDS","virtualInterline":false,"lowCostCarrier":false,"alternateAirport":false,"alternateDate":false,"vfrFare":false,"missionaryFare":false,"infantSeatRequired":false,"nonBrandedFare":false,"milesEarned":"2,450 miles","cardFee":{"amount":{"amount":1500,"currency":"USD"},"percentBasisPoints":150},"brandFares":[{"brandName":"LIGHT","price":{"amount":256800,"currency":"USD"},"cabin":"ECONOMY","baggageSummary":"1 carry-on bag and 1 personal item; checked bag for a fee","features":{"included":["CARRY_ON","PERSONAL_ITEM"],"chargeable":["CHECKED_BAG","SEAT_SELECTION","CHANGE"],"notAvailable":["UPGRADE"]},"selected":true,"tier":1,"meals":["Meal for purchase"]},{"brandName":"STANDARD","price":{"amount":301600,"currency":"USD"},"cabin":"ECONOMY","baggageSummary":"1 carry-on bag, 1 personal item and 1 checked bag of 23 kg","features":{"included":["CARRY_ON","PERSONAL_ITEM","CHECKED_BAG","SEAT_SELECTION"],"chargeable":["CHANGE"],"notAvailable":["UPGRADE"]},"selected":false,"tier":2,"meals":["Complimentary meal"]}]}}}}]}}},"flightOutboundFirst":{"summary":"200 COMPLETE — FLT round trip in journeyMode OUTBOUND_FIRST: two outbound offers, returns chosen later (v1.27.0)","description":"Synthetic values. Two adults, SFO-BOM-SFO searched with journeyMode OUTBOUND_FIRST. Each offer is an outbound journey: extension.air.journey says complete false and nextSliceIndex 1, segments and slices hold slice 0 only, and price is the supplier's \"from\" round-trip price of that outbound for the party, after the tenant's pricing rules (pricing.indicative true). POST /v1/flights/returns on an offer lists its return options; the offer cannot be repriced or booked until the return is chosen (409 CONFLICT details.reason JOURNEY_INCOMPLETE).","value":{"context":{"domain":"FLT","action":"on_search","version":"2026-09-30","timestamp":"2026-10-17T15:04:11Z","message_id":"msg_f0b0f001","took_ms":38400,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-F-00000101","warnings":[]},"message":{"status":"COMPLETE","suppliers":{"done":1,"pending":0,"failed":0},"offers":[{"offerRef":"OF-F-00000101","expiresAt":"2026-10-17T15:34:11Z","price":{"amount":189000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-17T15:00Z-01"},"pricing":{"indicative":true},"conditions":{"refundable":false,"holdable":false,"instantConfirm":true,"confirmSlaSeconds":0},"fareSource":{"supplier":"SUP-AIR-EXAMPLE","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"extension":{"air":{"journey":{"mode":"OUTBOUND_FIRST","complete":false,"selectedSlices":[0],"nextSliceIndex":1},"segments":[{"marketingCarrier":"AI","marketingCarrierName":"Air India","flightNumber":"9180","origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"cabin":"ECONOMY","bookingClass":"S"}],"slices":[{"index":0,"origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"stops":0,"segmentIndexes":[0]}],"fare":{"sourceType":"GDS","validatingCarrier":"AI"}}}},{"offerRef":"OF-F-00000102","expiresAt":"2026-10-17T15:34:11Z","price":{"amount":162000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-17T15:00Z-01"},"pricing":{"indicative":true},"conditions":{"refundable":false,"holdable":false,"instantConfirm":true,"confirmSlaSeconds":0},"fareSource":{"supplier":"SUP-AIR-EXAMPLE","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"extension":{"air":{"journey":{"mode":"OUTBOUND_FIRST","complete":false,"selectedSlices":[0],"nextSliceIndex":1},"segments":[{"marketingCarrier":"LH","marketingCarrierName":"Lufthansa","flightNumber":"9455","origin":"SFO","destination":"FRA","departure":"2026-12-18T15:40:00-08:00","arrival":"2026-12-19T11:25:00+01:00","durationMinutes":645,"cabin":"ECONOMY","bookingClass":"K","layoverMinutes":125,"connecting":true},{"marketingCarrier":"LH","marketingCarrierName":"Lufthansa","flightNumber":"9756","origin":"FRA","destination":"BOM","departure":"2026-12-19T13:30:00+01:00","arrival":"2026-12-20T01:15:00+05:30","durationMinutes":435,"cabin":"ECONOMY","bookingClass":"K"}],"slices":[{"index":0,"origin":"SFO","destination":"BOM","departure":"2026-12-18T15:40:00-08:00","arrival":"2026-12-20T01:15:00+05:30","durationMinutes":1205,"stops":1,"segmentIndexes":[0,1]}],"fare":{"sourceType":"GDS","validatingCarrier":"LH"}}}}]}}},"hotelRich":{"summary":"200 COMPLETE — HTL: property detail, rooms, nightly breakdown, pay-at-property fee (v1.25.0)","description":"Synthetic values. Two adults, two nights, one room. price.breakdown has a BASE and a TAX line with quantity 2 (nights) and unitAmount per night, and pricing.basis is per NIGHT; pricing.payAtProperty discloses a resort fee collected by the property in its own currency (PriceBreakdownLine.currency, v1.25.0), outside the total. conditions.cancellation carries the typed ladder and the supplier's policyText (informational; the ladder is authoritative). extension.hotel.property carries the display detail, images (public CDN URLs) and amenities (no icon URLs; a client maps code or name to its own icons); rooms[] carries the room detail; unitsAvailable is indicative.","value":{"context":{"domain":"HTL","action":"on_search","version":"2026-09-30","timestamp":"2026-10-16T17:04:11Z","message_id":"msg_h0t3l1","took_ms":1830,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-H-00000001","warnings":[]},"message":{"status":"COMPLETE","suppliers":{"done":1,"pending":0,"failed":0},"offers":[{"offerRef":"OF-H-00000001","expiresAt":"2026-10-16T17:34:11Z","price":{"amount":42000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-16T17:00Z-01","breakdown":[{"type":"BASE","amount":36000,"label":"Room charge","quantity":2,"unitAmount":18000},{"type":"TAX","amount":6000,"label":"Taxes and fees","quantity":2,"unitAmount":3000}]},"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-08T23:59:00-05:00","penalties":[{"from":"2026-11-09","amount":{"amount":21000,"currency":"USD"}}],"policyText":"Free cancellation until 8 Nov 2026 23:59 hotel time; after that, one night is charged."},"holdable":false,"amendable":{"dates":false,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"requiredInfo":[{"field":"traveler[0].email","group":"PER_BOOKING","requirement":"MANDATORY"},{"field":"traveler[0].phone","group":"PER_BOOKING","requirement":"MANDATORY"}],"fareSource":{"supplier":"SUP-HTL-EXAMPLE2","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"pricing":{"basis":{"per":"NIGHT","units":2},"payAtProperty":[{"type":"RESORT_FEE","amount":5000,"currency":"EUR","label":"Resort fee","collectedBy":"PROPERTY","payableAt":"PROPERTY","included":false,"quantity":2,"unitAmount":2500}]},"extension":{"hotel":{"property":{"canonicalId":"PROP-SYN-1001","provisional":false,"name":"Example Harbour Hotel","propertyType":"Hotel","starRating":4.5,"address":{"line1":"1 Example Quay","city":"Example City","postalCode":"00000","countryCode":"US"},"geo":{"lat":40.7,"lon":-74},"chain":{"code":"EXH","name":"Example Hotels"},"guestRating":{"score":8.6,"scale":10,"count":1240,"source":"supplier","scores":[{"name":"Cleanliness","score":8.9},{"name":"Location","score":9.2}]},"images":[{"url":"https://images.example.com/hotels/syn-1001/exterior.jpg","caption":"Exterior","isPrimary":true},{"url":"https://images.example.com/hotels/syn-1001/lobby.jpg","caption":"Lobby","isPrimary":false}],"amenities":[{"code":"WIFI","name":"Free Wi-Fi","top":true},{"code":"POOL","name":"Outdoor pool","top":true},{"code":"GYM","name":"Fitness centre","top":false}],"checkInTime":"15:00","checkOutTime":"11:00","phone":"+1 000 000 0000","description":"A synthetic harbour-front hotel used in contract examples.","timeZone":"America/New_York","policies":[{"name":"Pets","lines":["Pets are not allowed."]},{"name":"Children","lines":["Children of all ages are welcome.","Cribs are free on request."]}],"nearby":[{"name":"Example Airport","category":"Airport","distanceKm":18.5},{"name":"Example Museum","category":"Attraction","distanceKm":0.8}],"distanceKm":1.2},"rooms":[{"roomGroup":"RG-1","description":"Deluxe King, harbour view","quantity":1,"occupancy":{"adults":2},"bedConfig":"1 king bed","board":"BB","rateNotes":["Breakfast for two included"],"images":[{"url":"https://images.example.com/hotels/syn-1001/deluxe-king.jpg","caption":"Deluxe King room","isPrimary":true}],"amenities":[{"code":"AIR_CONDITIONING","name":"Air conditioning","top":true},{"code":"MINIBAR","name":"Minibar","top":false}],"inclusions":[{"code":"BREAKFAST","name":"Breakfast for two","top":true}],"maxOccupancy":3,"bedCount":1,"size":{"value":32,"unit":"SQM"},"view":"Harbour view","unitsAvailable":3,"petsAllowed":false}],"rate":{"refundability":"FREE_UNTIL","payType":"PREPAID","revalidation":"MANDATORY","bookingModel":"INSTANT","ratePlanName":"Flexible rate, breakfast included"},"stay":{"checkIn":"2026-11-10","checkOut":"2026-11-12","nights":2}}}}]}}},"diningComplete":{"summary":"200 COMPLETE — FNB: a table slot, an experience slot paid at the restaurant (v1.7.0)","description":"Offer.price is zero: a table is FREE. The experience price is not collected by the platform; it is a pay-at-property money line (pricing.payAtProperty, collectedBy PROPERTY) and the dining block shows the experience. context.warnings carries EXAMPLEDINING_* codes when a restaurant was skipped.","value":{"context":{"domain":"FNB","action":"on_search","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":910,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[{"code":"EXAMPLEDINING_RESTAURANT_SKIPPED","message":"One restaurant did not answer availability and was left out.","supplier":"EXAMPLEDINING"}]},"message":{"status":"COMPLETE","suppliers":{"done":1,"pending":0,"failed":0},"offers":[{"offerRef":"OF-D-5c11a9e0","expiresAt":"2026-10-05T17:09:11Z","price":{"amount":0,"currency":"USD"},"conditions":{"refundable":true,"holdable":true,"holdTtlSeconds":300,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":true,"names":"NONE","partialCancel":false}},"requiredInfo":[{"field":"phoneCountry","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","pattern":"^[A-Z]{2}$","label":"Phone country (ISO 3166 alpha-2)"},{"field":"specialRequest","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","maxLength":75,"label":"Special request"},{"field":"restaurantEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from the restaurant"},{"field":"opentableEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from Example Dining"},{"field":"smsOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"Text messages about this reservation"}],"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"},"extension":{"dining":{"restaurant":{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-12T19:30","partySize":2,"diningAreaId":"1","area":"Main dining room","environment":"INDOOR","tableAttributes":["default"]},"creditCardRequired":false,"cancellationPolicy":{"type":"FREE"},"bookingPolicies":[{"type":"STANDARD","message":"Tables are held for 15 minutes past the reservation time."}],"payment":{"creditCardRequired":false,"processor":"SPREEDLY","cardCapture":"PAYMENT_METHOD_TOKEN"},"poweredBy":"Example Dining"}}},{"offerRef":"OF-D-7e02b4c1","expiresAt":"2026-10-05T17:09:11Z","price":{"amount":0,"currency":"USD"},"conditions":{"refundable":true,"holdable":true,"holdTtlSeconds":300,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":true,"names":"NONE","partialCancel":false}},"requiredInfo":[{"field":"phoneCountry","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","pattern":"^[A-Z]{2}$","label":"Phone country (ISO 3166 alpha-2)"},{"field":"specialRequest","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","maxLength":75,"label":"Special request"},{"field":"restaurantEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from the restaurant"},{"field":"opentableEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from Example Dining"},{"field":"smsOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"Text messages about this reservation"}],"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"},"extension":{"dining":{"restaurant":{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-12T19:30","partySize":2,"diningAreaId":"1","area":"Main dining room","environment":"INDOOR","tableAttributes":["default"]},"creditCardRequired":false,"cancellationPolicy":{"type":"FREE"},"bookingPolicies":[{"type":"STANDARD","message":"Tables are held for 15 minutes past the reservation time."}],"payment":{"creditCardRequired":false,"processor":"SPREEDLY","cardCapture":"PAYMENT_METHOD_TOKEN"},"poweredBy":"Example Dining","experience":{"experienceId":900101,"version":3,"name":"Chef's counter tasting","type":"EXPERIENCE","priceType":"PER_PERSON","prepaymentRequired":false,"payableAt":"RESTAURANT","prices":[{"priceId":1,"title":"Tasting menu","price":{"amount":12500,"currency":"USD"},"allInclusive":false}],"gratuity":{"basisPoints":2000,"mandatory":false}}}},"pricing":{"payAtProperty":[{"type":"EXPERIENCE","amount":25000,"label":"Chef's counter tasting, 2 x USD 125.00","collectedBy":"PROPERTY","payableAt":"PROPERTY","included":false,"quantity":2,"unitAmount":12500},{"type":"GRATUITY","amount":5000,"label":"Suggested gratuity 20%","collectedBy":"PROPERTY","payableAt":"PROPERTY","included":false,"rateBasisPoints":2000}]}}]}}}}}}},"202":{"$ref":"#/components/responses/SearchWarming"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/search/{searchRef}":{"get":{"operationId":"pollSearch","x-mcp":{"expose":"tool","toolset":"public","name":"v1_search_poll","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"Async poll / paginate / filter surface for every async product (03 §3 #2, §5)","description":"Generalized from the flights-only sector poll. Filtering/sorting/pagination run against the server-side result store (14 §3). Same URL with Accept text/event-stream upgrades to SSE (D-78; polling is the guaranteed floor).\n","parameters":[{"$ref":"#/components/parameters/SearchRefPath"},{"name":"cursor","in":"query","description":"Opaque; the message.nextCursor of the previous page (as served, BE-2 !64). A cursor this route did not issue is 400 VALIDATION details.field cursor; a valid cursor sent with a different sort or filter restarts at page one (not an error). With none of cursor, filter or sort the poll answers the whole current snapshot unpaged; with any of them it pages at a fixed 50 offers (no page-size parameter).\n","schema":{"type":"string"}},{"name":"filter","in":"query","description":"Comma-separated key:value terms, ANDed: supplier:<name> (case-insensitive, the offer's fareSource.supplier), maxPrice:<minor units> / minPrice:<minor units> (run currency), refundable:true|false, holdable:true|false — e.g. supplier:EXAMPLEGDS,maxPrice:118400. Any other key or a malformed term is 400 VALIDATION details.field filter. Filtering never changes message.status.\n","schema":{"type":"string"}},{"name":"sort","in":"query","description":"One key, - for descending: price | -price | expiresAt | -expiresAt. Absent: the run's stable order (planned supplier order, then landing order). Anything else is 400 VALIDATION details.field sort.\n","schema":{"type":"string"}},{"name":"view","in":"query","description":"Where offer selection is on for the run's domain: SELECTED (one entry per item, the winner, its other offers in alternatives[]) or ALL (the flat list, every offer tagged with its item in selection). Absent: the view the tenant and platform policies resolve to. Any other value is 400 VALIDATION details.field context.selection.view. Ignored where selection is off.\n","schema":{"type":"string","enum":["SELECTED","ALL"]}}],"responses":{"200":{"description":"Current snapshot; poll until status COMPLETE. With Accept text/event-stream the same 200 is an SSE stream instead (D-78, 03 §5): event names result-batch | supplier-complete | search-complete, each data frame one SearchResponse (JSON) carrying the offers and the suppliers progress so far. Polling is the guaranteed floor.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"},"examples":{"complete":{"summary":"200 COMPLETE — poll finished; suppliers all done","description":"03 §10.2: poll until COMPLETE. Filtering/sorting/pagination run against the server-side result store — a client never re-searches to re-sort (03 §5).","value":{"context":{"domain":"FLT","action":"on_search","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":38,"from_cache":true,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-F-7b31d0a9","warnings":[]},"message":{"status":"COMPLETE","suppliers":{"done":5,"pending":0,"failed":0},"offers":[{"offerRef":"OF-F-31c9d0a4","expiresAt":"2026-09-14T17:24:11Z","price":{"amount":118400,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":false,"holdable":true,"holdTtlSeconds":900,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false}},"fareSource":{"supplier":"EXAMPLEGDS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}]}}},"stillPartial":{"summary":"200 PARTIAL — 3 of 5 suppliers in (03 §10.2)","value":{"context":{"domain":"FLT","action":"on_search","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":22,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-F-7b31d0a9","warnings":[]},"message":{"status":"PARTIAL","retryAfterMs":800,"suppliers":{"done":3,"pending":2,"failed":0},"offers":[]}}}}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events; each data line is one SearchResponse as JSON"},"example":"event: result-batch\ndata: {\"message\":{\"status\":\"PARTIAL\",\"suppliers\":{\"done\":1,\"pending\":1,\"failed\":0},\"offers\":[]}}\n\nevent: search-complete\ndata: {\"message\":{\"status\":\"COMPLETE\",\"suppliers\":{\"done\":2,\"pending\":0,\"failed\":0},\"offers\":[]}}\n"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"description":"Legacy flights clients only: frozen SEARCH_WARMING 429 form (03 §5, 18 §4)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"legacyWarming":{"summary":"429 SEARCH_WARMING — frozen legacy flights form (03 §5, 18 §4)","description":"Only for tenants on async.legacyWarming. New clients get the 202 form on POST /v1/search; this one sunsets with the 12-month window (18 §4).","value":{"code":"SEARCH_WARMING","message":"Results are still warming; retry in 1200ms","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"searchRef":"SR-F-7b31d0a9","retryAfterMs":1200,"legacyWarming":true}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/detail":{"post":{"operationId":"detail","x-mcp":{"expose":"tool","toolset":"public","name":"v1_detail","class":"read","autonomy":"L0","tasks":["compare_offers"]},"tags":["shop"],"summary":"Offer/product drill-down for all domains (03 §3 #4)","description":"Absorbs /v1/options — accepts an optional selection refinement and may be called repeatedly to narrow (rooms to rate; cabin category to deck to cabin; date to timeslot). FLT (v1.27.0): on an OUTBOUND_FIRST offer whose journey is not complete, detail answers the offer as it stands with its extension.air.journey state; it does not list or choose return options. That is POST /v1/flights/returns.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetailRequest"},"examples":{"dining":{"summary":"FNB — the restaurant behind a dining offer: experiences, menus, reviews, payment (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"offerRef":"OF-D-5c11a9e0"}}}}}}}},"responses":{"200":{"description":"Detail payload under message (per-domain shape, deliberately not unified)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetailResponse"},"examples":{"hotelRooms":{"summary":"200 — HTL drill-down to room groups and rates (03 §10.1 step 2)","description":"message is per-domain and deliberately NOT unified (03 §1 #4) — this example shows the HTL shape; it is an illustration of one domain, not a schema. See OD-79.","value":{"context":{"domain":"HTL","action":"on_detail","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":430,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"offerRef":"OF-H-8c1f4a2e","roomGroups":[{"roomGroup":"RG-2","name":"Deluxe King, city view","rates":[{"offerRef":"OF-H-8c1f4a2e-R1","boardCode":"BB","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0}}]}]}}},"diningRestaurant":{"summary":"200 — FNB drill-down: the dining block with experiences, menus, reviews and payment facts (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_detail","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":780,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"offerRef":"OF-D-5c11a9e0","expiresAt":"2026-10-05T17:09:11Z","price":{"amount":0,"currency":"USD"},"conditions":{"refundable":true,"holdable":true,"holdTtlSeconds":300,"instantConfirm":true,"confirmSlaSeconds":0,"amendable":{"dates":true,"names":"NONE","partialCancel":false}},"requiredInfo":[{"field":"phoneCountry","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","pattern":"^[A-Z]{2}$","label":"Phone country (ISO 3166 alpha-2)"},{"field":"specialRequest","type":"STRING","group":"PER_BOOKING","requirement":"OPTIONAL","maxLength":75,"label":"Special request"},{"field":"restaurantEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from the restaurant"},{"field":"opentableEmailOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"E-mails from Example Dining"},{"field":"smsOptIn","type":"BOOLEAN","group":"PER_BOOKING","requirement":"OPTIONAL","label":"Text messages about this reservation"}],"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"},"extension":{"dining":{"restaurant":{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-12T19:30","partySize":2,"diningAreaId":"1","area":"Main dining room","environment":"INDOOR","tableAttributes":["default"]},"creditCardRequired":false,"cancellationPolicy":{"type":"FREE"},"bookingPolicies":[{"type":"STANDARD","message":"Tables are held for 15 minutes past the reservation time."}],"payment":{"creditCardRequired":false,"processor":"SPREEDLY","cardCapture":"PAYMENT_METHOD_TOKEN"},"poweredBy":"Example Dining","experiences":[{"experienceId":900101,"version":3,"name":"Chef's counter tasting","type":"EXPERIENCE","priceType":"PER_PERSON","prepaymentRequired":false,"payableAt":"RESTAURANT","prices":[{"priceId":1,"title":"Tasting menu","price":{"amount":12500,"currency":"USD"},"allInclusive":false}],"gratuity":{"basisPoints":2000,"mandatory":false}}],"menus":[{"menuId":"mnu-dinner","name":"Dinner","currency":"USD","sections":[{"name":"Starters","items":[{"name":"Grilled sardines","description":"Lemon, parsley","price":{"amount":1600,"currency":"USD"}}]},{"name":"Mains","items":[{"name":"Market fish","price":{"amount":3800,"currency":"USD"}}]}]},{"menuId":"mnu-wine","name":"Wine list","webMenuUrl":"https://ot.example.test/menus/100123/wine"}],"reviews":{"summary":{"count":1284,"ratings":{"overall":"4.6","food":"4.7","service":"4.5","ambience":"4.4","value":"4.2","noise":"2.0"},"recommendations":1000,"recommendedYes":912},"recent":[{"overall":5,"ratings":{"food":5,"service":5,"ambience":4,"value":4},"title":"Lovely evening","text":"Great fish and a friendly team.","dinedOn":"2026-09-20","submittedAt":"2026-09-21T09:12:00Z"}]}}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/OfferUnsellable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/flights/returns":{"post":{"operationId":"flightReturns","x-mcp":{"expose":"tool","toolset":"public","name":"flight_returns","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"FLT two-step round trip: list the returns of a chosen outbound, or choose one (v1.27.0)","description":"The second step of a round trip or multi-city search made with FlightSearchIntent.journeyMode OUTBOUND_FIRST (v1.27.0). A per-vertical FLT route in the style of the /v1/hotels/search alias (03 §1): the unified envelope, context.domain FLT, the same credentials, permission (search:flights) and tenant scoping as an FLT search; a foreign tenant's offerRef is 404 NOT_FOUND. Read-only: no Idempotency-Key, safe to retry; it never mints a repriceRef. LIST: message.intent {offerRef} of an offer whose extension.air.journey.complete is false. The platform asks the supplier for the options of slice journey.nextSliceIndex that go WITH this outbound (for SUP-AIR-EXAMPLE the itineraries of the next sector for this itinerary and fare) and answers 200 with message.offer (the outbound offer as it stands), message.returnOptions (AirReturnOption[], in the supplier's order), message.returnOptionsComplete and message.warnings. The list is the supplier's answer for THIS outbound; each price is the total journey price for the party after the tenant's pricing rules, never a supplier net, and priceDelta is against the outbound's \"from\" price. A key is valid only for this offerRef, only until the offer's expiresAt, and only from the latest listing: listing again may answer other keys and prices. When the supplier says there are no options for this outbound the answer is 200 with an empty list, returnOptionsComplete true and a warning NO_RETURN_OPTIONS; choose another outbound. CHOOSE: message.intent {offerRef, returnOption}. 200 with message.offer narrowed under the SAME offerRef, as the HTL room-rate narrowing keeps its offerRef; expiresAt is not extended. It carries every chosen slice in segments and slices and the journey price the supplier states for the combination (equal to the option's price, pricing.indicative false). Once every slice is chosen journey.complete is true and nextSliceIndex is absent, and /v1/reprice and /v1/orders treat the offer like a COMPLETE one; on a multi-city journey with slices left, complete stays false, nextSliceIndex moves on and the client lists again. Choosing again with another key of the latest listing replaces the choice; the last choice is what reprice prices. Errors use only the frozen codes, the reason in details.reason: 400 VALIDATION NOT_FLT_OFFER (the offerRef is not an FLT offer, or context.domain is not FLT); 404 NOT_FOUND RETURN_OPTION_UNKNOWN (a key this offer's latest listing did not answer, or one from before it expired; an unknown offerRef is the plain 404 NOT_FOUND); 409 OFFER_EXPIRED details.reason OFFER_EXPIRED (the offer is past expiresAt; search again); 409 CONFLICT JOURNEY_COMPLETE_ALREADY (the offer is complete: a COMPLETE search offer, or every slice already chosen; reprice it). Until the journey is complete, /v1/reprice and /v1/orders answer 409 CONFLICT details.reason JOURNEY_INCOMPLETE with details.nextSliceIndex.\n","parameters":[{"$ref":"#/components/parameters/SandboxTest"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightReturnsRequest"},"examples":{"listReturns":{"summary":"FLT — list the returns of an OUTBOUND_FIRST outbound offer (v1.27.0)","value":{"context":{"domain":"FLT","currency":"USD"},"message":{"intent":{"offerRef":"OF-F-00000101"}}}},"chooseReturn":{"summary":"FLT — choose one of the listed returns (v1.27.0)","value":{"context":{"domain":"FLT","currency":"USD"},"message":{"intent":{"offerRef":"OF-F-00000101","returnOption":"ro_00000101_2"}}}}}}}},"responses":{"200":{"description":"Listing: the outbound offer with returnOptions and returnOptionsComplete. Choosing: the offer narrowed under the same offerRef (FlightReturnsResponse).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightReturnsResponse"},"examples":{"returnOptions":{"summary":"200 — three return options for one SFO-BOM outbound (v1.27.0)","description":"Synthetic values. The outbound OF-F-00000101 of the flightOutboundFirst search, \"from\" USD 1,890.00 for two adults. Each option's price is the total round trip for the party if chosen; priceDelta is against the \"from\" price and may be zero or negative. Keys are valid for this offerRef only, until it expires.","value":{"context":{"domain":"FLT","action":"on_flight_returns","version":"2026-09-30","timestamp":"2026-10-17T15:06:02Z","message_id":"msg_f0b0f002","took_ms":6200,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"offer":{"offerRef":"OF-F-00000101","expiresAt":"2026-10-17T15:34:11Z","price":{"amount":189000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-17T15:00Z-01"},"pricing":{"indicative":true},"fareSource":{"supplier":"SUP-AIR-EXAMPLE","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"extension":{"air":{"journey":{"mode":"OUTBOUND_FIRST","complete":false,"selectedSlices":[0],"nextSliceIndex":1},"segments":[{"marketingCarrier":"AI","flightNumber":"9180","origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"cabin":"ECONOMY","bookingClass":"S"}],"slices":[{"index":0,"origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"stops":0,"segmentIndexes":[0]}]}}},"returnOptions":[{"key":"ro_00000101_1","sliceIndex":1,"slices":[{"index":1,"origin":"BOM","destination":"SFO","departure":"2027-01-06T02:00:00+05:30","arrival":"2027-01-06T06:00:00-08:00","durationMinutes":1050,"stops":0,"segmentIndexes":[0]}],"segments":[{"marketingCarrier":"AI","flightNumber":"9181","origin":"BOM","destination":"SFO","departure":"2027-01-06T02:00:00+05:30","arrival":"2027-01-06T06:00:00-08:00","durationMinutes":1050,"cabin":"ECONOMY","bookingClass":"S"}],"fare":{"sourceType":"GDS","validatingCarrier":"AI"},"price":{"amount":189000,"currency":"USD"},"priceDelta":{"amount":0,"currency":"USD"}},{"key":"ro_00000101_2","sliceIndex":1,"slices":[{"index":1,"origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"stops":0,"segmentIndexes":[0]}],"segments":[{"marketingCarrier":"AI","flightNumber":"9173","origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"cabin":"ECONOMY","bookingClass":"S"}],"fare":{"sourceType":"GDS","validatingCarrier":"AI"},"fareRules":{"changeBeforeDeparture":{"allowed":true,"amount":{"amount":15000,"currency":"USD"}}},"price":{"amount":201400,"currency":"USD"},"priceDelta":{"amount":12400,"currency":"USD"},"conditions":{"refundable":false,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false}}},{"key":"ro_00000101_3","sliceIndex":1,"slices":[{"index":1,"origin":"BOM","destination":"SFO","departure":"2027-01-06T03:15:00+05:30","arrival":"2027-01-06T13:20:00-08:00","durationMinutes":1415,"stops":1,"segmentIndexes":[0,1]}],"segments":[{"marketingCarrier":"LH","flightNumber":"9757","origin":"BOM","destination":"FRA","departure":"2027-01-06T03:15:00+05:30","arrival":"2027-01-06T08:15:00+01:00","durationMinutes":570,"cabin":"ECONOMY","bookingClass":"K","layoverMinutes":135,"connecting":true},{"marketingCarrier":"LH","flightNumber":"9454","origin":"FRA","destination":"SFO","departure":"2027-01-06T10:30:00+01:00","arrival":"2027-01-06T13:20:00-08:00","durationMinutes":710,"cabin":"ECONOMY","bookingClass":"K"}],"fare":{"sourceType":"GDS","validatingCarrier":"LH","virtualInterline":false},"price":{"amount":184500,"currency":"USD"},"priceDelta":{"amount":-4500,"currency":"USD"}}],"returnOptionsComplete":true,"warnings":[]}}},"returnChosen":{"summary":"200 — the return chosen; the complete round-trip offer under the same offerRef (v1.27.0)","description":"Synthetic values. ro_00000101_2 chosen on OF-F-00000101: the same offerRef, both slices and their segments, journey complete, and the journey price the supplier states for the combination (the option's price), no longer indicative. /v1/reprice and /v1/orders now treat it like a COMPLETE offer.","value":{"context":{"domain":"FLT","action":"on_flight_returns","version":"2026-09-30","timestamp":"2026-10-17T15:07:40Z","message_id":"msg_f0b0f003","took_ms":410,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"offer":{"offerRef":"OF-F-00000101","expiresAt":"2026-10-17T15:34:11Z","price":{"amount":201400,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-10-17T15:00Z-01"},"pricing":{"indicative":false},"conditions":{"refundable":false,"holdable":false,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"fareSource":{"supplier":"SUP-AIR-EXAMPLE","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"extension":{"air":{"journey":{"mode":"OUTBOUND_FIRST","complete":true,"selectedSlices":[0,1]},"segments":[{"marketingCarrier":"AI","flightNumber":"9180","origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"cabin":"ECONOMY","bookingClass":"S"},{"marketingCarrier":"AI","flightNumber":"9173","origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"cabin":"ECONOMY","bookingClass":"S"}],"slices":[{"index":0,"origin":"SFO","destination":"BOM","departure":"2026-12-18T11:00:00-08:00","arrival":"2026-12-19T17:30:00+05:30","durationMinutes":1020,"stops":0,"segmentIndexes":[0]},{"index":1,"origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"stops":0,"segmentIndexes":[1]}],"fareRules":{"changeBeforeDeparture":{"allowed":true,"amount":{"amount":15000,"currency":"USD"}}},"fare":{"sourceType":"GDS","validatingCarrier":"AI"}}}},"warnings":[]}}}}}}},"400":{"description":"VALIDATION — a bad intent shape (BadRequest), or details.reason NOT_FLT_OFFER: the offerRef is not an FLT offer, or context.domain is not FLT (v1.27.0)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notFltOffer":{"summary":"400 VALIDATION NOT_FLT_OFFER — the offerRef is not a flight offer (v1.27.0)","value":{"code":"VALIDATION","message":"offerRef OF-H-00000001 is not a flight offer; /v1/flights/returns takes an FLT offer.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.offerRef","reason":"NOT_FLT_OFFER"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"description":"NOT_FOUND — an unknown offerRef (also another tenant's), or details.reason RETURN_OPTION_UNKNOWN: a returnOption key the latest listing of this offer did not answer (v1.27.0)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"returnOptionUnknown":{"summary":"404 NOT_FOUND RETURN_OPTION_UNKNOWN — a key this offer did not list (v1.27.0)","value":{"code":"NOT_FOUND","message":"returnOption ro_00000101_9 is not one the latest listing of this offer answered; list the returns again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.returnOption","reason":"RETURN_OPTION_UNKNOWN","offerRef":"OF-F-00000101"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"OFFER_EXPIRED (details.reason OFFER_EXPIRED) — the offer is past expiresAt; search again. Or CONFLICT details.reason JOURNEY_COMPLETE_ALREADY — the offer's journey is complete; reprice it (v1.27.0)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"journeyCompleteAlready":{"summary":"409 CONFLICT JOURNEY_COMPLETE_ALREADY — nothing left to choose (v1.27.0)","value":{"code":"CONFLICT","message":"Every slice of this offer is already chosen; reprice it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.offerRef","reason":"JOURNEY_COMPLETE_ALREADY","offerRef":"OF-F-00000101"}}},"offerExpired":{"summary":"409 OFFER_EXPIRED — the outbound offer expired; search again (v1.27.0)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-F-00000101 expired at 2026-10-17T15:34:11Z; search again","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OFFER_EXPIRED","offerRef":"OF-F-00000101","expiredAt":"2026-10-17T15:34:11Z"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/OfferUnsellable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/schedules":{"post":{"operationId":"schedules","x-mcp":{"expose":"tool","toolset":"public","name":"get_schedules","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"Departures / sailings / showtimes — the calendar of time (03 §3 #6)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulesRequest"}}}},"responses":{"200":{"description":"Schedule entries under message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulesResponse"},"examples":{"showtimes":{"summary":"200 — MOV showtimes (the calendar of time, 03 §3 #6)","description":"Per-domain message (03 §1 #4) — MOV shown here; FLT/CRU/RAIL carry departures and sailings.","value":{"context":{"domain":"MOV","action":"on_schedules","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":210,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"entries":[{"scheduleId":"SCH-MOV-88213","startsAt":"2026-09-14T19:30:00-05:00","venue":"Cinemark Salvador Shopping","screen":"6","attributes":{"format":"IMAX","language":"pt-BR","subtitled":true}}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/seatmap":{"post":{"operationId":"seatmap","x-mcp":{"expose":"tool","toolset":"public","name":"get_seatmap","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"Canonical seat/berth/cabin/table grid with per-domain decorations (03 §3 #7, 06 §3)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatmapRequest"}}}},"responses":{"200":{"description":"Canonical grid under message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatmapResponse"},"examples":{"seatGrid":{"summary":"200 — canonical seat grid with per-domain decoration (06 §3)","description":"One canonical grid model; entity type is SEAT | BERTH | CABIN | TABLE per domain (06 §3). The grid itself is under the deliberately-unspecified message payload (03 §1 #4).","value":{"context":{"domain":"MOV","action":"on_seatmap","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":260,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"scheduleId":"SCH-MOV-88213","grid":{"rows":["A","B"],"columns":["1","2","3"],"entities":[{"id":"A1","type":"SEAT","status":"AVAILABLE","price":{"amount":4500,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"}},{"id":"A2","type":"SEAT","status":"OCCUPIED"},{"id":"B1","type":"SEAT","status":"AVAILABLE","price":{"amount":5200,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"attributes":{"wheelchairAccessible":true}}]}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/OfferExpired"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/OfferUnsellable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/ancillaries":{"post":{"operationId":"ancillaries","x-mcp":{"expose":"tool","toolset":"public","name":"list_ancillaries","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"Pre-book ancillary catalog for a selection (03 §3 #8, 02 §6)","description":"Input is a repriceRef or offerRef. Post-booking adds go via amend ADD_ANCILLARY.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AncillariesRequest"}}}},"responses":{"200":{"description":"Ancillary catalog under message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AncillariesResponse"},"examples":{"catalog":{"summary":"200 — pre-book ancillary catalog for a selection (03 §3 #8)","description":"selectionRef is what POST /v1/orders takes in intent.ancillaries[] (03 §7). Post-booking adds go through amend with amendType ADD_ANCILLARY, not here (03 §3 #20).","value":{"context":{"domain":"HTL","action":"on_ancillaries","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":390,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"repriceRef":"RP-H-2ab91c","ancillaries":[{"selectionRef":"ANC-EC-4410","type":"EARLY_CHECKIN","price":{"amount":2500,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"refundable":false},{"selectionRef":"ANC-BF-9921","type":"BREAKFAST","price":{"amount":1800,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"refundable":true}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/rules":{"post":{"operationId":"rules","x-mcp":{"expose":"tool","toolset":"public","name":"v1_rules","class":"read","autonomy":"L0"},"tags":["shop"],"summary":"Fare rules / cancellation policy / T&Cs for a selection, pre-book (03 §3 #9)","description":"Distinct from detail because rules are often a slower, supplier-billed call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RulesRequest"},"examples":{"dining":{"summary":"FNB — booking and cancellation policies of a dining slot (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"offerRef":"OF-D-5c11a9e0"}}}}}}}},"responses":{"200":{"description":"Rules payload under message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RulesResponse"},"examples":{"cancellationPolicy":{"summary":"200 — the offer with its cancellation ladder (03 §3 #9, OD-341 (d))","description":"Separate from detail because rules are often a slower, supplier-billed call you must not pay on every detail (03 §3 #9).","value":{"context":{"domain":"HTL","action":"on_rules","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":2410,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"offerRef":"OF-H-8c1f4a2e","expiresAt":"2026-09-14T17:34:11Z","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]}},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/OfferExpired"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/OfferUnsellable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/reprice":{"post":{"operationId":"reprice","x-mcp":{"expose":"tool","toolset":"public","name":"v1_reprice","class":"read","autonomy":"L0","example":"{\"domain\":\"FLT\",\"message\":{\"intent\":{\"offerRef\":\"OF-F-31c9d0a4\"}}}"},"tags":["price"],"summary":"The one firm-price verb; never cached (03 §3 #10)","parameters":[{"$ref":"#/components/parameters/SandboxTest"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepriceRequest"},"examples":{"dining":{"summary":"FNB — re-read the slot; the price stays zero (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"offerRef":"OF-D-5c11a9e0"}}}},"diningExperience":{"summary":"FNB — firm an experience slot with two tasting menus and one pairing (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"offerRef":"OF-D-7e02b4c1","experienceSelection":{"experienceId":900101,"version":3,"prices":[{"priceId":1,"count":2},{"priceId":2,"count":1}]}}}}}}}}},"responses":{"200":{"description":"Firm price with repriceRef (RP-). priceChanged true requires a client interstitial (law, 03 §10.1 F1).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepriceResponse"},"examples":{"priceChanged":{"summary":"200 priceChanged TRUE — interstitial is mandatory (03 §10.1 F1)","description":"Both money figures are server fields. A client shows them side by side and does NOT compute the delta — PLAT-DESIGNSYS §9.1 forbids client-side money arithmetic (see OD-80).","value":{"context":{"domain":"HTL","action":"on_reprice","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":1180,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"repriceRef":"RP-H-2ab91c","priceChanged":true,"previousPrice":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"price":{"amount":88010,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:05Z-02"},"appliedRules":[{"ruleId":"rule_tnt_htl_10pct","version":7,"layer":"TENANT","action":"MARKUP_PCT","delta":{"amount":8001,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:05Z-02"},"floorClamped":false}],"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"},{"field":"traveler[0].passportNumber","when":"international"}]}}},"firm":{"summary":"200 priceChanged FALSE — firm price, proceed to orders","value":{"context":{"domain":"HTL","action":"on_reprice","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":940,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"repriceRef":"RP-H-2ab91c","priceChanged":false,"price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"appliedRules":[{"ruleId":"rule_tnt_htl_10pct","version":7,"layer":"TENANT","action":"MARKUP_PCT","delta":{"amount":7655,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"floorClamped":false}],"conditions":{"refundable":true,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"holdable":true,"holdTtlSeconds":1800,"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false},"instantConfirm":true,"confirmSlaSeconds":0},"requiredInfo":[{"field":"traveler[].dateOfBirth","when":"always"},{"field":"traveler[0].passportNumber","when":"international"}]}}},"diningFirm":{"summary":"200 — FNB slot still offered; FREE table, price zero (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_reprice","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":520,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"repriceRef":"RP-D-3f9a10","priceChanged":false,"price":{"amount":0,"currency":"USD"},"conditions":{"refundable":true,"holdable":true,"holdTtlSeconds":300,"instantConfirm":true,"confirmSlaSeconds":0}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"OFFER_EXPIRED — re-shop required; or PRICE_CHANGED — the supplier moved the price at reprice, with details.pricesWithheld true when the amounts were withheld (03 §9, OD-295). A moved price is normally NOT this error: it is the 200 with priceChanged true and both figures. The 409 is the exception where the move could not be stated as sell prices at one rule-set version (OD-291), so every amount is withheld; reprice again. Or (v1.27.0) CONFLICT details.reason JOURNEY_INCOMPLETE with details.nextSliceIndex: an OUTBOUND_FIRST FLT offer whose later slices are not chosen yet; choose them through POST /v1/flights/returns, then reprice. No supplier is called.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"offerExpired":{"summary":"409 OFFER_EXPIRED — the offer TTL ran out; re-search (03 §4)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-H-8c1f4a2e expired at 2026-09-14T17:34:11Z","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","expiredAt":"2026-09-14T17:34:11Z","reshopRequired":true}}},"priceChangedWithheld":{"summary":"409 PRICE_CHANGED — the supplier moved the price; not every amount could be priced (OD-291, OD-295)","description":"Reprice rewrites a supplier price movement into SELL prices under the tenant's rules (FEAT-013 §1). When any amount in the refusal cannot be priced at one rule-set version, every amount is removed and details.pricesWithheld is true, so a supplier net never reaches a client. Reprice again to see and accept the new price (DR-06).\n","value":{"code":"PRICE_CHANGED","message":"The price of this offer changed at the supplier. Review the new price and reprice again to accept it (DR-06).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","pricesWithheld":true}}},"journeyIncomplete":{"summary":"409 CONFLICT JOURNEY_INCOMPLETE — an outbound-first FLT offer whose return is not chosen (v1.27.0)","description":"The offer came from a journeyMode OUTBOUND_FIRST search and carries only its outbound (extension.air.journey.complete false). Choose the slice details.nextSliceIndex through POST /v1/flights/returns, then reprice the same offerRef. No supplier is called.\n","value":{"code":"CONFLICT","message":"Choose the return flight first; this offer holds only its outbound journey.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.offerRef","reason":"JOURNEY_INCOMPLETE","offerRef":"OF-F-00000101","nextSliceIndex":1}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/Unprocessable"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/hold":{"post":{"operationId":"hold","x-mcp":{"expose":"tool","toolset":"public","name":"v1_hold","class":"commit-lite","autonomy":"L1","tasks":["hold_offer"],"example":"{\"domain\":\"FLT\",\"message\":{\"intent\":{\"repriceRef\":\"RP-F-7c3d20\"}},\"idempotencyKey\":\"3f1c9a52-7b1e-4c0e-9d2a-5e8f1b7a6c34\"}"},"tags":["price"],"summary":"Reserve without payment where capability exists (03 §3 #11)","description":"Returns holdRef + holdExpiresAt. TTLs range from ~90s (MOV) to multi-day (CRU); declared in capabilities and offer conditions. Expiry auto-releases + hold.expired event. While the tenant is SUSPENDED a new hold is refused 403 TENANT_SUSPENDED, with one exception: a same-key, same-body retry of a hold this tenant completed before the suspension is served its stored answer (Idempotency-Replayed true) — nothing new is started. A fresh, expired or still in-flight key is 403 TENANT_SUSPENDED, a different body under the key 409 IDEMPOTENCY_CONFLICT (FEAT-020 §18c).\n","parameters":[{"$ref":"#/components/parameters/SandboxTest"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldRequest"},"examples":{"dining":{"summary":"FNB — lock the slot (Example Dining slot lock, about five minutes) (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"repriceRef":"RP-D-3f9a10"}}}},"diningExperience":{"summary":"FNB — lock the experience slot with the same price split (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"repriceRef":"RP-D-4c2e81","experienceSelection":{"experienceId":900101,"version":3,"prices":[{"priceId":1,"count":2},{"priceId":2,"count":1}]}}}}}}}}},"responses":{"200":{"description":"Hold created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldResponse"},"examples":{"held":{"summary":"200 — seats held for 90s (the tightest ladder, 03 §10.3)","description":"TTLs run from ~90s (MOV) to multi-day (CRU); the value is declared in capabilities and in the offer's conditions.holdTtlSeconds. Expiry auto-releases and emits hold.expired (03 §4).","value":{"context":{"domain":"MOV","action":"on_hold","version":"2026-09-30","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":520,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"holdRef":"HD-M-4419ab","holdExpiresAt":"2026-09-14T17:05:41Z"}}},"diningLocked":{"summary":"200 — FNB slot locked at the supplier (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_hold","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":430,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[{"code":"EXAMPLEDINING_LOCK_LAPSES_AT_SUPPLIER","message":"The slot lock lapses at Example Dining at holdExpiresAt; book before then.","supplier":"EXAMPLEDINING"}]},"message":{"holdRef":"HD-D-8b2c71","holdExpiresAt":"2026-10-05T17:09:11Z"}}}}}}},"307":{"$ref":"#/components/responses/HomeRegionRedirect"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"State conflict (03 §9): OFFER_EXPIRED | HOLD_EXPIRED | IDEMPOTENCY_CONFLICT | CONFLICT | QUOTE_EXPIRED. CONFLICT with details.reason HOLD_NOT_SUPPORTED when the offer cannot be held (API-0004 §4; the registry is closed by DR-20, so this is a reason, not a code). QUOTE_EXPIRED carries details.reason NO_SELL_PRICE_STAMP or SELL_PRICE_STAMP_MISMATCH when the repriceRef has no sell price the platform can hold (OD-291). One live hold per repriceRef (BE-7 !72): CONFLICT details.reason REPRICE_REF_HELD (details.field, ref, heldUntil) when another Idempotency-Key already holds the RP-, and REPRICE_REF_SPENT when it was booked; both are refused before the supplier is called.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"conflict":{"summary":"409 CONFLICT — generic state conflict (03 §9)","description":"03 §9's catch-all 409. The specific codes above are always preferred; this exists so a state conflict the registry has no narrower code for is still a declared, mockable branch rather than an undeclared status (OD-CONTRACT2-01).\n","value":{"code":"CONFLICT","message":"The order is in a state that does not allow this transition","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"state":"CANCELLED","attempted":"amend"}}},"offerExpired":{"summary":"409 OFFER_EXPIRED — re-shop required (03 §4, §9)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-H-8c1f4a2e expired at 2026-09-14T17:34:11Z; re-reprice","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","expiredAt":"2026-09-14T17:34:11Z"}}},"holdExpired":{"summary":"409 HOLD_EXPIRED — hold auto-released (03 §10.3 F1)","value":{"code":"HOLD_EXPIRED","message":"holdRef HD-M-4419ab expired at 2026-09-14T17:05:41Z; the seats were released","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"holdRef":"HD-M-4419ab","expiredAt":"2026-09-14T17:05:41Z"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"holdNotSupported":{"summary":"409 CONFLICT HOLD_NOT_SUPPORTED — this offer cannot be held; book it directly (API-0004 §4)","value":{"code":"CONFLICT","message":"This offer cannot be held. Book it directly or choose a holdable offer.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"HOLD_NOT_SUPPORTED","repriceRef":"RP-H-2ab91c"}}},"repriceRefHeld":{"summary":"409 CONFLICT REPRICE_REF_HELD — the RP- is already held under another key (BE-7)","value":{"code":"CONFLICT","message":"RP-H-2ab91c is already held; book or release that hold.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.repriceRef","reason":"REPRICE_REF_HELD","ref":"RP-H-2ab91c","heldUntil":"2026-09-14T17:05:41Z"}}},"repriceRefSpent":{"summary":"409 CONFLICT REPRICE_REF_SPENT — the RP- was already booked (BE-7)","value":{"code":"CONFLICT","message":"RP-H-2ab91c was already booked; reprice again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.repriceRef","reason":"REPRICE_REF_SPENT","ref":"RP-H-2ab91c"}}},"quoteExpiredNoSellPriceStamp":{"summary":"409 QUOTE_EXPIRED — the repriceRef carries no sell-price stamp (OD-291)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has no sell price this platform can hold. Reprice again, then hold the new RP-.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"NO_SELL_PRICE_STAMP"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/Unprocessable"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/HoldRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/orders":{"post":{"operationId":"createOrder","x-mcp":{"expose":"tool","toolset":"public","name":"book_offer","class":"commit-money","autonomy":"L1","reason":"Proposal-only. book_offer reprices live and files POST /v1/agent-proposals with execution.action orders.create, bound to the offerRef and the live amount. It never calls POST /v1/orders (the plane's transport refuses it). A human approves with a D-50 grant and the platform executes once (DR-38, OD-506; D-50 coverage DR-39).","example":"{\"offerRef\":\"OF-F-31c9d0a4\",\"domain\":\"FLT\",\"travelers\":[{\"firstName\":\"Ada\",\"lastName\":\"Example\",\"type\":\"ADT\",\"dateOfBirth\":\"1990-04-12\",\"email\":\"ada@example.test\"}],\"clientReference\":\"acme-flt-0042\"}"},"tags":["orders"],"summary":"The one booking write (03 §7, ADR-C02)","description":"Replaces /v1/book, /v1/bookings (write) and /v1/taxi/book (12-month aliases, 18 §5). Book never re-quotes — price movement at commit is 409 PRICE_CHANGED, never a silent swap. Ambiguous supplier outcome is 202 AMBIGUOUS_RESULT with the order in PENDING_RECONCILIATION (06 §8, ADR-C07). While the tenant is SUSPENDED a new order is refused 403 TENANT_SUSPENDED, with one exception: a same-key, same-body retry of an order request this tenant completed before the suspension is served its stored answer (Idempotency-Replayed true) — nothing new is booked. A fresh, expired or still in-flight key is 403 TENANT_SUSPENDED, a different body under the key 409 IDEMPOTENCY_CONFLICT (FEAT-020 §18c). Payment check (OD-301 (b), OD-360, OD-361 (1)): when payments are served and message.intent.payment.paymentRef names an intent, the platform checks it covers THIS order before the write-ahead row and before any supplier byte. Anything but a funded intent for this tenant, this repriceRef and this total is 409 CONFLICT with details.field message.intent.payment.paymentRef and details.reason one of NOT_FOUND (unknown ref, or another tenant's — the same bytes for both), OTHER_QUOTE (the intent was created from a different repriceRef), AMOUNT_MISMATCH (details.detail AMOUNT or CURRENCY), WRONG_STATE (details.detail is the intent's state: CREATED, REQUIRES_ACTION, CANCELED, FAILED, or PENDING_RECONCILIATION while a payment operation is unresolved), REFUNDED, AUTH_EXPIRED (an uncaptured authorization past its validity), ALREADY_USED (another order already booked on this payment; that order is never named). Nothing was sent to a supplier and the Idempotency-Key is released, so the client fixes the payment and retries. retryable is true only for WRONG_STATE with detail PENDING_RECONCILIATION, and false for every other reason. A second order on a repriceRef that is already spent (OD-360 (b) spends the RP- at book) is 409 CONFLICT with details.field message.intent.repriceRef and details.reason REPRICE_REF_SPENT (OD-487); when a payment is attached the payment check answers first. A same-key retry of an order that already booked is answered from its stored result without re-checking. Two-step round trip (v1.27.0): an order on an OUTBOUND_FIRST FLT offer whose journey is not complete (extension.air.journey.complete false) is 409 CONFLICT with details.reason JOURNEY_INCOMPLETE and details.nextSliceIndex, not retryable; nothing is sent to a supplier and the Idempotency-Key is released. Choose the remaining slices through POST /v1/flights/returns and reprice first. Card-secured slots (v1.7.0, D3 / OD-875): message.intent.payment.paymentMethod carries a payment-method token the client obtained from Spreedly; the platform hands the supplier the token and never a card number or security code, and none can be sent: PaymentMethodToken has no such field and a card-number-shaped value anywhere is refused (INSTRUMENT_DATA_REFUSED). D-50 (12 §B2.4, OD-458): an AI-channel call must carry X-Approval-Token, a grant from POST /v1/approvals bound to orders.create, the repriceRef and the order total; without one it is 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED, and a spent, expired, unknown or mismatched grant is PERMISSION_DENIED with APPROVAL_TOKEN_USED | _EXPIRED | _INVALID | _MISMATCH. Single use, retried with the same Idempotency-Key. Human sessions and system keys (DR-40) are exempt; an ai key and every service token are the AI channel (DR-39). Wallet (v1.18.0, served since 1.18.1, FEAT-030 §5.5, §12): with message.intent.payment.mode WALLET the total is held on the tenant's wallet inside the write-ahead flow (F-03) before any supplier byte, settled at ticketing or confirmation and released when the supplier fails; an ambiguous outcome keeps the hold until reconciliation, never debiting twice. Too little available is 402 PAYMENT_FAILED with details.reason WALLET_INSUFFICIENT and details.available (Money), and no supplier is called (sandbox scenario Test wallet_insufficient). Where the wallet is off (aikonic.wallet.enabled, on at DEV and SANDBOX only) a WALLET order is refused the same way, with nothing available, and never booked unpaid. A tier cap (FEAT-030 §5.7, OD-ENR-1, only when auto-live is on; not built — capped LIVE waits on OD-ENR-1) is 403 PERMISSION_DENIED details.reason TIER_CAP_REACHED with details.cap and details.resetAt. connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b): TIER_CAP_REACHED is un-reserved. An order of a LIVE_CAPPED tenant that is not a sandbox twin (on any rung) whose total would take today's spend past the daily cap is 403 PERMISSION_DENIED details.reason TIER_CAP_REACHED with details.cap, details.usedToday (Money) and details.resetAt, checked before any payment is taken, any wallet hold is placed or any supplier byte is sent; nothing is written. The cap currency is INR when the activation country is IN, else USD; usedToday counts the tenant's non-cancelled orders since 00:00 UTC, converted to the cap currency at each order's FX snapshot; resetAt is the next 00:00 UTC. The check is per order against today's confirmed and in-flight spend and is SOFT under concurrency: concurrent orders, each under the cap, may together exceed it by at most the orders in flight (a reservation-based hard cap is an option for OD-ENR-26). An order whose currency the FX snapshot cannot convert to the cap currency is 500 INTERNAL details.reason FX_SNAPSHOT_UNAVAILABLE (the money verbs' existing sub-reason; retryable) and nothing is charged, held or sent. A sandbox twin's orders and tenants of other tiers are never capped. With payment.mode SAVED_METHOD while aikonic.payment-methods.enabled is off the order is 501 NOT_IMPLEMENTED details.reason PAYMENT_METHODS_UNAVAILABLE. payment.mode SAVED_METHOD with payment.methodId charges a saved payment method (POST /v1/payments/methods); a decline is 402 PAYMENT_FAILED details.reason CARD_DECLINED.\n","parameters":[{"$ref":"#/components/parameters/SandboxTest"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCreateRequest"},"examples":{"dining":{"summary":"FNB — book a held table; no payment (FREE) (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-8b2c71","clientReference":"acme-din-0001","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"requiredInfoAnswers":{"phoneCountry":"US","phone":"4155550100","specialRequest":"Window table if possible","restaurantEmailOptIn":false,"opentableEmailOptIn":false,"smsOptIn":false},"payment":{"mode":"NONE"}}}}},"diningCardRequired":{"summary":"FNB — book a card-secured slot with a Spreedly payment-method token, never a card number (D3, v1.7.0)","description":"The client tokenised the card in the Spreedly iFrame and sends only the token. The platform passes Example Dining its token-shaped credit_card object and keeps provider, brand and last4.","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-91d0e4","clientReference":"acme-din-0002","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"requiredInfoAnswers":{"phoneCountry":"US"},"payment":{"mode":"PAYMENT_METHOD_TOKEN","paymentMethod":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa"}}}}}},"diningScaRetry":{"summary":"FNB — retry after SCA_REQUIRED: the same token plus the 3-D Secure result (v1.7.0)","description":"Sent with a NEW Idempotency-Key after the first order was refused with details.reason SCA_REQUIRED and details.retryWith setupIntent.","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-91d0e4","clientReference":"acme-din-0002","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"requiredInfoAnswers":{"phoneCountry":"US"},"payment":{"mode":"PAYMENT_METHOD_TOKEN","paymentMethod":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa","threeDs":{"redirectUrl":"https://app.acme-travel.example/checkout/3ds-return","setupIntent":"syn-spreedly-setup-intent-0001"}}}}}}},"diningMpiData":{"summary":"FNB — card authenticated by the client MPI: mpiData on the token (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-91d0e4","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"payment":{"mode":"PAYMENT_METHOD_TOKEN","paymentMethod":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa","threeDs":{"mpiData":{"threeDSVersion":"2.2.0","eci":"05","cavv":"syn-cavv-placeholder-0001","dsTransId":"f25084f0-5b16-4c0a-ae5d-b24808a95e4b"}}}}}}}},"diningSmsLink":{"summary":"FNB — In-House card-secured slot: no token, Example Dining texts the diner a card link (smsOptIn true) (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-a40c17","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"requiredInfoAnswers":{"phoneCountry":"US","smsOptIn":true},"payment":{"mode":"OPENTABLE_SMS_LINK"}}}}},"diningExperience":{"summary":"FNB — book the experience with the price split; paid at the restaurant (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"holdRef":"HD-D-5f7a02","travelers":[{"type":"ADT","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","lead":true}],"experienceSelection":{"experienceId":900101,"version":3,"prices":[{"priceId":1,"count":2},{"priceId":2,"count":1}]},"requiredInfoAnswers":{"phoneCountry":"US","phone":"4155550100","specialRequest":"One guest is vegetarian","restaurantEmailOptIn":false,"opentableEmailOptIn":false,"smsOptIn":true},"payment":{"mode":"NONE"}}}}},"savedMethod":{"summary":"HTL — pay with a saved card (payment.mode SAVED_METHOD, v1.51.0 PROPOSAL)","value":{"context":{"domain":"HTL","currency":"INR"},"message":{"intent":{"repriceRef":"RP-H-2ab91c","clientReference":"sample-0001","travelers":[{"type":"ADT","firstName":"Arjun","lastName":"Mehta","email":"arjun@sampletravels.in","phoneNumber":"+919800000000","lead":true}],"payment":{"mode":"SAVED_METHOD","methodId":"PM-01JB2C4V"}}}}}}}}},"responses":{"201":{"description":"Order created; state per DOMAIN-CONTRACTS C1 FSM (or PENDING_SUPPLIER for on-request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"examples":{"confirmed":{"summary":"201 — instant-confirm order (03 §10.1 step 4)","value":{"context":{"domain":"HTL","action":"on_orders","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":2140,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"CONFIRMED","clientReference":"acme-91827","totals":{"amount":84210,"currency":"USD","fxApplied":false},"items":[{"itemRef":"OI-1","domain":"HTL","state":"CONFIRMED","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"conditions":{"refundable":true,"instantConfirm":true,"confirmSlaSeconds":0,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false}}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}},"pendingSupplier":{"summary":"201 — on-request product; supplier answers within its SLA (03 §10.4)","value":{"context":{"domain":"VISA","action":"on_orders","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":3310,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-4QP-7TT-1LM","state":"PENDING_SUPPLIER","items":[{"itemRef":"OI-1","domain":"VISA","state":"PENDING_SUPPLIER","price":{"amount":9900,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEVISAS","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"conditions":{"refundable":false,"instantConfirm":false,"confirmSlaSeconds":259200}}],"fareSource":{"supplier":"EXAMPLEVISAS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}},"partiallyConfirmed":{"summary":"201 — CART, BEST_EFFORT atomicity: one line failed (03 §10.5)","description":"Money is only ever captured for confirmed lines (08 §7). The failed line is also pushed as order.item_failed (03 §10.5).","value":{"context":{"domain":"CART","action":"on_orders","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":5820,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[{"code":"SUPPLIER_DROPPED","message":"XFR supplier unavailable at commit","supplier":"EXAMPLETRANSFERS"}]},"message":{"orderRef":"MO-3HH-8WD-2KC","state":"PARTIALLY_CONFIRMED","items":[{"itemRef":"OI-1","domain":"FLT","state":"CONFIRMED","price":{"amount":118400,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEGDS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}},{"itemRef":"OI-2","domain":"HTL","state":"CONFIRMED","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}},{"itemRef":"OI-3","domain":"XFR","state":"FAILED","fareSource":{"supplier":"EXAMPLETRANSFERS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}]}}},"diningConfirmed":{"summary":"201 — FNB table confirmed; zero total, FREE money model (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_orders","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":1310,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-4DN-7QK-2TB","state":"CONFIRMED","clientReference":"acme-din-0001","totals":{"amount":0,"currency":"USD"},"items":[{"itemRef":"OI-1","domain":"FNB","state":"CONFIRMED","price":{"amount":0,"currency":"USD"},"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"},"extension":{"dining":{"restaurant":{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-12T19:30","partySize":2,"diningAreaId":"1","area":"Main dining room","environment":"INDOOR","tableAttributes":["default"]},"creditCardRequired":false,"cancellationPolicy":{"type":"FREE"},"bookingPolicies":[{"type":"STANDARD","message":"Tables are held for 15 minutes past the reservation time."}],"payment":{"creditCardRequired":false,"processor":"SPREEDLY","cardCapture":"PAYMENT_METHOD_TOKEN"},"poweredBy":"Example Dining","confirmationNumber":"2141"}}}],"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"}}}},"diningCardSecuredConfirmed":{"summary":"201 — FNB card-secured table confirmed: the guarantee and the card the order was booked with (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_orders","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n7","took_ms":1620,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-7KC-3PD-8WX","state":"CONFIRMED","clientReference":"acme-din-0002","totals":{"amount":0,"currency":"USD"},"items":[{"itemRef":"OI-1","domain":"FNB","state":"CONFIRMED","price":{"amount":0,"currency":"USD"},"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"},"extension":{"dining":{"restaurant":{"rid":"100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-13T20:00","partySize":6,"area":"Terrace","environment":"OUTDOOR"},"creditCardRequired":true,"payment":{"creditCardRequired":true,"processor":"SPREEDLY","cardCapture":"PAYMENT_METHOD_TOKEN","cardProvider":"SPREEDLY","cardBrand":"visa","cardLast4":"1111"},"guarantee":{"type":"CREDIT_CARD_DEPOSIT","amount":{"amount":15000,"currency":"USD"},"capture":"PAYMENT_METHOD_TOKEN","chargedBy":"RESTAURANT","platformCharge":false,"refundableUntilDaysBefore":1,"policyId":"cp-88123"},"poweredBy":"Example Dining","confirmationNumber":"2187"}}}],"fareSource":{"supplier":"EXAMPLEDINING","credentialSet":"MONDEE","moneyModel":"FREE"}}}}}}}},"202":{"$ref":"#/components/responses/AmbiguousResult"},"307":{"$ref":"#/components/responses/HomeRegionRedirect"},"400":{"description":"VALIDATION or UNKNOWN_DOMAIN (03 §9). A message.intent.payment.paymentRef shaped like a card number is VALIDATION with details.reason INSTRUMENT_DATA_REFUSED and the digits are never echoed: card data never passes through the platform (OD-301, 15 §2). Dining (v1.7.0): details.reason PAYMENT_METHOD_REQUIRED (a card-secured slot without a token) and SCA_REQUIRED (the supplier demands 3-D Secure; nothing was booked; details.subCode, details.intent, details.paymentMethod and details.retryWith say how to retry: run the challenge, then resend with paymentMethod.threeDs and a NEW Idempotency-Key). connect-v1 1.51.0 (PROPOSAL): payment.mode SAVED_METHOD with paymentRef, paymentMethod or encryptedCard is VALIDATION details.reason PAYMENT_MODE_CONFLICT.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — intent schema violation (03 §2.4, §9)","value":{"code":"VALIDATION","message":"message.intent.repriceRef or message.intent.holdRef is required","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.repriceRef"}}},"instrumentDataRefused":{"summary":"400 VALIDATION — a card-number-shaped paymentRef is refused, digits not echoed (OD-301)","value":{"code":"VALIDATION","message":"message.intent.payment.paymentRef must be a payment id from POST /v1/payments/intent, never card data.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"INSTRUMENT_DATA_REFUSED"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry; accepted list in details (03 §9)","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'HOTELS'. Did you mean HTL?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["HTL","FLT"]}}},"paymentMethodRequired":{"summary":"400 VALIDATION PAYMENT_METHOD_REQUIRED — a card-secured dining slot booked without a payment-method token (v1.7.0)","value":{"code":"VALIDATION","message":"This slot is card-secured: send message.intent.payment.paymentMethod (a Spreedly token).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentMethod","reason":"PAYMENT_METHOD_REQUIRED","supplierCode":"EXAMPLEDINING_CARD_REQUIRED"}}},"scaRequired":{"summary":"400 VALIDATION SCA_REQUIRED — the supplier demands 3-D Secure; nothing was booked (v1.7.0)","description":"Run the challenge with the vault using details.intent, then retry with a new Idempotency-Key, the same token and paymentMethod.threeDs (setupIntent or paymentIntent, as details.retryWith says). subCode is the supplier sub-code; intent and paymentMethod are the vault ids returned to the client, never card data.","value":{"code":"VALIDATION","message":"The card needs 3-D Secure authentication; nothing was booked. Authenticate and retry.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentMethod","reason":"SCA_REQUIRED","supplierCode":"EXAMPLEDINING_SCA_REQUIRED","provider":"SPREEDLY","subCode":"SCAForSetup","intent":"syn-spreedly-intent-0001","paymentMethod":"syn-spreedly-pm-token-0001","retryWith":"setupIntent"}}},"savedMethodModeConflict":{"summary":"400 VALIDATION PAYMENT_MODE_CONFLICT — SAVED_METHOD sent with paymentRef, paymentMethod or encryptedCard (v1.51.0 PROPOSAL)","value":{"code":"VALIDATION","message":"payment.mode SAVED_METHOD names a saved method; do not also send paymentRef, paymentMethod or encryptedCard.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PAYMENT_MODE_CONFLICT","field":"message.intent.payment.paymentRef"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentFailed"},"403":{"$ref":"#/components/responses/OrderForbiddenOrSuspended"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"OFFER_EXPIRED | PRICE_CHANGED | QUOTE_EXPIRED | HOLD_EXPIRED | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS | DUPLICATE_ORDER | CONFLICT (03 §9). QUOTE_EXPIRED carries details.reason NO_SELL_PRICE_STAMP or SELL_PRICE_STAMP_MISMATCH when the repriceRef has no sell price the platform can book (FEAT-013 §12, OD-295). CONFLICT is the payment check (OD-301 (b), OD-360, OD-361 (1)), a spent repriceRef (REPRICE_REF_SPENT, OD-487) or an incomplete FLT journey (JOURNEY_INCOMPLETE, v1.27.0); its details.reason values, and which one is retryable, are listed in the operation description.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"paymentAlreadyUsed":{"summary":"409 CONFLICT ALREADY_USED — another order already booked on this payment (OD-360)","value":{"code":"CONFLICT","message":"This payment has already been used by another order. Create a new payment intent for this booking.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"ALREADY_USED"}}},"repriceRefSpent":{"summary":"409 CONFLICT REPRICE_REF_SPENT — a second order on a repriceRef that already booked (OD-487)","value":{"code":"CONFLICT","message":"This quote has already been booked. Reprice to get a new repriceRef.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.repriceRef","reason":"REPRICE_REF_SPENT"}}},"journeyIncomplete":{"summary":"409 CONFLICT JOURNEY_INCOMPLETE — an order on an outbound-first FLT offer whose return is not chosen (v1.27.0)","value":{"code":"CONFLICT","message":"Choose the return flight and reprice before booking; this offer holds only its outbound journey.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.offerRef","reason":"JOURNEY_INCOMPLETE","offerRef":"OF-F-00000101","nextSliceIndex":1}}},"paymentAmountMismatch":{"summary":"409 CONFLICT AMOUNT_MISMATCH — the intent does not cover this order's total (OD-301 (b))","value":{"code":"CONFLICT","message":"The payment amount does not match this order's total.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"AMOUNT_MISMATCH","detail":"AMOUNT"}}},"paymentOtherQuote":{"summary":"409 CONFLICT OTHER_QUOTE — the intent was created from a different repriceRef","value":{"code":"CONFLICT","message":"The payment was created for a different quote than this order books.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"OTHER_QUOTE"}}},"paymentNotFound":{"summary":"409 CONFLICT NOT_FOUND — no such payment for this tenant","value":{"code":"CONFLICT","message":"No payment with this reference exists for this tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"NOT_FOUND"}}},"paymentNotFunded":{"summary":"409 CONFLICT WRONG_STATE — the intent is not funded (details.detail is its state)","value":{"code":"CONFLICT","message":"The payment is not authorized or captured, so it cannot pay for this order.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"WRONG_STATE","detail":"REQUIRES_ACTION"}}},"paymentPendingReconciliation":{"summary":"409 CONFLICT WRONG_STATE PENDING_RECONCILIATION — a payment operation is unresolved; retryable","value":{"code":"CONFLICT","message":"A payment operation on this intent is still being reconciled. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"field":"message.intent.payment.paymentRef","reason":"WRONG_STATE","detail":"PENDING_RECONCILIATION","retryAfterMs":5000}}},"paymentRefunded":{"summary":"409 CONFLICT REFUNDED — money has been returned on this intent","value":{"code":"CONFLICT","message":"The payment has been refunded, so it cannot pay for this order.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"REFUNDED"}}},"paymentAuthExpired":{"summary":"409 CONFLICT AUTH_EXPIRED — the authorization lapsed before capture","value":{"code":"CONFLICT","message":"The payment authorization has expired. Create a new payment intent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentRef","reason":"AUTH_EXPIRED"}}},"priceChanged":{"summary":"409 PRICE_CHANGED — supplier moved the price at commit; BOTH figures in details","description":"Book NEVER re-quotes (03 §7): a price movement at supplier commit is this error, never a silent swap. details carries the old and the new money object so the mandatory client interstitial can show both figures without computing anything (03 §10.1 F1, OD-80). The client re-reprices and retries with a NEW Idempotency-Key.","value":{"code":"PRICE_CHANGED","message":"The supplier repriced at commit. The order was not created and nothing was captured.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"previousPrice":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"price":{"amount":88010,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:05Z-02"},"repriceRef":"RP-H-2ab91c","repriceRequired":true}}},"priceChangedWithheld":{"summary":"409 PRICE_CHANGED — the price moved at commit; every amount withheld (OD-291, OD-295)","description":"As built: both figures the platform holds at commit are the supplier's, and neither may reach a client. The new SELL price exists only after a reprice, so the refusal carries no amounts: details.pricesWithheld and details.repriceRequired are true. Reprice again, accept the new price, and retry with a NEW Idempotency-Key (DR-06).\n","value":{"code":"PRICE_CHANGED","message":"The price changed between the quote and the commit, so this booking does not proceed at the price you agreed to. Reprice again to see the new price and accept it (DR-06).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","repriceRequired":true,"pricesWithheld":true}}},"quoteExpiredNoSellPriceStamp":{"summary":"409 QUOTE_EXPIRED — the repriceRef carries no sell-price stamp (FEAT-013 §12, OD-295)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has no sell price this platform can book. Reprice again, then book the new RP-.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"NO_SELL_PRICE_STAMP"}}},"quoteExpiredStampMismatch":{"summary":"409 QUOTE_EXPIRED — the stamp does not match the repriceRef's price (FEAT-013 §12, OD-295)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has no sell price this platform can book. Reprice again, then book the new RP-.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"SELL_PRICE_STAMP_MISMATCH"}}},"offerExpired":{"summary":"409 OFFER_EXPIRED — re-reprice, then retry with a NEW key (03 §10.1 F2)","value":{"code":"OFFER_EXPIRED","message":"repriceRef RP-H-2ab91c expired at 2026-09-14T17:14:00Z","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","expiredAt":"2026-09-14T17:14:00Z","repriceRequired":true}}},"holdExpired":{"summary":"409 HOLD_EXPIRED — commit landed past the hold window (03 §10.3 F1)","value":{"code":"HOLD_EXPIRED","message":"holdRef HD-M-4419ab expired at 2026-09-14T17:05:41Z; the seats were released","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"holdRef":"HD-M-4419ab","expiredAt":"2026-09-14T17:05:41Z"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was already used with a different body","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":1500}}},"duplicateOrder":{"summary":"409 DUPLICATE_ORDER — same clientReference + itinerary (03 §9)","description":"The backstop for a client that re-books on a different key while an order is PENDING_RECONCILIATION (03 §10.1 F3).","value":{"code":"DUPLICATE_ORDER","message":"An order already exists for clientReference acme-91827 on this itinerary","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"clientReference":"acme-91827","orderRef":"MO-8KD-2FQ-9XT"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/Unprocessable"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"description":"INTERNAL (03 §9, retryable). Also raised when the payment check passed but the platform could not record that this order uses the payment (OD-360): details.field names message.intent.payment.paymentRef and carries no reason. Nothing was sent to a supplier and the Idempotency-Key is released, so a retry under the same key is safe.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"internal":{"summary":"500 INTERNAL — traceId is the support handle (03 §9, 13 §3)","value":{"code":"INTERNAL","message":"An unexpected error occurred. Quote the traceId to support.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{}}},"paymentClaimFailed":{"summary":"500 INTERNAL — the payment could not be bound to this order; nothing booked, retry (OD-360)","value":{"code":"INTERNAL","message":"The payment could not be reserved for this order. Nothing was booked. Retry with the same Idempotency-Key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"field":"message.intent.payment.paymentRef"}}},"tierCapFxUnavailable":{"summary":"500 INTERNAL FX_SNAPSHOT_UNAVAILABLE — a LIVE_CAPPED order could not be converted to the cap currency (v1.51.0 PROPOSAL)","value":{"code":"INTERNAL","message":"The order total could not be converted to the daily limit's currency at the order's FX snapshot. Nothing was charged, held or sent; retry.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"FX_SNAPSHOT_UNAVAILABLE","currency":"USD","capCurrency":"INR"}}}}}}},"501":{"description":"NOT_IMPLEMENTED — honest fallback (04 §2 rule 2), as components/responses/NotImplemented. connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): also payment.mode SAVED_METHOD while saved payment methods are off on the deployment (aikonic.payment-methods.enabled), details.reason PAYMENT_METHODS_UNAVAILABLE; nothing is charged, written or sent to a supplier.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notImplemented":{"summary":"501 NOT_IMPLEMENTED — and a capabilities-doc bug (04 §2 rule 2)","value":{"code":"NOT_IMPLEMENTED","message":"seatmap is NOT_APPLICABLE for HTL; GET /v1/capabilities says so","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"HTL","verb":"seatmap","capabilitiesStatus":"NOT_APPLICABLE"}}},"noOrderReader":{"summary":"501 NOT_IMPLEMENTED — no order reader assembled on this deployment (OD-P9-01)","value":{"code":"NOT_IMPLEMENTED","message":"This deployment has no order reader; orders cannot be read here","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false}},"paymentMethodsUnavailable":{"summary":"501 NOT_IMPLEMENTED PAYMENT_METHODS_UNAVAILABLE — payment.mode SAVED_METHOD while saved methods are off (v1.51.0 PROPOSAL)","value":{"code":"NOT_IMPLEMENTED","message":"Saved payment methods are not available on this deployment; pay another way.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PAYMENT_METHODS_UNAVAILABLE","field":"message.intent.payment.mode"}}}}}}},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/orders/search":{"post":{"operationId":"searchOrders","x-mcp":{"expose":"tool","toolset":"public","name":"v1_orders_search","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"Filter orders by clientReference, traveler, date, product, state (03 §3 #14)","description":"POST because filter objects outgrow query strings. The crash-recovery entry point (06 §8 step 6). Read-path failures (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3): when the order store times out the answer is 502 UPSTREAM_UNAVAILABLE, retryable true (retry with backoff; nothing is written by a read); when the deployment has no order reader assembled it is 501 NOT_IMPLEMENTED, retryable false.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderSearchRequest"}}}},"responses":{"200":{"description":"Paged order summaries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderSearchResponse"},"examples":{"byClientReference":{"summary":"200 — the crash-recovery lookup by clientReference (06 §8 step 6)","description":"orders[] items are still an untyped object in this contract — no section of 03 or 04 defines an order-summary shape, so this example illustrates one server's projection and is NOT a schema. See OD-CONTRACT-04; read the full order with GET /v1/orders/{orderRef}.","value":{"context":{"domain":"HTL","action":"on_orders_search","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":61,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","page":{"total":1}},"message":{"orders":[{"orderRef":"MO-8KD-2FQ-9XT","state":"CONFIRMED","domain":"HTL","clientReference":"acme-91827","createdAt":"2026-09-14T17:06:02Z","total":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"}}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/orders/{orderRef}":{"get":{"operationId":"getOrder","x-mcp":{"expose":"tool","toolset":"public","name":"v1_orders_get","class":"read","autonomy":"L0","tasks":["get_trip_status"],"example":"{\"orderRef\":\"MO-8KD-2FQ-9XT\"}"},"tags":["orders"],"summary":"Order read (absorbs /v1/booking-detail and GET /v1/bookings/{ref} as aliases)","description":"Read-path failures (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3): when the order store times out the answer is 502 UPSTREAM_UNAVAILABLE, retryable true (a read writes nothing, so a retry is safe); when the deployment has no order reader assembled it is 501 NOT_IMPLEMENTED, retryable false.\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"Order with per-item states, payments[], refunds[], links (allowed next verbs)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"examples":{"confirmed":{"summary":"200 — confirmed order with its per-item states","description":"The third truth level: links/allowed next verbs narrow the offer's conditions, which narrow the capabilities document (04 §2 rule 3).","value":{"context":{"domain":"HTL","action":"on_order","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":44,"from_cache":true,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"CONFIRMED","clientReference":"acme-91827","totals":{"amount":84210,"currency":"USD","fxApplied":false},"items":[{"itemRef":"OI-1","domain":"HTL","state":"CONFIRMED","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"conditions":{"refundable":true,"instantConfirm":true,"confirmSlaSeconds":0,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false}}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}},"pendingReconciliation":{"summary":"200 — the order a client polls after a 202 AMBIGUOUS_RESULT (03 §10.1 F3)","value":{"context":{"domain":"HTL","action":"on_order","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":39,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"PENDING_RECONCILIATION","items":[{"itemRef":"OI-1","domain":"HTL","state":"PENDING_RECONCILIATION","fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}}}}}},"307":{"$ref":"#/components/responses/HomeRegionRedirect"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/orders/{orderRef}/documents":{"get":{"operationId":"listOrderDocuments","x-mcp":{"expose":"resource","toolset":"public","name":"list_order_documents","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"Vouchers, e-tickets, invoices, QR for an order (03 §3 #15)","description":"Read-path failures (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3): when the order store times out the answer is 502 UPSTREAM_UNAVAILABLE, retryable true; when the deployment has no order reader assembled it is 501 NOT_IMPLEMENTED, retryable false.\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"Document descriptors + fetch URLs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentsResponse"},"examples":{"voucherReady":{"summary":"200 — voucher ready, invoice still generating (03 §3 #15)","description":"url is present only once status is READY; the URL is unguessable and time-boxed (15 §5). A PENDING document lands as a document-ready event (03 §3 #15).","value":{"context":{"domain":"HTL","action":"on_documents","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":57,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"documents":[{"documentId":"DOC-9f21a4","type":"VOUCHER","status":"READY","itemRef":"OI-1","url":"https://docs.example/d/9f21a4?sig=7c1e8b02a4d54f19bd3e6c0a91f27de4","expiresAt":"2026-09-15T17:06:02Z"},{"documentId":"DOC-9f21a5","type":"INVOICE","status":"PENDING","itemRef":"OI-1"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}},"post":{"operationId":"regenerateOrderDocuments","x-mcp":{"expose":"tool","toolset":"public","name":"regenerate_order_documents","class":"commit-lite","autonomy":"L1"},"tags":["orders"],"summary":"Regenerate documents (async)","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"202":{"description":"Generation queued; document-ready pushed as an event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentsResponse"},"examples":{"regenerating":{"summary":"202 — regeneration queued; await the document-ready event","value":{"context":{"domain":"HTL","action":"on_documents_regenerate","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":31,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"documents":[{"documentId":"DOC-9f21a6","type":"VOUCHER","status":"PENDING","itemRef":"OI-1"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/orders/{orderRef}/tickets":{"get":{"operationId":"getOrderTickets","x-mcp":{"expose":"resource","toolset":"public","name":"get_order_tickets","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"The booking's tickets, read live from the supplier (v1.16.0)","description":"connect-v1 1.16.0. Each ticket of the order as the supplier states it NOW — the passenger, the fare category, the seat, ISSUED or CANCELLED, the price the supplier displays (its own currency) and the ticket's own document. Read live on every call and never stored: the order holds no traveller (FEAT-F03 §14), so this answer is personal data handed to the caller, Cache-Control no-store. orders:read and the order's own tab, as GET /v1/orders/{orderRef}; another tenant's order is 404. 409 CONFLICT when the order holds no booked supplier reference yet (details.reason ORDER_NOT_TICKETED) or its supplier has no ticket read (TICKETS_NOT_READABLE). First supplier: SUP-BUS-EXAMPLE (GET /tickets/{id}).\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"The order's tickets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderTicketsResponse"},"examples":{"busTickets":{"summary":"200 — a bus booking's ticket (a bus supplier)","value":{"context":{"domain":"BUS","action":"on_tickets","version":"2026-09-30","timestamp":"2026-10-10T01:10:02Z","message_id":"msg_c41a9e","took_ms":612,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-Zodb7cExPJ9H0e3HpNcOzw","tickets":[{"ticketId":"13429803","reference":"EXAMPLEBUS_BOOK_123","passenger":{"firstName":"Test","lastName":"Rider","category":"adult"},"status":"ISSUED","price":{"amount":1000,"currency":"CAD"},"pdf":"https://tickets.example.test/rider_8657006_13429803.pdf"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/orders/{orderRef}/fare-rules":{"get":{"operationId":"getOrderFareRules","x-mcp":{"expose":"resource","toolset":"public","name":"get_order_fare_rules","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"The booked fare's rule text, read live from the supplier (v1.43.0)","description":"connect-v1 1.43.0. The airline's fare-rule text of the fare the order holds, one entry per flown segment in itinerary order — cancellation, change and refund terms as the airline files them (category text, verbatim and in the airline's own words, typically upper case). Read live from the supplier on every call and never stored; the text is the supplier's statement, not a quote: what a cancel or a change costs is answered by POST /v1/orders/{orderRef}/cancel-quote and the amend quote. orders:read and the order's own tab, as GET /v1/orders/{orderRef}; another tenant's order is 404. Cache-Control no-store. text is null on a segment the supplier stated no rules for. 409 CONFLICT when the order holds no booked supplier reference yet (details.reason ORDER_NOT_BOOKED) or its supplier has no fare-rule read (FARE_RULES_NOT_READABLE). First supplier: SUP-AIR-EXAMPLE (airbooking /farerules, keyed per segment from the PNR).\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"The order's fare-rule text","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderFareRulesResponse"},"examples":{"flightFareRules":{"summary":"200 — a held flight's fare rules (a flight supplier)","value":{"context":{"domain":"FLT","action":"on_fare_rules","version":"2026-09-30","timestamp":"2026-10-23T11:20:04Z","message_id":"msg_7f30d2","took_ms":4980,"from_cache":false,"traceId":"0af7651916cd43dd8448eb211c80319c"},"message":{"orderRef":"MO-Zodb7cExPJ9H0e3HpNcOzw","fareRules":[{"origin":"SFO","destination":"LAX","carrier":"WN","departureDate":"2027-02-23","fareBasis":"VXNVH2E","bookingClass":"V","text":"CANCELLATIONS\nTICKET IS NON-REFUNDABLE IN CASE OF CANCEL/NO-SHOW/\nREFUND.\nCHANGES\nCHANGES NOT PERMITTED."}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/orders/{orderRef}/visa-assistance":{"post":{"operationId":"createOrderVisaAssistance","x-mcp":{"expose":"excluded","toolset":"public","name":"create_order_visa_assistance","class":"commit-lite","autonomy":"L1","reason":"Sends travellers' names and e-mail addresses to a third party (Example Visas, a visa supplier) that e-mails each traveller; a person sends it, per order, with the travellers' consent, so no agent may call it (CV-9). Supplier writes are excluded by default (v1.34.0)."},"tags":["orders"],"summary":"Visa assistance: send a booked trip to the visa provider (v1.34.0)","description":"connect-v1 1.34.0. Hands the trip behind a booked order to the visa provider behind SUP-VIS-EXAMPLE (Example Visas), one trip per traveller, 1 to 20 travellers. The provider e-mails each traveller the visa and entry requirements for the trip with a link to apply, and answers one prepared application link per traveller, returned as applyUrl (the traveller applies and pays on the provider's site; no money passes through the platform). A person chooses to send it, per order, with the travellers' consent: nothing is sent automatically. Sent once under the Idempotency-Key; a replay answers the first result and never sends twice (the provider keeps no reference). The platform records each send (the traveller's first name, last initial and masked e-mail, the countries, dates, link and time) and never stores the full e-mail. Any domain's order once it holds a booked supplier reference; otherwise 400 VALIDATION details.reason ORDER_NOT_BOOKED, before any provider call. A trip the provider refuses is 400 VALIDATION details.reason VISA_TRIP_INVALID with the provider's lines in details.errors. Not provisioned or switched off is 503 PROVIDER_DOWN details.reason VISA_ASSISTANCE_NOT_PROVISIONED. orders:write; another tenant's order is 404.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["orders:write"]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderVisaAssistanceRequest"},"examples":{"oneTraveller":{"summary":"One traveller, US passport, a trip to India","value":{"travellers":[{"firstName":"Test","lastName":"Traveller","email":"traveller@example.com","passportCountry":"US"}],"from":"US","to":["IN"],"startDate":"2026-11-20T00:00:00Z","endDate":"2026-11-27T00:00:00Z"}}}}}},"responses":{"201":{"description":"One send per traveller, each with the provider's prepared application link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderVisaAssistanceResponse"},"examples":{"sent":{"summary":"201 — sent; the provider e-mailed the traveller","value":{"orderRef":"MO-ri1IdksWcCAWsPPncUh-mw","provider":"Example Visas","supplier":"SUP-VIS-EXAMPLE","sends":[{"sendId":"vas_7k2m9q4x1p","traveller":"Test T. · t***@example.com","from":"US","to":["IN"],"startDate":"2026-11-20T00:00:00Z","endDate":"2026-11-27T00:00:00Z","applyUrl":"https://visas.example/apply?action=prepared&data=%7B%22vss%22%3A%7B%22ci%22%3A214%2C%22li%22%3A214%2C%22st%22%3A%22dc%22%2C%22pr%22%3Afalse%2C%22cs%22%3A%5B%7B%22c%22%3A94%2C%22v%22%3A2%2C%22p%22%3Afalse%2C%22s%22%3A%22dc%22%7D%5D%7D%2C%22vm%22%3A%221%22%7D","emailedBy":"Example Visas","sentAt":"2026-10-21T10:43:48Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"get":{"operationId":"listOrderVisaAssistance","x-mcp":{"expose":"excluded","toolset":"public","name":"list_order_visa_assistance","class":"read","autonomy":"L0","reason":"Lists what an order sent to the visa provider (masked travellers and the provider's application links); console-only until an agent use case for visa assistance exists (v1.34.0)."},"tags":["orders"],"summary":"Visa assistance: what this order sent to the visa provider (v1.34.0)","description":"connect-v1 1.34.0. Every send this order made to the visa provider, newest first: the masked traveller, the countries and dates, the prepared application link and when. Read from the platform's own record; never calls the provider. An order that sent nothing answers an empty list. orders:read; another tenant's order is 404.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["orders:read"]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"The order's sends, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderVisaAssistanceResponse"},"examples":{"oneSend":{"summary":"200 — one earlier send","value":{"orderRef":"MO-ri1IdksWcCAWsPPncUh-mw","provider":"Example Visas","supplier":"SUP-VIS-EXAMPLE","sends":[{"sendId":"vas_7k2m9q4x1p","traveller":"Test T. · t***@example.com","from":"US","to":["IN"],"startDate":"2026-11-20T00:00:00Z","endDate":"2026-11-27T00:00:00Z","applyUrl":"https://visas.example/apply?action=prepared&data=%7B%22vss%22%3A%7B%22ci%22%3A214%2C%22li%22%3A214%2C%22st%22%3A%22dc%22%2C%22pr%22%3Afalse%2C%22cs%22%3A%5B%7B%22c%22%3A94%2C%22v%22%3A2%2C%22p%22%3Afalse%2C%22s%22%3A%22dc%22%7D%5D%7D%2C%22vm%22%3A%221%22%7D","emailedBy":"Example Visas","sentAt":"2026-10-21T10:43:48Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/orders/{orderRef}/events":{"get":{"operationId":"listOrderEvents","x-mcp":{"expose":"tool","toolset":"public","name":"list_order_events","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"The order's timeline — its events, oldest first, in the GET /v1/events vocabulary (03 §3 #26)","description":"One order's events, in the same WebhookEnvelope / EventPage shape and the same event-type catalog as GET /v1/events and webhook deliveries, so a timeline, the replay feed and a webhook receiver read one vocabulary. Reconciliation is not a separate event type: entering and leaving PENDING_RECONCILIATION are order.state_changed transitions carrying data.from and data.to. payment.* events that carry this orderRef belong on the timeline too. Oldest first; `cursor` continues where nextCursor left off, `type` filters to one event type, both with GET /v1/events' semantics. actor is optional, and actor.kind = ai is mandatory on AI-initiated events (the envelope law). Same permission as the order read: orders:read plus the tab permission of the order's domain; an absent or another tenant's order is a 404. Read-path failures (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3): a `cursor` this route never issued is 400 VALIDATION (details.field cursor), not an empty page; when the order store times out the answer is 502 UPSTREAM_UNAVAILABLE, retryable true; when the deployment has no order reader assembled it is 501 NOT_IMPLEMENTED, retryable false.\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"name":"cursor","description":"The nextCursor of the previous page; absent for the first page.","in":"query","schema":{"type":"string"}},{"name":"type","description":"Only events of this type, for example order.state_changed.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The order's events, oldest first, with a next cursor when more remain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEventsResponse"},"examples":{"booked":{"summary":"200 — written ahead, then confirmed by the supplier","value":{"context":{"domain":"HTL","action":"on_events","version":"2026-09-30","timestamp":"2026-09-23T20:39:59Z","message_id":"msg_3c8e41","took_ms":4,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"events":[{"eventId":"order.state_changed:MO-6fZGaP1WR9H0l1fQjfAYFg:1","type":"order.state_changed","occurredAt":"2026-09-23T20:39:59Z","tenant":"TEN-DEMO","data":{"orderRef":"MO-6fZGaP1WR9H0l1fQjfAYFg","from":null,"to":"PENDING_RECONCILIATION"}},{"eventId":"order.state_changed:MO-6fZGaP1WR9H0l1fQjfAYFg:2","type":"order.state_changed","occurredAt":"2026-09-23T20:39:59Z","tenant":"TEN-DEMO","data":{"orderRef":"MO-6fZGaP1WR9H0l1fQjfAYFg","from":"PENDING_RECONCILIATION","to":"CONFIRMED"}}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/orders/{orderRef}/cancel-quote":{"post":{"operationId":"cancelQuoteOrder","x-mcp":{"expose":"tool","toolset":"public","name":"v1_orders_cancel_quote","class":"read","autonomy":"L0","example":"{\"orderRef\":\"MO-8KD-2FQ-9XT\",\"domain\":\"FLT\",\"message\":{\"intent\":{}}}"},"tags":["orders"],"summary":"Penalty preview before commitment (03 §3 #16, Example Activities mechanism)","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelQuoteRequest"},"examples":{"dining":{"summary":"FNB — quote cancelling a table (free, or the card policy) (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{}}}}}}}},"responses":{"200":{"description":"cancelQuoteRef + penalty breakdown + refund estimate + validUntil","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelQuoteResponse"},"examples":{"penaltyQuote":{"summary":"200 — penalty preview before commitment (03 §3 #16)","description":"Passing cancelQuoteRef to POST /v1/orders/{ref}/cancel guarantees this penalty until validUntil; without it the supplier-current penalty applies with PENALTY_CHANGED 409 protection (03 §3 #17). Both figures are server fields — a client shows them, never sums them.","value":{"context":{"domain":"HTL","action":"on_cancel_quote","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":1420,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"cancelQuoteRef":"CQ-H-77b301","penalty":{"amount":15000,"currency":"USD"},"refundEstimate":{"amount":69210,"currency":"USD"},"validUntil":"2026-09-14T17:36:02Z","policyCheck":{"checked":true,"status":"MATCHES_BOOKED_TERMS"}}}},"penaltyQuoteConverted":{"summary":"200 — a converted order is quoted in its own currency, at its booked FX snapshot (OD-367)","description":"The supplier prices in EUR and the order was booked in USD. The supplier's penalty and refund are restated in USD at the order's own booked snapshot (not the one current now) and at its sell level, so both carry fxApplied and that fxSnapshotId. This order was booked before its cancellation terms were snapshotted, so policyCheck says the penalty was not checked against them.\n","value":{"context":{"domain":"HTL","action":"on_cancel_quote","version":"2026-09-30","timestamp":"2026-09-28T15:02:11Z","message_id":"msg_c02f7a","took_ms":1310,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"cancelQuoteRef":"CQ-H-91d4e0","penalty":{"amount":2646,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"refundEstimate":{"amount":23814,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"validUntil":"2026-09-28T15:32:11Z","policyCheck":{"checked":false,"status":"NO_POLICY_SNAPSHOT","detail":"This order carries no snapshot of the cancellation terms it was booked on, so the supplier's penalty was not checked against them."}}}},"diningFree":{"summary":"200 — FNB cancel quote: zero penalty (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_cancel_quote","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":610,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"cancelQuoteRef":"CQ-D-5a7e20","outcome":"QUOTED","penalty":{"amount":0,"currency":"USD"},"refundEstimate":{"amount":0,"currency":"USD"},"validUntil":"2026-10-05T17:34:11Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT | PENALTY_CHANGED (03 §9). CONFLICT — the order's state forbids a quote: it is over (CANCELLED, REJECTED, FAILED_COMPENSATED; OD-368), it has no supplier locator yet, or it is PENDING_RECONCILIATION (details.state names it). PENALTY_CHANGED with details.reason CONTRADICTS_BOOKED_TERMS — the supplier's penalty contradicts the cancellation terms the order was booked on (OD-369).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"conflictOrderIsOver":{"summary":"409 CONFLICT — the order is over; nothing left to cancel-quote (OD-368)","description":"cancel-quote and cancel on a CANCELLED, REJECTED or FAILED_COMPENSATED order (API-0006 §4: \"cancel on CANCELLED\"). Refused before any supplier call, with nothing written; on cancel the Idempotency-Key is released. details carry the order's current state, the verb refused and the routes the order still offers (no servicing verb among them).\n","value":{"code":"CONFLICT","message":"This order is CANCELLED, so there is nothing left to cancel. Nothing was sent to the supplier and the order is unchanged.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","state":"CANCELLED","attempted":"cancel-quote","links":{"self":"/v1/orders/MO-8KD-2FQ-9XT","documents":"/v1/orders/MO-8KD-2FQ-9XT/documents","events":"/v1/orders/MO-8KD-2FQ-9XT/events"}}}},"penaltyContradictsBookedTerms":{"summary":"409 PENALTY_CHANGED — the supplier's penalty contradicts the terms the order was booked on (OD-369)","description":"The order row carries a snapshot of the offer's cancellation policy taken at commit; the supplier's quoted penalty is compared with what those terms charge now. Here the booking is inside its free-cancellation window and the supplier quoted a charge. Both figures are in the order's currency at its booked FX snapshot. Raised before any consent is read and before anything reaches the supplier's commit; no consent overrides it — the order is resolved outside the API.\n","value":{"code":"PENALTY_CHANGED","message":"The supplier's cancellation penalty is not the one the terms this order was booked on charge now. Both are in details, in the order's currency; nothing was cancelled.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","reason":"CONTRADICTS_BOOKED_TERMS","snapshotPenalty":{"amount":0,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"supplierPenalty":{"amount":2646,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"at":"2026-09-28T15:02:11Z"}}},"cancelCutoffPassed":{"summary":"409 CONFLICT <SUPPLIER>_CANCEL_CUTOFF_PASSED — past the online cutoff of a card-secured table; the restaurant keeps the guarantee (v1.7.0)","description":"Not a penalty the platform collects: forfeit is what the restaurant may keep or charge to the card on file (chargedBy RESTAURANT, platformCharge false). Contact the restaurant.","value":{"code":"CONFLICT","message":"The online cancellation cutoff has passed on a card-secured reservation; contact the restaurant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CANCEL_CUTOFF_PASSED","supplierCode":"EXAMPLEDINING_CANCEL_CUTOFF_PASSED","guaranteeType":"CREDIT_CARD_DEPOSIT","forfeit":{"amount":15000,"currency":"USD"},"cutoff":"2026-11-12T19:30:00Z","chargedBy":"RESTAURANT","platformCharge":false}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"description":"INTERNAL (03 §9) — a bug, traceId the support handle; or, on this verb, details.reason FX_SNAPSHOT_UNAVAILABLE (OD-367): the supplier stated the servicing figures in a currency the order's own booked FX snapshot cannot convert to the order's currency, so they are not shown in another currency (drop-and-count) and no quote is answered. Not retryable: a retry reads the same booked snapshot.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"internal":{"summary":"500 INTERNAL — traceId is the support handle (03 §9, 13 §3)","value":{"code":"INTERNAL","message":"An unexpected error occurred. Quote the traceId to support.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{}}},"fxSnapshotUnavailable":{"summary":"500 INTERNAL FX_SNAPSHOT_UNAVAILABLE — the booked snapshot cannot restate the supplier's figures (OD-367)","description":"A USD order whose supplier answered the cancel-quote in EUR, where the order's booked snapshot carries no EUR to USD rate. No amount is answered and nothing changed.\n","value":{"code":"INTERNAL","message":"The supplier stated this order's servicing figures in a currency the order's own FX snapshot cannot convert to USD, so they are not shown in another currency and nothing was cancelled or changed (details.reason says why).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","reason":"FX_SNAPSHOT_UNAVAILABLE","currency":"USD","fxSnapshotId":"fx-2026-09-14T17:00Z-01","supplierCurrency":"EUR"}}}}}}},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/orders/{orderRef}/cancel":{"post":{"operationId":"cancelOrder","x-mcp":{"expose":"tool","toolset":"public","name":"cancel_with_quote","class":"commit-money","autonomy":"L1","reason":"Proposal-only. cancel_with_quote reads the order, takes a live cancel-quote and files POST /v1/agent-proposals with execution.action orders.cancel, bound to the orderRef and the quoted penalty (intent.acceptedPenalty equals amount). It never calls the cancel verb. A human approves with a D-50 grant and the platform executes once (DR-38, OD-506; D-50 coverage DR-39).","example":"{\"orderRef\":\"MO-8KD-2FQ-9XT\",\"reason\":\"Traveler's plans changed; cancel if the penalty is acceptable\"}"},"tags":["orders"],"summary":"Cancel; optional cancelQuoteRef guarantees the quoted penalty until validUntil (03 §3 #17)","description":"Consent-first cancel (DR-06): send intent.acceptedPenalty from the cancel-quote. D-50 (12 §B2.4, OD-458): an AI-channel call must carry X-Approval-Token, a grant from POST /v1/approvals bound to orders.cancel, the orderRef and the accepted penalty; without one it is 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED, and a spent, expired, unknown or mismatched grant is PERMISSION_DENIED with APPROVAL_TOKEN_USED | _EXPIRED | _INVALID | _MISMATCH. Single use, retried with the same Idempotency-Key. Human sessions and system keys (DR-40) are exempt; an ai key and every service token are the AI channel (DR-39).\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/ApprovalToken"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"},"examples":{"dining":{"summary":"FNB — cancel the table, consenting to the quoted zero penalty (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"cancelQuoteRef":"CQ-D-5a7e20","acceptedPenalty":{"amount":0,"currency":"USD"}}}}}}}}},"responses":{"200":{"description":"Cancellation result; refunds appear on the order's refunds[] block","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"examples":{"cancelled":{"summary":"200 — cancelled at the quoted penalty","description":"Refunds are OUTCOMES of cancel/amend and are read from the order's payments[]/refunds[] blocks — there is deliberately no standalone refund verb (03 §3 #19).","value":{"context":{"domain":"HTL","action":"on_cancel","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":2010,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"CANCELLED","policyCheck":{"checked":true,"status":"MATCHES_BOOKED_TERMS"},"cancellation":{"appliedPenalty":{"amount":0,"currency":"USD"},"appliedRefund":{"amount":84210,"currency":"USD"}},"items":[{"itemRef":"OI-1","domain":"HTL","state":"CANCELLED","price":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"conditions":{"refundable":true,"instantConfirm":true,"confirmSlaSeconds":0,"cancellation":{"freeUntil":"2026-11-01T23:59:00-05:00","penalties":[{"from":"2026-11-02","amount":{"amount":15000,"currency":"USD"}}]},"amendable":{"dates":true,"names":"CORRECTION_ONLY","partialCancel":false}}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}},"cancelledAmountsWithheld":{"summary":"200 — cancelled, every amount withheld; the booked FX snapshot cannot restate the supplier's figures (OD-367)","description":"The supplier has already committed the cancellation, so the outcome is answered; its figures are in a currency the order's booked FX snapshot cannot convert, so no amount is shown rather than one in another currency. amountsWithheld names the reason.\n","value":{"context":{"domain":"HTL","action":"on_cancel","version":"2026-09-30","timestamp":"2026-09-28T15:04:40Z","message_id":"msg_c0a3b1","took_ms":1990,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"CANCELLED","items":[{"itemRef":"OI-1","domain":"HTL","state":"CANCELLED","fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"},"amountsWithheld":"FX_SNAPSHOT_UNAVAILABLE","policyCheck":{"checked":false,"status":"NO_POLICY_SNAPSHOT","detail":"This order carries no snapshot of the cancellation terms it was booked on, so the supplier's penalty was not checked against them."}}}}}}}},"202":{"$ref":"#/components/responses/AmbiguousResult"},"307":{"$ref":"#/components/responses/HomeRegionRedirect"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/PostBookingForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"PENALTY_CHANGED | CONFLICT (03 §9). PENALTY_CHANGED — the penalty moved against the one the client accepted, or (details.reason CONTRADICTS_BOOKED_TERMS) the supplier's penalty contradicts the cancellation terms the order was booked on (OD-369), before any consent is read. CONFLICT — the order's state forbids a cancel: it is over (CANCELLED, REJECTED, FAILED_COMPENSATED; OD-368), it has no supplier locator yet, or it is PENDING_RECONCILIATION (details.state names it). A cancel sent without intent.acceptedPenalty cancels nothing: it is PENALTY_CHANGED with details.penalty (the current penalty) and requoteRequired true, so consent always comes first (DR-06, OD-341 (f)). QUOTE_EXPIRED — the cancelQuoteRef is past its validUntil; re-quote. IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"penaltyNotAccepted":{"summary":"409 PENALTY_CHANGED — no acceptedPenalty was sent, so nothing was cancelled (OD-341 (f))","value":{"code":"PENALTY_CHANGED","message":"Accept the current cancellation penalty to cancel. Nothing was cancelled.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","penalty":{"amount":15000,"currency":"USD"},"requoteRequired":true}}},"quoteExpired":{"summary":"409 QUOTE_EXPIRED — the cancel quote is past validUntil; re-quote (03 §4)","value":{"code":"QUOTE_EXPIRED","message":"cancelQuoteRef CQ-H-77b301 was valid until 2026-09-14T17:36:02Z; re-quote before cancelling","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"cancelQuoteRef":"CQ-H-77b301","validUntil":"2026-09-14T17:36:02Z","requoteRequired":true}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":1500}}},"penaltyChanged":{"summary":"409 PENALTY_CHANGED — supplier penalty moved vs the quote (03 §9)","description":"The cancel-side twin of PRICE_CHANGED: both money figures are server fields so the client can show was/now without arithmetic.","value":{"code":"PENALTY_CHANGED","message":"The supplier penalty moved since the quote; re-quote before cancelling","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"cancelQuoteRef":"CQ-H-77b301","previousPenalty":{"amount":15000,"currency":"USD"},"penalty":{"amount":22000,"currency":"USD"},"requoteRequired":true}}},"penaltyContradictsBookedTerms":{"summary":"409 PENALTY_CHANGED — the supplier's penalty contradicts the terms the order was booked on (OD-369)","description":"The order row carries a snapshot of the offer's cancellation policy taken at commit; the supplier's quoted penalty is compared with what those terms charge now. Here the booking is inside its free-cancellation window and the supplier quoted a charge. Both figures are in the order's currency at its booked FX snapshot. Raised before any consent is read and before anything reaches the supplier's commit; no consent overrides it — the order is resolved outside the API.\n","value":{"code":"PENALTY_CHANGED","message":"The supplier's cancellation penalty is not the one the terms this order was booked on charge now. Both are in details, in the order's currency; nothing was cancelled.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","reason":"CONTRADICTS_BOOKED_TERMS","snapshotPenalty":{"amount":0,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"supplierPenalty":{"amount":2646,"currency":"USD","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"at":"2026-09-28T15:02:11Z"}}},"conflictOrderIsOver":{"summary":"409 CONFLICT — the order is over; nothing left to cancel (OD-368)","description":"cancel-quote and cancel on a CANCELLED, REJECTED or FAILED_COMPENSATED order (API-0006 §4: \"cancel on CANCELLED\"). Refused before any supplier call, with nothing written; on cancel the Idempotency-Key is released. details carry the order's current state, the verb refused and the routes the order still offers (no servicing verb among them).\n","value":{"code":"CONFLICT","message":"This order is CANCELLED, so there is nothing left to cancel. Nothing was sent to the supplier and the order is unchanged.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","state":"CANCELLED","attempted":"cancel","links":{"self":"/v1/orders/MO-8KD-2FQ-9XT","documents":"/v1/orders/MO-8KD-2FQ-9XT/documents","events":"/v1/orders/MO-8KD-2FQ-9XT/events"}}}},"cancelCutoffPassed":{"summary":"409 CONFLICT <SUPPLIER>_CANCEL_CUTOFF_PASSED — past the online cutoff of a card-secured table; the restaurant keeps the guarantee (v1.7.0)","description":"Not a penalty the platform collects: forfeit is what the restaurant may keep or charge to the card on file (chargedBy RESTAURANT, platformCharge false). Contact the restaurant.","value":{"code":"CONFLICT","message":"The online cancellation cutoff has passed on a card-secured reservation; contact the restaurant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CANCEL_CUTOFF_PASSED","supplierCode":"EXAMPLEDINING_CANCEL_CUTOFF_PASSED","guaranteeType":"CREDIT_CARD_DEPOSIT","forfeit":{"amount":15000,"currency":"USD"},"cutoff":"2026-11-12T19:30:00Z","chargedBy":"RESTAURANT","platformCharge":false}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"description":"INTERNAL (03 §9) — a bug, traceId the support handle; or, on this verb, details.reason FX_SNAPSHOT_UNAVAILABLE (OD-367): the supplier stated the servicing figures in a currency the order's own booked FX snapshot cannot convert to the order's currency, so they are not shown in another currency (drop-and-count) and nothing is cancelled: the refusal comes before any supplier commit. Not retryable: a retry reads the same booked snapshot.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"internal":{"summary":"500 INTERNAL — traceId is the support handle (03 §9, 13 §3)","value":{"code":"INTERNAL","message":"An unexpected error occurred. Quote the traceId to support.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{}}},"fxSnapshotUnavailable":{"summary":"500 INTERNAL FX_SNAPSHOT_UNAVAILABLE — the booked snapshot cannot restate the supplier's figures (OD-367)","description":"A USD order whose supplier answered the cancel in EUR, where the order's booked snapshot carries no EUR to USD rate. No amount is answered and nothing changed.\n","value":{"code":"INTERNAL","message":"The supplier stated this order's servicing figures in a currency the order's own FX snapshot cannot convert to USD, so they are not shown in another currency and nothing was cancelled or changed (details.reason says why).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","reason":"FX_SNAPSHOT_UNAVAILABLE","currency":"USD","fxSnapshotId":"fx-2026-09-14T17:00Z-01","supplierCurrency":"EUR"}}}}}}},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/orders/{orderRef}/amend":{"post":{"operationId":"amendOrder","x-mcp":{"expose":"excluded","toolset":"public","name":"amend_order","class":"commit-money","autonomy":"L1","reason":"Amend commit moves money and amend.commit proposals are refused at submit (DR-44: AMEND_COMMIT_NOT_EXECUTABLE), so the proposal flow cannot carry it."},"tags":["orders"],"summary":"Two-phase amend in one endpoint (03 §3 #18)","description":"mode QUOTE returns amendRef + delta price; mode COMMIT with amendRef executes. Covers date shift, exchange, name correction, and post-booking ancillary adds (amendType ADD_ANCILLARY) plus document resubmission (VISA ADD_DOCUMENT). D-50 (12 §B2.4, OD-458): an AI-channel call must carry X-Approval-Token, a grant from POST /v1/approvals bound to amend.commit, the orderRef and the amendment delta (COMMIT only; a QUOTE needs none); without one it is 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED, and a spent, expired, unknown or mismatched grant is PERMISSION_DENIED with APPROVAL_TOKEN_USED | _EXPIRED | _INVALID | _MISMATCH. Single use, retried with the same Idempotency-Key. Human sessions and system keys (DR-40) are exempt; an ai key and every service token are the AI channel (DR-39).\n","parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/ApprovalToken"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmendRequest"},"examples":{"diningQuote":{"summary":"FNB — DATE_SHIFT quote: a new time and party size (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"mode":"QUOTE","amendType":"DATE_SHIFT","changes":{"dateTime":"2026-11-12T20:30","partySize":4}}}}},"diningQuoteDateAndTime":{"summary":"FNB — DATE_SHIFT quote with date + time instead of dateTime (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"mode":"QUOTE","amendType":"DATE_SHIFT","changes":{"date":"2026-11-14","time":"19:15"}}}}},"diningCommit":{"summary":"FNB — commit the quoted amendment (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"mode":"COMMIT","amendType":"DATE_SHIFT","amendRef":"AM-D-61c0f2"}}}},"diningQuoteCardSecured":{"summary":"FNB — DATE_SHIFT quote to a card-secured slot; the token is validated only, not kept (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"mode":"QUOTE","amendType":"DATE_SHIFT","changes":{"dateTime":"2026-11-13T20:00","partySize":6},"payment":{"paymentMethod":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa"}}}}}},"diningCommitCardSecured":{"summary":"FNB — commit an amend to a card-secured slot: the token MUST be sent again on the COMMIT (v1.7.0)","value":{"context":{"domain":"FNB","currency":"USD"},"message":{"intent":{"mode":"COMMIT","amendType":"DATE_SHIFT","amendRef":"AM-D-7a1e33","payment":{"paymentMethod":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa"}}}}}}}}}},"responses":{"200":{"description":"Quote (amendRef + delta) or commit result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmendResponse"},"examples":{"quote":{"summary":"200 mode=QUOTE — amendRef + delta price (03 §3 #18)","description":"deltaPrice is a SERVER field, not the client's subtraction of two totals (PLAT-DESIGNSYS §9.1).","value":{"context":{"domain":"HTL","action":"on_amend","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":1880,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"amendRef":"AM-H-30cc91","deltaPrice":{"amount":4200,"currency":"USD"},"validUntil":"2026-09-14T17:36:02Z"}}},"committed":{"summary":"200 mode=COMMIT — executed against the amendRef","value":{"context":{"domain":"HTL","action":"on_amend","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_b71e4d","took_ms":2960,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"amendRef":"AM-H-30cc91","deltaPrice":{"amount":4200,"currency":"USD"},"validUntil":"2026-09-14T17:36:02Z"}}},"diningQuoted":{"summary":"200 mode=QUOTE — FNB: the new slot is offered, delta zero (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_amend","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":720,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"amendRef":"AM-D-61c0f2","deltaPrice":{"amount":0,"currency":"USD"},"validUntil":"2026-10-05T17:09:11Z","outcome":"QUOTED","handledBy":"SUPPLIER_API"}}},"diningQuoteCardSecuredTarget":{"summary":"200 mode=QUOTE — FNB: the target slot is card-secured; capture the card before committing (v1.7.0)","value":{"context":{"domain":"FNB","action":"on_amend","version":"2026-09-30","timestamp":"2026-10-06T10:00:00Z","message_id":"msg_d1n1n9","took_ms":690,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[{"code":"EXAMPLEDINING_CARD_TOKEN_UNUSED","message":"The card token was checked and not kept; send it again on the COMMIT.","supplier":"EXAMPLEDINING"}]},"message":{"amendRef":"AM-D-7a1e33","deltaPrice":{"amount":0,"currency":"USD"},"validUntil":"2026-10-06T10:05:00Z","outcome":"QUOTED","handledBy":"SUPPLIER_API","targetSlot":{"dateTime":"2026-11-13T20:00","partySize":6,"creditCardRequired":true,"guarantee":{"type":"CREDIT_CARD_DEPOSIT","amount":{"amount":15000,"currency":"USD"},"capture":"PAYMENT_METHOD_TOKEN","chargedBy":"RESTAURANT","platformCharge":false,"refundableUntilDaysBefore":1}}}}}}}}},"202":{"$ref":"#/components/responses/AmbiguousResult"},"307":{"$ref":"#/components/responses/HomeRegionRedirect"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/PostBookingForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"QUOTE_EXPIRED — commit past validity (03 §4) | PRICE_CHANGED — the supplier moved the amendment's price at COMMIT; both figures in details, re-quote (03 §7) | CONFLICT — the order's state forbids an amendment (details.state; an order that is over or PENDING_RECONCILIATION) | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7). Both modes take an Idempotency-Key; a QUOTE sends a fresh one (OD-451 option (a)).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"priceChanged":{"summary":"409 PRICE_CHANGED — the amendment's price moved at COMMIT; re-quote (03 §7)","value":{"code":"PRICE_CHANGED","message":"The amendment's price moved since the quote. Nothing was changed; re-quote.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"amendRef":"AM-H-30cc91","previousPrice":{"amount":4200,"currency":"USD"},"price":{"amount":5100,"currency":"USD"},"requoteRequired":true}}},"conflictOrderState":{"summary":"409 CONFLICT — the order's state forbids an amendment","value":{"code":"CONFLICT","message":"Order MO-8KD-2FQ-9XT is CANCELLED and cannot be amended.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-8KD-2FQ-9XT","state":"CANCELLED","attempted":"amend"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":1500}}},"quoteExpired":{"summary":"409 QUOTE_EXPIRED — COMMIT arrived after validUntil (03 §4)","value":{"code":"QUOTE_EXPIRED","message":"amendRef AM-H-30cc91 was valid until 2026-09-14T17:36:02Z; re-quote","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"amendRef":"AM-H-30cc91","validUntil":"2026-09-14T17:36:02Z","requoteRequired":true}}},"cardSecuredSlotNotAmendable":{"summary":"409 CONFLICT CARD_SECURED_SLOT_NOT_AMENDABLE — the new slot needs a card the supplier cannot take on a modify (v1.7.0)","value":{"code":"CONFLICT","message":"The new slot is card-secured and cannot be moved to by an amendment; cancel and rebook.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CARD_SECURED_SLOT_NOT_AMENDABLE","supplierCode":"EXAMPLEDINING_CARD_REQUIRED","guaranteeType":"CREDIT_CARD_DEPOSIT"}}},"supplierRefused":{"summary":"409 CONFLICT SUPPLIER_REFUSED — the supplier definitely refused the commit; the booking is unchanged (amendment REFUSED, never AMBIGUOUS) (v1.7.0)","value":{"code":"CONFLICT","message":"The supplier refused the amendment; the reservation is unchanged.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_REFUSED","amendmentStatus":"REFUSED","amendRef":"AM-D-61c0f2","supplierCode":"EXAMPLEDINING_AMEND_SLOT_UNAVAILABLE"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/lookup/{type}":{"get":{"operationId":"lookup","x-mcp":{"expose":"tool","toolset":"public","name":"v1_lookup","class":"read","autonomy":"L0","example":"{\"type\":\"airports\",\"q\":\"sao paulo\"}"},"tags":["reference"],"summary":"The only lookup mechanism (03 §3 #21, 05)","description":"Existing five lookup routes become type values. Tenant-filtered; suppliers directory is type suppliers.","parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Lookup type key per 05 §3 (locations, airports, suppliers, currencies, amenity codes)"},{"name":"q","in":"query","schema":{"type":"string"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20, max 100 [assumption, API-0008 §2 / §10 Q5]"},{"name":"filter.country","in":"query","schema":{"type":"string"},"description":"Typed field filter (API-0008 §2 `filter.<field>`). Each lookup type accepts its own set of filter fields; a field the type does not declare is 400 VALIDATION with the accepted `filter.*` names in details.accepted. Alternatives are joined with `|` (filter.country=BR|AR). The fields declared here are the ones a registered type accepts today; others are type-specific and follow the same `filter.<field>` form.\n"},{"name":"filter.role","in":"query","schema":{"type":"string"},"description":"Place role, e.g. origin|destination — absorbs the legacy origins/destinations routes (API-0008 §2)"},{"name":"filter.kind","in":"query","schema":{"type":"string"},"description":"Record kind within a type, e.g. city (places)"},{"name":"filter.timezone","in":"query","schema":{"type":"string"},"description":"IANA time zone (airports)"},{"name":"filter.metroArea","in":"query","schema":{"type":"string"},"description":"Metro-area code grouping several airports (airports)"},{"name":"filter.currency","in":"query","schema":{"type":"string"},"description":"ISO-4217 currency (countries)"},{"name":"filter.area","in":"query","schema":{"type":"string"},"description":"Area grouping of a code list (amenity codes)"},{"name":"filter.packageType","in":"query","schema":{"type":"string","enum":["HOTEL_FLIGHT","HOTEL_BUS","SERVICE"]},"description":"Package type (package-origins, package-destinations, package-calendar)"},{"name":"filter.origin","in":"query","schema":{"type":"string"},"description":"A package-origins id (package-destinations, package-calendar)"},{"name":"filter.destination","in":"query","schema":{"type":"string"},"description":"A package-destinations id (package-calendar)"},{"name":"locale","in":"query","schema":{"type":"string"},"description":"Localized names; fallback chain pt-BR → pt → en (05 §5, API-0008 §2)"},{"name":"If-None-Match","in":"header","schema":{"type":"string"},"description":"Conditional GET against the per-type-version ETag; unchanged → 304 (API-0008 §2/§3)"}],"responses":{"200":{"description":"Reference entries (flat ApiResponse-style paging per 05)","headers":{"ETag":{"schema":{"type":"string"},"description":"Strong, per type-version (API-0008 §3)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"},"examples":{"airports":{"summary":"200 — lookup/airports?q=gru (03 §3 #21, 05 §3)","description":"data[] items are LookupRecord (API-0008 §3): {id, type, names, codes, geo, parents, version, deprecated}, plus name (the locale-resolved display name) and open per-type attributes. This example illustrates the airports type only.","value":{"requestId":"req_5a91c2","data":[{"id":"mondee:airport:GRU","type":"airports","name":"São Paulo–Guarulhos International","names":{"en":"São Paulo–Guarulhos International"},"codes":{"iata":"GRU","icao":"SBGR"},"geo":{"lat":-23.4356,"lon":-46.4731},"parents":["mondee:country:BR"],"version":20887,"deprecated":false,"attributes":{"city":"São Paulo","country":"BR","timezone":"America/Sao_Paulo"}},{"id":"mondee:airport:CGH","type":"airports","name":"São Paulo–Congonhas","names":{"en":"São Paulo–Congonhas"},"codes":{"iata":"CGH","icao":"SBSP"},"geo":{"lat":-23.6261,"lon":-46.6564},"parents":["mondee:country:BR"],"version":20887,"deprecated":false,"attributes":{"city":"São Paulo","country":"BR","timezone":"America/Sao_Paulo"}}],"page":{"nextCursor":"Z3J1LTI","total":2}}}}}}},"304":{"description":"Not modified — If-None-Match matched the current ETag (API-0008 §2)"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/lookup/{type}/export":{"get":{"operationId":"lookupExport","x-mcp":{"expose":"excluded","toolset":"public","name":"lookup_export","class":"read","autonomy":"L0","reason":"Signed-URL download edge (sig and expiry in the query); bytes for a browser, not an agent-readable resource."},"tags":["reference"],"summary":"Bulk + delta export of one lookup type as NDJSON (API-0008 §2/§3, 05 §6)","description":"The offline-sync mechanism, in two calls to this one route. ISSUE (no `sig`): without `since` it answers a handle to a full snapshot as of `asOfVersion`; with `since` a handle to only the records changed or tombstoned after it (a tombstone is a LookupRecord with deprecated true and, on a merge, successorId — the reference.entity_merged re-key protocol, 05 §4). DOWNLOAD: the handle's `url` is this same route on the platform's own origin with the query parameters `asOf`, `expires` and `sig` (plus `since` and `locale` as issued); a GET on it answers the NDJSON file itself, one LookupRecord per line, content type application/x-ndjson, header X-Export-As-Of-Version. The link is HMAC-signed and lives 15 minutes, and it is NOT presigned: the download still needs the caller's credential, like every lookup (OD-450 option (a); a bearer-less link needs an object store, OD-347). A tampered link is 403 PERMISSION_DENIED details.reason EXPORT_URL_INVALID, an expired one EXPORT_URL_EXPIRED, and a dataset that moved since the link was issued 400 VALIDATION EXPORT_SNAPSHOT_SUPERSEDED (issue a new export). Tenant-filtered like every lookup; answered with Cache-Control no-store. Built as of OD-297 — the earlier \"501 as built\" note is retired (OD-340). A `since` older than the delta-retention horizon needs a full re-export — the spec's [PROPOSED] 410 GONE is not in the 03 §9 registry (API-0008 §10 Q2), so it is not declared here.\n","parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Registered lookup type key (05 §2)"},{"name":"since","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0},"description":"Per-type monotonic version from a previous export's asOfVersion; absent = full snapshot. As built, a date-versioned dataset (version yyyyMMdd) also accepts an ISO-8601 date or instant here, read as \"changed after the day before\" (OD-340); the schema stays integer so the change is additive, and a typed client sends the version. A version ahead of the dataset is 400 VALIDATION details.reason SINCE_AHEAD; an instant on a dataset that is not date-versioned is 400 VALIDATION SINCE_INSTANT_UNSUPPORTED.\n"},{"name":"locale","in":"query","schema":{"type":"string"}},{"name":"asOf","in":"query","required":false,"schema":{"type":"integer","format":"int64"},"description":"DOWNLOAD only — the asOfVersion the signed link was issued at; set by the platform in `url`, never by hand"},{"name":"expires","in":"query","required":false,"schema":{"type":"integer","format":"int64"},"description":"DOWNLOAD only — link expiry, epoch seconds; set by the platform in `url`"},{"name":"sig","in":"query","required":false,"schema":{"type":"string"},"description":"DOWNLOAD only — the link's HMAC-SHA-256 signature (base64url); its presence makes the GET a download"}],"responses":{"200":{"description":"ISSUE: an export handle (application/json, API-0008 §3). DOWNLOAD: the NDJSON file (application/x-ndjson, one LookupRecord per line, header X-Export-As-Of-Version).\n","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store on the handle; private, no-store on the file"},"X-Export-As-Of-Version":{"schema":{"type":"integer","format":"int64"},"description":"DOWNLOAD only — the version the file is complete up to"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupExport"},"examples":{"delta":{"summary":"200 — delta export of airports since version 20880 (API-0008 §3)","value":{"type":"airports","format":"ndjson","url":"https://apiq-sandbox.tripproagencysites.com/v1/lookup/airports/export?since=20880&asOf=20887&expires=1790424900&sig=3q2-7w","since":20880,"asOfVersion":20887,"expiresAt":"2026-09-26T12:15:00Z"}}}},"application/x-ndjson":{"schema":{"type":"string","description":"One LookupRecord JSON object per line"},"example":"{\"id\":\"JFK\",\"type\":\"airports\",\"name\":\"John F. Kennedy International\",\"deprecated\":false}\n{\"id\":\"IDL\",\"type\":\"airports\",\"deprecated\":true,\"successorId\":\"JFK\"}\n"}}},"400":{"description":"VALIDATION (03 §9): a malformed parameter (details.field, details.value), or details.reason SINCE_AHEAD (details.currentVersion), SINCE_INSTANT_UNSUPPORTED, or EXPORT_SNAPSHOT_SUPERSEDED on a download whose dataset moved since issue (details.asOfVersion, details.currentVersion; OD-340).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"sinceAhead":{"summary":"400 VALIDATION SINCE_AHEAD — since is newer than the dataset","value":{"code":"VALIDATION","message":"since 30000 is ahead of the airports dataset, currently at 20887","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SINCE_AHEAD","field":"since","value":"30000","currentVersion":20887}}},"snapshotSuperseded":{"summary":"400 VALIDATION EXPORT_SNAPSHOT_SUPERSEDED — the dataset moved since the link was issued; issue a new export","value":{"code":"VALIDATION","message":"The airports dataset moved to version 20890 after this link was issued at 20887. Request a new export.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXPORT_SNAPSHOT_SUPERSEDED","asOfVersion":20887,"currentVersion":20890}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED (03 §9): the caller lacks the route permission, or on a download details.reason EXPORT_URL_INVALID (signature does not verify) or EXPORT_URL_EXPIRED.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"exportUrlInvalid":{"summary":"403 PERMISSION_DENIED EXPORT_URL_INVALID — the signed link was altered or issued elsewhere","value":{"code":"PERMISSION_DENIED","message":"The export link is not valid. Request a new export.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXPORT_URL_INVALID"}}},"exportUrlExpired":{"summary":"403 PERMISSION_DENIED EXPORT_URL_EXPIRED — the link's 15 minutes are up","value":{"code":"PERMISSION_DENIED","message":"The export link has expired. Request a new export.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXPORT_URL_EXPIRED"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/lookup/{type}/{id}":{"get":{"operationId":"lookupRecord","x-mcp":{"expose":"tool","toolset":"public","name":"v1_lookup_record","class":"read","autonomy":"L0"},"tags":["reference"],"summary":"Hydrate one reference record by canonical id (API-0008 §2, 05 §4)","description":"One-record form of the lookup mechanism. Canonical ids are mondee:{type}:{id}; prov- ids stay permanent aliases after promotion, so clients never re-key. A tenant-scoped record outside the caller's entitlement is a 404 (existence hidden, API-0008 §4).\n","parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Registered lookup type key (05 §2)"},{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Canonical id mondee:{type}:{id}, or a prov- provisional alias (05 §4)"},{"name":"locale","in":"query","schema":{"type":"string"}},{"name":"If-None-Match","in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":"The record under data[0]; no page block on single-record hydrate (API-0008 §3)","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"},"examples":{"place":{"summary":"200 — one place record (API-0008 §3)","value":{"requestId":"req_5a91c3","data":[{"id":"mondee:place:CUN-01","type":"places","names":{"en":"Cancún","pt-BR":"Cancún"},"codes":{"iata":"CUN"},"version":20887,"deprecated":false}]}}}}}},"304":{"description":"Not modified — If-None-Match matched the current ETag"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/content":{"post":{"operationId":"syncContent","x-mcp":{"expose":"tool","toolset":"public","name":"sync_content","class":"read","autonomy":"L0"},"tags":["reference"],"summary":"Static content catalog sync + delta feed (03 §3 #22, 05 §6, 06 §7)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRequest"}}}},"responses":{"200":{"description":"Content page / delta batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentResponse"},"examples":{"delta":{"summary":"200 — static content delta feed (03 §3 #22, 05 §6, 06 §7)","description":"message is per-domain and deliberately not unified (03 §1 #4); this shows one server's delta batch shape.","value":{"context":{"domain":"HTL","action":"on_content","version":"2026-09-30","timestamp":"2026-09-14T17:06:02Z","message_id":"msg_c40197","took_ms":120,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","page":{"nextCursor":"ZGVsdGEtMg","total":2}},"message":{"since":"2026-09-13T00:00:00Z","mode":"DELTA","records":[{"contentId":"HTL-HB-449021","op":"UPSERT","updatedAt":"2026-09-14T04:11:00Z"},{"contentId":"HTL-HB-449022","op":"DELETE","updatedAt":"2026-09-14T04:12:30Z"}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/capabilities":{"get":{"operationId":"getCapabilities","x-mcp":{"expose":"tool","toolset":"public","name":"v1_capabilities","class":"read","autonomy":"L0"},"tags":["reference"],"summary":"The load-bearing discovery doc (04, ADR-C03)","description":"Generated from the domain registry + supplier registry + tenant entitlements + credential health — never authored. Omission means not entitled; PLANNED means blocked on contract (D-21); a production 501 anywhere is a capabilities bug. ETag + Cache-Control max-age=300, private; a matching If-None-Match is 304 with no body (API-0007 §5). Every verb key is stated with a status (NOT_APPLICABLE or PLANNED, never absence); omission applies to products only (04 §2 rules 2 and 6).\n","parameters":[{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"The ETag of the document the client holds; a match is 304 Not Modified"},{"name":"domain","in":"query","schema":{"type":"string"}},{"name":"supplier","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Per-tenant capabilities document (04 §2 shape)","headers":{"ETag":{"schema":{"type":"string"}},"Cache-Control":{"schema":{"type":"string"},"description":"max-age=300, private (04 §2)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapabilitiesResponse"},"examples":{"hotelProduct":{"summary":"200 — the worked document from 04 §2, field for field","description":"THE capability fixture. verbs is a map, so an action control reads verbs[verb].status directly: hold is PARTIAL, seatmap is NOT_APPLICABLE. Omission means not entitled — it is never a 501 (04 §2 rule 2). This is the PLANNING truth level; an offer's conditions block overrides it at rate level (rule 5).","value":{"version":"2026-09-14.3","tenant":"acme-travel","generatedFrom":"domain-registry@sha-91ac3","products":[{"domain":"HTL","aliases":["EXAMPLEPACKAGES_HTL"],"tier":"GA","bookingModels":["INSTANT","ON_REQUEST"],"verbs":{"search":{"status":"GA","async":"AUTO","maxRooms":8},"poll":{"status":"GA"},"detail":{"status":"GA"},"schedules":{"status":"NOT_APPLICABLE"},"rules":{"status":"GA"},"ancillaries":{"status":"PLANNED"},"reprice":{"status":"GA"},"hold":{"status":"PARTIAL","note":"per-supplier; see suppliers[]"},"orders":{"status":"GA"},"cancelQuote":{"status":"GA"},"cancel":{"status":"GA"},"amend":{"status":"PARTIAL"},"seatmap":{"status":"NOT_APPLICABLE"}},"suppliers":[{"code":"EXAMPLEHOTELS","credentialSets":["MONDEE","tenant-acme-hb-01"],"flags":{"supportsHold":false,"holdTtlSeconds":null,"supportsPartialCancel":true,"supportsNameChange":"CORRECTION_ONLY","refundMode":"QUEUED","instantConfirm":true,"confirmSlaSeconds":0,"onRequestSlaHours":null,"minLeadTimeHours":0,"maxPax":9,"groupThresholdPax":10,"settlementCurrencies":["USD","EUR","BRL"],"sandboxAvailable":true}}],"limits":{"searchRps":20,"burst":60},"requiredInfoBase":["traveler[].firstName","traveler[].lastName","traveler[].email"]}],"ancillaryTypes":[{"type":"EARLY_CHECKIN","parents":["HTL","VR"]}]}},"plannedVerb":{"summary":"200 — a verb blocked on a supplier contract (04 §2 rule 6, D-21)","description":"Flights' book: 501 today, made machine-readable instead of tribal (04 §2 rule 6). Every verb key is stated; the statuses other than search, poll, reprice and orders are illustrative, not a statement about a real supplier.","value":{"version":"2026-09-14.3","tenant":"acme-travel","generatedFrom":"domain-registry@sha-91ac3","products":[{"domain":"FLT","tier":"GA","bookingModels":["INSTANT"],"verbs":{"search":{"status":"GA","async":"ALWAYS"},"poll":{"status":"GA"},"detail":{"status":"GA"},"schedules":{"status":"GA"},"seatmap":{"status":"GA"},"rules":{"status":"GA"},"ancillaries":{"status":"BETA"},"reprice":{"status":"GA"},"hold":{"status":"NOT_APPLICABLE"},"orders":{"status":"PLANNED","blockedOn":"supplier contract"},"cancelQuote":{"status":"PLANNED"},"cancel":{"status":"PLANNED"},"amend":{"status":"PLANNED"}},"suppliers":[{"code":"EXAMPLEGDS","credentialSets":["MONDEE"],"flags":{"supportsHold":true,"holdTtlSeconds":900,"instantConfirm":true,"confirmSlaSeconds":0,"sandboxAvailable":true}}],"limits":{"searchRps":8,"burst":24}}]}},"diningProduct":{"summary":"200 — the FNB (dining) product entry: verbs, the DATE_SHIFT amend type, Example Dining flags (v1.7.0)","value":{"version":"2026-10-05.1","tenant":"acme-travel","generatedFrom":"domain-registry@sha-4d1e0","products":[{"domain":"FNB","category":"DO","artifacts":["CONF_NUMBER"],"aliases":["REST","DINING","RESTAURANT","RESTAURANTS"],"tier":"BETA","bookingModels":["INSTANT"],"verbs":{"search":{"status":"GA","async":"NEVER"},"poll":{"status":"NOT_APPLICABLE"},"detail":{"status":"GA"},"schedules":{"status":"NOT_APPLICABLE"},"seatmap":{"status":"NOT_APPLICABLE"},"rules":{"status":"GA"},"ancillaries":{"status":"NOT_APPLICABLE"},"reprice":{"status":"GA"},"hold":{"status":"PARTIAL","note":"Example Dining slot lock, about five minutes"},"orders":{"status":"GA"},"cancelQuote":{"status":"GA"},"cancel":{"status":"GA"},"amend":{"status":"PARTIAL","amendTypes":["DATE_SHIFT"]}},"suppliers":[{"code":"EXAMPLEDINING","credentialSets":["MONDEE"],"flags":{"supportsHold":true,"holdTtlSeconds":300,"supportsPartialCancel":false,"supportsNameChange":"NONE","refundMode":"INSTANT","instantConfirm":true,"confirmSlaSeconds":0,"maxPax":20,"sandboxAvailable":true,"retrieveMode":"BY_LOCATOR","supportsRetrieve":true,"paymentFork":"SUPPLIER_HOSTED","bookingModel":"INSTANT"}}],"requiredInfoBase":["traveler[0].firstName","traveler[0].lastName","traveler[0].email","traveler[0].phoneNumber"]}]}}}}}},"304":{"description":"Not Modified — If-None-Match matched the current ETag; no body (API-0007 §5)","headers":{"ETag":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/config/rules":{"get":{"operationId":"getRuleSet","x-mcp":{"expose":"resource","toolset":"public","name":"get_rule_set","class":"read","autonomy":"L0"},"tags":["config"],"summary":"The tenant's published markup rule set + its version (FEAT-013 §6)","description":"Tenant-scoped (tenant from the token, never the body — 07 §2). Returns the currently PUBLISHED immutable rule-set version, or a prior version when `version` is given (rollback = republish a prior version's rules, never edit-in-place — FEAT-013 §5). Every tenant has a platform-seeded zero-markup pass-through version at provisioning, so this never 404s without `version` (FEAT-013 §10). Only rules the caller's layer may see are returned — no endpoint exposes another layer's rules or any supplier net amount (FEAT-013 §6, §14). Like capabilities and webhooks this is a plain tenant-admin resource outside the {context, message} envelope. Requires rules:manage [PROPOSED, FEAT-013 §9].\n","parameters":[{"name":"version","in":"query","required":false,"schema":{"type":"integer","minimum":1},"description":"A prior immutable rule-set version to read (absent = current published)"}],"responses":{"200":{"description":"The rule set; ETag mirrors its version","headers":{"ETag":{"schema":{"type":"string"},"description":"\"rs-{version}\" — changes on every publish"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSet"},"examples":{"published":{"summary":"200 — published version 7 (seeded pass-through + one tenant markup + EXAMPLEHOTELS4 floor)","value":{"version":7,"state":"PUBLISHED","publishedAt":"2026-09-20T10:12:00Z","publishedBy":"usr_rm_0142","simulationId":"sim_7c1e90","simulateRequired":true,"rules":[{"ruleId":"rule_plat_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0},{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL","currency":"USD"},"action":{"kind":"MARKUP_PCT","value":1000},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"publishRuleSet","x-mcp":{"expose":"excluded","toolset":"public","name":"publish_rule_set","class":"commit-lite","autonomy":"L1","reason":"Publishes the tenant's markup pricing for every later sale (rules:manage); tenant state that changes what customers pay, and no proposal action carries it."},"tags":["config"],"summary":"Publish a new immutable rule-set version (FEAT-013 §5, §6)","description":"Mints version baseVersion+1 from the full rule list in the body; prior versions stay readable forever (archaeology, FEAT-013 §7). Optimistic concurrency: `baseVersion` must equal the current published version, otherwise 409 CONFLICT with details.currentVersion — re-read, re-simulate, re-publish. Simulate-before-publish: while the tenant flag rules.simulateRequired is on [PROPOSED default true, FEAT-013 §10] `simulationId` must name a simulation run on exactly this rule list, otherwise 409 CONFLICT (details.reason SIMULATION_REQUIRED). Two rules tying at the same layer/priority/specificity, or a lower layer widening a higher layer's cap, are 400 VALIDATION at publish time — never a runtime coin-flip (FEAT-013 §9, §12). Negative-margin guard (09 §9 failure flows, OD-340, OD-346): when the quoted simulation priced any line at a negative margin, publish is 409 CONFLICT (details.reason NEGATIVE_MARGIN_OVERRIDE_REQUIRED, with details.simulationId and details.negativeMarginLines) unless the body sets negativeMarginOverride true AND gives a reason; an override with no reason (and no note) is 400 VALIDATION details.reason REASON_REQUIRED. The reason and the override are recorded on the version and on config.rules_published, not echoed on RuleSet. Emits config.rules_published on the event envelope (FEAT-013 §8). Requires rules:manage.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSetPublishRequest"},"examples":{"negativeMarginOverride":{"summary":"Publish over a simulation that found a negative-margin line — explicit override with reason (09 §9)","value":{"baseVersion":8,"simulationId":"sim_9e31b4","note":"Loss-leader HTL promo for the Q4 launch","reason":"Approved by revenue management for the Q4 launch week; margin restored in v10","negativeMarginOverride":true,"rules":[{"ruleId":"rule_plat_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0}]}},"raiseMarkup":{"summary":"Publish v8 on top of v7 after simulating it","value":{"baseVersion":7,"simulationId":"sim_8d20a1","note":"HTL USD markup 10% → 12%","rules":[{"ruleId":"rule_plat_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0},{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL","currency":"USD"},"action":{"kind":"MARKUP_PCT","value":1200},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]}}}}}},"responses":{"200":{"description":"Published; the body is the new version","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSet"},"examples":{"published":{"summary":"200 — version 8 published","value":{"version":8,"state":"PUBLISHED","publishedAt":"2026-09-25T09:30:00Z","publishedBy":"usr_rm_0142","simulationId":"sim_8d20a1","note":"HTL USD markup 10% → 12%","rules":[{"ruleId":"rule_plat_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0},{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL","currency":"USD"},"action":{"kind":"MARKUP_PCT","value":1200},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]}}}}}},"400":{"description":"VALIDATION (03 §9). details.reason names the rule that failed: INVALID_RULE, RULE_TIE, LAYER_AUTHORITY, CAP_EXCEEDED (FEAT-013 §9, §12), or REASON_REQUIRED when negativeMarginOverride is true with no reason (09 §9, OD-340).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"ruleTie":{"summary":"400 VALIDATION RULE_TIE — two rules tie at one layer/priority/specificity (FEAT-013 §9)","value":{"code":"VALIDATION","message":"Rules rule_tnt_htl_10pct and rule_tnt_htl_12pct tie at TENANT priority 100 with the same scope","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"RULE_TIE","field":"rules"}}},"reasonRequired":{"summary":"400 VALIDATION REASON_REQUIRED — a negative-margin override needs a reason (09 §9, OD-340)","value":{"code":"VALIDATION","message":"negativeMarginOverride requires a reason","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REASON_REQUIRED","field":"reason"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT (stale baseVersion, no matching simulation, or a negative-margin simulation without an override — details.reason STALE_VERSION | SIMULATION_REQUIRED | NEGATIVE_MARGIN_OVERRIDE_REQUIRED) | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7, §9)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"negativeMarginOverrideRequired":{"summary":"409 CONFLICT — the simulation priced lines at a negative margin; override with a reason (09 §9, OD-340)","value":{"code":"CONFLICT","message":"Simulation sim_9e31b4 priced 2 lines at a negative margin. Set negativeMarginOverride and give a reason to publish.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NEGATIVE_MARGIN_OVERRIDE_REQUIRED","simulationId":"sim_9e31b4","negativeMarginLines":2}}},"staleVersion":{"summary":"409 CONFLICT — someone published first; re-read and re-simulate","value":{"code":"CONFLICT","message":"baseVersion 7 is stale; the current published version is 8","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STALE_VERSION","baseVersion":7,"currentVersion":8}}},"simulationRequired":{"summary":"409 CONFLICT — publish blocked until this exact rule list is simulated (FEAT-013 §5)","value":{"code":"CONFLICT","message":"No simulation ran on this rule list; call POST /v1/config/rules:simulate first","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SIMULATION_REQUIRED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/config/rules:simulate":{"post":{"operationId":"simulateRuleSet","x-mcp":{"expose":"tool","toolset":"public","name":"simulate_rule_set","class":"read","autonomy":"L0"},"tags":["config"],"summary":"Price sample offers against a draft or published rule set — no side effects on prices (FEAT-013 §5)","description":"The ':simulate' custom-method path is FEAT-013 §6's own spelling; it is a literal path (no template), and every generator this contract feeds emits it verbatim. Envelope in/out per 03 §1: context.domain is the product of the samples. The candidate is either a draft rule list (intent.ruleSet.rules) or a stored version (intent.ruleSet.version); absent = the current published version. Each sample is priced under the candidate AND under the current published version so the editor can render old-vs-new plus floor-clamp counts. The returned simulationId is what PUT /v1/config/rules must quote to publish exactly this rule list. Samples are either a repriceRef (replayed from the RP- snapshot, FEAT-013 §5) or an inline base price supplied by the caller; the response never echoes a supplier net amount or floor value (kill-list, FEAT-013 §14). Instead of listing samples the caller may ask for the tenant's recent traffic: intent.sampleRecent {days 1..30, default 7; limit 1..200, default 50} replays that many recent RP- snapshots (OD-340); with nothing to replay the answer is 400 VALIDATION details.reason NO_RECENT_SAMPLES. summary.negativeMargin counts results priced below their base, which is what makes a later publish need negativeMarginOverride. Not a mutation, so no Idempotency-Key. Requires rules:manage.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSimulationRequest"},"examples":{"draft":{"summary":"Simulate a draft 12% HTL markup against one inline sample","value":{"context":{"domain":"HTL","currency":"USD"},"message":{"intent":{"ruleSet":{"rules":[{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL","currency":"USD"},"action":{"kind":"MARKUP_PCT","value":1200},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]},"samples":[{"sampleId":"s1","supplier":"EXAMPLEHOTELS4","basePrice":{"amount":76555,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-25T09:00Z-01"},"sellFloor":{"amount":90000,"currency":"USD"}}]}}}},"sampleRecent":{"summary":"Simulate a draft against the last 7 days of this tenant's HTL reprices (OD-340)","value":{"context":{"domain":"HTL","currency":"USD"},"message":{"intent":{"ruleSet":{"version":9},"sampleRecent":{"days":7,"limit":50}}}}}}}}},"responses":{"200":{"description":"Per-sample old-vs-new sell prices with breakdown + appliedRules, and a simulationId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSimulationResponse"},"examples":{"floorClamped":{"summary":"200 — the 12% draft lands below the Example Hotels sell floor, so the floor clamps (FEAT-013 §11)","value":{"context":{"domain":"HTL","action":"on_simulate","version":"2026-09-30","timestamp":"2026-09-25T09:29:02Z","message_id":"msg_b71d02","took_ms":14,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[]},"message":{"simulationId":"sim_8d20a1","baselineVersion":7,"summary":{"samples":1,"priced":1,"dropped":0,"floorClamped":1,"negativeMargin":0},"results":[{"sampleId":"s1","status":"PRICED","price":{"amount":90000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-25T09:00Z-01"},"baselinePrice":{"amount":90000,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-25T09:00Z-01"},"breakdown":[{"type":"BASE","amount":90000}],"appliedRules":[{"ruleId":"rule_tnt_htl_10pct","version":0,"layer":"TENANT","action":"MARKUP_PCT","delta":{"amount":13445,"currency":"USD"},"floorClamped":true}]}]}}}}}}},"400":{"description":"VALIDATION or UNKNOWN_DOMAIN (03 §9). details.reason INVALID_SAMPLE (a sampleRecent bound out of range, details.field sampleRecent.days or sampleRecent.limit), NO_RECENT_SAMPLES (nothing to replay in the window, details.days), or one of the publish-time rule checks.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"noRecentSamples":{"summary":"400 VALIDATION NO_RECENT_SAMPLES — no reprices to replay in the window (OD-340)","value":{"code":"VALIDATION","message":"No recent reprices to replay in the last 7 days","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NO_RECENT_SAMPLES","days":7}}},"invalidSample":{"summary":"400 VALIDATION INVALID_SAMPLE — sampleRecent bound out of range (OD-340)","value":{"code":"VALIDATION","message":"sampleRecent.days must be between 1 and 30","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INVALID_SAMPLE","field":"sampleRecent.days"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry; accepted list in details (03 §9)","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'HOTELS'. Did you mean HTL?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["HTL"]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/intent":{"post":{"operationId":"createPaymentIntent","x-mcp":{"expose":"excluded","toolset":"public","name":"create_payment_intent","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Create a payment intent from a repriceRef (03 §8), or on the tenant's own provider account (v1.49.0)","description":"Returns PSP client secret or Pix QR payload. Provider behind the seam is config (D-26). With paymentSource (v1.49.0, PLAT-FINSUP phase 2) the intent runs on the tenant's OWN account at the provider, registered as a tenant-owned credential set: the caller states amount, sends no repriceRef, and must send an Idempotency-Key; the intent never pays an Apiq order and the response carries paymentSource instead of fork. Production external API never accepts PAN (18 §6); SANDBOX/UAT accept a card sealed to the platform key on POST /v1/orders (payment.mode ENCRYPTED_CARD, v1.36.0, PLAT-CARDENV, OD-904), never on this route. CREDIT_LINE and BR installments ride the same intent object. A deployment with no payment provider configured (provider=none) answers 501 NOT_IMPLEMENTED on every payment route (OD-295).\n","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"OPTIONAL on intent create (API-0010 §5 [PROPOSED]; making it required would be a breaking change, OD-299) — except with paymentSource (v1.49.0), where it is REQUIRED (400 VALIDATION details.reason IDEMPOTENCY_KEY_REQUIRED) because it keys the provider's own idempotency. When sent it is honoured exactly as the 03 §7 key: 48h TTL, same body replays the original response with Idempotency-Replayed true, a different body is 409 IDEMPOTENCY_CONFLICT. A replay returns the STORED answer, so its `checkout` (the hosted session an intent may carry) may since have been consumed or expired: re-read the intent (GET /v1/payments/{paymentId}) for the live session (OD-447).\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentRequest"},"examples":{"fromReprice":{"summary":"On the platform's account — the amount derives from the repriceRef (03 §8)","value":{"repriceRef":"RP-H-2ab91c","method":"CARD","captureModel":"MANUAL_CAPTURE"}},"onTenantOwnAccount":{"summary":"On the tenant's own Example Pay account — the caller states the amount (v1.49.0)","value":{"amount":{"amount":24500,"currency":"EUR"},"method":"CARD","captureModel":"MANUAL_CAPTURE","paymentSource":{"supplier":"SUP-PAY-EXAMPLE","credentialSet":"TEN-7f3a:SUP-PAY-EXAMPLE:own:0a1b2c3d"}}}}}}},"responses":{"200":{"description":"Intent with client-side handoff material","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentResponse"},"examples":{"cardIntent":{"summary":"200 — card intent; amount derives from the repriceRef (03 §8)","description":"Clients never send free-form amounts; the amount comes from the repriceRef. The production external API never accepts a PAN (03 §8, 18 §6).","value":{"paymentId":"pay_int_91c4","clientSecret":"pi_3Qd…_secret_9Xk…","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"}}},"pixIntent":{"summary":"200 — Pix QR payload, BR first-class (03 §8, A-1)","value":{"paymentId":"pay_int_91c5","pixQr":"00020126580014BR.GOV.BCB.PIX0136a1f3…5204000053039865802BR","amount":{"amount":451800,"currency":"BRL","fxApplied":true,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"}}},"tenantOwnIntent":{"summary":"200 — on the tenant's own account; paymentSource present, fork absent (v1.49.0)","value":{"paymentId":"pay_int_91c6","amount":{"amount":24500,"currency":"EUR"},"paymentSource":{"kind":"TENANT_OWN","credentialSet":"TEN-7f3a:SUP-PAY-EXAMPLE:own:0a1b2c3d"}}}}}}},"400":{"$ref":"#/components/responses/PaymentIntentBadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentIntentCreateFailed"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"description":"NOT_FOUND (v1.49.0) — details.reason PAYMENT_SOURCE_NOT_FOUND: the tenant has no such credential set for the supplier. A set of another tenant answers the same.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"paymentSourceNotFound":{"summary":"404 NOT_FOUND PAYMENT_SOURCE_NOT_FOUND — no such own credential set (v1.49.0)","value":{"code":"NOT_FOUND","message":"The tenant has no own credential set for SUP-PAY-EXAMPLE.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"paymentSource","reason":"PAYMENT_SOURCE_NOT_FOUND"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT (v1.49.0): details.reason PAYMENT_SOURCE_NOT_READY — the named credential set cannot serve payments (its key was refused, or it is revoked or disabled). QUOTE_EXPIRED | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §9). QUOTE_EXPIRED: the repriceRef expired or was spent, or it has no sell price the platform can charge — details.reason NO_SELL_PRICE_STAMP or SELL_PRICE_STAMP_MISMATCH (FEAT-013 §12, OD-295, OD-299), or REPRICE_REF_HELD when a live intent already holds this RP- with a different method or capture model (details.paymentId, details.state; OD-340) — reuse that intent or reprice again. Reprice again and create the intent from the new RP-. IDEMPOTENCY_IN_PROGRESS also answers a second create on the same RP- while the first is still being created (details.reason INTENT_CREATE_IN_PROGRESS, retryable).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"quoteExpired":{"summary":"409 QUOTE_EXPIRED — the repriceRef expired or was spent (API-0010 §4)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has expired or been spent. Reprice again, then create the intent from the new RP- (API-0010 §4).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c"}}},"quoteExpiredNoSellPriceStamp":{"summary":"409 QUOTE_EXPIRED — the repriceRef carries no sell-price stamp (FEAT-013 §12)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has no sell price this platform can charge. Reprice again, then create the intent from the new RP-.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"NO_SELL_PRICE_STAMP"}}},"quoteExpiredStampMismatch":{"summary":"409 QUOTE_EXPIRED — the stamp does not match the repriceRef's price (FEAT-013 §12)","value":{"code":"QUOTE_EXPIRED","message":"The repriceRef has no sell price this platform can charge. Reprice again, then create the intent from the new RP-.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"SELL_PRICE_STAMP_MISMATCH"}}},"repriceRefHeld":{"summary":"409 QUOTE_EXPIRED REPRICE_REF_HELD — a live intent already holds this RP- on other terms (OD-340)","value":{"code":"QUOTE_EXPIRED","message":"repriceRef RP-H-2ab91c is held by payment pi_7Hq2 with a different method or capture model. Use that payment or reprice again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRef":"RP-H-2ab91c","reason":"REPRICE_REF_HELD","paymentId":"pi_7Hq2","state":"REQUIRES_ACTION"}}},"intentCreateInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS INTENT_CREATE_IN_PROGRESS — another create on this RP- is running (OD-340)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A payment intent for this repriceRef is still being created. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"repriceRef":"RP-H-2ab91c","reason":"INTENT_CREATE_IN_PROGRESS"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same optional key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — the first request with this key is still running (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still in flight. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}},"paymentSourceNotReady":{"summary":"409 CONFLICT PAYMENT_SOURCE_NOT_READY — the tenant's credential set cannot serve payments (v1.49.0)","value":{"code":"CONFLICT","message":"The payment source cannot serve payments: Example Pay refused the key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PAYMENT_SOURCE_NOT_READY","credentialSet":"TEN-7f3a:SUP-PAY-EXAMPLE:own:0a1b2c3d"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/{paymentId}/confirm":{"post":{"operationId":"confirmPayment","x-mcp":{"expose":"excluded","toolset":"public","name":"confirm_payment","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Confirm an intent → paymentRef usable in orders (03 §8)","parameters":[{"$ref":"#/components/parameters/SandboxTest"},{"name":"paymentId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActorKind"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentConfirmRequest"}}}},"responses":{"200":{"description":"Confirmed; paymentRef returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentConfirmResponse"},"examples":{"confirmed":{"summary":"200 — confirmed; paymentRef is usable in POST /v1/orders (03 §8)","value":{"paymentRef":"pay_91c4_cnf","status":"CONFIRMED"}},"requiresChallenge":{"summary":"200 — 3DS challenge required; finish via /v1/payments/3ds-callback","description":"For sub-2-minute hold TTLs, create the payment intent BEFORE the hold — a 3DS challenge that overruns the window is a HOLD_EXPIRED (03 §10.3 F3).","value":{"paymentRef":"pay_91c4_pend","status":"REQUIRES_CHALLENGE"}},"pendingReconciliation":{"summary":"200 — the provider call's outcome is unknown; being reconciled, never re-sent (OD-205, OD-295)","description":"The confirm reached the payment provider and no answer came back. The intent is PENDING_RECONCILIATION and is reconciled against the provider — a confirm is never re-sent. Poll by confirming again with the SAME Idempotency-Key (replayed) or a new confirm (409 CONFLICT while unresolved), or await payment.captured / payment.failed.\n","value":{"paymentRef":"pay_int_91c4","status":"PENDING_RECONCILIATION"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentFailed"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §9). CONFLICT carries details.reason: PENDING_RECONCILIATION (retryable true — a previous confirm reached the provider and its outcome is being reconciled, OD-205), PAYMENT_CANCELED, or CONCURRENT_UPDATE (OD-295, OD-299).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"pendingReconciliation":{"summary":"409 CONFLICT — a previous confirm is PENDING_RECONCILIATION; retry later (OD-205)","value":{"code":"CONFLICT","message":"A previous confirm of this intent reached the payment provider and its outcome is not yet known. It is being reconciled — never re-sent — so this confirm cannot act. Retry later; the answer will be the reconciled one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"PENDING_RECONCILIATION","paymentId":"pay_int_91c4"}}},"paymentCanceled":{"summary":"409 CONFLICT — the intent was canceled","value":{"code":"CONFLICT","message":"This payment intent was canceled.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PAYMENT_CANCELED","paymentId":"pay_int_91c4"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — the first confirm with this key is still running (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still in flight. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/3ds-callback":{"post":{"operationId":"threeDsCallback","x-mcp":{"expose":"excluded","toolset":"public","name":"three_ds_callback","class":"commit-money","autonomy":"L1","reason":"3DS challenge completion callback from the cardholder's browser; payment flow, not agent-callable (DR-44)."},"tags":["payments"],"summary":"3DS challenge completion callback (03 §8)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreeDsCallbackRequest"}}}},"responses":{"200":{"description":"Challenge result recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentConfirmResponse"},"examples":{"challengePassed":{"summary":"200 — challenge passed; paymentRef released (03 §8)","value":{"paymentRef":"pay_91c4_cnf","status":"CONFIRMED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"}}}},"/v1/payments/{paymentId}/capture":{"post":{"operationId":"capturePayment","x-mcp":{"expose":"excluded","toolset":"public","name":"capture_payment","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Capture an authorized payment, fully or partially — capture-on-ticketing (API-0010 §2 [PROPOSED], OD-308)","description":"Captures up to the authorized remainder. amount absent = capture the whole remainder. Partial and multiple captures follow the provider's capability flags. Captures serialize per payment on its state machine: a capture beyond the authorized remainder, or of an intent that is not AUTHORIZED / PARTIALLY_CAPTURED, is 409 CONFLICT — never an over-capture (a PARTIALLY_REFUNDED payment has nothing left to capture, so it is INVALID_STATE too) (API-0010 §5). Idempotency-Key is REQUIRED on this new operation. From an AI channel (X-Actor-Kind ai) the D-50 approval token is required (API-0010 §5). The answer is provisional until the provider's webhook lands; the confirmation signal is payment.captured (API-0010 §3/§6). Harness provider only today (DR-26). A post-booking operation on an intent that already exists: it stays up for a SUSPENDED tenant and, on the production deployment, for a tenant whose activation has not been approved (07 §5, FEAT-020 §12, §18b), so its 403 is PaymentForbidden — never TENANT_SUSPENDED or TENANT_NOT_LIVE.\n","parameters":[{"$ref":"#/components/parameters/PaymentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActorKind"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentCaptureRequest"},"examples":{"partial":{"summary":"Capture USD 500.00 of an authorized USD 842.10","value":{"amount":{"amount":50000,"currency":"USD"}}}}}}},"responses":{"200":{"description":"The payment intent after the capture","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentResponse"},"examples":{"partiallyCaptured":{"summary":"200 — USD 500.00 of USD 842.10 captured (API-0010 §3)","value":{"paymentId":"pay_int_91c4","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"method":"CARD_TOKEN","captureModel":"MANUAL_CAPTURE","state":"PARTIALLY_CAPTURED","authValidUntil":"2026-09-21T17:06:00Z","capturedAmount":{"amount":50000,"currency":"USD"}}}}}}},"400":{"$ref":"#/components/responses/PaymentAmountInvalid"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/PaymentForbidden"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/PaymentMoneyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/{paymentId}/void":{"post":{"operationId":"voidPayment","x-mcp":{"expose":"excluded","toolset":"public","name":"void_payment","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Void an authorized, uncaptured payment — release the hold (API-0010 §2, OD-306 (c), OD-497)","description":"Cancels the authorization of an intent that holds funds but has captured nothing: the intent becomes CANCELED and the provider releases the hold. An intent that has captured any money is 409 CONFLICT — refund it instead (POST /v1/payments/{paymentId}/refund); an intent already CANCELED answers 200 with its state unchanged. Idempotency-Key is REQUIRED. D-50 (DR-39): an AI-channel caller must carry X-Approval-Token, a grant from POST /v1/approvals bound to payments.void, the paymentId and the authorized amount; human sessions and system keys (DR-40) are exempt. The answer is provisional until the provider's webhook lands; the confirmation signal is payment.canceled. Harness provider only today (DR-26). A post-booking operation on an intent that already exists: it stays up for a SUSPENDED tenant and, on the production deployment, for a tenant whose activation has not been approved (07 §5, FEAT-020 §12, §18b), so its 403 is PaymentForbidden — never TENANT_SUSPENDED or TENANT_NOT_LIVE.\n","parameters":[{"$ref":"#/components/parameters/PaymentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActorKind"},{"$ref":"#/components/parameters/ApprovalToken"}],"responses":{"200":{"description":"The payment intent after the void","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentResponse"},"examples":{"voided":{"summary":"200 — the USD 842.10 authorization released (OD-497)","value":{"paymentId":"pay_int_91c4","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"method":"CARD_TOKEN","captureModel":"MANUAL_CAPTURE","state":"CANCELED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/PaymentForbidden"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/PaymentMoneyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/{paymentId}/refund":{"post":{"operationId":"refundPayment","x-mcp":{"expose":"excluded","toolset":"public","name":"refund_payment","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Execute a provider-level refund of captured money (API-0010 §2/§6 [PROPOSED], OD-308)","description":"The provider-execution layer that the order's cancel and amend verbs drive, plus console goodwill refunds — never a second client-facing refund truth: the order stays the money-back source of truth and order.refund_issued its event (03 §3 #19, API-0010 §6). amount absent = refund everything captured and not yet refunded. A refund beyond the captured-and-unrefunded amount is 409 CONFLICT, and on a cancelled order so is a refund beyond the order's committed cancellation refund (captured minus penalty, OD-707). A refund that leaves part of the captured amount unrefunded moves a CAPTURED payment (or a PARTIALLY_CAPTURED one whose authorization has lapsed) to PARTIALLY_REFUNDED, which accepts further refunds; the refund that brings cumulative refunds to the captured amount moves it to REFUNDED (terminal). A PARTIALLY_CAPTURED payment that can still capture keeps that state after a partial refund (refundedAmount shows it). payment.refunded is unchanged: one event per refund, amount = the rise. Idempotency-Key is REQUIRED on this new operation; from an AI channel the D-50 approval token is required. Harness provider only today (DR-26). A post-booking operation on an intent that already exists: it stays up for a SUSPENDED tenant and, on the production deployment, for a tenant whose activation has not been approved (07 §5, FEAT-020 §12, §18b), so its 403 is PaymentForbidden — never TENANT_SUSPENDED or TENANT_NOT_LIVE.\n","parameters":[{"$ref":"#/components/parameters/PaymentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActorKind"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRefundRequest"},"examples":{"partial":{"summary":"Refund USD 150.00 of the captured amount","value":{"amount":{"amount":15000,"currency":"USD"},"reason":"GOODWILL"}}}}}},"responses":{"200":{"description":"The payment intent after the refund; refundedAmount is cumulative","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentResponse"},"examples":{"partiallyRefunded":{"summary":"200 — USD 150.00 refunded of USD 842.10 captured (API-0010 §6; state PARTIALLY_REFUNDED since v1.6.2)","value":{"paymentId":"pay_int_91c4","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"method":"CARD_TOKEN","captureModel":"MANUAL_CAPTURE","state":"PARTIALLY_REFUNDED","capturedAmount":{"amount":84210,"currency":"USD"},"refundedAmount":{"amount":15000,"currency":"USD"}}}}}}},"400":{"$ref":"#/components/responses/PaymentAmountInvalid"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/PaymentForbidden"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/PaymentMoneyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/{paymentId}/checkout-session":{"post":{"operationId":"createCheckoutSession","x-mcp":{"expose":"excluded","toolset":"public","name":"create_checkout_session","class":"commit-money","autonomy":"L1","reason":"Payment verb. payments.* proposals are refused at submit (DR-44: ACTION_NOT_EXECUTABLE), so the proposal flow cannot carry it, and money never commits through MCP."},"tags":["payments"],"summary":"Open a hosted checkout for an intent; the caller never handles card data (API-0010 §11, 15 §2 SAQ-A)","description":"Mints a single-use hosted checkout session for an existing intent and answers the URL to open in the system browser. The hosted page collects the payment at the provider and confirms the intent there, so neither the calling app nor this platform ever sees a PAN (15 §2, SAQ-A). Only an intent in CREATED or REQUIRES_ACTION can get a session; any other state is 409 CONFLICT. A session expires after ~15 minutes (deployment property aikonic.payments.checkout.session-ttl, default PT15M) and is spent by the first submit on the hosted page. An intent has at most ONE active session: a new session (a new Idempotency-Key) voids the previous one, and a confirm of the intent by any other route voids it too. While a session is active, the intent's answers carry it as `checkout`. returnUrl, when sent, must be an aikonic:// URL or an https URL whose origin exactly equals one of the deployment's configured origins (aikonic.payments.checkout.allowed-return-origins, default empty); anything else is 400 VALIDATION with details.reason RETURN_URL_NOT_ALLOWED — never an open redirect. When the hosted page finishes it redirects to returnUrl with `paymentId` and `status` query parameters; `status` is advisory, and the caller re-reads the intent (GET /v1/payments/{paymentId}) before acting on it. payment.captured and payment.failed fire as for any intent (API-0009). Idempotency-Key is REQUIRED: the same key and body within 48h replay the original answer (Idempotency-Replayed true) — including its expiresAt, so a replayed session may already be expired or spent; create a new one with a new key. Permission is that of a NEW payment: payments:write plus the tenant's standing, so a SUSPENDED tenant is refused TENANT_SUSPENDED and, on the production deployment, a tenant whose activation is not approved is refused PERMISSION_DENIED TENANT_NOT_LIVE (403, as on intent create). No D-50 approval token: the money is committed by the human on the provider's page, and an AI channel never holds payments:write (15 §1.5). A deployment with no payment provider (provider=none) answers 501 NOT_IMPLEMENTED; a provider that has no hosted checkout, or the harness provider above DEV, answers 501 NOT_IMPLEMENTED with details.reason HOSTED_CHECKOUT_UNSUPPORTED (OD-447). The harness provider's hosted page is DEV-only and is not an operation of this contract (API-0010 §11).\n","parameters":[{"$ref":"#/components/parameters/PaymentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionRequest"},"examples":{"appReturn":{"summary":"Return to the agent app by its aikonic:// scheme","value":{"returnUrl":"aikonic://payment"}},"noReturn":{"summary":"No returnUrl — the hosted page ends on its own result screen","value":{}}}}}},"responses":{"200":{"description":"The hosted checkout session; open url in the system browser","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"},"examples":{"session":{"summary":"200 — a single-use hosted checkout session, valid ~15 minutes (API-0010 §11)","value":{"checkoutSessionId":"cs_7Hq2VbX9kLmN4pR8sT1wY6zA3cD5fG0j","url":"https://checkout.psp.example/c/cs_7Hq2VbX9kLmN4pR8sT1wY6zA3cD5fG0j","expiresAt":"2026-09-28T10:15:00Z","returnUrl":"aikonic://payment"}},"sessionNoReturn":{"summary":"200 — no returnUrl was sent, so none is echoed","value":{"checkoutSessionId":"cs_Q4wE8rT2yU6iO0pA9sD3fG7hJ1kL5zX","url":"https://checkout.psp.example/c/cs_Q4wE8rT2yU6iO0pA9sD3fG7hJ1kL5zX","expiresAt":"2026-09-28T10:15:00Z"}}}}}},"400":{"description":"VALIDATION (03 §9). details.reason RETURN_URL_NOT_ALLOWED when returnUrl is not an aikonic:// URL and not an https URL on a configured origin (details.field returnUrl); a missing Idempotency-Key is VALIDATION with details.header Idempotency-Key.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"returnUrlNotAllowed":{"summary":"400 VALIDATION — RETURN_URL_NOT_ALLOWED; never an open redirect (API-0010 §11)","value":{"code":"VALIDATION","message":"returnUrl must be an aikonic:// URL or an https URL on an origin this deployment allows.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"RETURN_URL_NOT_ALLOWED","field":"returnUrl"}}},"idempotencyKeyMissing":{"summary":"400 VALIDATION — Idempotency-Key is required on this route (03 §7)","value":{"code":"VALIDATION","message":"Idempotency-Key is required on this route.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"header":"Idempotency-Key"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §9). CONFLICT carries details.reason: INVALID_STATE (the intent is not CREATED or REQUIRES_ACTION — details.state names its state: already paid, failed or canceled) or PENDING_RECONCILIATION (retryable true — a provider call's outcome is being reconciled, OD-205; retry later or await payment.captured / payment.failed).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"invalidState":{"summary":"409 CONFLICT — INVALID_STATE; only CREATED or REQUIRES_ACTION can get a session","value":{"code":"CONFLICT","message":"This payment is not in a state that can be paid through a hosted checkout.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INVALID_STATE","paymentId":"pay_int_91c4","state":"AUTHORIZED"}}},"pendingReconciliation":{"summary":"409 CONFLICT — PENDING_RECONCILIATION; retry later (OD-205)","value":{"code":"CONFLICT","message":"A provider call for this intent has an outcome not yet known. It is being reconciled, so no checkout can be opened now. Retry later.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"PENDING_RECONCILIATION","paymentId":"pay_int_91c4"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — the first request with this key is still running (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still in flight. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/payments/card-key":{"get":{"operationId":"getCardEnvelopeKey","x-mcp":{"expose":"excluded","toolset":"public","name":"get_card_envelope_key","class":"read","autonomy":"L0","reason":"The card channel is never an AI channel: no agent seals or sends a card (PLAT-CARDENV §4.7), so the key that seals one is not a tool."},"tags":["payments"],"summary":"The public key a card is sealed to for payment.mode ENCRYPTED_CARD (v1.36.0, SANDBOX/UAT)","description":"connect-v1 1.36.0 (PLAT-CARDENV §4.1; SANDBOX/UAT only, OD-904). The RSA public key, as a JWK, that POST /v1/orders payment.encryptedCard is sealed to (EncryptedCardEnvelope). Public data, Cache-Control max-age=3600; after a rotation the previous kid still opens for 24 hours. Any authenticated caller, no extra scope. The channel switched off — every PROD deployment — answers 501 NOT_IMPLEMENTED details.reason CARD_PAYMENT_NOT_ENABLED.","responses":{"200":{"description":"The card-envelope public key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardEnvelopeKey"},"examples":{"key":{"summary":"200 — the live key","value":{"kid":"aikonic-card-3f9c1a7e","alg":"RSA-OAEP-256","enc":"A256GCM","publicKeyJwk":{"kty":"RSA","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw","e":"AQAB","kid":"aikonic-card-3f9c1a7e","use":"enc","alg":"RSA-OAEP-256"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/v1/payments/{paymentId}":{"get":{"operationId":"getPaymentIntent","x-mcp":{"expose":"excluded","toolset":"public","name":"get_payment_intent","class":"read","autonomy":"L0","reason":"The answer carries clientSecret (the PSP hosted-fields handoff) and pixQr, which are payment secrets, and the read requires payments:write, a scope the AI-kind MCP credential must not hold because payments.* never pass through the proposal flow (DR-44, DR-39)."},"tags":["payments"],"summary":"Read a payment intent — what a client re-reads after a hosted checkout returns (API-0010 §3, §11)","description":"The intent's current answer: the same PaymentIntentResponse the create, capture and refund routes answer, with `checkout` while a hosted checkout session is active. This is the read a client makes after the hosted page redirects back; the redirect's `status` query parameter is advisory and never authority. A read of an intent that already exists: it stays up for a SUSPENDED tenant and for a tenant whose production activation is not approved, like capture and refund, so its 403 is only PERMISSION_DENIED for a credential without payments:write (15 §1.3). An unknown id and another tenant's id are the same 404. A synchronous state is provisional until the provider's webhook lands (API-0010 §1).\n","parameters":[{"$ref":"#/components/parameters/PaymentIdPath"}],"responses":{"200":{"description":"The payment intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentResponse"},"examples":{"awaitingCheckout":{"summary":"200 — CREATED with an active hosted checkout session","value":{"paymentId":"pay_int_91c4","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"method":"CARD_TOKEN","captureModel":"MANUAL_CAPTURE","state":"CREATED","checkout":{"checkoutSessionId":"cs_7Hq2VbX9kLmN4pR8sT1wY6zA3cD5fG0j","url":"https://checkout.psp.example/c/cs_7Hq2VbX9kLmN4pR8sT1wY6zA3cD5fG0j","expiresAt":"2026-09-28T10:15:00Z"}}},"authorized":{"summary":"200 — AUTHORIZED after the hosted checkout; the session is spent, so no checkout","value":{"paymentId":"pay_int_91c4","amount":{"amount":84210,"currency":"USD","fxApplied":false,"fxSnapshotId":"fx-2026-09-14T17:00Z-01"},"method":"CARD_TOKEN","captureModel":"MANUAL_CAPTURE","state":"AUTHORIZED","authValidUntil":"2026-10-05T10:06:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED (03 §9) — the credential lacks payments:write (15 §1.3)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the credential lacks payments:write","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: payments:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"payments:write"}}}}}}},"404":{"$ref":"#/components/responses/PaymentNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"}}}},"/v1/payments/methods":{"get":{"operationId":"listPaymentMethods","x-mcp":{"expose":"excluded","toolset":"public","name":"list_payment_methods","class":"read","autonomy":"L0","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["payments"],"summary":"The tenant's saved cards and UPI methods, default first (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-25). The payment methods saved for the tenant, the default first, then newest first. A method is a PSP token and display facts only (brand, last4, a masked VPA); a card number, security code or full VPA is never stored or answered (OD-875, 15 §2). kind is x-extensible-enum. Where the flag is off the route is 501 NOT_IMPLEMENTED details.reason PAYMENT_METHODS_UNAVAILABLE. Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs payments:write [assumption: one permission for the whole family, read included, because a method spends money].\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The saved methods, default first","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedPaymentMethodList"},"examples":{"cardAndUpi":{"summary":"200 — a default Visa card and a UPI method","value":{"items":[{"id":"PM-01JB2C4V","kind":"CARD","label":"Visa ending 1111","brand":"VISA","last4":"1111","vpaMasked":null,"isDefault":true,"createdAt":"2026-10-26T09:20:00Z"},{"id":"PM-01JB2C9U","kind":"UPI","label":"UPI sa…@okicici","brand":null,"last4":null,"vpaMasked":"sa…@okicici","isDefault":false,"createdAt":"2026-10-26T09:22:00Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentMethodsUnavailable"}}},"post":{"operationId":"addPaymentMethod","x-mcp":{"expose":"excluded","toolset":"public","name":"add_payment_method","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["payments"],"summary":"Save a card (as a PSP token) or a UPI method (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-25, OD-875). kind CARD takes token, a payment-method token the client obtained from the PSP's hosted fields (Spreedly in production, OD-875; the harness PSP on DEV and SANDBOX, whose test tokens are tok_harness_success and tok_harness_decline); kind UPI takes vpa. A card number, security code or any card-number-shaped value is refused 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED and nothing is stored. The token is verified with the PSP before it is saved [assumption: a zero-amount authorisation]; a declined token is 402 PAYMENT_FAILED details.reason CARD_DECLINED (details.declineCode) and nothing is saved. The first method saved becomes the default. With the money model CARD (POST /v1/activation/money-model) rail step money_model is done once one method is saved. brand and last4 are derived by the server from the PSP token, never sent by the client. Human session only; Owner only, as the money model (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required: a replay returns the original method and saves nothing twice. Audited (payments.method_added, never the token). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): the card provider and the UPI provider are deployment settings (aikonic.payment-methods.card-provider harness or stripe; aikonic.payment-methods.upi-provider harness or razorpay; harness on DEV and SANDBOX only). With the card provider stripe the client first calls POST /v1/payments/methods:setup, confirms the card with the card provider's browser library (its hosted card fields: the card number never reaches Apiq) and then sends kind CARD with token the card provider's payment-method id (pm_ followed by the id) the confirmed setup carries; the server retrieves it from the card provider, takes brand and last4 from it, keeps it attached to the tenant's own customer at the card provider and stores the id only. A payment method the card provider does not know, or one attached to another customer, is 400 VALIDATION details.field token. With the UPI provider razorpay the vpa is validated with the provider before it is saved; a VPA the provider says does not exist or cannot receive is 400 VALIDATION details.reason VPA_INVALID (details.field vpa) and nothing is saved. A provider that cannot be reached is 502 UPSTREAM_UNAVAILABLE (retryable, nothing saved).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodCreate"},"examples":{"card":{"summary":"A card, as the harness PSP's success token (brand and last4 come from the PSP)","value":{"kind":"CARD","token":"tok_harness_success"}},"upi":{"summary":"A UPI method","value":{"kind":"UPI","vpa":"sampletravels@okicici"}},"stripeCard":{"summary":"A card confirmed with the card provider's browser library — its payment-method id, never card data (v1.63.0 round 3)","value":{"kind":"CARD","token":"pm_example0000000000000001"}}}}}},"responses":{"201":{"description":"The saved method","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedPaymentMethod"},"examples":{"card":{"summary":"201 — the card is saved and, as the first method, the default","value":{"id":"PM-01JB2C4V","kind":"CARD","label":"Visa ending 1111","brand":"VISA","last4":"1111","vpaMasked":null,"isDefault":true,"createdAt":"2026-10-26T09:20:00Z"}},"upi":{"summary":"201 — the UPI method is saved; the VPA is shown masked","value":{"id":"PM-01JB2C9U","kind":"UPI","label":"UPI sa…@okicici","brand":null,"last4":null,"vpaMasked":"sa…@okicici","isDefault":false,"createdAt":"2026-10-26T09:22:00Z"}}}}}},"400":{"description":"VALIDATION — a missing or malformed member, or card data instead of a token (INSTRUMENT_DATA_REFUSED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — kind CARD without a token","value":{"code":"VALIDATION","message":"A card is saved as a PSP token; token is required when kind is CARD.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"token"}}},"instrumentDataRefused":{"summary":"400 VALIDATION INSTRUMENT_DATA_REFUSED — a card number was sent; nothing is stored","value":{"code":"VALIDATION","message":"Card numbers are never accepted; send the PSP token from the hosted card fields.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INSTRUMENT_DATA_REFUSED","field":"token"}}},"vpaInvalid":{"summary":"400 VALIDATION VPA_INVALID — the UPI provider says the VPA does not exist or cannot receive; nothing is saved (v1.63.0 round 3)","value":{"code":"VALIDATION","message":"This UPI ID could not be verified. Check it and try again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VPA_INVALID","field":"vpa"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"description":"PAYMENT_FAILED CARD_DECLINED — the PSP declined the card when it was verified; nothing is saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cardDeclined":{"summary":"402 PAYMENT_FAILED CARD_DECLINED — tok_harness_decline","value":{"code":"PAYMENT_FAILED","message":"The card was declined. Try another card.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CARD_DECLINED","declineCode":"card_declined","psp":"HARNESS"}}}}}}},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentMethodsUnavailable"},"502":{"$ref":"#/components/responses/PaymentMethodProviderUnreachable"}}}},"/v1/payments/methods:setup":{"post":{"operationId":"createPaymentMethodSetup","x-mcp":{"expose":"excluded","toolset":"public","name":"create_payment_method_setup","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["payments"],"summary":"Start saving a card with the card provider — a SetupIntent for the hosted card fields (FEAT-030 §18c)","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; OD-ENR-25, OD-875). The first half of saving a card. With the card provider stripe (aikonic.payment-methods.card-provider) the server creates a card setup for off-session use on the tenant's own customer at the card provider (one customer per tenant, created on first use) and answers provider STRIPE, setupRef (the setup id), clientSecret (the setup's client secret, handed to the card provider's browser library only; never logged or audited) and publishableKey. The console mounts the card provider's hosted card fields with them, confirms the card in the browser (3-D Secure included) and then saves it with POST /v1/payments/methods, kind CARD, token the confirmed payment-method id; the card number never reaches Apiq. With the harness card provider (DEV and SANDBOX only) the answer is provider HARNESS, publishableKey null, and the console keeps the harness test tokens (tok_harness_success, tok_harness_decline). kind is CARD (UPI needs no setup). Each call starts a new setup; an unused one simply expires (expiresAt when the provider states it). Human session only; Owner only, as the saved methods (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required: a replay answers the original setup. A card provider that cannot be reached is 502 UPSTREAM_UNAVAILABLE (retryable). Audited (payments.method_setup_started, never the client secret).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodSetupRequest"},"examples":{"card":{"summary":"Start saving a card","value":{"kind":"CARD"}}}}}},"responses":{"201":{"description":"The setup to hand to the card provider's hosted fields","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodSetup"},"examples":{"stripe":{"summary":"201 — a card setup at the card provider for its hosted card fields (illustrative values)","value":{"provider":"STRIPE","setupRef":"seti_example0000000000000001","clientSecret":"seti_example0000000000000001_secret_example","publishableKey":"pk_test_example","expiresAt":null}},"harness":{"summary":"201 — the harness card provider (DEV and SANDBOX); use the harness test tokens","value":{"provider":"HARNESS","setupRef":"setup_hrn_0000000001","clientSecret":"setup_hrn_0000000001_client","publishableKey":null,"expiresAt":"2026-10-29T11:00:00Z"}}}}}},"400":{"description":"VALIDATION — kind missing or not CARD","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — kind UPI needs no setup","value":{"code":"VALIDATION","message":"Only a card is set up first; save a UPI method directly with its VPA.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"kind"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentMethodsUnavailable"},"502":{"$ref":"#/components/responses/PaymentMethodProviderUnreachable"}}}},"/v1/payments/methods/{methodId}":{"delete":{"operationId":"removePaymentMethod","x-mcp":{"expose":"excluded","toolset":"public","name":"remove_payment_method","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["payments"],"summary":"Remove a saved payment method (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-25). Deletes the method and its PSP token reference. Removing the default makes the newest remaining method the default [assumption]. Removing the last method of a tenant on the CARD money model: before the tenant is LIVE it is allowed and rail step money_model returns to needs_you (needs[] payment_method); once the tenant is LIVE it is 409 CONFLICT details.reason LAST_METHOD_IN_USE (add another method first). An unknown id and another tenant's id are the same 404 NOT_FOUND. Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required. Audited (payments.method_removed).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaymentMethodIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT LAST_METHOD_IN_USE — the last method of a LIVE tenant on the CARD money model; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"lastMethodInUse":{"summary":"409 CONFLICT LAST_METHOD_IN_USE — a live CARD tenant keeps at least one method","value":{"code":"CONFLICT","message":"This is the only payment method of a live account paying by card. Add another method before removing it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"LAST_METHOD_IN_USE"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentMethodsUnavailable"}}}},"/v1/payments/methods/{methodId}:make-default":{"post":{"operationId":"setDefaultPaymentMethod","x-mcp":{"expose":"excluded","toolset":"public","name":"set_default_payment_method","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["payments"],"summary":"Make a saved payment method the default (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-25). The ':make-default' literal-suffix custom method. The default is what a SAVED_METHOD order charges when it names no methodId [assumption], and what the console's review-and-pay step preselects. Making the default the default again is a 200 no-op. An unknown id and another tenant's id are the same 404 NOT_FOUND. Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required. Audited (payments.method_default_set).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaymentMethodIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The method, now the default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedPaymentMethod"},"examples":{"madeDefault":{"summary":"200 — the UPI method is now the default","value":{"id":"PM-01JB2C9U","kind":"UPI","label":"UPI sa…@okicici","brand":null,"last4":null,"vpaMasked":"sa…@okicici","isDefault":true,"createdAt":"2026-10-26T09:22:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentMethodsUnavailable"}}}},"/v1/payments/psp-webhook/{provider}":{"post":{"operationId":"receivePspWebhook","x-mcp":{"expose":"excluded","toolset":"public","name":"receive_psp_webhook","class":"commit-money","autonomy":"L1","reason":"Inbound payment-provider notification, signed by the PSP; not called by an agent."},"tags":["payments"],"summary":"The payment provider's asynchronous notification — the async source of truth for money state (API-0010 §1, OD-308)","description":"Provider keys served (BE-7 !72): harness, razorpay (x-razorpay-signature over the raw body; dedupe on x-razorpay-event-id; payment.authorized, payment.captured and order.paid trigger a provider read, every other event is recorded only) and payu (a signed form callback; a non-PAID status is recorded only). Called by the payment provider, not by a tenant, so it carries no platform credential (security is empty). Instead its signature is verified server-side against the provider's configured webhook secret before anything is read: a missing, invalid or stale signature, or a provider with no verification config, is 401 UNAUTHENTICATED and nothing is applied (MH-15 bad-signature half; the edge accepts nothing it cannot verify). A verified notification drives the PaymentIntent state machine and is surfaced to tenants as payment.captured / payment.failed / payment.refunded / payment.canceled (API-0009). Redelivery is normal: a notification already seen is 200 DUPLICATE with zero new state transitions (MH-15 dedupe), never a 409, so a provider does not retry forever. The body is the provider's own payload and is deliberately untyped here. Harness provider only today (DR-26).\n","security":[],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"},"description":"The payment-provider key behind the seam (D-26), e.g. harness"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"The provider's own notification payload; verified, never trusted unverified"},"examples":{"harnessCaptured":{"summary":"A harness-provider capture notification","value":{"id":"evt_h_7Qp1","type":"payment.captured","paymentRef":"pi_h_91c4","amount":84210,"currency":"USD"}}}}}},"responses":{"200":{"description":"Verified; ACCEPTED (applied once) or DUPLICATE (already seen, nothing applied)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PspWebhookAck"},"examples":{"accepted":{"summary":"200 ACCEPTED — verified, new, applied","value":{"status":"ACCEPTED","providerEventId":"evt_h_7Qp1","eventId":"ev_01J9P4","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}},"duplicate":{"summary":"200 DUPLICATE — already seen inside the dedupe window; nothing applied (MH-15)","value":{"status":"DUPLICATE","providerEventId":"evt_h_7Qp1","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}}}}}},"400":{"description":"VALIDATION (03 §9). details.reason UNKNOWN_PAYMENT_REF (details.field paymentRef), CURRENCY_MISMATCH (details.field currency), AMOUNT_REQUIRED, or NOTICE_CONTRADICTS_PAYMENT (details.field amount: the notice states an amount the payment does not have, so it is refused rather than applied; OD-340).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"noticeContradictsPayment":{"summary":"400 VALIDATION NOTICE_CONTRADICTS_PAYMENT — the PSP notice disagrees with the payment (OD-340)","value":{"code":"VALIDATION","message":"The notice's amount contradicts the payment it names; it was not applied.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NOTICE_CONTRADICTS_PAYMENT","field":"amount"}}},"unknownPaymentRef":{"summary":"400 VALIDATION UNKNOWN_PAYMENT_REF — the notice names no payment of this platform","value":{"code":"VALIDATION","message":"The notice names an unknown payment reference.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"UNKNOWN_PAYMENT_REF","field":"paymentRef"}}}}}}},"401":{"description":"UNAUTHENTICATED — the notification could not be verified (details.reason REJECTED_SIGNATURE | REJECTED_STALE | REJECTED_UNVERIFIABLE). Rejected and alarmed.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"badSignature":{"summary":"401 UNAUTHENTICATED — signature did not verify (MH-15)","value":{"code":"UNAUTHENTICATED","message":"payment-provider webhook rejected at the ingestion edge","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"provider":"harness","reason":"REJECTED_SIGNATURE"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"}}}},"/v1/payments/psp-webhook/{provider}/{sourceRef}":{"post":{"operationId":"receivePspSourceWebhook","x-mcp":{"expose":"excluded","toolset":"public","name":"receive_psp_source_webhook","class":"commit-money","autonomy":"L1","reason":"Inbound payment-provider notification about one tenant's own account, signed with that account's own secret; not called by an agent."},"tags":["payments"],"summary":"The payment provider's notification about payments on ONE tenant's own account (v1.49.0, PLAT-FINSUP phase 2)","description":"The endpoint a tenant registers at its own provider account (provider key stripe; sourceRef is the tenant's credential set id). No platform credential (security is empty): the signature, made with THAT account's own webhook signing secret (the set's webhookSecret field), is the credential, verified before anything is read — a missing, invalid or stale signature, an unknown source or a revoked set is 401 UNAUTHENTICATED and nothing is applied. A verified notification applies to that tenant's payments on that credential set only: one tenant's secret can never move another tenant's payment, nor a payment on the platform's account (such a reference answers 400 VALIDATION details.reason UNKNOWN_PAYMENT_REF). Redelivery is 200 DUPLICATE.\n","security":[],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"},"description":"The payment-provider key, e.g. stripe"},{"name":"sourceRef","in":"path","required":true,"schema":{"type":"string","maxLength":160},"description":"The tenant's credential set id the endpoint was registered for"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"The provider's own notification payload; verified, never trusted unverified"},"examples":{"stripeCheckoutCompleted":{"summary":"An Example Pay checkout.session.completed event about the tenant's own account (abridged)","value":{"id":"evt_1Q2w3E4r5T6y","object":"event","type":"checkout.session.completed","data":{"object":{"id":"cs_test_a1B2c3D4","object":"checkout.session","client_reference_id":"stck_5f0c2a9e","payment_status":"paid"}}}}}}}},"responses":{"200":{"description":"Verified; ACCEPTED (applied once, or acknowledged and ignored) or DUPLICATE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PspWebhookAck"},"examples":{"accepted":{"summary":"200 ACCEPTED — verified with the tenant's own secret, new, applied","value":{"status":"ACCEPTED","providerEventId":"evt_1Q2w3E4r5T6y","eventId":"ev_01J9P5","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}},"duplicate":{"summary":"200 DUPLICATE — already seen; nothing applied","value":{"status":"DUPLICATE","providerEventId":"evt_1Q2w3E4r5T6y","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}}}}}},"400":{"description":"VALIDATION (03 §9). details.reason UNKNOWN_PAYMENT_REF — including a payment of another tenant, another set or the platform's account — CURRENCY_MISMATCH or NOTICE_CONTRADICTS_PAYMENT.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"unknownPaymentRef":{"summary":"400 VALIDATION UNKNOWN_PAYMENT_REF — the notice names no payment on this source","value":{"code":"VALIDATION","message":"The notice names an unknown payment reference.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"UNKNOWN_PAYMENT_REF","field":"paymentRef"}}}}}}},"401":{"description":"UNAUTHENTICATED: the signature is missing, invalid or stale, or the source is unknown or revoked (details.reason REJECTED_SIGNATURE | REJECTED_STALE | REJECTED_UNVERIFIABLE).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"badSignature":{"summary":"401 UNAUTHENTICATED — the signature did not verify with the source's own secret","value":{"code":"UNAUTHENTICATED","message":"payment-provider webhook rejected at the ingestion edge","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"provider":"stripe","reason":"REJECTED_SIGNATURE"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/PaymentsNotImplemented"}}}},"/v1/approvals":{"post":{"operationId":"mintApprovalGrant","x-mcp":{"expose":"excluded","toolset":"public","name":"mint_approval_grant","class":"commit-money","autonomy":"L1","reason":"Mints the D-50 grant that authorises a money action. It is human session only, and the ai transport refuses POST /v1/approvals, so an agent can never authorise its own spend (DR-38, DR-39)."},"tags":["payments"],"summary":"Mint a D-50 approval grant for one AI-channel money action (API-0010 §5, Q3)","description":"D-50: AI proposes, never commits. A money-touching action an AI caller asks for (booking, cancel, amend commit, payment confirm / capture / void / refund) is refused 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED until a HUMAN approves it in a UI the platform controls (12 §B2.4, 15 §1.4). This route is where that human's approval becomes a grant: it mints a single-use token of about 2 minutes, bound to the tenant, the action, the resource and the exact amount, which the AI caller then presents as X-Approval-Token on the verb it binds, retried with the SAME Idempotency-Key. Any drift at redemption (another resource, another amount, another action) burns the token: 403 PERMISSION_DENIED APPROVAL_TOKEN_MISMATCH. HUMAN SESSION ONLY: an API key or a service token from POST /v1/oauth/token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED whatever its scopes, because the API surface never mints approval tokens for machine callers (OD-403, 15 §1.4). An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39). The model can never mint. The minter must hold the permission the action needs: orders:write for orders.create, orders:cancel for orders.cancel, orders:amend for amend.commit, payments:write for payments.confirm, payments.capture, payments.void and payments.refund. The token value is returned once (Cache-Control no-store) and never logged; approvalTokenId is what audit rows and the approved action carry (13 §1.1). The console's POST /admin/v1/approvals (API-0012 §3.9) is the console spelling of the same mint (OD-458). The caller's actor kind is bound to its credential, never to a request header (OD-305). A mint is NOT replayed: re-sending a mint under an Idempotency-Key that already minted does not re-serve the token (a token value is returned exactly once). It answers 409 CONFLICT details.reason APPROVAL_ALREADY_MINTED, meaning mint again under a new key. The same key from a different user is 409 IDEMPOTENCY_CONFLICT.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalGrantRequest"},"examples":{"confirmPayment":{"summary":"A traveller approves the AI desk's USD 842.10 payment in the white-label sheet","value":{"action":"payments.confirm","resourceRef":"pi_7Hq2","amount":{"amount":84210,"currency":"USD"}}}}}}},"responses":{"201":{"description":"The grant — present approvalToken as X-Approval-Token on the bound verb within expiresAt","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store — the token value is returned exactly once"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalGrant"},"examples":{"minted":{"summary":"201 — single-use, amount-bound, 2-minute grant","value":{"approvalToken":"apt_Vb2m9QxR1sLk4TzW8cN0aYhE5uJ3pD7fG6iO2qA1bC4","approvalTokenId":"apr_01J9ZK8Q4M2N","action":"payments.confirm","resourceRef":"pi_7Hq2","amount":{"amount":84210,"currency":"USD"},"singleUse":true,"mintedBy":"usr_0142","mintedAt":"2026-09-29T15:04:00Z","expiresAt":"2026-09-29T15:06:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ApprovalMintForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT with details.reason APPROVAL_ALREADY_MINTED: this Idempotency-Key already minted a grant, and the token is never re-served, so mint again under a new key. IDEMPOTENCY_CONFLICT when the same key is reused by a different user or with a different body; IDEMPOTENCY_IN_PROGRESS while the first mint is still executing.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"approvalAlreadyMinted":{"summary":"409 CONFLICT APPROVAL_ALREADY_MINTED: replay of a mint; the token is not re-served","value":{"code":"CONFLICT","message":"This Idempotency-Key already minted an approval grant; the token is returned once. Mint again with a new key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_ALREADY_MINTED","idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT: same key, different user or body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used by a different caller or body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS: duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-proposals":{"post":{"operationId":"submitAgentProposal","x-mcp":{"expose":"excluded","toolset":"public","name":"submit_agent_proposal","class":"commit-money","autonomy":"L1","reason":"Filed only inside book_offer and cancel_with_quote, which build the binding (amount, resource, execution.intent) from live reprice or cancel-quote state; a raw mirror would let agent-supplied amounts reach the reviewer (DR-38)."},"tags":["agents"],"summary":"An agent submits an L1 proposal — the uniform reviewable envelope (FEAT-019 §6, OD-75)","description":"FEAT-019 §6's uniform L1 envelope, published for non-console callers so a proposal lives in the platform rather than in the proposing process (OD-75 option (a)): it survives a restart, every AI-plane replica sees it, its expiry is enforced once, and the console renders it. Called by an AGENT service principal — an API key or a POST /v1/oauth/token service token holding agents:propose (FEAT-019 §9: each agent is its own least-privilege principal). The platform stamps actor {kind: ai, agentId} from that credential; the body's agentId must be the credential's agent, otherwise 403 PERMISSION_DENIED details.reason AGENT_MISMATCH. A system key (DR-40) or a human session filing is 403 PERMISSION_DENIED details.reason AGENT_CREDENTIAL_REQUIRED: a system key can never file a proposal or act as the AI plane. Actor kind is bound to the credential, never to a request header (OD-305). A paused agent is 409 CONFLICT details.reason AGENT_PAUSED (D-54 kill switch, honoured here and not only in the agent runtime, FEAT-019 §14). The proposal is OPEN until a human approves or rejects it or it expires (default 48h, capped at the idempotency window, FEAT-019 §7); nothing is executed at submission. A money-touching proposal has requiresApprovalToken true and amount set (12 §B2.5, no tenant override): its approve needs a D-50 grant from POST /v1/approvals. Emits agent.proposal.created. FEAT-019 is still draft: this shape follows the console form already published (ConsoleAgentProposal, API-0012) so there is one envelope, not two (OD-457).\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalSubmission"},"examples":{"rebooking":{"summary":"AG-22 proposes a rebooking after a schedule change (money-touching, L1)","value":{"agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"execution":{"action":"orders.create","domain":"FLT","currency":"USD","intent":{"repriceRef":"RP-F-7c3d20"}},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window"}}}}}},"responses":{"201":{"description":"Stored OPEN; nothing executed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"open":{"summary":"201/200 — OPEN, awaiting a human","value":{"proposalId":"prop_01J9ZK2","agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","state":"OPEN","actor":{"kind":"ai","id":"key_ai_plane_01","agentId":"agt_rebook_22"},"resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window","executionSummary":{"action":"orders.create","resourceRef":"RP-F-7c3d20","serverAmount":{"amount":84210,"currency":"USD"},"travelerNames":["Ana Silva"],"paymentRef":null,"holdRef":null},"createdAt":"2026-09-29T15:00:00Z","expiresAt":"2026-10-01T15:00:00Z","decidedBy":null,"decidedAt":null,"decisionReason":null}}}}}},"400":{"description":"VALIDATION (03 §9): a malformed body, or a money-touching proposal (by type, changes or requiresApprovalToken) submitted with requiresApprovalToken false (details.field requiresApprovalToken) or without approvalAction (details.field approvalAction) — D-50 cannot be opted out of (12 §B2.5). A money proposal without `execution` is 400 VALIDATION details.field execution (DR-38). details.reason AMEND_COMMIT_NOT_EXECUTABLE: an amend.commit proposal (approvalAction or execution.action) is refused at submit until quote-then-commit exists (DR-44); amend.commit stays in the action enums on purpose so this refusal is reachable. details.reason ACTION_NOT_EXECUTABLE: a money proposal whose approvalAction is payments.* (confirm, capture, void, refund) is refused at submit (DR-44); only the order verbs execute. details.reason EXECUTION_INCONSISTENT: execution.action differs from approvalAction, or execution.currency differs from amount.currency (details.field names which). An orders.cancel execution.intent must carry acceptedPenalty equal to the proposal's amount.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"approvalActionMissing":{"summary":"400 VALIDATION — a money-touching proposal needs its D-50 action","value":{"code":"VALIDATION","message":"A REBOOKING proposal moves money and must name approvalAction.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"approvalAction"}}},"amendCommitNotExecutable":{"summary":"400 VALIDATION AMEND_COMMIT_NOT_EXECUTABLE — amend.commit cannot be executed yet","value":{"code":"VALIDATION","message":"amend.commit is not executable until quote-then-commit exists.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AMEND_COMMIT_NOT_EXECUTABLE","approvalAction":"amend.commit"}}},"actionNotExecutable":{"summary":"400 VALIDATION ACTION_NOT_EXECUTABLE — a money proposal whose approvalAction is payments.* (DR-44)","value":{"code":"VALIDATION","message":"A money proposal whose approvalAction is payments.refund cannot be executed; only the order verbs execute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ACTION_NOT_EXECUTABLE","approvalAction":"payments.refund"}}},"executionMissing":{"summary":"400 VALIDATION — a money proposal must carry execution (DR-38)","value":{"code":"VALIDATION","message":"A money-touching proposal must carry execution, the verb approve will run.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"execution"}}},"executionInconsistent":{"summary":"400 VALIDATION EXECUTION_INCONSISTENT — execution disagrees with approvalAction or amount","value":{"code":"VALIDATION","message":"execution.currency must equal amount.currency.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXECUTION_INCONSISTENT","field":"execution.currency"}}},"approvalOptOutRefused":{"summary":"400 VALIDATION — requiresApprovalToken false on a money-touching proposal is refused","value":{"code":"VALIDATION","message":"A REBOOKING proposal moves money; requiresApprovalToken cannot be false (D-50).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"requiresApprovalToken"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED (03 §9): the credential lacks agents:propose; the body's agentId is not the credential's agent (details.reason AGENT_MISMATCH); or the caller is not an agent credential at all, a system key or a human session (details.reason AGENT_CREDENTIAL_REQUIRED, DR-40). TENANT_MISMATCH, TAB_DISABLED and OUT_OF_SCOPE as on every route.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentCredentialRequired":{"summary":"403 PERMISSION_DENIED AGENT_CREDENTIAL_REQUIRED — a system key or a human session filing (DR-40)","value":{"code":"PERMISSION_DENIED","message":"Only an agent credential files a proposal; a system key or a human session cannot.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_CREDENTIAL_REQUIRED"}}},"agentMismatch":{"summary":"403 PERMISSION_DENIED AGENT_MISMATCH — the body's agentId is not the credential's agent","value":{"code":"PERMISSION_DENIED","message":"The proposal's agentId is not the agent this credential belongs to.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_MISMATCH"}}},"permissionDenied":{"summary":"403 PERMISSION_DENIED — the credential lacks agents:propose","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: agents:propose","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"agents:propose"}}},"tabDisabled":{"summary":"403 TAB_DISABLED — the tenant does not sell this product (03 §9)","description":"Distinct from PERMISSION_DENIED on purpose (03 §9). It is the entitlement twin of 04 §2 rule 2's \"a product the tenant isn't entitled to is ABSENT from capabilities\": the product is missing from GET /v1/capabilities and calling it answers this.\n","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"outOfScope":{"summary":"403 OUT_OF_SCOPE — scope-ladder violation (03 §9, 07 §1)","value":{"code":"OUT_OF_SCOPE","message":"The token's scope ladder does not reach this customer subtree","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requestedCustomerId":"cus_91ac3"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — tenant-wall violation (03 §9, 07 §3)","description":"The ref belongs to another tenant. Refs are unguessable (15 §5) so this is the cross-tenant probe answer, and it is deliberately NOT 404 — the platform records the probe rather than pretending the ref does not exist.\n","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/AgentProposalConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-proposals/{proposalId}":{"get":{"operationId":"getAgentProposal","x-mcp":{"expose":"tool","toolset":"public","name":"get_proposal_status","class":"read","autonomy":"L0","example":"{\"proposalId\":\"prop_01J9ZK2\",\"waitSeconds\":30}"},"tags":["agents"],"summary":"Read one proposal — the reviewable object (FEAT-019 §6, §7)","description":"Any caller of the tenant holding agents:propose or a human session with agents:act reads it; another tenant's proposal is 404 like an unknown one. EXPIRED is reported as soon as expiresAt has passed, whether or not a sweep has run; only an OPEN proposal expires, never EXECUTING or EXECUTION_UNKNOWN.\n","parameters":[{"$ref":"#/components/parameters/ProposalIdPath"}],"responses":{"200":{"description":"The proposal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"open":{"summary":"201/200 — OPEN, awaiting a human","value":{"proposalId":"prop_01J9ZK2","agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","state":"OPEN","actor":{"kind":"ai","id":"key_ai_plane_01","agentId":"agt_rebook_22"},"resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window","executionSummary":{"action":"orders.create","resourceRef":"RP-F-7c3d20","serverAmount":{"amount":84210,"currency":"USD"},"travelerNames":["Ana Silva"],"paymentRef":null,"holdRef":null},"createdAt":"2026-09-29T15:00:00Z","expiresAt":"2026-10-01T15:00:00Z","decidedBy":null,"decidedAt":null,"decisionReason":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-proposals/{proposalId}/approve":{"post":{"operationId":"approveProposal","x-mcp":{"expose":"excluded","toolset":"public","name":"approve_proposal","class":"commit-money","autonomy":"L1","reason":"Approving executes the proposal once, and a money approve needs a D-50 grant. It is human session only, so an agent can never approve its own proposal (DR-38, OD-506; D-50 coverage DR-39)."},"tags":["agents"],"summary":"A human approves an OPEN proposal; it executes once (FEAT-019 §6, D-50)","description":"HUMAN SESSION ONLY, with agents:act: an agent's proposal can never be approved by another agent (FEAT-019 §9) — an API key or a service token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED. An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39). A proposal with requiresApprovalToken true needs approvalToken, a D-50 grant from POST /v1/approvals bound to its action, resource and amount; without one it is 400 VALIDATION (details.field approvalToken), and a spent, expired, unknown or mismatched grant is 403 PERMISSION_DENIED with details.reason APPROVAL_TOKEN_USED | _EXPIRED | _INVALID | _MISMATCH (the /v1 dialect, OD-327). Preconditions are re-validated at approval time (FEAT-019 §12). What the human approves is the proposal's server-derived executionSummary; if the stored execution no longer matches the approved resource or amount, nothing executes and the answer is 409 CONFLICT details.reason EXECUTION_MISMATCH. A proposal that is not OPEN (EXECUTING, APPROVED, REJECTED, EXPIRED, EXECUTION_UNKNOWN) is 409 CONFLICT details.reason WRONG_STATE with details.state; a paused agent 409 CONFLICT AGENT_PAUSED. The same Idempotency-Key replays the stored answer, so the action executes at most once. Emits agent.proposal.approved. Approve claims the proposal (OPEN to EXECUTING) and runs its verb once. A definitive failure proven NOT dispatched (PRICE_CHANGED, OFFER_EXPIRED, a 402 PAYMENT_FAILED before dispatch, a supplier refusal that was not sent) returns the proposal to OPEN, gives the D-50 grant back and answers the verb's own error. Any failure that may have dispatched makes it EXECUTION_UNKNOWN: terminal for the API, the grant stays spent, the verb is never retried, and the desk resolves it. A stale EXECUTING (a crash mid-run) is likewise resolved by the desk to EXECUTION_UNKNOWN. Expiry applies only to OPEN: an EXECUTING or EXECUTION_UNKNOWN proposal never expires. Any failure after the claim that is not proven undispatched answers 500 INTERNAL details.reason EXECUTION_UNKNOWN, never a retryable INTERNAL. The ambiguous answer carries details.proposalId and emits agent.proposal.execution_unknown so the desk reconciles it; do not re-approve, read GET /v1/agent-proposals/{proposalId}.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/ProposalIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalDecision"},"examples":{"withGrant":{"summary":"Approve a money-touching proposal with the D-50 grant minted for it","value":{"approvalToken":"apt_Vb2m9QxR1sLk4TzW8cN0aYhE5uJ3pD7fG6iO2qA1bC4","reason":"Customer confirmed by phone"}}}}}},"responses":{"200":{"description":"APPROVED and executed; approvalTokenId names the grant it consumed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"approved":{"summary":"200 — APPROVED with the D-50 grant it consumed","value":{"proposalId":"prop_01J9ZK2","agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","state":"APPROVED","actor":{"kind":"ai","id":"key_ai_plane_01","agentId":"agt_rebook_22"},"resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window","executionResult":{"orderRef":"MO-9XT-4KD-1AB","orderStatus":"CONFIRMED","executedAt":"2026-09-29T15:05:12Z"},"createdAt":"2026-09-29T15:00:00Z","expiresAt":"2026-10-01T15:00:00Z","decidedBy":"usr_0142","decidedAt":"2026-09-29T15:05:10Z","decisionReason":"Customer confirmed by phone","approvalTokenId":"apr_01J9ZK8Q4M2N"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/AgentProposalDecisionConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/AgentProposalExecutionUnknown"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/agent-proposals/{proposalId}/reject":{"post":{"operationId":"rejectProposal","x-mcp":{"expose":"excluded","toolset":"public","name":"reject_proposal","class":"commit-lite","autonomy":"L1","reason":"The decision on an agent proposal belongs to a human session only (x-aikonic-credential: human-session). An agent deciding its own proposals would void the human approval step (DR-38, OD-506)."},"tags":["agents"],"summary":"A human rejects an OPEN proposal (FEAT-019 §6)","description":"HUMAN SESSION ONLY, with agents:act (an API key or service token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED). Nothing executes; the reason is kept on the proposal and on agent.proposal.rejected. A proposal that is not OPEN is 409 CONFLICT details.reason WRONG_STATE. An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39).\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/ProposalIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalRejection"},"examples":{"reject":{"summary":"Reject with a reason","value":{"reason":"Customer prefers a refund to a rebooking"}}}}}},"responses":{"200":{"description":"REJECTED; nothing executed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"rejected":{"summary":"200 — REJECTED, nothing executed","value":{"proposalId":"prop_01J9ZK2","agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","state":"REJECTED","actor":{"kind":"ai","id":"key_ai_plane_01","agentId":"agt_rebook_22"},"resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window","executionSummary":{"action":"orders.create","resourceRef":"RP-F-7c3d20","serverAmount":{"amount":84210,"currency":"USD"},"travelerNames":["Ana Silva"],"paymentRef":null,"holdRef":null},"createdAt":"2026-09-29T15:00:00Z","expiresAt":"2026-10-01T15:00:00Z","decidedBy":"usr_0142","decidedAt":"2026-09-29T15:05:10Z","decisionReason":"Customer prefers a refund to a rebooking"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/AgentProposalDecisionConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks":{"post":{"operationId":"createWebhook","x-mcp":{"expose":"excluded","toolset":"public","name":"create_webhook","class":"commit-lite","autonomy":"L1","reason":"Webhook subscriptions are standing integrations that send tenant events to an external URL (webhooks:manage); an injected agent could add an exfiltration destination. Configured by a human."},"tags":["events"],"summary":"Subscribe to signed webhook deliveries (03 §3 #26)","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionRequest"}}}},"responses":{"201":{"description":"Subscription created; signing secret returned once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"},"examples":{"created":{"summary":"201 — signing secret is returned ONCE, at creation (03 §3 #26)","description":"Every delivery carries X-Mondee-Signature (HMAC-SHA256 over timestamp + body with this secret) and X-Mondee-Timestamp; receivers verify and reject stale timestamps. Missed deliveries are recovered with GET /v1/events cursor replay.","value":{"webhookId":"wh_7d31c0","url":"https://acme.example.com/hooks/mondee","events":["order.state_changed","hold.expired","capabilities.changed"],"signingSecret":"whsec_9f2a1cbb7e0d4c5e8a1f3b6d2c4e5f70"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listWebhooks","x-mcp":{"expose":"resource","toolset":"public","name":"list_webhooks","class":"read","autonomy":"L0"},"tags":["events"],"summary":"List webhook subscriptions","responses":{"200":{"description":"Subscriptions for the tenant","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}},"examples":{"list":{"summary":"200 — subscriptions for the tenant; signingSecret is never re-returned","value":[{"webhookId":"wh_7d31c0","url":"https://acme.example.com/hooks/mondee","events":["order.state_changed","hold.expired","capabilities.changed"]}]}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{webhookId}":{"get":{"operationId":"getWebhook","x-mcp":{"expose":"resource","toolset":"public","name":"get_webhook","class":"read","autonomy":"L0"},"tags":["events"],"summary":"Read one webhook subscription (API-0009 §2); signingSecret is never re-returned","parameters":[{"name":"webhookId","description":"The subscription to read, as creating it returned it.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"},"examples":{"active":{"summary":"200 — an active subscription (API-0009 §3)","value":{"webhookId":"wh_7d31c0","url":"https://acme.example.com/hooks/mondee","events":["order.state_changed","hold.expired"],"state":"ACTIVE","description":"prod order pipeline","createdAt":"2026-09-14T12:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteWebhook","x-mcp":{"expose":"excluded","toolset":"public","name":"delete_webhook","class":"commit-lite","autonomy":"L1","reason":"Removing a subscription silently stops the tenant's order, payment and proposal events (including agent.proposal.*) reaching its systems. Webhook configuration (webhooks:manage) stays with a human."},"tags":["events"],"summary":"Remove a webhook subscription","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{webhookId}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","x-mcp":{"expose":"excluded","toolset":"public","name":"rotate_webhook_secret","class":"commit-lite","autonomy":"L1","reason":"Rotates a webhook signing secret; an agent never handles secrets."},"tags":["events"],"summary":"Rotate a subscription's signing secret with a dual-secret overlap window (API-0009 §3/§5, 12 §A17)","description":"Mints a new signing secret and returns it ONCE, in this response — like signingSecret at creation, it is never re-returned. During the overlap window every delivery is signed with BOTH secrets (one v1= value per secret in Mondee-Signature), so a receiver can switch without dropping a delivery; at previousSecretExpiresAt the old secret stops signing. overlapSeconds absent = the platform default (86400 [PROPOSED], 12 §A17 names the overlap, not its length). Rotating again while a window is open ends the older window immediately: at most two secrets sign at once. Idempotency-Key is REQUIRED (a replay returns the original response, the same new secret, rather than minting a third). Unknown or cross-tenant webhookId is 404 (existence hidden).\n","parameters":[{"$ref":"#/components/parameters/WebhookIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotationRequest"},"examples":{"oneHourOverlap":{"summary":"Keep the old secret signing for one hour","value":{"overlapSeconds":3600}}}}}},"responses":{"200":{"description":"The new secret (returned once) and when the previous one stops signing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotation"},"examples":{"rotated":{"summary":"200 — new secret returned ONCE; the old one keeps signing until previousSecretExpiresAt","value":{"webhookId":"wh_7d31c0","signingSecret":"whsec_4be1a09c7d2f4e8b9a6c3d5e1f0a2b7c","rotatedAt":"2026-09-26T10:00:00Z","previousSecretExpiresAt":"2026-09-26T11:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{webhookId}/deliveries":{"get":{"operationId":"listWebhookDeliveries","x-mcp":{"expose":"tool","toolset":"public","name":"list_webhook_deliveries","class":"read","autonomy":"L0"},"tags":["events"],"summary":"The stored delivery-attempt log of one subscription (API-0009 §7, 12 §A17)","description":"The tenant-visible debugging surface: one row per (subscription, event) delivery with its state and every attempt — timestamp, HTTP status, latency, a bounded response snippet (never a header, the secret or the request body). Newest first; cursor-paged. At most 50 attempts are kept per delivery (the attempts counter keeps counting). A DEAD_LETTERED event stays recoverable through GET /v1/events — the replay cursor is the redrive (API-0009 §5). Unknown or cross-tenant webhookId is 404 (existence hidden).\n","parameters":[{"$ref":"#/components/parameters/WebhookIdPath"},{"name":"state","in":"query","schema":{"type":"string"},"description":"PENDING | DELIVERED | DEAD_LETTERED | CANCELLED — filter by delivery state"},{"name":"eventId","in":"query","schema":{"type":"string"},"description":"Only the delivery of this event"},{"name":"cursor","description":"The nextCursor of the previous page; absent for the first page.","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20 [assumption]"}],"responses":{"200":{"description":"A page of deliveries with their attempt logs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPage"},"examples":{"page":{"summary":"200 — one delivered after a retry, one still pending (API-0009 §5, §7)","value":{"deliveries":[{"deliveryId":"dlv_wh_7d31c0_ev_01J8Q2","webhookId":"wh_7d31c0","eventId":"ev_01J8Q2","type":"order.state_changed","state":"DELIVERED","attempts":2,"createdAt":"2026-09-14T17:06:04Z","log":[{"attempt":1,"at":"2026-09-14T17:06:05Z","httpStatus":503,"latencyMs":212,"outcome":"HTTP_ERROR","detail":"Service Unavailable"},{"attempt":2,"at":"2026-09-14T17:06:35Z","httpStatus":200,"latencyMs":88,"outcome":"DELIVERED"}]},{"deliveryId":"dlv_wh_7d31c0_ev_01J8Q3","webhookId":"wh_7d31c0","eventId":"ev_01J8Q3","type":"hold.expired","state":"PENDING","attempts":1,"createdAt":"2026-09-14T17:05:41Z","nextAttemptAt":"2026-09-14T17:07:42Z","log":[{"attempt":1,"at":"2026-09-14T17:05:42Z","latencyMs":10000,"outcome":"NETWORK_ERROR","detail":"connect timed out"}]}],"nextCursor":"ZGx2XzAx"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/login":{"post":{"operationId":"login","x-mcp":{"expose":"excluded","toolset":"public","name":"login","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Console sign-in — {username, password, siteId} → access + refresh token (FEAT-020 §5.4, 15 §1.1)","description":"First-party UIs only. Answers the same 401 for an unknown user, a wrong password, a wrong site and a locked account — details.reason LOGIN_FAILED — so the route cannot be used to enumerate accounts. The access token is an ES256 JWT (15 min); the refresh token is opaque and rotates on every refresh. Not envelope-shaped (see the identity tag).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"},"examples":{"demoAdmin":{"summary":"The FEAT-020 demo tenant's admin","value":{"username":"demo-admin","password":"correct-horse-battery-staple","siteId":"SITE-DEMO"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"},"examples":{"session":{"summary":"A fresh session — 15-minute access token, rotating refresh token","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-09-23T15:40:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-23T15:25:00Z","context":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-DEMO","siteId":"SITE-DEMO","segment":null,"permissions":["keys:manage","orders:read","orders:write","search:hotels"],"tenantStatus":"TRIAL","productionWithheld":false}}}}}}},"202":{"description":"MFA is due: the user has a second factor enrolled. Complete the challenge at POST /v1/auth/mfa/verify to receive the session.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaChallenge"},"examples":{"mfa":{"summary":"202 — a second factor is due","value":{"challengeId":"mfa_01JA9P0","methods":["TOTP","WEBAUTHN","RECOVERY"],"expiresAt":"2026-10-02T08:10:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/sign-in":{"post":{"operationId":"signIn","x-mcp":{"expose":"excluded","toolset":"public","name":"sign_in","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"One sign-in for every realm — {username, password, siteId?} → the account's realm and its session (v1.14.0, OD-882)","description":"Added in v1.14.0 (OD-882). One login page for Apiq staff (OPERATOR), customer users (TENANT) and supplier users (SUPPLIER): the platform finds the account by its email in the platform-wide login directory (one email is one account on the whole platform) and runs that realm's own login, with the realm's own lockout, MFA and session rules. The realm routes (/v1/auth/login, /ops/v1/auth/login, /supplier/v1/auth/login) are unchanged. 200 is a session of the named realm: exactly one of tenantSession, operatorSession or supplierSession is present. 202 is a next step after the password was accepted: MFA (the challenge is completed at that realm's own mfa/verify route) or SITE_CHOICE, when a customer user belongs to more than one site and sent no siteId (repeat the call with one of the listed siteIds). An unknown email, a wrong password, a locked or disabled account and a siteId the user does not belong to are one and the same 401 details.reason LOGIN_FAILED, so the route cannot be used to learn which realm, if any, an email belongs to. Plain JSON, not an envelope. Rate-limited per client and per subject as the identity routes are.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequest"},"examples":{"anyone":{"summary":"Email and password only — the platform finds the realm","value":{"username":"owner@aikonic.example","password":"correct-horse-battery-staple"}},"withSite":{"summary":"A customer user with several sites, after SITE_CHOICE","value":{"username":"owner@aikonic.example","password":"correct-horse-battery-staple","siteId":"SITE-DEMO"}}}}}},"responses":{"200":{"description":"Signed in — the session of the realm named in `realm`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInResult"},"examples":{"customer":{"summary":"200 — a customer user (TENANT)","value":{"realm":"TENANT","tenantSession":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-09-23T15:40:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-23T15:25:00Z","context":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-DEMO","siteId":"SITE-DEMO","segment":null,"permissions":["keys:manage","orders:read"],"tenantStatus":"TRIAL","productionWithheld":false}}}},"supplier":{"summary":"200 — a supplier user (SUPPLIER)","value":{"realm":"SUPPLIER","supplierSession":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-01T09:20:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-31T09:05:00Z","context":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read"],"actorKind":"human"}}}}}}}},"202":{"description":"The password was accepted; one more step — MFA or a site choice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInNextStep"},"examples":{"siteChoice":{"summary":"202 — a customer user with two sites sent no siteId","value":{"realm":"TENANT","step":"SITE_CHOICE","sites":[{"siteId":"SITE-DEMO","name":"Demo storefront"},{"siteId":"SITE-DEMO-B2B","name":"Demo agent portal"}]}},"mfa":{"summary":"202 — a second factor is due; verify at /supplier/v1/auth/mfa/verify","value":{"realm":"SUPPLIER","step":"MFA","challenge":{"challengeId":"mfa_01JA7N2","methods":["TOTP"],"expiresAt":"2026-10-01T09:10:00Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/refresh":{"post":{"operationId":"refreshSession","x-mcp":{"expose":"excluded","toolset":"public","name":"refresh_session","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Rotate the refresh token and issue a new access token (15 §1.1)","description":"A refresh token is single-use. Presenting one twice is treated as theft: every session in its family ends (401, details.reason REFRESH_REUSED) and the user must sign in again. A client with several requests in flight must therefore refresh once and share the result.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"},"examples":{"refresh":{"summary":"The refresh token from the last login or refresh","value":{"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb"}}}}}},"responses":{"200":{"description":"Rotated — the old refresh token is now spent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"},"examples":{"session":{"summary":"A fresh session — 15-minute access token, rotating refresh token","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-09-23T15:40:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-23T15:25:00Z","context":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-DEMO","siteId":"SITE-DEMO","segment":null,"permissions":["keys:manage","orders:read","orders:write","search:hotels"],"tenantStatus":"TRIAL","productionWithheld":false}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/logout":{"post":{"operationId":"logout","x-mcp":{"expose":"excluded","toolset":"public","name":"logout","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["identity"],"summary":"End this session — revokes the refresh family of the presented access token","security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Signed out. The access token lapses by its own expiry (≤ 15 min)."},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/logout-all":{"post":{"operationId":"logoutAll","x-mcp":{"expose":"excluded","toolset":"public","name":"logout_all","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["identity"],"summary":"End every session of the calling user (FEAT-020 §5.5)","security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Every refresh family of this user revoked"},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/token":{"post":{"operationId":"issueOAuthToken","x-mcp":{"expose":"excluded","toolset":"public","name":"issue_oauth_token","class":"commit-lite","autonomy":"L1","reason":"The client-credentials token endpoint takes the whole API key as client_secret (oauthClientBasic). The MCP server obtains its own service token outside the tool surface and never hands key or token material to an agent."},"tags":["identity"],"summary":"Exchange an API key for a short-lived Bearer access token (OAuth2 client-credentials grant, RFC 6749 §4.4)","description":"Exchanges an API key for a short-lived Bearer access token (an ES256 JWT, at most 15 min, no refresh token — RFC 6749 §4.4.3). The client IS the key: client_id is the key's keyPrefix and client_secret is the WHOLE API key exactly as shown at mint (not only the part after the prefix). Authenticate by HTTP Basic (each part form-urlencoded, joined by a colon, base64 — RFC 6749 §2.3.1) OR by client_id + client_secret in the body — never both (invalid_request). X-Api-Key and any other Authorization scheme are refused invalid_client. The body must be application/x-www-form-urlencoded, at most 16 KiB; a query string of any kind and a parameter sent twice are refused invalid_request (a secret in a URL lands in access logs). Checks run in the order request shape, client authentication, grant_type, scope, so an unauthenticated caller learns nothing about grant or scope handling. scope may only NARROW the key's scopes; absent, the token gets all of them. The token is accepted exactly as its key would be, and the key is re-checked on EVERY call made with the token: revoking the key refuses its tokens at once, and a token never holds more than the key holds now. A key in its rotation overlap gets a token that expires no later than the overlap, with the key's Deprecation / Sunset headers. 4xx answers other than 429 are RFC 6749 §5.2's OAuthError, not ApiErrorResponse; error_description is fixed text and never echoes input. The abuse limit's 429 RATE_LIMITED (RFC 6749 names no error for it) and a platform fault's 500 are ApiErrorResponse, whose traceId is the support handle. The limit is counted per client address (every attempt), per address and per (address, client_id) (failed client authentications) and per client_id (tokens issued, spent only by a request that passed every check) — never per client_id before the secret is checked; see IdentityRateLimited. connect-v1 1.26.0 (OD-884 Phase 2, DR-67; ADR-C10 §Phase 2 design D1 to D3): four more grant types, served only where aikonic.mcp.grants.enabled is true (DEV and SANDBOX until OD-28 signs; elsewhere unsupported_grant_type). authorization_code: an MCP client (public, no secret) redeems the code from the consent redirect with its PKCE code_verifier, the same redirect_uri, client_id and resource; the code is single-use and lives 60 s, and a second use revokes the grant. refresh_token: rotates; a spent refresh token revokes the whole grant (reason REFRESH_REUSE); every refresh re-checks that the bound human session is live and re-caps the grant's expiry at the earlier of 8 hours from approval and the session's expiry. urn:ietf:params:oauth:grant-type:device_code: polled with the device_code no faster than every 5 s; authorization_pending until the human decides, slow_down when polled too fast, access_denied when denied, expired_token after 10 minutes. These three answer an access token of at most 10 minutes, never past the grant's expiry, whose audience is the MCP resource; the platform API refuses it (401 TOKEN_WRONG_AUDIENCE). urn:ietf:params:oauth:grant-type:token-exchange (RFC 8693): the AI plane only, authenticated by HTTP Basic as a configured exchange client, exchanges a client's access token (subject_token) for the downstream platform token it calls the platform with, and so learns that the grant is live. It checks the signature, that the token's audience equals resource, that this exchange client may exchange for resource, that the grant is ACTIVE and unexpired, that the bound session is live, the human or supplier principal active, the tenant admitted (not SUSPENDED or OFFBOARDED), the tenant's toolset switch ON and grants enabled; for an operator grant also that operator grants are enabled and aikonic.ops.mfa.required is true (else invalid_target reason OPERATOR_GRANTS_DISABLED or OPERATOR_MFA_NOT_ENFORCED). Refusals are 400 invalid_grant, invalid_target, invalid_client or unauthorized_client with a reason (OAuthError). aikonic_grant.writeOperations is computed at each exchange: the console writes ticked at consent that the tenant still has ON in writesEnabled, empty when the platform write ceiling is off (ADR-C10 Phase 3, DR-68); aikonic_grant.crossTenantReads names the operator cross-tenant reads ticked at consent. Exchanges are limited per grant (aikonic.mcp.grants.exchange.per-grant-per-minute, default 120): past it the answer is 429 RATE_LIMITED (ApiErrorResponse, IdentityRateLimited) details.reason EXCHANGE_RATE_LIMITED, details.action oauth-exchange-grant, with Retry-After; another grant is unaffected. The downstream token lives at most 5 minutes and never past the subject token or the grant's expiry; it is never a session (no session family) and reaches only the grant's toolset's read operations its grant lists, plus, for a console grant, the commit-lite operations in writeOperations that the tenant has ON at the time of the call (else 403 SESSION_REQUIRED). An operator downstream token reaches /ops/v1 reads only, never a step-up route (it carries no MFA time). The client's token is never accepted by the platform API, and the plane never forwards it.\n","security":[{"oauthClientBasic":[]},{}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthTokenRequest"},"examples":{"basicAuth":{"summary":"Client authenticated by HTTP Basic — the body carries the grant only","value":{"grant_type":"client_credentials","scope":"search:hotels orders:read"}},"bodyAuth":{"summary":"Client authenticated in the body — client_secret is the whole API key","value":{"grant_type":"client_credentials","client_id":"mo_test_jqgukb22egiu642n","client_secret":"mo_test_jqgukb22egiu642n_muvv3f7zfnpcmgfmqstqkhbroopdatriv4jr3kkzhi3wohdm3llf"}},"authorizationCode":{"summary":"An MCP client redeems its code with the PKCE verifier (connect-v1 1.26.0)","value":{"grant_type":"authorization_code","code":"mca_EXAMPLEONLYcode0001","redirect_uri":"http://127.0.0.1:53682/callback","code_verifier":"EXAMPLEONLY-verifier-dBjftJeZ4CVPmB92K27uhbUJU1p1r","client_id":"https://assistant.example.com/oauth/client.json","resource":"https://mcp.example/mcp/console"}},"refresh":{"summary":"An MCP client rotates its refresh token (connect-v1 1.26.0)","value":{"grant_type":"refresh_token","refresh_token":"mcr_EXAMPLEONLYnotARealRefreshToken0000","client_id":"https://assistant.example.com/oauth/client.json"}},"devicePoll":{"summary":"A headless MCP client polls with its device code (connect-v1 1.26.0)","value":{"grant_type":"urn:ietf:params:oauth:grant-type:device_code","device_code":"mcd_EXAMPLEONLYnotARealDeviceCode00","client_id":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6"}},"tokenExchange":{"summary":"The AI plane exchanges a client's token (HTTP Basic as the exchange client; connect-v1 1.26.0)","value":{"grant_type":"urn:ietf:params:oauth:grant-type:token-exchange","subject_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJleGFtcGxlIjoibWNwLWNsaWVudC10b2tlbiJ9.c2lnbmF0dXJl","subject_token_type":"urn:ietf:params:oauth:token-type:access_token","resource":"https://mcp.example/mcp/console"}}}}}},"responses":{"200":{"description":"The access token. Send it as Authorization Bearer on every other route.","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store (RFC 6749 §5.1)"},"Pragma":{"schema":{"type":"string"},"description":"no-cache (RFC 6749 §5.1)"},"Deprecation":{"schema":{"type":"string"},"description":"true — only when the key is in its rotation overlap (15 §1.1)"},"Sunset":{"schema":{"type":"string"},"description":"When the rotating key, and so this token, stops working (RFC 1123 date)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenResponse"},"examples":{"narrowed":{"summary":"A token for two of the key's scopes","value":{"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJjbGllbnRfaWQiOiJtb190ZXN0X2pxZ3VrYjIyZWdpdTY0Mm4ifQ.c2lnbmF0dXJl","token_type":"Bearer","expires_in":899,"scope":"orders:read search:hotels"}},"mcpGrant":{"summary":"An MCP grant's tokens — audience the MCP resource, 10 minutes, a rotating refresh token (connect-v1 1.26.0)","value":{"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJleGFtcGxlIjoibWNwLWNsaWVudC10b2tlbiJ9.c2lnbmF0dXJl","token_type":"Bearer","expires_in":600,"scope":"mcp:console:read","refresh_token":"mcr_EXAMPLEONLYnotARealRefreshToken0001"}},"tokenExchange":{"summary":"The downstream token and the grant it belongs to, for the AI plane only (connect-v1 1.26.0)","value":{"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJleGFtcGxlIjoiZG93bnN0cmVhbS10b2tlbiJ9.c2lnbmF0dXJl","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":300,"scope":"mcp:console:read","aikonic_grant":{"grantId":"mcg_01JAD4P2T6Y8RJ3W5K9H0Q2Z7N","realm":"TENANT","toolset":"console","tenantId":"acme-travel","supplierCode":null,"onBehalfOf":"1e9bb43d-5e35-42ba-90f1-152117834fb9","clientId":"https://assistant.example.com/oauth/client.json","clientName":"Example IDE Assistant","clientVerified":false,"operations":["list_agent_proposals","get_queue"],"writeOperations":[],"crossTenantReads":[],"expiresAt":"2026-10-15T18:01:10Z"}}},"tokenExchangeWrite":{"summary":"A console grant with write consent; claim_queue_item is ON for the tenant (connect-v1 1.26.0, ADR-C10 Phase 3)","value":{"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJleGFtcGxlIjoiZG93bnN0cmVhbS10b2tlbiJ9.c2lnbmF0dXJl","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":300,"scope":"mcp:console:read mcp:console:write","aikonic_grant":{"grantId":"mcg_01JAE2W8K4P6RT0Y2U4I6O8Q1S","realm":"TENANT","toolset":"console","tenantId":"acme-travel","supplierCode":null,"onBehalfOf":"1e9bb43d-5e35-42ba-90f1-152117834fb9","clientId":"https://assistant.example.com/oauth/client.json","clientName":"Example IDE Assistant","clientVerified":false,"operations":["get_queue","list_queue_items"],"writeOperations":["claim_queue_item"],"crossTenantReads":[],"expiresAt":"2026-10-15T18:01:10Z"}}},"tokenExchangeOperator":{"summary":"An operator grant with one cross-tenant read ticked, at most 1 hour (connect-v1 1.26.0, ADR-C10 Phase 4)","value":{"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyIsInR5cCI6ImF0K2p3dCJ9.eyJleGFtcGxlIjoiZG93bnN0cmVhbS10b2tlbiJ9.c2lnbmF0dXJl","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":300,"scope":"mcp:operator:read","aikonic_grant":{"grantId":"mcg_01JAE3X9L5Q7SU1Z3V5J7P9R2T","realm":"OPERATOR","toolset":"operator","tenantId":null,"supplierCode":null,"onBehalfOf":"op_3c9a71","clientId":"https://assistant.example.com/oauth/client.json","clientName":"Example IDE Assistant","clientVerified":false,"operations":["operator_me","ops_list_supplier_data_runs","ops_list_tenants"],"writeOperations":[],"crossTenantReads":["ops_list_tenants"],"expiresAt":"2026-10-15T11:01:10Z"}}}}}}},"400":{"description":"RFC 6749 §5.2 refusal — invalid_request (malformed, query string, repeated parameter, not form-encoded, both authentication methods), invalid_grant (the key's principal or tenant is no longer active), unauthorized_client (the key's IP allowlist does not admit the caller), unsupported_grant_type (a grant type not served here) or invalid_scope (a scope the key does not hold, or a malformed scope list). connect-v1 1.26.0, the MCP grants: invalid_grant (a wrong code_verifier, a reused or expired code, a spent refresh token, which revokes the grant, or an exchange the grant no longer passes, with its reason), invalid_target (a resource that does not match), authorization_pending, slow_down, expired_token and access_denied (device_code polling, RFC 8628 §3.5)\n","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"invalidRequest":{"summary":"invalid_request — OAuth parameters travel in the body, never the URL","value":{"error":"invalid_request","error_description":"OAuth parameters are accepted in the request body only."}},"invalidGrant":{"summary":"invalid_grant — the key verified, but its tenant is no longer active","value":{"error":"invalid_grant","error_description":"The tenant behind this credential is not active."}},"unauthorizedClient":{"summary":"unauthorized_client — the key's IP allowlist does not admit the caller","value":{"error":"unauthorized_client","error_description":"This API key is not permitted from the calling address."}},"unsupportedGrantType":{"summary":"unsupported_grant_type — only client_credentials is issued","value":{"error":"unsupported_grant_type","error_description":"This endpoint issues the client_credentials grant only."}},"invalidScope":{"summary":"invalid_scope — scope may only narrow the key's scopes","value":{"error":"invalid_scope","error_description":"The requested scope may only narrow the API key's scopes; it names a permission the key does not hold."}},"refreshReuse":{"summary":"invalid_grant, REFRESH_REUSE — a spent refresh token; the whole grant is now revoked (connect-v1 1.26.0)","value":{"error":"invalid_grant","error_description":"The refresh token is not valid.","reason":"REFRESH_REUSE"}},"pkceMismatch":{"summary":"invalid_grant, PKCE_MISMATCH — the code_verifier does not match the code_challenge (connect-v1 1.26.0)","value":{"error":"invalid_grant","error_description":"The authorization code is not valid.","reason":"PKCE_MISMATCH"}},"authorizationPending":{"summary":"authorization_pending — the human has not decided yet; poll again after the interval (connect-v1 1.26.0)","value":{"error":"authorization_pending","error_description":"The user has not yet approved the request."}},"exchangeAudienceMismatch":{"summary":"invalid_target, AUDIENCE_MISMATCH — a console token exchanged for the supplier resource (connect-v1 1.26.0)","value":{"error":"invalid_target","error_description":"The token was not issued for this resource.","reason":"AUDIENCE_MISMATCH"}},"exchangeGrantRevoked":{"summary":"invalid_grant, SESSION_ENDED — the human behind the grant signed out (connect-v1 1.26.0)","value":{"error":"invalid_grant","error_description":"The grant is no longer active.","reason":"SESSION_ENDED"}},"exchangeOperatorMfaNotEnforced":{"summary":"invalid_target, OPERATOR_MFA_NOT_ENFORCED — an operator token on a rung without operator MFA (connect-v1 1.26.0)","value":{"error":"invalid_target","error_description":"Operator agent access is not available in this environment.","reason":"OPERATOR_MFA_NOT_ENFORCED"}}}}}},"401":{"description":"RFC 6749 §5.2 invalid_client — no client credential, a malformed Basic credential, an unknown, wrong, revoked, rotated-out or wrong-environment key, a client_id that is not the key's prefix, or an unsupported authentication method (X-Api-Key, Bearer)\n","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Basic realm=\"aikonic\", charset=\"UTF-8\""},"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"invalidClient":{"summary":"invalid_client — the key is not valid","value":{"error":"invalid_client","error_description":"The API key is not valid."}},"clientIdMismatch":{"summary":"invalid_client — client_id must be the prefix of the key sent as client_secret","value":{"error":"invalid_client","error_description":"The client_id is not the prefix of the API key sent as client_secret."}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signup":{"post":{"operationId":"requestSignup","x-mcp":{"expose":"excluded","toolset":"public","name":"request_signup","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Self-serve sandbox signup, step 1 — send a code to the address (FEAT-020 §5 flow 1, 11 §6)","description":"Answers the same 202 for every well-formed address, so it confirms nothing about who has an account. Disabled unless the deployment enables signup (FEAT-020 §10) and has a way to deliver the code (FEAT-020 Q3); either missing is a 501 that names which. Not envelope-shaped (see the identity tag). connect-v1 1.18.0, served since 1.18.1 (FEAT-030 §5.1 step 1, WP-1): organization is optional — the work e-mail alone starts a signup, and the platform prefills the organisation name from the e-mail domain (rule on SignupRequest.organization). The console may still send it. Signup is off by default (aikonic.signup.enabled) and on at DEV and SANDBOX; UAT and PROD keep it off. Where it is on it needs a sender: identity mail goes through SMTP (Mailgun, OD-ENR-7; RB-10 §7) on every rung, and a rung whose SMTP credential is not deployed still answers 501 NO_VERIFICATION_SENDER (OD-ENR-16).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"},"examples":{"acme":{"summary":"A prospect signing up for a sandbox","value":{"email":"founder@acme.example","organization":"Acme Travel"}},"emailOnly":{"summary":"Work e-mail only — the organisation is prefilled from the domain (FEAT-030 §5.1)","value":{"email":"founder@acme-travel.example"}}}}}},"responses":{"202":{"description":"Accepted — a code is on its way if the address can receive it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityAccepted"},"examples":{"sent":{"summary":"The one answer, whatever the address","value":{"status":"If the address can receive it, a message is on its way."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/v1/signup/verify":{"post":{"operationId":"verifySignup","x-mcp":{"expose":"excluded","toolset":"public","name":"verify_signup","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Self-serve sandbox signup, step 2 — spend the code; get an evaluation tenant and a sandbox key","description":"Provisions an EVALUATION tenant in TRIAL (07 §5) with the person as its admin under the password they chose (12 to 1024 characters), and mints a sandbox key returned here ONCE. Production stays behind the D-60 approval gate. A wrong, expired, spent or exhausted code (6 digits, 15 minutes, at most 5 attempts) is one and the same 400. connect-v1 1.18.0, served since 1.18.1 (FEAT-030 §5.1 steps 2 and 3, WP-1), additive: (1) password is optional — the password moves out of signup and the person sets a password later from the console (POST /v1/auth/password/set, connect-v1 1.29.0); when sent it keeps the 12 to 1024 rule. An account created without one has no password until it is set, so POST /v1/auth/login and /v1/auth/sign-in refuse it with the usual 401 LOGIN_FAILED. (2) organization is optional, prefilled from the e-mail domain by the rule on SignupRequest.organization. (3) buildingIntent (optional, skippable) records the answer to \"What are you building?\" and later reads back as ActivationState.buildingIntent. (4) The console session starts on code verification: SignupResult.session carries a TENANT-realm session — the same SessionResponse POST /v1/auth/login answers (15-minute access token, single-use rotating refresh token), for the new admin on the new tenant's site — so a first-party console (its BFF) stores it as it stores a login's session and never needs the password. A brand-new account has no second factor, so there is no MFA step here. Like apiKey, session is a secret in the response body: it is never logged, and the BFF keeps the tokens server-side (httpOnly cookie), never in the page. No session is issued where the tenant admin-MFA policy (DR-51) demands a second factor for the new admin (UAT, PROD), nor when issuing it fails; the tenant and the key exist either way (201). A client that gets no session signs in with POST /v1/auth/login as before. buildingIntent never fails a signup: a rail that cannot be written is logged and skipped. (5) The first key's channel (FEAT-030 phase 5, decided 2026-10-10): on a rung whose keys are SANDBOX the key minted here is a SYSTEM key (actorKind system, DR-40) — the person is the new tenant's admin and no real money moves — so the console quick-start books with it without a D-50 approval grant (DR-39). On a rung whose keys are LIVE no key is minted (unchanged). A key handed to an AI agent should be minted actorKind ai from POST /v1/keys; the signup key is not marked AI-channel. Its scopes are search:hotels, search:flights, orders:write, orders:read and orders:cancel — enough for the whole quick-start (Search, Price, Book, Cancel; AC-030-03). (6) connect-v1 1.47.0 (FEAT-033, OD-906 proposed), additive: an individual account. accountType INDIVIDUAL creates a tenant for one person with no company: givenName and familyName are then required (a missing one is 400 VALIDATION details.reason NAME_REQUIRED, details.field naming it), organization is ignored and the tenant is named \"<givenName> <familyName>\". accountType absent is BUSINESS, today's signup, unchanged; a BUSINESS signup may send the names (stored on the user). country (ISO 3166-1 alpha-2) is optional for both. SignupResult.accountType echoes the type. An INDIVIDUAL tenant's activation rail is the five-step rail of GET /v1/activation. (7) connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): the proposed one-page BUSINESS sign-up sends the code with the person's names (givenName, familyName — stored on the admin user and read back as AccessContext.givenName and familyName), country, organization and password, all on the 1.47.0 fields; nothing new is added to this body. For a BUSINESS tenant country is also recorded on the business profile (BusinessProfile.country) and reads back as ActivationState.country, which decides whether the India-only steps of the CAPPED flow are offered. The `business` example below.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupVerifyRequest"},"examples":{"verify":{"summary":"The code from the message, and a chosen password","value":{"email":"founder@acme.example","code":"482913","password":"correct horse battery staple","organization":"Acme Travel"}},"codeOnly":{"summary":"The code only — no password, organisation prefilled from the domain (FEAT-030 §5.1)","value":{"email":"founder@acme-travel.example","code":"482913"}},"withBuildingIntent":{"summary":"The code, the prefilled organisation as edited, and the \"What are you building?\" answer (FEAT-030 §5.1)","value":{"email":"founder@acme-travel.example","code":"482913","organization":"Acme Travel Group","buildingIntent":"AI_AGENT"}},"individual":{"summary":"An individual — one person, no company; the tenant is named after the person (FEAT-033)","value":{"email":"priya.sharma@mail.example","code":"482913","password":"correct horse battery staple","accountType":"INDIVIDUAL","givenName":"Priya","familyName":"Sharma","country":"IN"}},"business":{"summary":"A business — code, the person's names, country, company and password on one page (FEAT-030 §18b, v1.51.0)","value":{"email":"arjun@sampletravels.in","code":"482913","password":"correct horse battery staple","givenName":"Arjun","familyName":"Mehta","country":"IN","organization":"Sample Travels"}}}}}},"responses":{"201":{"description":"The evaluation tenant, its site, the admin username, and a sandbox key (shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupResult"},"examples":{"created":{"summary":"Signed up — sign in to the console, or call the API with the key","value":{"tenantId":"TEN-EVAL-K7Q2MZ4P","sandboxTenantId":"TEN-EVAL-K7Q2MZ4P-SBX","siteId":"SITE-B3XK9T2Q7NWR5M4A","username":"founder@acme.example","apiKey":"mo_test_AB3KQ7XN2M4P9R5T_x2mv8q4nzl6c3bfw9k1pd7rt5ysj0ae2hg4uo6xi8mc3nq5vb7h7Jd","key":{"keyPrefix":"mo_test_AB3KQ7XN2M4P9R5T","display":"mo_test_AB3KQ7XN2M4P9R5T…h7Jd","name":"sandbox","environment":"sandbox","scopes":["search:hotels","search:flights","orders:write","orders:read","orders:cancel"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-09-26T10:00:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","principalId":"svc-5b2f0c9e","actorKind":"system"}}},"createdWithSession":{"summary":"Signed up without a password — the console session starts here (FEAT-030 §5.1)","value":{"tenantId":"TEN-EVAL-K7Q2MZ4P","sandboxTenantId":"TEN-EVAL-K7Q2MZ4P-SBX","siteId":"SITE-B3XK9T2Q7NWR5M4A","username":"founder@acme-travel.example","apiKey":"mo_test_AB3KQ7XN2M4P9R5T_x2mv8q4nzl6c3bfw9k1pd7rt5ysj0ae2hg4uo6xi8mc3nq5vb7h7Jd","key":{"keyPrefix":"mo_test_AB3KQ7XN2M4P9R5T","display":"mo_test_AB3KQ7XN2M4P9R5T…h7Jd","name":"sandbox","environment":"sandbox","scopes":["search:hotels","search:flights","orders:write","orders:read","orders:cancel"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-09T10:00:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","principalId":"svc-5b2f0c9e","actorKind":"system"},"session":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-09T10:15:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-11-08T10:00:00Z","context":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-EVAL-K7Q2MZ4P","siteId":"SITE-B3XK9T2Q7NWR5M4A","segment":null,"permissions":["keys:manage","orders:read","orders:write","search:hotels","users:manage"],"tenantStatus":"TRIAL","productionWithheld":false}}}},"createdIndividual":{"summary":"An individual signed up — the tenant is named after the person (FEAT-033)","value":{"tenantId":"TEN-EVAL-P4S9WD2M","sandboxTenantId":"TEN-EVAL-P4S9WD2M-SBX","siteId":"SITE-H6RT2Y8KQ3MZ5N7C","username":"priya.sharma@mail.example","accountType":"INDIVIDUAL","apiKey":"mo_test_AB3KQ7XN2M4P9R5T_x2mv8q4nzl6c3bfw9k1pd7rt5ysj0ae2hg4uo6xi8mc3nq5vb7h7Jd","key":{"keyPrefix":"mo_test_AB3KQ7XN2M4P9R5T","display":"mo_test_AB3KQ7XN2M4P9R5T…h7Jd","name":"sandbox","environment":"sandbox","scopes":["search:hotels","search:flights","orders:write","orders:read","orders:cancel"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-24T10:00:00Z","createdBy":"7c1f2a90-3d4e-4b8a-9f61-2e5d8c0b4a17","principalId":"svc-8d3e1a7f","actorKind":"system"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/v1/signup/device":{"post":{"operationId":"startDeviceSignup","x-mcp":{"expose":"excluded","toolset":"public","name":"start_device_signup","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) pre-credential sign-up: it runs before any tenant or key exists and MCP serves a tenant credential, so no agent tool can carry it (FEAT-034 §5.2). The agent calls it over REST from the manifest."},"tags":["identity"],"summary":"Start a device-code sign-up — an agent asks a person to approve a new tenant (FEAT-034 §5.2, RFC 8628 shape)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.signup.device-flow.enabled is on — off by default, on at DEV and SANDBOX — else 501 NOT_IMPLEMENTED details.reason DEVICE_SIGNUP_UNAVAILABLE). A coding agent starts a sign-up for the person whose work e-mail it was given and receives an RFC 8628 §3.2 shaped answer: a device_code to poll POST /v1/signup/device:token with, and a user_code the person confirms at verification_uri (the consent page, which reads GET /v1/signup/device/requests/{userCode}). The platform e-mails the person a 6-digit code at the same moment; approving needs it (POST /v1/signup/device:approve), so only the owner of the inbox can create the tenant [assumption: FEAT-034 §5.2 also names Google and Microsoft sign-in, which wait on OD-ENR-5]. user_code is 8 letters of the RFC 8628 §6.1 base-20 alphabet shown as XXXX-XXXX, single use; the request lives 15 minutes (expires_in 900); poll no faster than interval (5 s). The body takes the sign-up fields of POST /v1/signup/verify (organization, accountType, givenName, familyName, country) and the agent name; agent falls back to the X-Agent header. Nothing is created until the person approves; the tenant then carries origin agent and the agent name (DeviceSignupResult). The start answers 200 whatever the e-mail's domain (OD-AGT-21 option (a)): whether the domain already has a tenant is decided only once the e-mail is proven, at POST /v1/signup/device:approve (409 CONFLICT details.reason TENANT_EXISTS to the person, nextAction request_invite; the request is REFUSED) and then at the agent's poll (the same 409), and only then is that tenant's Owner notified (signup.tenant_exists). So the anonymous start tells no one which domains have an account, notifies no Owner, and no second tenant is created for a domain (an agent cannot fork a company's account). A free-mail domain is accepted for a sandbox tenant only (OD-AGT-4 default). On DEV and SANDBOX a harness identity domain (AgentManifest.signup.harnessDomains) is approved without inbox proof and creates a sandbox tenant, limited by the per-domain count below. Limits: 5 starts per e-mail and 20 per domain per UTC day (aikonic.signup.rate.per-identity-per-day, .per-domain-per-day), beyond them 429 RATE_LIMITED details.reason SIGNUP_RATE_LIMITED with Retry-After; the address limits of IdentityRateLimited also apply (details.action signup-device). Not envelope-shaped (see the identity tag). MCP: excluded.\n","security":[],"parameters":[{"$ref":"#/components/parameters/XAgent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupRequest"},"examples":{"agentSignup":{"summary":"Claude Code signs up a person at a travel company","value":{"email":"priya@skyhop.example","organization":"SkyHop Travel","givenName":"Priya","familyName":"Raman","country":"IN","agent":"claude-code/2.4 (claude-fable-5-1)"}}}}}},"responses":{"200":{"description":"The codes (RFC 8628 §3.2 shape); the person approves at verification_uri","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupStart"},"examples":{"started":{"summary":"200 — the agent prints WDJB-MJHT and polls every 5 seconds","value":{"device_code":"dsu_Gk3Lr9QzVb7XnM2pWc5Ht8Jd4Fs6Ay1E","user_code":"WDJB-MJHT","verification_uri":"https://console.example/signup/device","verification_uri_complete":"https://console.example/signup/device?code=WDJB-MJHT","expires_in":900,"interval":5,"token_uri":"https://apiq-sandbox.tripproagencysites.com/v1/signup/device:token"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"description":"RATE_LIMITED SIGNUP_RATE_LIMITED — more than 5 sign-ups for this e-mail or 20 for this domain today; or the address limits of IdentityRateLimited (details.action signup-device)\n","headers":{"Retry-After":{"description":"Whole seconds until the limit admits another start","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"signupRateLimited":{"summary":"429 RATE_LIMITED SIGNUP_RATE_LIMITED — nextAction slow_down","value":{"code":"RATE_LIMITED","message":"Too many sign-ups for this e-mail today. Try again after the Retry-After interval.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"nextAction":"slow_down","docs":"https://apiq.example/developers/errors#signup-rate-limited","details":{"reason":"SIGNUP_RATE_LIMITED","limit":5,"windowSeconds":86400,"retryAfterMs":41000000}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — device-code sign-up is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"deviceSignupUnavailable":{"summary":"501 NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — use POST /v1/signup","value":{"code":"NOT_IMPLEMENTED","message":"Device-code sign-up is not available on this deployment. Sign up with POST /v1/signup.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","docs":"https://apiq.example/developers/errors#device-signup-unavailable","details":{"reason":"DEVICE_SIGNUP_UNAVAILABLE"}}}}}}}}}},"/v1/signup/device:token":{"post":{"operationId":"pollDeviceSignup","x-mcp":{"expose":"excluded","toolset":"public","name":"poll_device_signup","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) pre-credential sign-up poll that returns the first session and key: it runs before any tenant exists and MCP serves a tenant credential (FEAT-034 §5.2)."},"tags":["identity"],"summary":"Poll a device-code sign-up — pending, slow down, or the new tenant with its sandbox key (FEAT-034 §5.2)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.signup.device-flow.enabled is on, else 501 NOT_IMPLEMENTED details.reason DEVICE_SIGNUP_UNAVAILABLE). The agent polls with the device_code of POST /v1/signup/device. Until the person decides it is 400 OAuthError authorization_pending; polling faster than interval is slow_down (add 5 s to the interval, RFC 8628 §3.5); a request not approved within 15 minutes is expired_token; a denied request, or one cancelled because it was approved with another e-mail (reason EMAIL_MISMATCH), is access_denied; an unknown device_code, or one already redeemed, is invalid_grant. Once approved, the first poll creates the tenant and answers 200 DeviceSignupResult — the FEAT-030 §5.1 SignupResult of the new tenant with its SYSTEM sandbox key (its secret shown once; the sandbox default scope of aikonic.keys.defaults.sandbox, FEAT-034 §10; principal the Owner, agent the sign-up agent) plus the MCP endpoint and the manifest URL — and the device_code is spent. It never carries session (OD-AGT-20 option (a)): no Owner session reaches the agent; the person signs in to the console themselves (the e-mailed code, or a password they set). Under aikonic.keys.agent-mint.enabled the key may mint narrower keys for the agent (POST /v1/keys, the narrowing rule). A request whose e-mail, once proven, belongs to a domain that already has a tenant was REFUSED at approval: the poll answers 409 CONFLICT details.reason TENANT_EXISTS (nextAction request_invite) and the device_code is spent (OD-AGT-21). The 400s speak RFC 6749 §5.2 OAuthError (as POST /v1/oauth/token), because a device-flow client parses {error}; the 409 and the 5xx are ApiErrorResponse. MCP: excluded.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupTokenRequest"},"examples":{"poll":{"summary":"Poll with the device_code","value":{"device_code":"dsu_Gk3Lr9QzVb7XnM2pWc5Ht8Jd4Fs6Ay1E"}}}}}},"responses":{"200":{"description":"Approved — the new tenant and its sandbox key, the MCP endpoint and the manifest; never a session (OD-AGT-20)","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupResult"},"examples":{"approved":{"summary":"200 — the person approved; the key's secret is shown once, and no session reaches the agent","value":{"tenantId":"TEN-EVAL-S5KH8Q2W","sandboxTenantId":"TEN-EVAL-S5KH8Q2W-SBX","siteId":"SITE-Q7WN3R5T8KZ2M4XB","username":"priya@skyhop.example","accountType":"BUSINESS","apiKey":"mo_test_SK7HQ2WN4R8T5M3Z_p4n8k2q6z0m4x8b2c6v0d4f8g2h6j0k4l8p2r6t0y4u8i2o6e0a4s8d2f6g0h4j8Kw","key":{"keyPrefix":"mo_test_SK7HQ2WN4R8T5M3Z","display":"mo_test_SK7HQ2WN4R8T5M3Z…j8Kw","name":"sandbox","environment":"sandbox","scopes":["search:hotels","search:flights","orders:write","orders:read","orders:cancel"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:12:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-2a7c9e1f","actorKind":"system","kind":"own","writes":"book","products":null,"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","state":"ACTIVE","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":null},"mcp":{"url":"https://mcp.example/mcp/public","toolset":"public"},"manifest":"https://apiq.example/.well-known/aikonic.json","origin":"agent"}}}}}},"400":{"description":"RFC 8628 §3.5 — authorization_pending (keep polling), slow_down (poll 5 s slower), expired_token (start again), access_denied (the person denied, or the request was cancelled), invalid_grant (an unknown or spent device_code), or invalid_request (no device_code)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"authorizationPending":{"summary":"authorization_pending — the person has not decided yet","value":{"error":"authorization_pending","error_description":"The sign-up is waiting for approval."}},"slowDown":{"summary":"slow_down — polled faster than interval","value":{"error":"slow_down","error_description":"Polling too fast; add 5 seconds to the interval."}},"expiredToken":{"summary":"expired_token — not approved within 15 minutes","value":{"error":"expired_token","error_description":"The sign-up request expired. Start again."}},"accessDenied":{"summary":"access_denied — denied, or cancelled because another e-mail approved it","value":{"error":"access_denied","error_description":"The sign-up request was not approved.","reason":"EMAIL_MISMATCH"}},"invalidGrant":{"summary":"invalid_grant — unknown or already redeemed","value":{"error":"invalid_grant","error_description":"The device code is not valid."}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT TENANT_EXISTS — the e-mail was proven and its domain already has a tenant; the request was REFUSED and nothing was created. Ask that tenant's Owner for an invite (nextAction request_invite); the Owner has been notified (signup.tenant_exists).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"tenantExists":{"summary":"409 CONFLICT TENANT_EXISTS — nextAction request_invite; the Owner is notified","value":{"code":"CONFLICT","message":"An account already exists for skyhop.example. Ask its Owner to invite you.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"request_invite","docs":"https://apiq.example/developers/errors#tenant-exists","details":{"reason":"TENANT_EXISTS"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — device-code sign-up is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"deviceSignupUnavailable":{"summary":"501 NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Device-code sign-up is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"DEVICE_SIGNUP_UNAVAILABLE"}}}}}}}}}},"/v1/signup/device/requests/{userCode}":{"get":{"operationId":"getDeviceSignupRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"get_device_signup_request","class":"read","autonomy":"L0","reason":"A user-code lookup for a human consent page (security: []), read before any tenant exists; an agent never reads the consent it is asking for (FEAT-034 §5.2)."},"tags":["identity"],"summary":"Read a pending device-code sign-up for the consent page — who asks, for which e-mail, what will be created","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.signup.device-flow.enabled is on, else 501 NOT_IMPLEMENTED details.reason DEVICE_SIGNUP_UNAVAILABLE). The consent page reads the request the person typed the user_code for and shows \"Let <agent> set up Apiq for <e-mail>\" with what approving creates (a tenant and a sandbox key for the agent; no session reaches the agent, and the person signs in to the console themselves, OD-AGT-20). The e-mail is masked (first two characters of the local part, then *** and the domain); the page asks the person to type it in full on approval. An unknown, used or expired user_code is 404 NOT_FOUND, alike for all three. Limited per client address (IdentityRateLimited, details.action signup-device-lookup) so codes cannot be enumerated [assumption: the action name]. MCP: excluded.\n","security":[],"parameters":[{"name":"userCode","in":"path","required":true,"schema":{"type":"string","pattern":"^[BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz]{4}-?[BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz]{4}$"},"description":"The user_code as shown (XXXX-XXXX); the hyphen is optional and case is ignored"}],"responses":{"200":{"description":"The pending request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupRequestView"},"examples":{"pending":{"summary":"200 — Claude Code asks to set up an account for pr***@skyhop.example","value":{"userCode":"WDJB-MJHT","email":"pr***@skyhop.example","organization":"SkyHop Travel","agent":"claude-code/2.4 (claude-fable-5-1)","state":"PENDING","expiresAt":"2026-10-27T09:27:00Z","creates":{"tenant":true,"sandboxKey":true,"ownerSession":false}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — device-code sign-up is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"deviceSignupUnavailable":{"summary":"501 NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Device-code sign-up is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DEVICE_SIGNUP_UNAVAILABLE"}}}}}}}}}},"/v1/signup/device:approve":{"post":{"operationId":"approveDeviceSignup","x-mcp":{"expose":"excluded","toolset":"public","name":"approve_device_signup","class":"commit-lite","autonomy":"L1","reason":"A human decision route: the person proves the inbox with the e-mailed code and approves the tenant an agent asked for; an agent never approves its own sign-up (FEAT-034 §5.2, §14)."},"tags":["identity"],"summary":"Approve a device-code sign-up with the e-mailed code — the agent's next poll creates the tenant (FEAT-034 §5.2)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.signup.device-flow.enabled is on, else 501 NOT_IMPLEMENTED details.reason DEVICE_SIGNUP_UNAVAILABLE). The person types the user_code, their e-mail in full and the 6-digit code e-mailed when the agent started the request. The code proves the inbox, exactly as POST /v1/signup/verify does, and lives as long as the request (15 minutes) with at most 5 wrong attempts [assumption: the sign-up code's attempt limit]. Once the code proves the inbox, a domain that already has a tenant is refused (OD-AGT-21 option (a)): 409 CONFLICT details.reason TENANT_EXISTS with nextAction request_invite, answered to the person here; the request becomes REFUSED, the agent's poll answers the same 409, and only then does that tenant's Owner receive the notification signup.tenant_exists. Otherwise the request is APPROVED; the agent's next poll of POST /v1/signup/device:token creates the tenant (origin agent, signupAgent the agent name; the FEAT-030 §5.1 sign-up with the request's fields and its SYSTEM sandbox key) and receives the key, so the one-time secret is never stored at rest. No session is issued to the agent (OD-AGT-20): the person, now the Owner, signs in to the console themselves (the e-mailed code, or a password they set). The event signup.device.approved and the notification of the same kind are recorded for the new tenant when the poll creates it. An e-mail other than the requested one is 400 VALIDATION details.reason EMAIL_MISMATCH and CANCELS the request (the agent's poll answers access_denied); a wrong or expired code is 400 VALIDATION details.reason CODE_INVALID. A request already approved, denied, refused, cancelled or expired is 409 CONFLICT details.reason DEVICE_SIGNUP_DECIDED; an unknown user_code is 404. On DEV and SANDBOX only, aikonic.signup.device-flow.harness-approve approves a request for a harness identity domain (AgentManifest.signup.harnessDomains) without a person and without inbox proof (AC-034-16), creating a sandbox tenant, limited per domain (20 starts a day); a harness identity domain is never treated as an existing tenant's domain [assumption], so it never meets TENANT_EXISTS. Refused at boot above SANDBOX. Address limits: IdentityRateLimited, details.action signup-device-approve [assumption: the action name]. MCP: excluded.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupApproval"},"examples":{"approve":{"summary":"The person approves with the e-mailed code","value":{"userCode":"WDJB-MJHT","email":"priya@skyhop.example","code":"482913"}}}}}},"responses":{"200":{"description":"Approved; the agent's next poll creates the tenant and receives its sandbox key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupDecision"},"examples":{"approved":{"summary":"200 — approved","value":{"userCode":"WDJB-MJHT","state":"APPROVED"}}}}}},"400":{"description":"VALIDATION — EMAIL_MISMATCH (another e-mail; the request is cancelled), CODE_INVALID (a wrong or expired code), or details.field naming a malformed member\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"emailMismatch":{"summary":"400 VALIDATION EMAIL_MISMATCH — the request is cancelled; the agent is told access_denied","value":{"code":"VALIDATION","message":"This sign-up was requested for another e-mail address and has been cancelled.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"EMAIL_MISMATCH","field":"email"}}},"codeInvalid":{"summary":"400 VALIDATION CODE_INVALID — wrong or expired code","value":{"code":"VALIDATION","message":"The code is not valid. Check the latest e-mail and try again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CODE_INVALID","field":"code"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT — DEVICE_SIGNUP_DECIDED (the request was already approved, denied, refused, cancelled or expired), or TENANT_EXISTS (the e-mail is proven and its domain already has a tenant: the request is now REFUSED, the agent's poll answers the same, and that tenant's Owner is notified; OD-AGT-21)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"decided":{"summary":"409 CONFLICT DEVICE_SIGNUP_DECIDED","value":{"code":"CONFLICT","message":"This sign-up request has already been decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DEVICE_SIGNUP_DECIDED","state":"APPROVED"}}},"tenantExists":{"summary":"409 CONFLICT TENANT_EXISTS — the request is REFUSED; ask the Owner for an invite","value":{"code":"CONFLICT","message":"An account already exists for skyhop.example. Ask its Owner to invite you; they have been told you asked.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"request_invite","docs":"https://apiq.example/developers/errors#tenant-exists","details":{"reason":"TENANT_EXISTS","state":"REFUSED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — device-code sign-up is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"deviceSignupUnavailable":{"summary":"501 NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Device-code sign-up is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DEVICE_SIGNUP_UNAVAILABLE"}}}}}}}}}},"/v1/signup/device:deny":{"post":{"operationId":"denyDeviceSignup","x-mcp":{"expose":"excluded","toolset":"public","name":"deny_device_signup","class":"commit-lite","autonomy":"L1","reason":"A human decision route on the consent page (security: []); an agent never decides a sign-up (FEAT-034 §5.2)."},"tags":["identity"],"summary":"Deny a device-code sign-up from the consent page (FEAT-034 §5.2)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.signup.device-flow.enabled is on, else 501 NOT_IMPLEMENTED details.reason DEVICE_SIGNUP_UNAVAILABLE). The person refuses the request; nothing is created, the request is DENIED and the agent's next poll answers access_denied. Needs only the user_code: denying creates nothing and spends no code. A request already decided or expired is 409 CONFLICT details.reason DEVICE_SIGNUP_DECIDED; an unknown user_code is 404. Address limits: IdentityRateLimited, the per-address limiter of the lookup and the approval, counted with the approvals (details.action signup-device-approve). MCP: excluded.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupDenial"},"examples":{"deny":{"summary":"The person refuses","value":{"userCode":"WDJB-MJHT"}}}}}},"responses":{"200":{"description":"Denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceSignupDecision"},"examples":{"denied":{"summary":"200 — denied; nothing was created","value":{"userCode":"WDJB-MJHT","state":"DENIED"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT DEVICE_SIGNUP_DECIDED — the request was already decided or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"decided":{"summary":"409 CONFLICT DEVICE_SIGNUP_DECIDED","value":{"code":"CONFLICT","message":"This sign-up request has already been decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DEVICE_SIGNUP_DECIDED","state":"EXPIRED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE — device-code sign-up is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"deviceSignupUnavailable":{"summary":"501 NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Device-code sign-up is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DEVICE_SIGNUP_UNAVAILABLE"}}}}}}}}}},"/v1/invites":{"post":{"operationId":"createInvite","x-mcp":{"expose":"excluded","toolset":"public","name":"create_invite","class":"commit-lite","autonomy":"L1","reason":"Changes users, invitations, roles, permissions or entitlements; access control stays with a human."},"tags":["identity"],"summary":"Invite a colleague into the tenant (FEAT-020 §5 flow 3)","description":"Needs users:manage, which no API key can hold. The permissions granted must be a subset of the inviter's; the invitee joins the site the inviter is signed in to. The invite token goes to the invitee only — it is never in this response — and is valid for 7 days, once. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2; served when aikonic.activation.capped-flow.enabled is on, else role is 400 VALIDATION details.field role): invite by role. The body carries exactly one of role (a ConsoleRole) or permissions; both or neither is 400 VALIDATION details.field role (the backend enforces it, no oneOf). With role the invitee is granted that role's permission bundle intersected with the permissions the inviter holds: a permission the inviter lacks is dropped, never granted, and the 201 permissions list exactly what was granted (role echoes the role). Inviting as OWNER needs an Owner [assumption: 403 PERMISSION_DENIED details.reason OWNER_ONLY otherwise]. With permissions, as before.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"},"examples":{"reporting":{"summary":"A read-only colleague on hotels","value":{"email":"colleague@acme.example","permissions":["orders:read","search:hotels"]}},"byRole":{"summary":"A finance colleague invited by role (v1.63.0 PROPOSAL)","value":{"email":"accounts@sampletravels.co.uk","role":"FINANCE"}}}}}},"responses":{"201":{"description":"Invited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invited"},"examples":{"invited":{"summary":"What the inviter is told — not the token","value":{"email":"colleague@acme.example","permissions":["orders:read","search:hotels"],"siteIds":["SITE-B3XK9T2Q7NWR5M4A"],"expiresAt":"2026-10-03T10:00:00Z"}},"invitedByRole":{"summary":"201 — invited as FINANCE; the bundle the inviter could grant (v1.63.0 PROPOSAL)","value":{"email":"accounts@sampletravels.co.uk","role":"FINANCE","permissions":["finance:read","finance:write","orders:read"],"siteIds":["SITE-B3XK9T2Q7NWR5M4A"],"expiresAt":"2026-11-04T10:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/CredentialForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/v1/invites/accept":{"post":{"operationId":"acceptInvite","x-mcp":{"expose":"excluded","toolset":"public","name":"accept_invite","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Accept an invite with a chosen password — once (FEAT-020 §5 flow 3)","description":"The token is carried in the BODY, deliberately, rather than the path FEAT-020 §6 sketched: a path lands in access logs and proxy caches. A token that is unknown, used or expired is one and the same 400. An address that already has an account in the tenant is a 409.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAcceptRequest"},"examples":{"accept":{"summary":"The token from the invite, and a chosen password","value":{"token":"q8Zp3VxN6tYb1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","password":"another long passphrase"}}}}}},"responses":{"201":{"description":"The account the invite created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAccepted"},"examples":{"accepted":{"summary":"Sign in with this username and the chosen password","value":{"username":"colleague@acme.example","tenantId":"TEN-EVAL-K7Q2MZ4P","siteIds":["SITE-B3XK9T2Q7NWR5M4A"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/password/forgot":{"post":{"operationId":"forgotPassword","x-mcp":{"expose":"excluded","toolset":"public","name":"forgot_password","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Ask for a password-reset code (FEAT-020 §5 flow 5)","description":"The same 202 whether or not the account exists (no account enumeration); a code is sent only when it does. With no way to deliver a code the deployment answers 501.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"},"examples":{"forgot":{"summary":"Username and site, as at sign-in","value":{"username":"founder@acme.example","siteId":"SITE-B3XK9T2Q7NWR5M4A"}}}}}},"responses":{"202":{"description":"Accepted — a code is on its way if the account exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityAccepted"},"examples":{"sent":{"summary":"The one answer, account or not","value":{"status":"If the address can receive it, a message is on its way."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/v1/auth/password/reset":{"post":{"operationId":"resetPassword","x-mcp":{"expose":"excluded","toolset":"public","name":"reset_password","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Set a new password with the code — ends every session of the user (FEAT-020 §5 flow 5)","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"},"examples":{"reset":{"summary":"The code, and the new password","value":{"username":"founder@acme.example","siteId":"SITE-B3XK9T2Q7NWR5M4A","code":"771204","newPassword":"a brand new long passphrase"}}}}}},"responses":{"204":{"description":"Password set; the lockout is cleared and every session has ended"},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/me":{"get":{"operationId":"getMe","x-mcp":{"expose":"resource","toolset":"public","name":"get_me","class":"read","autonomy":"L0"},"tags":["identity"],"summary":"The caller's resolved AccessContext — who the platform thinks you are (FEAT-020 §3a)","description":"What a console renders its shell from, and the quickest way for an integrator to check a key: the tenant, site and permissions come from the verified credential, never the body. tenantStatus and productionWithheld say what the tenant may do on this deployment, read on this request: the console badges a SUSPENDED tenant from tenantStatus first, then a tenant whose production is withheld from productionWithheld (FEAT-020 §18c). For a human session passwordSet says whether the account has a password; a false one is offered POST /v1/auth/password/set (connect-v1 1.29.0, OD-ENR-15). connect-v1 1.47.0 (FEAT-033): accountType says whether the tenant is a BUSINESS or an INDIVIDUAL (absent: treat as BUSINESS), and a human session carries givenName and familyName when they are known (set at signup); an API key or service token carries neither.\n","responses":{"200":{"description":"The resolved caller","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessContext"},"examples":{"demoAdmin":{"summary":"The FEAT-020 demo tenant's admin","value":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-DEMO","siteId":"SITE-DEMO","segment":null,"permissions":["keys:manage","orders:amend","orders:cancel","orders:read","orders:write","search:flights","search:hotels"],"tenantStatus":"TRIAL","productionWithheld":false}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/keys":{"get":{"operationId":"listApiKeys","x-mcp":{"expose":"tool","toolset":"public","name":"list_api_keys","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 §6, OD-AGT-1): served on /mcp/public only while the plane's onboard flag (AIKONIC_MCP_ONBOARD_ENABLED) and the platform's aikonic.keys.agent-mint.enabled are on; it lists the calling key and the keys it minted, masked. Otherwise the platform refuses the AI-kind key with 403 PERMISSION_DENIED and the plane returns that body as an isError result."},"tags":["identity"],"summary":"The tenant's API keys — metadata only, never a secret (15 §1.1)","description":"Session only, as built (IMP-P2-041): every /v1/keys route accepts a human session token and nothing else — an API key or a service token is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED (keys:manage is never on a key). Credentials are checked before the body is read. connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else session only as above): an API key (either actorKind — the device sign-up hands the agent a system key, OD-AGT-20) may also list, and sees only itself and the keys it minted, metadata only (an MCP tool, list_api_keys). Every item carries the key's scope (kind, writes, products, spendPerDay, lookToBook, reqPerSec, holdsPerHour, cancelsPerDay, expiresAt), its principal (masked e-mail) and principalUserId, agent, mintedBy, state (ACTIVE, PAUSED, REVOKED, EXPIRED), the pause reason and evidence of a PAUSED key, successorKeyPrefix and today's usage. A key minted before 1.57.0 reads kind own, state ACTIVE and no caps. With aikonic.keys.velocity.enabled on, a PAUSED key is 423 and a key over its own rate 429.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Key metadata, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"},"examples":{"oneKey":{"summary":"One sandbox booking key","value":{"keys":[{"keyPrefix":"mo_test_AB3KQ7XN2M4P9R5T","display":"mo_test_AB3KQ7XN2M4P9R5T…h7Jd","name":"checkout-service","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-09-23T15:30:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","principalId":"svc-5b2f0c9e","actorKind":"ai"}]}},"agentKeys":{"summary":"The device sign-up key sees itself and the key it minted; the child is PAUSED","value":{"keys":[{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","display":"mo_test_AG3NT7KQ2M4P9R5X…r2Vb","name":"claude-code-quickstart","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:14:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-6d1e8b3a","actorKind":"ai","kind":"agt","writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"PAUSED","pausedReason":"LOOP_DETECTED","pausedEvidence":{"rule":"LOOP_HOLDS","dimension":"holdsWithoutOutcomePer10Minutes","observed":212,"limit":50,"windowSeconds":540,"summary":"212 holds in 9 min","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736"],"at":"2026-10-27T10:02:00Z"},"successorKeyPrefix":null,"usageToday":{"requests":1412,"errors":3,"searches":118,"bookings":0,"holds":212,"cancels":0,"approvals":0,"spend":null}},{"keyPrefix":"mo_test_SK7HQ2WN4R8T5M3Z","display":"mo_test_SK7HQ2WN4R8T5M3Z…j8Kw","name":"sandbox","environment":"sandbox","scopes":["search:hotels","search:flights","orders:write","orders:read","orders:cancel"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:12:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-2a7c9e1f","actorKind":"system","kind":"own","writes":"book","products":null,"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":null,"state":"ACTIVE","pausedReason":null,"pausedEvidence":null,"successorKeyPrefix":null,"usageToday":{"requests":2140,"errors":3,"searches":160,"bookings":1,"holds":218,"cancels":1,"approvals":0,"spend":{"amount":1540000,"currency":"INR"}}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"mintApiKey","x-mcp":{"expose":"tool","toolset":"public","name":"mint_api_key","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 §5.3, §6, OD-AGT-1): served on /mcp/public only while the plane's onboard flag (AIKONIC_MCP_ONBOARD_ENABLED) and the platform's aikonic.keys.agent-mint.enabled are on, and then only under the narrowing rule (every scope dimension equal or narrower than the calling key, principal fixed); classed commit-lite by the revokeApiKey precedent (FEAT-031 §6). Otherwise the platform refuses the AI-kind key with 403 PERMISSION_DENIED and the plane returns that body as an isError result."},"tags":["identity"],"summary":"Mint a scoped API key — the secret is in this response and nowhere else, ever (15 §1.1)","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"description":"Requires keys:manage (session only, as built — an API key is 403 ACCESS_TOKEN_REQUIRED). The key's scopes must be a subset of the minting user's; the key is a service principal that inherits nothing from the user who minted it. Stored hashed: a lost key is revoked and re-minted, never recovered. A key can also be exchanged for a short-lived Bearer token at POST /v1/oauth/token (FEAT-020 phase C). While the tenant is SUSPENDED no key is minted or rotated (403 TENANT_SUSPENDED); revoke still works. actorKind (DR-40, default ai) sets the channel the key acts as: an ai key is the AI channel and needs a D-50 grant on money verbs; a system key is the SYSTEM channel for non-AI server integrations and skips the D-50 gate, so minting one also needs the tenant admin role (403 PERMISSION_DENIED details.reason SYSTEM_KEY_REQUIRES_ADMIN otherwise). A system key can never file an agent proposal (POST /v1/agent-proposals, AGENT_CREDENTIAL_REQUIRED). connect-v1 v1.46.0: suppliers (optional) narrows the key to a supplier allowlist on top of its scopes — see MintApiKeyRequest.suppliers for the validation and the runtime answers. It is fixed at mint and survives rotation; console sessions are never narrowed by it. connect-v1 1.57.0 (PROPOSAL, FEAT-034; the scope members are served when aikonic.keys.agent-mint.enabled is on, else a request carrying any of them is 501 NOT_IMPLEMENTED details.reason AGENT_KEYS_UNAVAILABLE [assumption] and the route is as above). The request takes the key's scope — writes (KeyWrites), products, spendPerDay, lookToBook, reqPerSec, holdsPerHour, cancelsPerDay, expiresAt — and onBehalfOf, the Member the key acts for (default the caller; its principal and principalUserId). Minted by a session, the key is kind own and a member the request leaves out or sends null sets no key cap (the tenant's limits still govern), unchanged for people [assumption]; a session's onBehalfOf naming a Member other than the caller needs users:manage (else 403 PERMISSION_DENIED details.reason MISSING_PERMISSION) [assumption]. The key records the agent of the X-Agent header. NARROWING RULE (FEAT-034 §5.3, OD-AGT-1): an API key may mint, only while aikonic.keys.agent-mint.enabled is on, and only a key whose every dimension is equal or narrower than its own — scopes and suppliers a subset, environment the same, writes no higher on the KeyWrites ladder, products a subset, each numeric cap no higher, expiresAt no later. Either actorKind may mint: the device sign-up hands the agent a system key (OD-AGT-20). A cap the request leaves out or sends null is the parent's cap, never null where the parent has one [assumption]; the child keeps the parent's actorKind unless it asks for ai under a system parent (the narrower channel: the D-50 gate applies), and an ai parent asking for system is widening (dimension actorKind) [assumption]. Anything wider is 403 PERMISSION_DENIED details.reason SCOPE_WIDENING with details.dimensions naming each wider member (nextAction widen_scope_via_owner; three in an hour count as a soft velocity breach, FEAT-034 §12). The child is kind agt, mintedBy the parent's keyPrefix, and its principal is the parent's, fixed at mint: an onBehalfOf naming anyone else is 403 PERMISSION_DENIED details.reason PRINCIPAL_UNKNOWN, as is an onBehalfOf that is not an active Member of the tenant. A child never widens its ancestors' blast radius (FEAT-034 G4) [assumption; OD-AGT-6]: every call of a minted key also counts against each ancestor key's velocity and spend windows, and while an ancestor is PAUSED its descendants answer 423 KEY_PAUSED (resuming the ancestor resumes them), so minting N children never multiplies what one key may do. With aikonic.keys.agent-mint.enabled off an API key is refused 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED, as today (FEAT-034 AC-034-07 calls it SESSION_REQUIRED). Emits key.minted. MCP: the tool mint_api_key, on the same rule.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintApiKeyRequest"},"examples":{"bookingKey":{"summary":"A sandbox key that can shop hotels and book","value":{"name":"checkout-service","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"environment":"sandbox"}},"supplierLimitedKey":{"summary":"A hotels key that reaches two suppliers only","value":{"name":"checkout-service","scopes":["search:hotels","orders:write","orders:read"],"suppliers":["SUP-HTL-EXAMPLE6","SUP-HTL-EXAMPLE7"],"environment":"sandbox"}},"systemKey":{"summary":"A system key for a non-AI server integration (tenant admin only, DR-40)","value":{"name":"erp-sync","scopes":["orders:read"],"ipAllowlist":["203.0.113.0/24"],"environment":"sandbox","actorKind":"system"}},"agentNarrowKey":{"summary":"The device sign-up key mints a hold-only hotels key for a sub-task, on the AI channel (narrowing rule)","value":{"name":"claude-code-quickstart","scopes":["search:hotels","orders:write","orders:read"],"environment":"sandbox","actorKind":"ai","writes":"hold","products":["hotels"],"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z"}}}}}},"responses":{"201":{"description":"Minted. Show apiKey once; it cannot be retrieved again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintedApiKey"},"examples":{"minted":{"summary":"The secret, once, beside its metadata","value":{"apiKey":"mo_test_AB3KQ7XN2M4P9R5T_x2mv8q4nzl6c3bfw9k1pd7rt5ysj0ae2hg4uo6xi8mc3nq5vb7h7Jd","key":{"keyPrefix":"mo_test_AB3KQ7XN2M4P9R5T","display":"mo_test_AB3KQ7XN2M4P9R5T…h7Jd","name":"checkout-service","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-09-23T15:30:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","principalId":"svc-5b2f0c9e","actorKind":"ai"}}},"mintedAgentKey":{"summary":"An agent key's narrower child — kind agt, the parent's principal, mintedBy the parent","value":{"apiKey":"mo_test_AG3NT7KQ2M4P9R5X_k8w2n6q0z4m8x2b6c0v4d8f2g6h0j4k8l2p6r0t4y8u2i6o0e4a8s2d6f0r2Vb","key":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","display":"mo_test_AG3NT7KQ2M4P9R5X…r2Vb","name":"claude-code-quickstart","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:14:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-6d1e8b3a","actorKind":"ai","kind":"agt","writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","pausedReason":null,"pausedEvidence":null,"successorKeyPrefix":null}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/CredentialForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — a scope member was sent while scoped agent keys are off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentKeysUnavailable":{"summary":"501 NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — mint without the scope members","value":{"code":"NOT_IMPLEMENTED","message":"Key scopes beyond scopes and suppliers are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_KEYS_UNAVAILABLE"}}}}}}}}}},"/v1/keys/{keyPrefix}":{"delete":{"operationId":"revokeApiKey","x-mcp":{"expose":"tool","toolset":"public","name":"revoke_api_key","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 §6, OD-AGT-1): served on /mcp/public only while the plane's onboard flag (AIKONIC_MCP_ONBOARD_ENABLED) and the platform's aikonic.keys.agent-mint.enabled are on, and then only on the calling key itself or a key it minted (another key reads 404). Otherwise the platform refuses the AI-kind key with 403 PERMISSION_DENIED and the plane returns that body as an isError result."},"tags":["identity"],"summary":"Revoke an API key now (FEAT-020 §5 flow 6)","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"description":"Needs keys:manage (session only, as built — an API key is 403 ACCESS_TOKEN_REQUIRED). Idempotent — revoking a revoked key is a 204. A key of another tenant is a 404, indistinguishable from none (FEAT-017). Revoke is a compare-and-set on the key: when the key keeps changing under it (a rotation starting at the same moment) it re-reads up to three times, then answers 409 CONFLICT with details.reason KEY_CHANGED_CONCURRENTLY — read the key list and revoke again. connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else session only as above): an API key (either actorKind) may revoke itself or a key it minted; any other key is 404, as a key of another tenant. A PAUSED key can be revoked. Emits key.revoked. MCP: the tool revoke_api_key, on the same rule.\n","parameters":[{"name":"keyPrefix","description":"The key to revoke, by the prefix the key list shows.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Revoked; the key is refused from now on"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateApiKeyScope","x-mcp":{"expose":"excluded","toolset":"public","name":"update_api_key_scope","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 phase 1): narrowing a key is a console action; the phase-1 public toolset gains only onboard, list_api_keys, mint_api_key and revoke_api_key (AC-034-05), and exposing it later is additive (18 §1)."},"tags":["identity"],"summary":"Narrow an API key's scope — never widen it (FEAT-034 §5.3)","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else 501 NOT_IMPLEMENTED details.reason AGENT_KEYS_UNAVAILABLE [assumption: the scoped-key routes ride the agent-mint flag; FEAT-034 §10 names none for them]). Changes the scope of a live key in place; it takes effect on the key's next request, no re-issue (FEAT-034 §14). Every member is optional and only the members sent change. Each dimension may only stay equal or get narrower than the key's current scope: writes lower on the ladder (KeyWrites), products and scopes a subset, suppliers a subset (an absent list meaning every supplier, so setting one narrows), a numeric cap lower or a cap set where there was none, expiresAt earlier or set where there was none. Anything wider is 403 PERMISSION_DENIED details.reason SCOPE_WIDENING with details.dimensions naming each wider member, and nothing changes; widening means minting a new key from a person's session. name is free. A REVOKED or EXPIRED key is 409 CONFLICT details.reason KEY_NOT_ACTIVE (a PAUSED key may be narrowed). Human session only, as every /v1/keys route (an API key is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED); needs keys:manage, as the other key routes [assumption: FEAT-034 §6 says users:manage], or to be the user who minted the key. Audited as key.narrowed with the before and after scope. MCP: excluded.\n","parameters":[{"name":"keyPrefix","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyScopePatch"},"examples":{"narrow":{"summary":"Hold only, hotels only, 5 requests a second, expiring sooner","value":{"writes":"hold","products":["hotels"],"reqPerSec":5,"expiresAt":"2026-12-31T00:00:00Z"}}}}}},"responses":{"200":{"description":"The key with its narrowed scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyMetadata"},"examples":{"narrowed":{"summary":"200 — narrowed; effective on the key's next request","value":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","display":"mo_test_AG3NT7KQ2M4P9R5X…r2Vb","name":"claude-code-quickstart","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:14:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-6d1e8b3a","actorKind":"ai","kind":"agt","writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":5,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2026-12-31T00:00:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","pausedReason":null,"pausedEvidence":null,"successorKeyPrefix":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — SCOPE_WIDENING (details.dimensions; nextAction widen_scope_via_owner), ACCESS_TOKEN_REQUIRED (an API key), or MISSING_PERMISSION (neither keys:manage nor the minter)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"scopeWidening":{"summary":"403 PERMISSION_DENIED SCOPE_WIDENING — a PATCH may only narrow","value":{"code":"PERMISSION_DENIED","message":"This change would widen the key on: writes, reqPerSec. Mint a new key from a signed-in session instead.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","docs":"https://apiq.example/developers/errors#scope-widening","details":{"reason":"SCOPE_WIDENING","dimensions":["writes","reqPerSec"]}}},"accessTokenRequired":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — an API key presented","value":{"code":"PERMISSION_DENIED","message":"Key scopes are changed from a signed-in session only.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","details":{"reason":"ACCESS_TOKEN_REQUIRED"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT KEY_NOT_ACTIVE — the key is REVOKED or EXPIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"keyNotActive":{"summary":"409 CONFLICT KEY_NOT_ACTIVE","value":{"code":"CONFLICT","message":"This key is revoked and cannot be changed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"KEY_NOT_ACTIVE","state":"REVOKED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — scoped agent keys are off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentKeysUnavailable":{"summary":"501 NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Scoped agent keys are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_KEYS_UNAVAILABLE"}}}}}}}}}},"/v1/keys/{keyPrefix}/rotate":{"post":{"operationId":"rotateApiKey","x-mcp":{"expose":"excluded","toolset":"public","name":"rotate_api_key","class":"commit-money","autonomy":"L1","reason":"Mints a replacement API key whose secret is returned once in the response (RotatedApiKey.apiKey). An agent never receives key material or changes tenant credentials (DR-40)."},"tags":["identity"],"summary":"Rotate an API key with an overlap window (15 §1.1)","description":"Mints the replacement — same name, scopes and allowlist, its secret shown once here — and keeps the old key working until overlapUntil (default 24h, at most 7d), its calls carrying a Deprecation header and a Sunset naming when it stops. After that the old key is refused. A key already rotating, or revoked, is a 409: rotate the replacement. connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3; served when aikonic.keys.agent-mint.enabled is on, else as above): the successor carries the parent's whole scope — kind, writes, products, the caps, principal, agent and mintedBy — with expiresAt = min(now + the key's lifetime, the minting key's expiresAt), so a child never outlives its parent [assumption: the lifetime restarts at rotation]; the old key's successorKeyPrefix names it, and after overlapUntil the old key reads state EXPIRED. A key with an expiresAt rotated by itself or by a Member session may rotate only within 7 days of it, else 409 CONFLICT details.reason ROTATION_TOO_EARLY (details.expiresAt); an Owner or Admin session may rotate at any time (an emergency rotation) [assumption]. The key itself (an API key presenting its own keyPrefix) may rotate itself while the flag is on; any other key is 404. Emits key.rotated. MCP: unchanged, excluded.\n","parameters":[{"name":"keyPrefix","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"The replacement, and until when the old key still works","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotatedApiKey"},"examples":{"rotated":{"summary":"Deploy the new key before overlapUntil","value":{"apiKey":"mo_test_ZX9PQ4LM7N2B8V6C_w3n7k1q5z9m2x8b4c6v0d3f7g1h5j9k2l6p0r4t8y2u6i0o4e8a2s6d0f4g8h2p9Qa","replaces":"mo_test_AB3KQ7XN2M4P9R5T","overlapUntil":"2026-09-27T10:00:00Z","key":{"keyPrefix":"mo_test_ZX9PQ4LM7N2B8V6C","display":"mo_test_ZX9PQ4LM7N2B8V6C…p9Qa","name":"checkout-service","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-09-26T10:00:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","principalId":"svc-8c1d3e7f","actorKind":"ai"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/CredentialForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"State conflict, as components/responses/Conflict: CONFLICT when the key is already rotating or is revoked; connect-v1 1.57.0 (PROPOSAL, FEAT-034) also CONFLICT details.reason ROTATION_TOO_EARLY for a key with an expiresAt more than 7 days away, rotated by itself or a Member session (never an Owner or Admin session).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"conflict":{"summary":"409 CONFLICT — generic state conflict (03 §9)","value":{"code":"CONFLICT","message":"The order is in a state that does not allow this transition","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"state":"CANCELLED","attempted":"amend"}}},"offerExpired":{"summary":"409 OFFER_EXPIRED — re-shop required (03 §4, §9)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-H-8c1f4a2e expired at 2026-09-14T17:34:11Z; re-reprice","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","expiredAt":"2026-09-14T17:34:11Z"}}},"holdExpired":{"summary":"409 HOLD_EXPIRED — hold auto-released (03 §10.3 F1)","value":{"code":"HOLD_EXPIRED","message":"holdRef HD-M-4419ab expired at 2026-09-14T17:05:41Z; the seats were released","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"holdRef":"HD-M-4419ab","expiredAt":"2026-09-14T17:05:41Z"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"rotationTooEarly":{"summary":"409 CONFLICT ROTATION_TOO_EARLY — rotate within 7 days of expiresAt","value":{"code":"CONFLICT","message":"This key can be rotated from 2027-01-18T09:14:00Z, 7 days before it expires.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ROTATION_TOO_EARLY","expiresAt":"2027-01-25T09:14:00Z"}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/keys/{keyPrefix}:pause":{"post":{"operationId":"pauseApiKey","x-mcp":{"expose":"excluded","toolset":"public","name":"pause_api_key","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session), Owner or Admin; FEAT-034 §6 gives pause no tool, so no agent tool serves it."},"tags":["identity"],"summary":"Pause an API key by hand, with a reason (FEAT-034 §5.4)","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else 501 NOT_IMPLEMENTED details.reason AGENT_KEYS_UNAVAILABLE [assumption: the scoped-key routes ride the agent-mint flag]). An Owner or Admin pauses a key by hand with a reason (1 to 500 characters, audited). From the next request every call with the key is 423 PERMISSION_DENIED details.reason KEY_PAUSED, nextAction stop (components/responses/KeyPaused); holds and orders already made are untouched and holds expire on their TTL. The key's state is PAUSED (status stays ACTIVE), pausedReason is the reason and pausedEvidence carries rule MANUAL. The velocity and loop rules of aikonic.keys.velocity.enabled pause a key the same way, without this route (FEAT-034 §5.4). Emits key.paused (webhook and notification). Human session only (an API key is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED, as every /v1/keys route); Owner or Admin, else 403 PERMISSION_DENIED details.reason OWNER_OR_ADMIN_ONLY. A key that is not ACTIVE (already PAUSED, REVOKED or EXPIRED) is 409 CONFLICT details.reason KEY_NOT_ACTIVE. MCP: excluded, never a tool (FEAT-034 §6).\n","parameters":[{"name":"keyPrefix","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyStateChange"},"examples":{"pause":{"summary":"An Owner pauses a key it suspects is looping","value":{"reason":"Investigating a hold loop in the quick-start script","category":"other"}}}}}},"responses":{"200":{"description":"The key in its new state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyMetadata"},"examples":{"paused":{"summary":"200 — the key is now PAUSED","value":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","display":"mo_test_AG3NT7KQ2M4P9R5X…r2Vb","name":"claude-code-quickstart","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:14:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-6d1e8b3a","actorKind":"ai","kind":"agt","writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"PAUSED","pausedReason":"Investigating a hold loop in the quick-start script","pausedEvidence":{"rule":"MANUAL","dimension":null,"observed":null,"limit":null,"windowSeconds":null,"summary":"Paused by an Owner","requestIds":[],"at":"2026-10-27T10:02:00Z"},"successorKeyPrefix":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — OWNER_OR_ADMIN_ONLY (a Member or Developer), or ACCESS_TOKEN_REQUIRED (an API key or a service token; an agent never pauses a key over REST or MCP)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"ownerOrAdminOnly":{"summary":"403 PERMISSION_DENIED OWNER_OR_ADMIN_ONLY","value":{"code":"PERMISSION_DENIED","message":"Only an Owner or an Admin can pause a key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","details":{"reason":"OWNER_OR_ADMIN_ONLY"}}},"accessTokenRequired":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — an API key presented","value":{"code":"PERMISSION_DENIED","message":"A key is paused by a signed-in Owner or Admin only.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"ACCESS_TOKEN_REQUIRED"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT KEY_NOT_ACTIVE — the key is already PAUSED, or REVOKED or EXPIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"keyNotActive":{"summary":"409 CONFLICT KEY_NOT_ACTIVE","value":{"code":"CONFLICT","message":"This key is already paused.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"KEY_NOT_ACTIVE","state":"PAUSED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — scoped agent keys are off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentKeysUnavailable":{"summary":"501 NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Scoped agent keys are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_KEYS_UNAVAILABLE"}}}}}}}}}},"/v1/keys/{keyPrefix}:resume":{"post":{"operationId":"resumeApiKey","x-mcp":{"expose":"excluded","toolset":"public","name":"resume_api_key","class":"commit-lite","autonomy":"L1","reason":"Resume is never reachable by an agent (FEAT-034 §5.4, §6, AC-034-10): human session only (x-aikonic-credential: human-session), Owner or Admin."},"tags":["identity"],"summary":"Resume a paused API key, with a reason — a person only (FEAT-034 §5.4)","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else 501 NOT_IMPLEMENTED details.reason AGENT_KEYS_UNAVAILABLE [assumption: the scoped-key routes ride the agent-mint flag]). An Owner or Admin resumes a PAUSED key with a reason (1 to 500 characters) and a category (false_positive, fixed, limits_changed, other); both are audited, and a false_positive resume within 10 minutes of the pause feeds the false-positive pause metric (FEAT-034 §13). The key is ACTIVE from its next request; pausedReason and pausedEvidence clear. Emits key.resumed (webhook and notification). Resume is never reachable by an agent (AC-034-10): an API key or service token is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED, as every /v1/keys route [assumption: FEAT-034 calls it SESSION_REQUIRED; OD-AGT-16]; a Member or Developer session is 403 PERMISSION_DENIED details.reason OWNER_OR_ADMIN_ONLY. A key that is not PAUSED is 409 CONFLICT details.reason KEY_NOT_PAUSED. MCP: excluded; no resume tool exists on any toolset.\n","parameters":[{"name":"keyPrefix","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyStateChange"},"examples":{"resume":{"summary":"The Owner fixed the script and resumes the key","value":{"reason":"The quick-start retry loop is fixed","category":"fixed"}}}}}},"responses":{"200":{"description":"The key in its new state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyMetadata"},"examples":{"resumed":{"summary":"200 — the key is now ACTIVE","value":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","display":"mo_test_AG3NT7KQ2M4P9R5X…r2Vb","name":"claude-code-quickstart","environment":"sandbox","scopes":["search:hotels","orders:write","orders:read"],"ipAllowlist":[],"status":"ACTIVE","createdAt":"2026-10-27T09:14:00Z","createdBy":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","principalId":"svc-6d1e8b3a","actorKind":"ai","kind":"agt","writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z","principal":"pr***@skyhop.example","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","pausedReason":null,"pausedEvidence":null,"successorKeyPrefix":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — OWNER_OR_ADMIN_ONLY (a Member or Developer), or ACCESS_TOKEN_REQUIRED (an API key or a service token; an agent never resumes a key over REST or MCP)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"ownerOrAdminOnly":{"summary":"403 PERMISSION_DENIED OWNER_OR_ADMIN_ONLY","value":{"code":"PERMISSION_DENIED","message":"Only an Owner or an Admin can resume a key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","details":{"reason":"OWNER_OR_ADMIN_ONLY"}}},"accessTokenRequired":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — an API key presented","value":{"code":"PERMISSION_DENIED","message":"A key is resumed by a signed-in Owner or Admin only.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"ACCESS_TOKEN_REQUIRED"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT KEY_NOT_PAUSED — the key is not PAUSED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"keyNotPaused":{"summary":"409 CONFLICT KEY_NOT_PAUSED","value":{"code":"CONFLICT","message":"This key is not paused.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"KEY_NOT_PAUSED","state":"ACTIVE"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — scoped agent keys are off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentKeysUnavailable":{"summary":"501 NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Scoped agent keys are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_KEYS_UNAVAILABLE"}}}}}}}}}},"/v1/keys/{keyPrefix}/usage":{"get":{"operationId":"getApiKeyUsage","x-mcp":{"expose":"excluded","toolset":"public","name":"get_api_key_usage","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 phase 1): the phase-1 public toolset is onboard and the three key tools (AC-034-05); a key reads its own usage over REST, and exposing it later is additive (18 §1)."},"tags":["identity"],"summary":"A key's counters and spend today against its scope (FEAT-034 §5.7)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.keys.agent-mint.enabled is on, else 501 NOT_IMPLEMENTED details.reason AGENT_KEYS_UNAVAILABLE [assumption: the scoped-key routes ride the agent-mint flag]). Today's counters (UTC day) of one key — requests, errors, searches, bookings, holds, cancels, approvals and spend — beside the scope's limits, the soft breaches today (the third pauses the key, FEAT-034 §5.4) and the key's state. Replays with the same Idempotency-Key are not counted (F-02). A human session with keys:manage reads any key of the tenant; an API key reads only itself and the keys it minted (another key is 404, as a key of another tenant). The M8 Agent keys screen reads it. MCP: excluded.\n","parameters":[{"name":"keyPrefix","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Today's usage and the limits it is counted against","headers":{"X-Principal":{"$ref":"#/components/headers/X-Principal"},"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyUsageReport"},"examples":{"today":{"summary":"200 — 1,412 requests, 6 holds, one booking, no breach","value":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","day":"2026-10-27","usage":{"requests":1412,"errors":0,"searches":118,"bookings":1,"holds":6,"cancels":1,"approvals":0,"spend":{"amount":61200,"currency":"USD"}},"limits":{"writes":"hold","products":["hotels"],"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":"2027-01-25T09:12:00Z"},"softBreachesToday":0,"state":"ACTIVE"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — scoped agent keys are off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentKeysUnavailable":{"summary":"501 NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Scoped agent keys are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_KEYS_UNAVAILABLE"}}}}}}}}}},"/v1/activation":{"get":{"operationId":"getActivation","x-mcp":{"expose":"resource","toolset":"public","name":"get_activation","class":"read","autonomy":"L0"},"tags":["identity"],"summary":"The activation rail — every step with its state and what is still needed (FEAT-030 §5.3)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-2, published in 1.18.0). One rail per tenant, the same answer from any device. Steps carry state todo | in_progress | needs_you | in_review | done | blocked; every needs_you step names in needs[] the exact field or document still missing. States are derived from facts (for example sandbox_booking from firstSandboxOrderAt), never set by hand. Order is a suggestion: steps 3 to 8 may run in parallel. tier is null while the tenant is TRIAL; a tenant made LIVE before FEAT-030 reads tier LIVE_ENTERPRISE. Needs tenant:read, which the Owner and Admin role bundles carry (OD-ENR-8); the users:manage fallback is retired; a human session or an API key (a sandbox key reads the rail of the tenant it belongs to). Step keys, states, tier and buildingIntent are x-extensible-enum: a client renders the values it knows and tolerates the rest. connect-v1 1.47.0 (FEAT-033), additive: the rail is derived per account type (accountType). A BUSINESS tenant keeps the nine steps above, unchanged. An INDIVIDUAL tenant has five: verify_email, sandbox_booking, identity_verification, terms, go_live — no business profile, no business verification, no money model, no wallet (an individual pays each booking by card through the payment provider's hosted checkout) and no API certification. identity_verification is done when an identity check (POST /v1/activation/identity-checks) is VERIFIED; its needs[] names what is missing (taxId). go_live is blocked until identity_verification and terms are done, and one Owner's sign-off is enough (GET /admin/v1/go-live requiredSignoffs 1). identityCheck carries the latest check, null until one ran; the full identity number is never returned. connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b): flow says which rail the tenant is on (NINE_STEP, as above, or CAPPED) and every step carries required. On CAPPED the required steps are business_verification (GST register look-up, confirm, signatory check; hosted KYB the fallback), money_model (WALLET, CARD with a saved method, or the ops-routed models) and terms, plus fund_wallet only when the model is WALLET (verify_email is required too, always done at sign-up); every other step is required false: sandbox_booking and certification (recommended), business_profile (completed by the registration confirm on the India path; elsewhere the hosted KYB collects the company details) and go_live (done when the tenant turns live). The transition that completes the last required step activates the tenant itself (TRIAL to LIVE, actor system:capped-auto-live) with tier LIVE_CAPPED and a daily cap (limits; GET /v1/activation/limits), with no go-live signoff and no ops approval; go_live then reads done. A step in_review (a KYB case the provider reviews) never completes it. With the flag off every tenant reads NINE_STEP and nothing changes; LIVE_STANDARD and LIVE_ENTERPRISE stay on the OD-870 ops path. The CAPPED flow (connect-v1 1.51.0) applies to BUSINESS tenants only: an INDIVIDUAL tenant keeps the five-step rail of connect-v1 1.47.0 (FEAT-033) above, never reads flow CAPPED and is never activated by it. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2; served when aikonic.activation.capped-flow.enabled is on): the CAPPED rail lists, in this order, verify_email, business_profile, business_verification, terms, money_model, sandbox_booking (required false) and go_live; fund_wallet and certification are not listed on CAPPED. Every listed step but sandbox_booking is required. business_profile is done when country, businessStructure, industry, website (or noWebsite with productDescription), phone and monthlyVolume are set (PATCH /v1/activation/profile). business_verification is done only when the registration is confirmed (by GSTIN, or by scheme and number) or the hosted KYB documents are approved, the signatory matched (POST /v1/activation/signatory-checks) and the business people are submitted (PUT /v1/activation/business-people with owners, or noOwnerAt25Percent true). The CAPPED flow is for BUSINESS tenants only; a person selling in their own name signs up as an INDIVIDUAL account (FEAT-033) and is never on this rail. money_model is done for CARD with a saved method, for WALLET with an elected currency and a wallet balance above zero, and for OWN_ACCREDITATION or BYOC when the operator review (OpsApproval kind MONEY_MODEL_REVIEW) is approved; it reads in_review meanwhile. go_live is required and reads done when the tenant is LIVE: on CAPPED going live is now the Owner's explicit POST /v1/activation:go-live, which replaces the automatic promotion of 1.51.0 (no step transition activates the tenant by itself any more). workEmail says whether a work e-mail must be proved first (POST /v1/activation/work-email:send-code, then :confirm); go-live is refused while workEmail.required is true and workEmail.verifiedAt is null. Test mode (the sandbox twin and sandbox keys) is unaffected. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): on CAPPED, business_verification is the verification provider's verdict. It is done only when the provider's case is verified (with the registration confirmed where the country has a register, the signatory matched and the business people submitted); submitting the forms is not enough. businessVerification carries the case: status not_submitted leaves the step as round 2 reads it (what is still to fill), in_progress (submitted, the provider has not answered) reads in_progress, in_review reads in_review, needs_more reads needs_you with needs[] the outstanding requirement codes (businessVerification.outstanding), rejected reads blocked, verified reads done. The provider is routed per country (deployment settings aikonic.kyb.provider.<country>, default aikonic.kyb.provider; harness on DEV and SANDBOX only, stripe_connect, or a registered India KYC adapter). go_live stays refused with 409 CONFLICT PREREQUISITES_OPEN (details.open business_verification) until the verdict is verified; once verified, going live is immediate. A client re-reads this rail while businessVerification.status is in_progress or in_review. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): going live on CAPPED is an operator review. POST /v1/activation:go-live opens it and goLiveReview carries it (GoLiveReview: status, submittedAt, decidedAt, expectedBy, note, and per product its status and checks business_details and test_calls); the tenant stays TRIAL until an operator approves. go_live reads in_review while the review is in_review, needs_you (needs[] go_live.changes_requested) on changes_requested, blocked (needs[] go_live.rejected) on rejected and done once approved (tier LIVE_CAPPED). It replaces \"once verified, going live is immediate\" above. A client re-reads this rail while goLiveReview.status is in_review. sandbox_booking is required on CAPPED (owner direction 2026-10-29): the test calls (search, reprice, orders, cancel in the sandbox) for every requested product must be seen before an operator can approve; the request itself may come first, and sandbox_booking's needs[] names what is missing (<product>.<verb>).\n","responses":{"200":{"description":"The rail","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivationState"},"examples":{"midway":{"summary":"200 — sandbox order done, profile waiting on the website, KYB in review","value":{"tenantId":"TEN-ACME","buildingIntent":"API_INTEGRATION","registrationGstin":null,"registrationConfirmedAt":null,"tier":null,"moneyModel":"WALLET","firstSandboxCallAt":"2026-10-09T09:02:11Z","firstSandboxOrderAt":"2026-10-09T09:14:40Z","liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":null,"acceptedAt":null},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-09T09:00:30Z","needs":[]},{"key":"sandbox_booking","state":"done","updatedAt":"2026-10-09T09:14:40Z","needs":[]},{"key":"business_profile","state":"needs_you","updatedAt":"2026-10-09T09:20:00Z","needs":["website"]},{"key":"business_verification","state":"in_review","updatedAt":"2026-10-09T10:05:00Z","needs":[]},{"key":"money_model","state":"done","updatedAt":"2026-10-09T09:25:00Z","needs":[]},{"key":"fund_wallet","state":"todo","updatedAt":null,"needs":["wallet.balance"]},{"key":"certification","state":"in_progress","updatedAt":"2026-10-09T09:40:00Z","needs":["webhook_verified","deprecation_logged"]},{"key":"terms","state":"todo","updatedAt":null,"needs":["terms.accept"]},{"key":"go_live","state":"blocked","updatedAt":null,"needs":["business_verification","fund_wallet","certification","terms"]}]}},"cappedLive":{"summary":"200 — CAPPED flow, three required steps done; LIVE_CAPPED with a daily cap (v1.51.0 PROPOSAL)","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":"LIVE_CAPPED","moneyModel":"CARD","firstSandboxCallAt":null,"firstSandboxOrderAt":null,"liveAt":"2026-10-26T10:31:00Z","terms":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-26T10:31:00Z"},"flow":"CAPPED","country":"IN","registrationGstin":"29AABCS1234F1Z5","registrationConfirmedAt":"2026-10-26T09:12:00Z","limits":{"tier":"LIVE_CAPPED","dailyCap":{"amount":40000000,"currency":"INR"},"usedToday":{"amount":0,"currency":"INR"},"resetAt":"2026-10-27T00:00:00Z"},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-26T09:01:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"todo","updatedAt":null,"needs":[],"required":false},{"key":"business_profile","state":"done","updatedAt":"2026-10-26T09:12:00Z","needs":[],"required":false},{"key":"business_verification","state":"done","updatedAt":"2026-10-26T09:14:00Z","needs":[],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-26T09:20:00Z","needs":[],"required":true},{"key":"fund_wallet","state":"done","updatedAt":"2026-10-26T09:20:00Z","needs":[],"required":false},{"key":"certification","state":"todo","updatedAt":null,"needs":["idempotent_retry","price_change_handled","async_respected","webhook_verified","ambiguity_rule","deprecation_logged"],"required":false},{"key":"terms","state":"done","updatedAt":"2026-10-26T10:31:00Z","needs":[],"required":true},{"key":"go_live","state":"done","updatedAt":"2026-10-26T10:31:00Z","needs":[],"required":false}]}},"cappedReadyToGoLive":{"summary":"200 — CAPPED round 2, every required step done but go_live, work e-mail proved (v1.63.0 PROPOSAL)","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":null,"moneyModel":"CARD","firstSandboxCallAt":"2026-10-28T09:05:00Z","firstSandboxOrderAt":null,"liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-28T10:20:00Z"},"flow":"CAPPED","country":"GB","registrationGstin":null,"registrationConfirmedAt":"2026-10-28T09:30:00Z","limits":null,"workEmail":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-28T09:02:00Z","required":true},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-28T09:00:30Z","needs":[],"required":true},{"key":"business_profile","state":"done","updatedAt":"2026-10-28T09:20:00Z","needs":[],"required":true},{"key":"business_verification","state":"done","updatedAt":"2026-10-28T09:45:00Z","needs":[],"required":true},{"key":"terms","state":"done","updatedAt":"2026-10-28T10:20:00Z","needs":[],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-28T10:10:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"in_progress","updatedAt":"2026-10-28T09:05:00Z","needs":[],"required":true},{"key":"go_live","state":"todo","updatedAt":null,"needs":["go_live.confirm"],"required":true}]}},"cappedVerificationNeedsMore":{"summary":"200 — CAPPED round 3, the verification provider needs the representative's full identity number and an owner's date of birth (v1.63.0 PROPOSAL, amended 2026-10-29)","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":null,"moneyModel":"CARD","firstSandboxCallAt":"2026-10-29T09:05:00Z","firstSandboxOrderAt":null,"liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":null,"acceptedAt":null},"flow":"CAPPED","country":"US","registrationGstin":null,"registrationConfirmedAt":"2026-10-29T09:30:00Z","limits":null,"liveAvailable":true,"workEmail":{"email":"arjun@sampletravels.example","verifiedAt":"2026-10-29T09:02:00Z","required":true},"businessVerification":{"status":"needs_more","provider":"stripe_connect","submittedAt":"2026-10-29T09:44:00Z","decidedAt":null,"outstanding":[{"code":"representative.id_number","field":"representative.idNumber","reason":"missing","dueBy":"2026-11-12T00:00:00Z"},{"code":"owners[1].dob","field":"owners[1].dateOfBirth","reason":"missing","dueBy":"2026-11-12T00:00:00Z"}],"rejectionReason":null},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-29T09:00:30Z","needs":[],"required":true},{"key":"business_profile","state":"done","updatedAt":"2026-10-29T09:20:00Z","needs":[],"required":true},{"key":"business_verification","state":"needs_you","updatedAt":"2026-10-29T09:46:00Z","needs":["representative.id_number","owners[1].dob"],"required":true},{"key":"terms","state":"todo","updatedAt":null,"needs":["terms.accept"],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-29T10:10:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"in_progress","updatedAt":"2026-10-29T09:05:00Z","needs":[],"required":true},{"key":"go_live","state":"blocked","updatedAt":null,"needs":["business_verification","terms"],"required":true}]}},"cappedGoLiveChangesRequested":{"summary":"200 — CAPPED go-live review, the operator asked for changes; the Owner fixes them and asks again (v1.63.0 go-live review)","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":null,"moneyModel":"CARD","firstSandboxCallAt":"2026-10-29T09:05:00Z","firstSandboxOrderAt":"2026-10-29T10:35:00Z","liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-29T10:20:00Z"},"flow":"CAPPED","country":"GB","registrationGstin":null,"registrationConfirmedAt":"2026-10-29T09:30:00Z","limits":null,"liveAvailable":true,"workEmail":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-29T09:02:00Z","required":true},"businessVerification":{"status":"verified","provider":"stripe_connect","submittedAt":"2026-10-29T09:44:00Z","decidedAt":"2026-10-29T09:58:00Z","outstanding":[],"rejectionReason":null},"goLiveReview":{"id":"GLR-01JB7Q2M4X","status":"changes_requested","submittedAt":"2026-10-29T11:00:00Z","decidedAt":"2026-10-30T09:15:00Z","expectedBy":"2026-10-30T11:00:00Z","note":"Your website does not show the travel products you sell. Add them, then ask to go live again.","products":[{"product":"FLT","status":"changes_requested","decidedAt":"2026-10-30T09:15:00Z","note":null,"checks":[{"key":"business_details","status":"failed","by":"operator","at":"2026-10-30T09:15:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]},{"product":"HTL","status":"changes_requested","decidedAt":"2026-10-30T09:15:00Z","note":null,"checks":[{"key":"business_details","status":"failed","by":"operator","at":"2026-10-30T09:15:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]}]},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-29T09:00:30Z","needs":[],"required":true},{"key":"business_profile","state":"done","updatedAt":"2026-10-29T09:20:00Z","needs":[],"required":true},{"key":"business_verification","state":"done","updatedAt":"2026-10-29T09:58:00Z","needs":[],"required":true},{"key":"terms","state":"done","updatedAt":"2026-10-29T10:20:00Z","needs":[],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-29T10:10:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"done","updatedAt":"2026-10-29T10:35:00Z","needs":[],"required":true},{"key":"go_live","state":"needs_you","updatedAt":"2026-10-30T09:15:00Z","needs":["go_live.changes_requested"],"required":true}]}},"cappedGoLiveApproved":{"summary":"200 — CAPPED go-live review approved; the tenant is LIVE, tier LIVE_CAPPED (v1.63.0 go-live review)","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":"LIVE_CAPPED","moneyModel":"CARD","firstSandboxCallAt":"2026-10-29T09:05:00Z","firstSandboxOrderAt":"2026-10-29T10:35:00Z","liveAt":"2026-10-30T09:15:00Z","terms":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-29T10:20:00Z"},"flow":"CAPPED","country":"GB","registrationGstin":null,"registrationConfirmedAt":"2026-10-29T09:30:00Z","limits":{"tier":"LIVE_CAPPED","dailyCap":{"amount":500000,"currency":"USD"},"usedToday":{"amount":0,"currency":"USD"},"resetAt":"2026-10-31T00:00:00Z"},"liveAvailable":true,"workEmail":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-29T09:02:00Z","required":true},"businessVerification":{"status":"verified","provider":"stripe_connect","submittedAt":"2026-10-29T09:44:00Z","decidedAt":"2026-10-29T09:58:00Z","outstanding":[],"rejectionReason":null},"goLiveReview":{"id":"GLR-01JB7Q2M4X","status":"approved","submittedAt":"2026-10-29T11:00:00Z","decidedAt":"2026-10-30T09:15:00Z","expectedBy":"2026-10-30T11:00:00Z","note":"Welcome aboard. Your daily limit starts at USD 5,000.","products":[{"product":"FLT","status":"approved","decidedAt":"2026-10-30T09:15:00Z","note":null,"checks":[{"key":"business_details","status":"passed","by":"operator","at":"2026-10-30T09:15:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]},{"product":"HTL","status":"approved","decidedAt":"2026-10-30T09:15:00Z","note":null,"checks":[{"key":"business_details","status":"passed","by":"operator","at":"2026-10-30T09:15:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]}]},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-29T09:00:30Z","needs":[],"required":true},{"key":"business_profile","state":"done","updatedAt":"2026-10-29T09:20:00Z","needs":[],"required":true},{"key":"business_verification","state":"done","updatedAt":"2026-10-29T09:58:00Z","needs":[],"required":true},{"key":"terms","state":"done","updatedAt":"2026-10-29T10:20:00Z","needs":[],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-29T10:10:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"done","updatedAt":"2026-10-29T10:35:00Z","needs":[],"required":true},{"key":"go_live","state":"done","updatedAt":"2026-10-30T09:15:00Z","needs":[],"required":true}]}},"individual":{"summary":"200 — an individual's five-step rail; identity verified, terms still to accept (FEAT-033)","value":{"tenantId":"TEN-EVAL-P4S9WD2M","accountType":"INDIVIDUAL","buildingIntent":null,"tier":null,"moneyModel":null,"firstSandboxCallAt":"2026-10-24T10:02:11Z","firstSandboxOrderAt":"2026-10-24T10:09:40Z","liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":null,"acceptedAt":null},"identityCheck":{"checkId":"idc_01JB2K7Q9M4X","status":"VERIFIED","scheme":"IN_PAN","maskedValue":"ABCPE••••F","nameMatched":true,"failureReason":null,"provider":"harness","checkedAt":"2026-10-24T10:12:03Z"},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-24T10:00:30Z","needs":[]},{"key":"sandbox_booking","state":"done","updatedAt":"2026-10-24T10:09:40Z","needs":[]},{"key":"identity_verification","state":"done","updatedAt":"2026-10-24T10:12:03Z","needs":[]},{"key":"terms","state":"todo","updatedAt":null,"needs":["terms.accept"]},{"key":"go_live","state":"blocked","updatedAt":null,"needs":["terms"]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/activation/profile":{"patch":{"operationId":"updateActivationProfile","x-mcp":{"expose":"excluded","toolset":"public","name":"update_activation_profile","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Tell us about the business — rail step 3 (FEAT-030 §5.3, §7)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-2, published in 1.18.0). Partial update of the tenant's business profile: a member that is absent is unchanged. Step business_profile is done once businessType, country, website and volumeBand are all set. gstin and pan are accepted only when country is IN (400 VALIDATION details.reason INDIA_ONLY_FIELD otherwise); they feed FEAT-026 tax setup so they are asked once. Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs users:manage. Idempotency-Key required. Audited. connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b, OD-ENR-30): buildingIntent may be set here after sign-up (the Welcome screen's question); it reads back as ActivationState.buildingIntent and changes no step. Once a GST registration is confirmed (ActivationState.registrationConfirmedAt set), a body naming a gstin or pan different from the confirmed one is 409 CONFLICT details.reason REGISTRATION_LOCKED (details.field names it) and nothing is changed: a confirmed registration changes only through POST /v1/activation/business-registration:confirm. Sending the confirmed values again is a no-op for them. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): the profile also takes businessStructure (the country's legal form label, a sole proprietorship being one of them), industry (with industryOther when it is OTHER), website or noWebsite true with productDescription (10 to 500 characters, required when noWebsite is true: 400 VALIDATION details.field productDescription otherwise), phone (E.164) and monthlyVolume (a band in a currency; the bands per currency are on MonthlyVolume). On the CAPPED flow step business_profile is done, and complete is true, when country, businessStructure, industry, website or noWebsite with productDescription, phone and monthlyVolume are set; businessType and volumeBand stay for the NINE_STEP rail and are not read on CAPPED. industryOther without industry OTHER, or website together with noWebsite true, is 400 VALIDATION with details.field naming the member. The website also sets the domain a work e-mail must be on (POST /v1/activation/work-email:send-code). With the capped flow off the new members are 400 VALIDATION details.field naming the member.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessProfileUpdate"},"examples":{"india":{"summary":"An Indian travel agency with its GSTIN","value":{"businessType":"TRAVEL_AGENCY","country":"IN","website":"https://acme-travel.example","volumeBand":"FROM_10K_TO_100K","gstin":"27AAPFU0939F1ZV"}},"buildingIntentOnly":{"summary":"The \"What are you building?\" answer, given on the Welcome screen (v1.51.0 PROPOSAL)","value":{"buildingIntent":"BOOKING_SITE"}},"companyRound2":{"summary":"A UK private limited company with a website (v1.63.0 PROPOSAL)","value":{"country":"GB","businessStructure":"Private limited company","industry":"TRAVEL_AGENCY","website":"https://sampletravels.co.uk","phone":"+442071234567","monthlyVolume":{"band":"BAND_2","currency":"GBP"}}},"soleProprietorNoWebsite":{"summary":"An Indian sole proprietorship with no website (v1.63.0 PROPOSAL)","value":{"country":"IN","businessStructure":"Sole proprietorship","industry":"INDEPENDENT_ADVISOR","noWebsite":true,"productDescription":"Holiday packages and flight bookings for families in Pune, sold over phone and chat.","phone":"+919876543210","monthlyVolume":{"band":"BAND_1","currency":"INR"}}}}}}},"responses":{"200":{"description":"The profile as stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessProfile"},"examples":{"ok":{"summary":"200 — profile complete; step business_profile done","value":{"businessType":"TRAVEL_AGENCY","country":"IN","website":"https://acme-travel.example","volumeBand":"FROM_10K_TO_100K","gstin":"27AAPFU0939F1ZV","pan":null,"complete":true,"updatedAt":"2026-10-09T09:31:00Z"}},"round2":{"summary":"200 — the round-2 profile set; step business_profile done on CAPPED (v1.63.0 PROPOSAL)","value":{"businessType":null,"country":"GB","website":"https://sampletravels.co.uk","volumeBand":null,"gstin":null,"pan":null,"businessStructure":"Private limited company","industry":"TRAVEL_AGENCY","industryOther":null,"noWebsite":false,"productDescription":null,"phone":"+442071234567","monthlyVolume":{"band":"BAND_2","currency":"GBP"},"complete":true,"updatedAt":"2026-10-28T09:20:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT REGISTRATION_LOCKED (connect-v1 1.51.0, PROPOSAL) — a gstin or pan different from the confirmed GST registration; or IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}},"registrationLocked":{"summary":"409 CONFLICT REGISTRATION_LOCKED — the GSTIN or PAN differs from the confirmed GST registration (v1.51.0 PROPOSAL)","value":{"code":"CONFLICT","message":"A confirmed registration changes only through POST /v1/activation/business-registration:confirm.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_LOCKED","field":"gstin"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/activation/verification-sessions":{"post":{"operationId":"startVerificationSession","x-mcp":{"expose":"excluded","toolset":"public","name":"start_verification_session","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Start or resume the business verification (KYB) session — rail step 4 (FEAT-030 §5.4)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-5, published in 1.18.0). The provider is per deployment, aikonic.kyb.provider: unset by default, so the route answers 501 NOT_IMPLEMENTED details.reason KYB_UNAVAILABLE; harness (the mock harness's fake provider, refused at boot above SANDBOX) is set on DEV and SANDBOX; the real provider waits on OD-ENR-2. Opens the provider's hosted session, or resumes the open one (same providerRef), and answers where to send the user: hostedUrl (a redirect) or embedToken (the provider's embedded component), with the status and the provider's outstanding list. Apiq prefills what it knows (legal name guess, domain, country) and stores only the provider reference, outcome, risk tier and timestamps, never a document. A tenant already verified is 409 CONFLICT details.reason ALREADY_VERIFIED. When the provider cannot be reached the answer is 502 UPSTREAM_UNAVAILABLE (retryable) and the rail shows step business_verification blocked; a deployment with no provider configured is 501 NOT_IMPLEMENTED details.reason KYB_UNAVAILABLE. Human session only; the caller must be an Owner (role OWNER or CUSTOMER_ADMIN, as OD-870), otherwise 403 PERMISSION_DENIED details.reason OWNER_ONLY. Idempotency-Key required. Audited.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationSessionRequest"},"examples":{"request":{"summary":"Come back to the activation page when done","value":{"returnUrl":"https://console.aikonic.example/console/activate"}}}}}},"responses":{"200":{"description":"The session to send the user to (opened or resumed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationSession"},"examples":{"hosted":{"summary":"200 — a hosted session, nothing outstanding yet","value":{"provider":"harness","providerRef":"kyb_hrn_01JA9Q7","status":"in_progress","hostedUrl":"https://harness.sandbox.aikonic.example/kyb/kyb_hrn_01JA9Q7","embedToken":null,"expiresAt":"2026-10-09T11:00:00Z","outstanding":[],"riskTier":null,"decidedAt":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT ALREADY_VERIFIED — the business is verified; nothing to resume","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"alreadyVerified":{"summary":"409 CONFLICT ALREADY_VERIFIED","value":{"code":"CONFLICT","message":"The business is already verified.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ALREADY_VERIFIED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED KYB_UNAVAILABLE — no verification provider is configured on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"kybUnavailable":{"summary":"501 NOT_IMPLEMENTED KYB_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Business verification is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"KYB_UNAVAILABLE"}}}}}}},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/activation/identity-checks":{"post":{"operationId":"startIdentityCheck","x-mcp":{"expose":"excluded","toolset":"public","name":"start_identity_check","class":"commit-lite","autonomy":"L1","reason":"A person enters their own identity number; never an agent. Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED (DR-39)."},"tags":["identity"],"summary":"Check the person's identity (India PAN) — the individual rail's identity_verification step (FEAT-033)","description":"connect-v1 1.47.0 (FEAT-033, OD-906 proposed). Checks one identity number against the identity provider and answers the result at once (KYC-lite: no document, no image, no upload). The only scheme is IN_PAN: taxId.value must match ^[A-Z]{5}[0-9]{4}[A-Z]$ (400 VALIDATION details.reason TAX_ID_FORMAT otherwise; the server checks it, not a schema pattern, so other schemes can be added), and for an INDIVIDUAL account the fourth character must be P, a personal PAN (400 VALIDATION details.reason PERSONAL_PAN_REQUIRED). Another scheme is 400 VALIDATION details.reason UNSUPPORTED_SCHEME. fullName is the name exactly as on the PAN card; the provider compares it (nameMatched). A check the provider answers is a 200 whatever its outcome: VERIFIED ticks rail step identity_verification; FAILED (failureReason NAME_MISMATCH, NOT_FOUND or INACTIVE) leaves the step needs_you and the person may try again; PENDING means the provider answers later and GET /v1/activation shows the outcome. The latest check also reads back as ActivationState.identityCheck. The full number is never stored, logged or returned: the platform keeps the masked value (first five and last character), a keyed hash of the number (to recognise a repeat), the result, the name and the time — no document and no image. A tenant whose latest check is VERIFIED is 409 CONFLICT details.reason ALREADY_VERIFIED. The provider is per deployment, aikonic.identity-check.provider: unset by default, so the route answers 501 NOT_IMPLEMENTED details.reason IDENTITY_CHECK_UNAVAILABLE; harness (the mock harness's deterministic provider, refused at boot above SANDBOX, as the KYB harness) is set on DEV and SANDBOX; a real provider waits on OD-906. When the provider cannot be reached the answer is 502 UPSTREAM_UNAVAILABLE (retryable). Human session only; the caller must be an Owner (role OWNER or CUSTOMER_ADMIN, as OD-870), otherwise 403 PERMISSION_DENIED details.reason OWNER_ONLY. Idempotency-Key required: a replay answers the first check, the number is not sent to the provider twice. Audited, with the masked value only.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityCheckRequest"},"examples":{"inPan":{"summary":"A personal PAN and the name as printed on the card","value":{"taxId":{"scheme":"IN_PAN","value":"ABCPE1234F"},"fullName":"Priya Sharma"}}}}}},"responses":{"200":{"description":"The check as the provider answered it (VERIFIED, FAILED or PENDING)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityCheck"},"examples":{"verified":{"summary":"200 — VERIFIED; step identity_verification done","value":{"checkId":"idc_01JB2K7Q9M4X","status":"VERIFIED","scheme":"IN_PAN","maskedValue":"ABCPE••••F","nameMatched":true,"failureReason":null,"provider":"harness","checkedAt":"2026-10-24T10:12:03Z"}},"nameMismatch":{"summary":"200 — FAILED NAME_MISMATCH; the step stays needs_you and the person may try again","value":{"checkId":"idc_01JB2K5R2D8W","status":"FAILED","scheme":"IN_PAN","maskedValue":"ABCPE••••F","nameMatched":false,"failureReason":"NAME_MISMATCH","provider":"harness","checkedAt":"2026-10-24T10:10:41Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT ALREADY_VERIFIED — the person's identity is verified; nothing to check","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"alreadyVerified":{"summary":"409 CONFLICT ALREADY_VERIFIED","value":{"code":"CONFLICT","message":"The identity is already verified.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ALREADY_VERIFIED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED IDENTITY_CHECK_UNAVAILABLE — no identity provider is configured on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"identityCheckUnavailable":{"summary":"501 NOT_IMPLEMENTED IDENTITY_CHECK_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Identity checks are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"IDENTITY_CHECK_UNAVAILABLE"}}}}}}},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/activation/money-model":{"post":{"operationId":"electMoneyModel","x-mcp":{"expose":"excluded","toolset":"public","name":"elect_money_model","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Choose how the tenant pays suppliers — rail step 5 (FEAT-030 §5.3, 08 §7)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-2, published in 1.18.0). Elects WALLET (prepaid through Apiq's own credential sets; currency names the wallet's settlement currency and is required), OWN_ACCREDITATION (own IATA / ARC, BSP or ARC cash; the accreditation check routes to ops) or BYOC (bring your own credentials, 08 §7; routes to ops). A TRIAL tenant may elect again and the last election wins; once the tenant is LIVE a change is 409 CONFLICT details.reason MONEY_MODEL_LOCKED (an ops change). Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required. Audited. connect-v1 1.51.0 (PROPOSAL; served when aikonic.payment-methods.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-25): CARD, pay each booking with a saved card or UPI method. currency is not read. Step money_model is done once the tenant has a saved method (POST /v1/payments/methods), needs_you (needs[] payment_method) until then; orders then pay with payment.mode SAVED_METHOD. Where the flag is off CARD is 400 VALIDATION details.field model. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2, OD-ENR-34): OWN_ACCREDITATION takes accreditationNumber (the IATA, ARC or TIDS number, at most 20 characters) and BYOC takes contractReference (the supplier contract or account reference, at most 200 characters); on the CAPPED flow each is required for its model (400 VALIDATION details.field naming it) and the election opens an operator approval of kind MONEY_MODEL_REVIEW (OpsApproval; scope.tenantId the tenant, effect the model and the reference). Step money_model then reads in_review and MoneyModelElection.reviewState PENDING until an operator decides: APPROVED completes the step, REJECTED returns it to needs_you (elect again). Each field is ignored for the other models; on NINE_STEP it is recorded for the ops check that already routes there. On CAPPED, WALLET is done with an elected currency and a wallet balance above zero (fund_wallet is not listed on CAPPED) and CARD with a saved method.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoneyModelElectionRequest"},"examples":{"wallet":{"summary":"Prepaid wallet in USD","value":{"model":"WALLET","currency":"USD"}},"card":{"summary":"Pay each booking with a saved card or UPI method (v1.51.0 PROPOSAL)","value":{"model":"CARD"}},"ownAccreditation":{"summary":"Own IATA accreditation, its number for the operator review (v1.63.0 PROPOSAL)","value":{"model":"OWN_ACCREDITATION","accreditationNumber":"14312345"}},"byoc":{"summary":"Bring your own supplier contract, its reference for the operator review (v1.63.0 PROPOSAL)","value":{"model":"BYOC","contractReference":"SAMPLE-AIRLINE-NDC-2026-0042"}}}}}},"responses":{"200":{"description":"The election as recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoneyModelElection"},"examples":{"ok":{"summary":"200 — WALLET elected; step money_model done","value":{"model":"WALLET","currency":"USD","electedAt":"2026-10-09T09:25:00Z","electedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"card":{"summary":"200 — CARD elected; step money_model done once a method is saved (v1.51.0 PROPOSAL)","value":{"model":"CARD","currency":null,"electedAt":"2026-10-26T09:18:00Z","electedBy":"7c2d9e14-5b8a-4f31-9d6e-2a1f0b3c4d5e"}},"ownAccreditationInReview":{"summary":"200 — OWN_ACCREDITATION elected on CAPPED; the operator review is PENDING (v1.63.0 PROPOSAL)","value":{"model":"OWN_ACCREDITATION","currency":null,"electedAt":"2026-10-28T10:05:00Z","electedBy":"7c2d9e14-5b8a-4f31-9d6e-2a1f0b3c4d5e","reviewState":"PENDING"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT MONEY_MODEL_LOCKED — the tenant is LIVE; ask ops to change the money model","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"moneyModelLocked":{"summary":"409 CONFLICT MONEY_MODEL_LOCKED","value":{"code":"CONFLICT","message":"The money model of a live tenant is changed by operations, not here.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MONEY_MODEL_LOCKED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/activation/terms:accept":{"post":{"operationId":"acceptActivationTerms","x-mcp":{"expose":"excluded","toolset":"public","name":"accept_activation_terms","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Accept the current terms version — rail step 8 (FEAT-030 §5.3)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-2, published in 1.18.0). termsVersion must equal GET /v1/activation terms.currentVersion; an older one is 409 CONFLICT details.reason TERMS_VERSION_STALE with details.currentVersion. Accepting the version already accepted is a 200 with the original acceptance. Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY) and step-up guarded with the body step-up pair of DR-53 / DR-54: exactly one of stepUpCode (a current code from the caller's MFA factor) or stepUpAssertion (a WebAuthn passkey assertion). A missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, STEP_UP_LOCKED when the user is locked out, and STEP_UP_UNAVAILABLE [assumption] when the caller has no MFA factor to step up with (enrol one at /v1/auth/mfa; on the CAPPED flow, confirm with an e-mailed code instead, below). Idempotency-Key required. Audited (activation.terms_accepted). connect-v1 1.52.0 (PROPOSAL; CAPPED flow only, aikonic.activation.capped-flow.enabled; FEAT-030 §18b, OD-ENR-3): a third proof, emailCode — the 6-digit code POST /v1/activation/terms:send-code e-mailed to the signed-in Owner — so an Owner with no authenticator (every new sign-up) can accept. The body carries exactly one of stepUpCode, stepUpAssertion or emailCode; none or more than one is 403 STEP_UP_FAILED. A wrong, expired, voided (a newer code was sent) or never-sent emailCode is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED; the 5th wrong attempt spends the code and further attempts are STEP_UP_LOCKED until a new code is sent (the code lives 15 minutes, as the sign-up code). A code is spent by a successful acceptance. On the NINE_STEP flow emailCode is 400 VALIDATION details.field emailCode, details.reason FLOW_NOT_CAPPED (step up with stepUpCode or stepUpAssertion there); an emailCode that is not six digits is 400 VALIDATION details.field emailCode. On CAPPED, STEP_UP_UNAVAILABLE answers only a stepUpCode or stepUpAssertion from a caller with no factor; its message points at terms:send-code. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2, OD-ENR-3 owner direction 2026-10-28): on the CAPPED flow the acceptance is a click-wrap — the body may carry termsVersion and no proof at all (no stepUpCode, stepUpAssertion or emailCode); the Owner's human session is the proof (the e-mail was confirmed at sign-up), and the console sends termsVersion only, once the Owner has ticked \"I agree\". Every other rule stands: human session only, Owner only (OWNER_ONLY), termsVersion must be the current one (TERMS_VERSION_STALE), the version already accepted answers the original acceptance, Idempotency-Key required, audited. A proof that is sent anyway on CAPPED is still judged as above (exactly one, a wrong one 403 STEP_UP_FAILED), so a client of 1.52.0 keeps working. The NINE_STEP flow is unchanged: no proof there is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED (or STEP_UP_UNAVAILABLE for a caller with no factor). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version, OD-ENR-37): where the tenant's country is routed to the hosted onboarding provider (ActivationState.businessVerification.provider stripe_connect), the terms the Owner accepts include that provider's connected-account agreement, shown by the console as a fourth document beside the platform's own; the acceptance is forwarded to the tenant's connected account as its terms acceptance (the date, the caller's IP address and user agent), at once when the account exists and otherwise when the business verification opens it. Same request and answer; termsVersion still names the platform's version.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermsAcceptRequest"},"examples":{"code":{"summary":"Accept with an authenticator code","value":{"termsVersion":"2026-10","stepUpCode":"000000"}},"emailCode":{"summary":"Accept with the code e-mailed by POST /v1/activation/terms:send-code (CAPPED flow, v1.52.0)","value":{"termsVersion":"2026-10","emailCode":"482913"}},"cappedClickWrap":{"summary":"Accept on the CAPPED flow with no proof — the Owner ticked \"I agree\" (v1.63.0 PROPOSAL)","value":{"termsVersion":"2026-10"}}}}}},"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermsAcceptance"},"examples":{"ok":{"summary":"200 — terms accepted; step terms done","value":{"termsVersion":"2026-10","acceptedAt":"2026-10-09T10:30:00Z","acceptedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"description":"VALIDATION (03 §9), including, on connect-v1 1.52.0, emailCode on the NINE_STEP flow (details.field emailCode, details.reason FLOW_NOT_CAPPED) or an emailCode that is not six digits (details.field emailCode)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — intent schema violation (03 §2.4, §9)","value":{"code":"VALIDATION","message":"message.intent.checkOut must be after message.intent.checkIn","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.checkOut","constraint":"after:checkIn"}}},"unissuedCursor":{"summary":"400 VALIDATION — a cursor GET /v1/orders/{orderRef}/events never issued (OD-P9-01)","value":{"code":"VALIDATION","message":"cursor is not one this route issued","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"cursor"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry; accepted list in details (03 §9)","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'HOTELS'. Did you mean HTL?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["HTL","VR","DAYUSE","FLT","CRU"]}}},"testHeaderNotAllowed":{"summary":"400 VALIDATION TEST_HEADER_NOT_ALLOWED — the Test header from a key or rung that may not simulate (BE-7)","value":{"code":"VALIDATION","message":"The Test header is accepted only from a sandbox (mo_test_) API key where simulation is on.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"Test","reason":"TEST_HEADER_NOT_ALLOWED"}}},"unknownTestScenario":{"summary":"400 VALIDATION UNKNOWN_TEST_SCENARIO — a Test value outside the list (BE-7)","value":{"code":"VALIDATION","message":"Unknown Test scenario.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"Test","reason":"UNKNOWN_TEST_SCENARIO","accepted":["price_changed","sold_out","ambiguous","hold_expired","payment_declined","supplier_timeout","partial_search","duplicate_order"]}}},"checkoutSignatureUnsupported":{"summary":"400 VALIDATION CHECKOUT_SIGNATURE_UNSUPPORTED — the provider has no checkout signature (BE-7)","value":{"code":"VALIDATION","message":"checkoutSignature is not supported by this payment's provider.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"checkoutSignature","reason":"CHECKOUT_SIGNATURE_UNSUPPORTED"}}},"paymentMethodRequired":{"summary":"400 VALIDATION PAYMENT_METHOD_REQUIRED — a card-secured dining slot booked or amended without a payment-method token (v1.7.0)","value":{"code":"VALIDATION","message":"The new slot is card-secured: send message.intent.payment.paymentMethod (a Spreedly token).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentMethod","reason":"PAYMENT_METHOD_REQUIRED","supplierCode":"EXAMPLEDINING_CARD_REQUIRED"}}},"onboardingStateInvalid":{"summary":"400 VALIDATION ONBOARDING_STATE_INVALID — the onboarding callback without the state the platform issued (v1.7.0)","value":{"code":"VALIDATION","message":"The onboarding state is missing, expired, already used or does not match this onboarding.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"session","reason":"ONBOARDING_STATE_INVALID"}}},"emailCodeNotCapped":{"summary":"400 VALIDATION FLOW_NOT_CAPPED — emailCode on the nine-step flow (v1.52.0)","value":{"code":"VALIDATION","message":"An e-mailed code is accepted on the capped activation only. Use your authenticator code or passkey.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"emailCode","reason":"FLOW_NOT_CAPPED"}}},"emailCodeFormat":{"summary":"400 VALIDATION — emailCode is not six digits (v1.52.0)","value":{"code":"VALIDATION","message":"emailCode must be the six digits from the e-mail.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"emailCode"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT TERMS_VERSION_STALE — a newer terms version is current","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"termsVersionStale":{"summary":"409 CONFLICT TERMS_VERSION_STALE","value":{"code":"CONFLICT","message":"Terms version 2026-09 is no longer current; read and accept 2026-10.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TERMS_VERSION_STALE","currentVersion":"2026-10"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/activation/terms:send-code":{"post":{"operationId":"sendActivationTermsCode","x-mcp":{"expose":"excluded","toolset":"public","name":"send_activation_terms_code","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"E-mail the signed-in Owner a code to accept the terms with — CAPPED flow only (FEAT-030 §18b)","description":"connect-v1 1.52.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-3). Sends a 6-digit code to the signed-in Owner's own e-mail address (the address they signed in with; never one taken from the request) so the Owner can accept the terms without an authenticator app: POST /v1/activation/terms:accept with emailCode. A new sign-up has no second factor, so on the CAPPED flow this is the step-up of the terms step. The code lives 15 minutes and allows at most 5 attempts, as the sign-up code; sending a new code voids the previous one. sentTo is the address masked (the first two characters of the local part, or the first one when it is shorter than three, then *** and the domain); expiresAt is when the code stops working; resendAfter is the earliest time another code may be sent (60 seconds after this one [assumption]). Served on the CAPPED flow only: a tenant whose GET /v1/activation flow is NINE_STEP is 409 CONFLICT details.reason FLOW_NOT_CAPPED (step up with stepUpCode or stepUpAssertion there). Rate limited per Owner as the identity code routes are (IdentityRateLimited): a send before resendAfter, or more than 5 sends in a 15-minute window [assumption], is 429 RATE_LIMITED details.reason SUBJECT_RATE_LIMITED, details.action terms-code-send, with Retry-After and details.retryAfterMs; nothing is sent. No request body (an empty JSON object is accepted and ignored). Human session only (an API key or a service token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY; role OWNER or CUSTOMER_ADMIN as OD-870, the terms acceptance rule). Idempotency-Key required: a replay answers the first 202 and sends nothing. Identity mail goes through SMTP (OD-ENR-7); a deployment without a sender is 501 NOT_IMPLEMENTED details.reason NO_VERIFICATION_SENDER. The code is never logged, audited or returned. Audited (activation.terms_code_sent, with sentTo masked). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): no longer used by the console on the CAPPED flow — the terms are accepted there with termsVersion only (click-wrap, POST /v1/activation/terms:accept); kept for compatibility, and an emailCode it sent is still judged by terms:accept.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"202":{"description":"Accepted — a code is on its way to the signed-in Owner's e-mail address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermsCodeSent"},"examples":{"sent":{"summary":"202 — a code sent to the Owner's address (masked), valid 15 minutes","value":{"sentTo":"ar***@sampletravels.in","expiresAt":"2026-10-26T09:45:00Z","resendAfter":"2026-10-26T09:31:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT FLOW_NOT_CAPPED — the tenant is on the nine-step rail; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"flowNotCapped":{"summary":"409 CONFLICT FLOW_NOT_CAPPED — e-mailed terms codes are for the CAPPED flow only","value":{"code":"CONFLICT","message":"This tenant is on the nine-step activation. Accept the terms with your authenticator code or passkey.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FLOW_NOT_CAPPED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"description":"RATE_LIMITED SUBJECT_RATE_LIMITED (action terms-code-send) — too soon after the last code, or too many codes","headers":{"Retry-After":{"description":"Whole seconds until another code may be sent; mirrored in details.retryAfterMs","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"resendTooSoon":{"summary":"429 RATE_LIMITED SUBJECT_RATE_LIMITED — a code was sent less than a minute ago","value":{"code":"RATE_LIMITED","message":"A code was sent a moment ago. Wait for the Retry-After interval before asking for another.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"SUBJECT_RATE_LIMITED","action":"terms-code-send","retryAfterMs":42000}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED — CAPPED_FLOW_UNAVAILABLE (aikonic.activation.capped-flow.enabled is off) or NO_VERIFICATION_SENDER (no identity mail sender is deployed, OD-ENR-16). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}},"noVerificationSender":{"summary":"501 NOT_IMPLEMENTED NO_VERIFICATION_SENDER — no identity mail sender on this deployment","value":{"code":"NOT_IMPLEMENTED","message":"This deployment cannot send e-mail codes. Accept the terms with an authenticator code or passkey.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NO_VERIFICATION_SENDER"}}}}}}}}}},"/v1/activation/business-registrations/{gstin}":{"get":{"operationId":"lookUpBusinessRegistration","x-mcp":{"expose":"excluded","toolset":"public","name":"look_up_business_registration","class":"read","autonomy":"L0","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Look a GSTIN up in the GST register — proposed rail step business_verification (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX — with a GST register provider configured, aikonic.gst-register.provider; FEAT-030 §18b, OD-ENR-24). Reads the registration a GSTIN names from the GST register and answers it for the person to confirm: legal and trade name, constitution, status, registration date, the company PAN and the principal place of business. Nothing is stored by the look-up; POST /v1/activation/business-registration:confirm stores it. India only: a tenant whose country (SignupVerifyRequest.country or BusinessProfile.country) is not IN, or not yet set, is 400 VALIDATION details.reason INDIA_ONLY_FIELD; a GSTIN that does not match the pattern is 400 VALIDATION details.field gstin. A well-formed GSTIN the register does not know is 404 NOT_FOUND details.reason GSTIN_NOT_FOUND. A CANCELLED or SUSPENDED registration is answered (200) so the screen can say why it cannot be used; confirming it is refused. status and constitution are x-extensible-enum. The register provider is per deployment: harness (the mock harness's fake register, refused at boot above SANDBOX) on DEV and SANDBOX; the real provider waits on OD-ENR-24. The capped flow off, or no provider configured, is 501 NOT_IMPLEMENTED (details.reason CAPPED_FLOW_UNAVAILABLE or GST_REGISTER_UNAVAILABLE); a provider that cannot be reached is 502 UPSTREAM_UNAVAILABLE (retryable). Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs users:manage, as the profile it fills.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"name":"gstin","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{2}[A-Z0-9]{13}$"},"description":"India GSTIN, 15 characters, upper case"}],"responses":{"200":{"description":"The registration as the GST register states it","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessRegistration"},"examples":{"active":{"summary":"200 — an ACTIVE registration; confirm it to fill the business profile","value":{"gstin":"29AABCS1234F1Z5","legalName":"SAMPLE TRAVELS PRIVATE LIMITED","tradeName":"Sample Travels","constitution":"PRIVATE_LIMITED_COMPANY","status":"ACTIVE","registeredOn":"2019-03-14","pan":"AABCS1234F","address":{"line1":"4th Floor, 12 Residency Road","city":"Bengaluru","region":"Karnataka","postalCode":"560025","country":"IN"}}},"cancelled":{"summary":"200 — a CANCELLED registration; shown, but confirming it is 409 GSTIN_NOT_ACTIVE","value":{"gstin":"29AABCS0000F1Z5","legalName":"SAMPLE TRAVELS PRIVATE LIMITED","tradeName":"Sample Travels","constitution":"PRIVATE_LIMITED_COMPANY","status":"CANCELLED","registeredOn":"2019-03-14","pan":"AABCS0000F","address":{"line1":"4th Floor, 12 Residency Road","city":"Bengaluru","region":"Karnataka","postalCode":"560025","country":"IN"}}}}}}},"400":{"$ref":"#/components/responses/GstinValidation"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/GstinNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/GstRegisterUnavailable"},"502":{"$ref":"#/components/responses/GstRegisterUnreachable"}}}},"/v1/activation/business-registrations:look-up":{"post":{"operationId":"lookUpBusinessRegistrationByNumber","x-mcp":{"expose":"excluded","toolset":"public","name":"look_up_business_registration_by_number","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b). Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Look a business registration up by country and number — GSTIN, company number, trade licence, EIN, BN (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX — with a register provider configured for the country; FEAT-030 §18b round 2, OD-ENR-31). Reads the registration a number names from the country's register and answers it for the person to confirm (POST /v1/activation/business-registration:confirm with scheme and number); nothing is stored. A read sent as POST so the number and the legal name stay out of the URL and the access logs. country picks the scheme: IN IN_GSTIN (the GST register, as GET /v1/activation/business-registrations/{gstin}, which stays), GB GB_COMPANY_NUMBER (the companies register), AE AE_TRADE_LICENCE (the licensing authority's register; jurisdiction names the emirate or free zone), US US_EIN (the IRS name and EIN match: legalName is required, 400 VALIDATION details.field legalName otherwise, and status reads ACTIVE when the name matches the EIN, else NOT_MATCHED; the IRS states no address and no registration date) and CA CA_BUSINESS_NUMBER (the federal business registry). The answer carries scheme, number and jurisdiction; gstin and pan are set on the India path only, and constitution, registeredOn and address only where the register states them. status is x-extensible-enum (ACTIVE, CANCELLED, SUSPENDED, DISSOLVED, EXPIRED, NOT_MATCHED): a registration that is not ACTIVE is answered (200) so the screen can say why, and confirming it is refused. country must be one of IN, GB, AE, US or CA and equal the tenant's country (ActivationState.country), else 400 VALIDATION details.field country; a number that does not match the scheme's format is 400 VALIDATION details.field number. A well-formed number the register does not know is 404 NOT_FOUND details.reason REGISTRATION_NOT_FOUND (an India GSTIN keeps GSTIN_NOT_FOUND). The capped flow off is 501 NOT_IMPLEMENTED details.reason CAPPED_FLOW_UNAVAILABLE; no register provider for the country is 501 details.reason REGISTER_UNAVAILABLE (GST_REGISTER_UNAVAILABLE for IN); a provider that cannot be reached is 502 UPSTREAM_UNAVAILABLE (retryable). The provider is per deployment and country (aikonic.business-register.provider.<country> [assumption]; India keeps aikonic.gst-register.provider): harness (the mock harness's fake registers, refused at boot above SANDBOX) on DEV and SANDBOX; the real providers wait on OD-ENR-31. Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs users:manage, as the GSTIN look-up.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessRegistrationLookup"},"examples":{"gbCompany":{"summary":"A UK company number","value":{"country":"GB","number":"09876543"}},"usEin":{"summary":"A US EIN with the legal name the IRS matches it against","value":{"country":"US","number":"12-3456789","legalName":"SAMPLE TRAVEL LLC"}}}}}},"responses":{"200":{"description":"The registration as the register states it","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessRegistration"},"examples":{"gbActive":{"summary":"200 — an ACTIVE UK company; confirm it with scheme and number","value":{"scheme":"GB_COMPANY_NUMBER","number":"09876543","jurisdiction":"England and Wales","legalName":"SAMPLE TRAVELS LTD","tradeName":null,"constitution":"PRIVATE_LIMITED_COMPANY","status":"ACTIVE","registeredOn":"2015-11-02","address":{"line1":"20 Sample Street","city":"London","region":"Greater London","postalCode":"EC1A 1AA","country":"GB"}}},"usMatched":{"summary":"200 — a US EIN whose IRS name matches; no address or date is stated","value":{"scheme":"US_EIN","number":"12-3456789","jurisdiction":null,"legalName":"SAMPLE TRAVEL LLC","status":"ACTIVE"}},"usNotMatched":{"summary":"200 — NOT_MATCHED; the IRS name for this EIN is not the name sent","value":{"scheme":"US_EIN","number":"12-3456789","jurisdiction":null,"legalName":"SAMPLE TOURS INC","status":"NOT_MATCHED"}},"aeExpired":{"summary":"200 — an EXPIRED UAE trade licence; shown, but confirming it is 409","value":{"scheme":"AE_TRADE_LICENCE","number":"1234567","jurisdiction":"Dubai","legalName":"SAMPLE TRAVEL L.L.C","tradeName":"Sample Travel","constitution":"OTHER","status":"EXPIRED","registeredOn":"2018-02-11"}}}}}},"400":{"$ref":"#/components/responses/BusinessRegistrationValidation"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/BusinessRegistrationNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/BusinessRegisterUnavailable"},"502":{"$ref":"#/components/responses/BusinessRegisterUnreachable"}}}},"/v1/activation/business-registration:confirm":{"post":{"operationId":"confirmBusinessRegistration","x-mcp":{"expose":"excluded","toolset":"public","name":"confirm_business_registration","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Confirm the looked-up registration; it fills the business profile (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-24). Re-reads the GSTIN from the register (never trusts a client copy) and, when its status is ACTIVE, stores it on the business profile: legalName, address, gstin and pan (the company PAN), with country IN and gstinConfirmedAt. Under the CAPPED flow this completes rail step business_profile (BusinessProfile.complete true) and opens the signatory check (POST /v1/activation/signatory-checks), which completes step business_verification. A registration that is not ACTIVE is 409 CONFLICT details.reason GSTIN_NOT_ACTIVE (details.status names it) and nothing is stored. A GSTIN already confirmed by ANOTHER tenant is 409 CONFLICT details.reason GSTIN_ALREADY_CLAIMED and nothing is stored; that business verifies through the hosted KYB session (POST /v1/activation/verification-sessions). Confirming the GSTIN already confirmed is a 200 with the profile as stored. Confirming a different GSTIN replaces the confirmed one: before the tenant is LIVE only an Owner may do it (403 PERMISSION_DENIED details.reason OWNER_ONLY otherwise) and it clears the signatory check, so step business_verification needs the check again; once the tenant is LIVE it is 409 CONFLICT details.reason REGISTRATION_LOCKED (an ops change). Errors as the look-up (400 INDIA_ONLY_FIELD, 404 GSTIN_NOT_FOUND, 501, 502). Human session only; needs users:manage (the Owner to replace). Idempotency-Key required. Audited (activation.registration_confirmed). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): the body carries exactly one of gstin (the India path above) or scheme with number (a registration POST /v1/activation/business-registrations:look-up answered: IN_GSTIN, GB_COMPANY_NUMBER, AE_TRADE_LICENCE, US_EIN or CA_BUSINESS_NUMBER; for US_EIN also legalName, as the look-up); neither, both, or a scheme without a number is 400 VALIDATION details.field gstin (the backend enforces it, no oneOf). The register is re-read and the confirmed registration is stored on the profile (legalName, address when the register states it, registrationScheme, registrationNumber, and businessStructure from the register's legal form when the profile has none); ActivationState.registrationConfirmedAt is set for every scheme, registrationGstin and gstinConfirmedAt for IN_GSTIN only. A status other than ACTIVE is 409 CONFLICT details.reason GSTIN_NOT_ACTIVE and a number another tenant confirmed is 409 GSTIN_ALREADY_CLAIMED, for every scheme (the 1.51.0 reasons are kept so a client branches once; details.status names the status). An unknown number is 404 REGISTRATION_NOT_FOUND; no provider for the country is 501 REGISTER_UNAVAILABLE. On the CAPPED rail of 1.63.0 the confirm no longer completes business_profile by itself (GET /v1/activation): it completes the registration part of business_verification.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessRegistrationConfirmRequest"},"examples":{"confirm":{"summary":"Confirm the registration the look-up answered","value":{"gstin":"29AABCS1234F1Z5"}},"byNumber":{"summary":"Confirm a UK company the look-up by number answered (v1.63.0 PROPOSAL)","value":{"scheme":"GB_COMPANY_NUMBER","number":"09876543"}}}}}},"responses":{"200":{"description":"The business profile as stored, filled from the registration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessProfile"},"examples":{"confirmed":{"summary":"200 — registration confirmed; step business_profile done","value":{"businessType":null,"country":"IN","website":null,"volumeBand":null,"gstin":"29AABCS1234F1Z5","pan":"AABCS1234F","legalName":"SAMPLE TRAVELS PRIVATE LIMITED","address":{"line1":"4th Floor, 12 Residency Road","city":"Bengaluru","region":"Karnataka","postalCode":"560025","country":"IN"},"gstinConfirmedAt":"2026-10-26T09:12:00Z","complete":true,"updatedAt":"2026-10-26T09:12:00Z"}},"confirmedByNumber":{"summary":"200 — a UK company confirmed; the profile carries the registration (v1.63.0 PROPOSAL)","value":{"businessType":null,"country":"GB","website":"https://sampletravels.co.uk","volumeBand":null,"gstin":null,"pan":null,"legalName":"SAMPLE TRAVELS LTD","address":{"line1":"20 Sample Street","city":"London","region":"Greater London","postalCode":"EC1A 1AA","country":"GB"},"gstinConfirmedAt":null,"registrationScheme":"GB_COMPANY_NUMBER","registrationNumber":"09876543","businessStructure":"Private limited company","complete":false,"updatedAt":"2026-10-28T09:30:00Z"}}}}}},"400":{"$ref":"#/components/responses/BusinessRegistrationValidation"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/BusinessRegistrationNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT GSTIN_NOT_ACTIVE (the registration is not ACTIVE), GSTIN_ALREADY_CLAIMED (another tenant confirmed this GSTIN; use hosted KYB) or REGISTRATION_LOCKED (the tenant is LIVE; replacing the GSTIN is an ops change); or IDEMPOTENCY_CONFLICT\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"gstinNotActive":{"summary":"409 CONFLICT GSTIN_NOT_ACTIVE — a cancelled or suspended registration cannot be confirmed","value":{"code":"CONFLICT","message":"GSTIN 29AABCS0000F1Z5 is CANCELLED in the GST register and cannot verify the business.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GSTIN_NOT_ACTIVE","status":"CANCELLED"}}},"gstinAlreadyClaimed":{"summary":"409 CONFLICT GSTIN_ALREADY_CLAIMED — another tenant confirmed this GSTIN; verify through hosted KYB","value":{"code":"CONFLICT","message":"This GSTIN is already confirmed by another account. Verify the business through the hosted check instead.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GSTIN_ALREADY_CLAIMED"}}},"registrationLocked":{"summary":"409 CONFLICT REGISTRATION_LOCKED — the tenant is LIVE; the confirmed GSTIN cannot be replaced here","value":{"code":"CONFLICT","message":"The business registration of a live account is changed by operations, not here.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_LOCKED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/BusinessRegisterUnavailable"},"502":{"$ref":"#/components/responses/BusinessRegisterUnreachable"}}}},"/v1/activation/signatory-checks":{"post":{"operationId":"checkSignatory","x-mcp":{"expose":"excluded","toolset":"public","name":"check_signatory","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Check the signing Owner's PAN against the confirmed company's directors (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-24). The Owner enters their own PAN and the name on it; the register provider checks it against the directors and signatories of the registration confirmed by POST /v1/activation/business-registration:confirm. DIRECTOR_MATCH completes rail step business_verification (no hosted KYB session needed); NOT_A_DIRECTOR is a 200 result, not an error: the step reads needs_you (needs[] signatory.pan) and the hosted KYB session (POST /v1/activation/verification-sessions) stays the fallback. result is x-extensible-enum. Before a registration is confirmed the check is 409 CONFLICT details.reason REGISTRATION_NOT_CONFIRMED. The PAN is sent to the provider and never stored or echoed: the answer carries panLast4 only [assumption: the DR-58 class of the check record]. A PAN that does not match the pattern is 400 VALIDATION details.field pan. The check is for the signed-in Owner only: when the Owner's name is known (AccessContext.givenName and familyName), nameOnPan must equal givenName + \" \" + familyName compared without regard to case or whitespace, else 400 VALIDATION details.reason SIGNATORY_NOT_SIGNED_IN_USER and nothing is sent to the provider. At most 5 checks per tenant per UTC day (replays of an Idempotency-Key do not count): the sixth is 429 RATE_LIMITED details.reason SIGNATORY_ATTEMPTS_EXCEEDED with Retry-After (seconds to the next 00:00 UTC) and details.retryAfterMs; step business_verification then reads needs_you (needs[] kyb.hosted) and the hosted KYB session is the path. Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY; role OWNER or CUSTOMER_ADMIN as OD-870). Idempotency-Key required. Audited (activation.signatory_checked, with the result and panLast4). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): method picks the check. PAN (the default when method is absent) is the check above and needs pan and nameOnPan (India). NAME_DOB needs dateOfBirth only: the signed-in Owner's name (AccessContext.givenName and familyName) and date of birth are checked against the directors, officers, members or managers the register names for the registration confirmed by scheme and number (GB, AE, US, CA). DIRECTOR_MATCH (GB, AE, CA) or OFFICER_MATCH (US, an officer or member named on the register) completes the signatory part of business_verification; NOT_A_DIRECTOR leaves it needs_you with hosted KYB the fallback. Exactly one method's fields: pan or nameOnPan with NAME_DOB, or dateOfBirth with PAN, is 400 VALIDATION details.field naming it, and a missing field of the method is 400 VALIDATION details.field naming it; NAME_DOB on an India registration is 400 VALIDATION details.field method (India checks by PAN). Either method needs a confirmed registration first (409 REGISTRATION_NOT_CONFIRMED, above). The date of birth is sent to the provider and not kept by this route (the business-people record holds the representative's). result also takes OFFICER_MATCH (x-extensible-enum); panLast4 is absent on NAME_DOB and method echoes the check made. The attempt limit above counts both methods.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatoryCheckRequest"},"examples":{"director":{"summary":"The Owner's own PAN and the name on it","value":{"pan":"ABCPM1234K","nameOnPan":"Arjun Mehta"}},"nameDob":{"summary":"The signed-in Owner's date of birth, checked with their name against the register (v1.63.0 PROPOSAL)","value":{"method":"NAME_DOB","dateOfBirth":"1984-05-12"}}}}}},"responses":{"200":{"description":"The check's result (a mismatch is a result, not an error)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatoryCheck"},"examples":{"directorMatch":{"summary":"200 — DIRECTOR_MATCH; step business_verification done","value":{"result":"DIRECTOR_MATCH","panLast4":"234K","checkedAt":"2026-10-26T09:14:00Z"}},"notADirector":{"summary":"200 — NOT_A_DIRECTOR; the step needs you, and hosted KYB is the fallback","value":{"result":"NOT_A_DIRECTOR","panLast4":"000K","checkedAt":"2026-10-26T09:15:00Z"}},"officerMatch":{"summary":"200 — OFFICER_MATCH (US); the signatory part of business_verification done (v1.63.0 PROPOSAL)","value":{"result":"OFFICER_MATCH","method":"NAME_DOB","checkedAt":"2026-10-28T09:40:00Z"}},"directorMatchNameDob":{"summary":"200 — DIRECTOR_MATCH by name and date of birth (GB); the signatory part done (v1.63.0 PROPOSAL)","value":{"result":"DIRECTOR_MATCH","method":"NAME_DOB","checkedAt":"2026-10-28T09:41:00Z"}}}}}},"400":{"description":"VALIDATION — a PAN that does not match the pattern (details.field pan), or SIGNATORY_NOT_SIGNED_IN_USER (nameOnPan is not the signed-in Owner's name); on connect-v1 1.63.0 also a field of the other method, or a missing field of this one (details.field names it)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"invalidPan":{"summary":"400 VALIDATION — the PAN does not match the 10-character pattern","value":{"code":"VALIDATION","message":"pan must be 10 characters, five letters, four digits and a letter.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"pan"}}},"signatoryNotSignedInUser":{"summary":"400 VALIDATION SIGNATORY_NOT_SIGNED_IN_USER — the name on the PAN is not the signed-in Owner's","value":{"code":"VALIDATION","message":"Check your own PAN. The name on it must match the name you signed up with.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SIGNATORY_NOT_SIGNED_IN_USER","field":"nameOnPan"}}},"methodFieldMissing":{"summary":"400 VALIDATION — NAME_DOB without dateOfBirth (v1.63.0 PROPOSAL)","value":{"code":"VALIDATION","message":"dateOfBirth is required for the name and date-of-birth check.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"dateOfBirth"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT REGISTRATION_NOT_CONFIRMED — confirm a GST registration first; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"registrationNotConfirmed":{"summary":"409 CONFLICT REGISTRATION_NOT_CONFIRMED","value":{"code":"CONFLICT","message":"Confirm the business's GST registration before checking a signatory.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_NOT_CONFIRMED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"description":"RATE_LIMITED SIGNATORY_ATTEMPTS_EXCEEDED — 5 signatory checks today; use hosted KYB or retry after 00:00 UTC","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the next 00:00 UTC; mirrored in details.retryAfterMs"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"signatoryAttemptsExceeded":{"summary":"429 RATE_LIMITED SIGNATORY_ATTEMPTS_EXCEEDED — the day's five checks are spent","value":{"code":"RATE_LIMITED","message":"Too many signatory checks today. Verify the business through the hosted check, or try again tomorrow.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"SIGNATORY_ATTEMPTS_EXCEEDED","limit":5,"retryAfterMs":52200000}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/BusinessRegisterUnavailable"},"502":{"$ref":"#/components/responses/BusinessRegisterUnreachable"}}}},"/v1/activation/business-people":{"get":{"operationId":"getBusinessPeople","x-mcp":{"expose":"excluded","toolset":"public","name":"get_business_people","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b). Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"The business's representative and owners of 25 percent or more, as submitted (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2, OD-ENR-31). The representative (the signed-in Owner acting for the business: job title, date of birth, home address and the last four characters of an identity number, never the full number) and the natural persons who own 25 percent or more, as last submitted with PUT; an empty record (representative null, owners empty, noOwnerAt25Percent false, submittedAt null) until then. Part of rail step business_verification on the CAPPED flow (GET /v1/activation). Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs users:manage, as the profile.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The record as stored, or the empty record","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessPeople"},"examples":{"submitted":{"summary":"200 — a UK company's director and its two owners, submitted","value":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Lane","city":"London","region":"Greater London","postalCode":"N1 1AA","country":"GB"},"idNumberLast4":"456C"},"owners":[{"fullName":"Arjun Mehta","sharePercent":60},{"fullName":"Kavya Rao","sharePercent":40}],"noOwnerAt25Percent":false,"submittedAt":"2026-10-28T09:44:00Z"}},"empty":{"summary":"200 — nothing submitted yet","value":{"representative":null,"owners":[],"noOwnerAt25Percent":false,"submittedAt":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}},"put":{"operationId":"putBusinessPeople","x-mcp":{"expose":"excluded","toolset":"public","name":"put_business_people","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b). Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Submit the business's representative and owners — part of business_verification (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2, OD-ENR-31). Replaces the whole record (BusinessPeople) and sets submittedAt. On the CAPPED flow business_verification is done only when the registration is confirmed (or the hosted KYB documents are approved), the signatory matched (POST /v1/activation/signatory-checks) and this record is submitted with representative set and owners non-empty or noOwnerAt25Percent true. owners holds at most 10 natural persons, each with sharePercent 25 to 100, together at most 100 (400 VALIDATION details.field owners); noOwnerAt25Percent true with owners non-empty is 400 VALIDATION details.field noOwnerAt25Percent. idNumberLast4 is the last four characters only (UK National Insurance, US SSN, UAE Emirates ID, Canadian SIN where asked); a longer value is 400 VALIDATION details.field representative.idNumberLast4 and is never stored; India leaves it absent (the PAN already checked stands for it). The date of birth and home address are personal data: kept for the KYB record only, never logged, and audited without their values (activation.business_people_submitted). Once the tenant is LIVE the record is changed by operations: 409 CONFLICT details.reason REGISTRATION_LOCKED. On the NINE_STEP flow 409 CONFLICT details.reason FLOW_NOT_CAPPED (the hosted KYB collects them there). Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY; role OWNER or CUSTOMER_ADMIN as OD-870). Idempotency-Key required. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): this PUT is \"Submit for verification\". It stores the record and opens the business verification case with the provider routed for the tenant's country (aikonic.kyb.provider.<country>, default aikonic.kyb.provider), prefilled from the confirmed registration, the business profile, the signatory result, the representative and the owners; a case already open is resumed with the changed data instead (one case per tenant). The pre-checks come first: where the country has a register (IN, GB, AE, US, CA) the registration must be confirmed (409 CONFLICT REGISTRATION_NOT_CONFIRMED) and the signatory matched (409 CONFLICT SIGNATORY_NOT_MATCHED), and nothing is stored or sent while either is open. business_verification turns done ONLY when the provider's verdict is verified; meanwhile ActivationState.businessVerification reads in_progress, in_review, needs_more (with the outstanding requirements) or rejected. After needs_more the Owner fixes the listed fields and PUTs the whole record again, which resumes the case. While the case is in_review a PUT is 409 CONFLICT VERIFICATION_IN_REVIEW, once verified 409 CONFLICT ALREADY_VERIFIED, once rejected 409 CONFLICT VERIFICATION_REJECTED (contact support); nothing changes. representative.idNumber (write-only) carries the full identity number only when the provider asks for it (requirement representative.id_number): it is sent to the provider and never stored, logged, audited or answered; idNumberLast4 is then set from it. Each owner may likewise carry dateOfBirth, address and a write-only idNumber when the provider asks for them (requirement fields owners[i].dateOfBirth, owners[i].address, owners[i].idNumber; none is needed at the first submission); an owner's idNumber is forwarded the same way and only its last four characters are kept (owners[i].idNumberLast4, read-only). No provider routed for the country is 501 NOT_IMPLEMENTED KYB_UNAVAILABLE (details.country); a provider that cannot be reached is 502 UPSTREAM_UNAVAILABLE (retryable; the record is kept, the case is not opened; PUT again). The verdict arrives by the provider's signed callback or the periodic reconcile, never by this call; the console re-reads GET /v1/activation.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessPeople"},"examples":{"resubmitWithIdNumber":{"summary":"Resubmitted after needs_more — the full identity number the provider asked for (v1.63.0 PROPOSAL, amended 2026-10-29)","value":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Street","city":"Austin","region":"TX","postalCode":"78701","country":"US"},"idNumber":"000000000"},"owners":[{"fullName":"Arjun Mehta","sharePercent":100}],"noOwnerAt25Percent":false}},"resubmitWithOwnerDetails":{"summary":"Resubmitted after needs_more — the owners' date of birth, home address and identity number the provider asked for (v1.63.0 PROPOSAL, amended 2026-10-29)","value":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Street","city":"Austin","region":"TX","postalCode":"78701","country":"US"},"idNumberLast4":"0000"},"owners":[{"fullName":"Arjun Mehta","sharePercent":60,"dateOfBirth":"1984-05-12","address":{"line1":"1 Sample Street","city":"Austin","region":"TX","postalCode":"78701","country":"US"},"idNumber":"000000000"},{"fullName":"Kavya Rao","sharePercent":40,"dateOfBirth":"1988-03-21","address":{"line1":"2 Sample Avenue","city":"Austin","region":"TX","postalCode":"78702","country":"US"},"idNumber":"000000001"}],"noOwnerAt25Percent":false}},"company":{"summary":"A UK company's director and its two owners","value":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Lane","city":"London","region":"Greater London","postalCode":"N1 1AA","country":"GB"},"idNumberLast4":"456C"},"owners":[{"fullName":"Arjun Mehta","sharePercent":60},{"fullName":"Kavya Rao","sharePercent":40}],"noOwnerAt25Percent":false}},"noOwnerAt25":{"summary":"A company where no one owns 25 percent or more","value":{"representative":{"jobTitle":"Managing Director","dateOfBirth":"1979-09-30","homeAddress":{"line1":"7 Sample Avenue","city":"Toronto","region":"Ontario","postalCode":"M5V 2T6","country":"CA"},"idNumberLast4":"0429"},"owners":[],"noOwnerAt25Percent":true}},"soleProprietorIndia":{"summary":"An Indian sole proprietorship; the proprietor owns it all","value":{"representative":{"jobTitle":"Proprietor","dateOfBirth":"1990-01-15","homeAddress":{"line1":"12 Sample Road, Kothrud","city":"Pune","region":"Maharashtra","postalCode":"411038","country":"IN"}},"owners":[{"fullName":"Priya Sharma","sharePercent":100}],"noOwnerAt25Percent":false}}}}}},"responses":{"200":{"description":"The record as stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessPeople"},"examples":{"submitted":{"summary":"200 — submitted; with a matched signatory and a confirmed registration, business_verification done","value":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Lane","city":"London","region":"Greater London","postalCode":"N1 1AA","country":"GB"},"idNumberLast4":"456C"},"owners":[{"fullName":"Arjun Mehta","sharePercent":60},{"fullName":"Kavya Rao","sharePercent":40}],"noOwnerAt25Percent":false,"submittedAt":"2026-10-28T09:44:00Z"}}}}}},"400":{"description":"VALIDATION — owners over the limits (details.field owners), noOwnerAt25Percent with owners, or more than the last four characters of an identity number\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"sharesOver100":{"summary":"400 VALIDATION — the owners' shares add up to more than 100 percent","value":{"code":"VALIDATION","message":"The owners' shares add up to more than 100 percent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"owners"}}},"ownersAndNoOwner":{"summary":"400 VALIDATION — noOwnerAt25Percent true with owners listed","value":{"code":"VALIDATION","message":"List the owners, or say no one owns 25 percent or more, not both.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"noOwnerAt25Percent"}}},"fullIdNumberRefused":{"summary":"400 VALIDATION — more than the last four characters of an identity number; nothing stored","value":{"code":"VALIDATION","message":"Send only the last four characters of the identity number.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"representative.idNumberLast4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT FLOW_NOT_CAPPED (the nine-step rail) or REGISTRATION_LOCKED (the tenant is LIVE); on connect-v1 1.63.0 round 3 (amended 2026-10-29) also REGISTRATION_NOT_CONFIRMED, SIGNATORY_NOT_MATCHED (the pre-checks), VERIFICATION_IN_REVIEW, ALREADY_VERIFIED and VERIFICATION_REJECTED (the provider's case); or IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"flowNotCapped":{"summary":"409 CONFLICT FLOW_NOT_CAPPED — served on the CAPPED flow only","value":{"code":"CONFLICT","message":"This tenant is on the nine-step activation. The hosted business verification collects the owners there.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FLOW_NOT_CAPPED"}}},"registrationLocked":{"summary":"409 CONFLICT REGISTRATION_LOCKED — the tenant is LIVE; operations change the record","value":{"code":"CONFLICT","message":"The business people of a live account are changed by operations, not here.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_LOCKED"}}},"registrationNotConfirmed":{"summary":"409 CONFLICT REGISTRATION_NOT_CONFIRMED — confirm the registration before submitting (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"Confirm your business registration before you submit it for verification.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_NOT_CONFIRMED"}}},"signatoryNotMatched":{"summary":"409 CONFLICT SIGNATORY_NOT_MATCHED — the signatory check has not matched (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"Check that you are a director or officer of the business before you submit it for verification.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SIGNATORY_NOT_MATCHED"}}},"verificationInReview":{"summary":"409 CONFLICT VERIFICATION_IN_REVIEW — the provider is reviewing the case; wait for its verdict (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"Your business is being reviewed. You can change these details once the review asks for more.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERIFICATION_IN_REVIEW"}}},"alreadyVerified":{"summary":"409 CONFLICT ALREADY_VERIFIED — the business is verified; operations change the record (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"Your business is verified. Contact support to change who acts for or owns it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ALREADY_VERIFIED"}}},"verificationRejected":{"summary":"409 CONFLICT VERIFICATION_REJECTED — the provider rejected the case; contact support (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"We could not verify your business. Contact support to find out what to do next.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERIFICATION_REJECTED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE — the capped flow is off on this deployment (aikonic.activation.capped-flow.enabled); or, connect-v1 1.63.0 round 3 (amended 2026-10-29), KYB_UNAVAILABLE — no business verification provider is routed for the tenant's country (aikonic.kyb.provider.<country> and aikonic.kyb.provider both unset), details.country. Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}},"kybUnavailable":{"summary":"501 NOT_IMPLEMENTED KYB_UNAVAILABLE — no verification provider for the tenant's country (v1.63.0 round 3)","value":{"code":"NOT_IMPLEMENTED","message":"Business verification is not available for your country yet.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"KYB_UNAVAILABLE","country":"AE"}}}}}}},"502":{"description":"UPSTREAM_UNAVAILABLE — connect-v1 1.63.0 round 3 (amended 2026-10-29): the verification provider routed for the tenant's country cannot be reached; the record is kept and the case is not opened. Retryable: PUT again (details.provider names the provider).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"kybProviderUnreachable":{"summary":"502 UPSTREAM_UNAVAILABLE — the verification provider is not answering (v1.63.0 round 3)","value":{"code":"UPSTREAM_UNAVAILABLE","message":"Business verification is not answering right now. Try again in a minute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"provider":"stripe_connect","retryAfterMs":30000}}}}}}}}}},"/v1/activation/work-email:send-code":{"post":{"operationId":"sendWorkEmailCode","x-mcp":{"expose":"excluded","toolset":"public","name":"send_work_email_code","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b). Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"E-mail a code to a work address the Owner must prove before going live — CAPPED flow only (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2, OD-ENR-32). The work-e-mail guardrail of the CAPPED flow, for every CAPPED BUSINESS tenant with no exception: when the sign-up e-mail is on a free-mail domain, or not on the domain of the profile's website, ActivationState.workEmail required is true and the Owner proves a work address before going live (POST /v1/activation:go-live is 409 CONFLICT details.reason WORK_EMAIL_REQUIRED until then). This sends a 6-digit code to email; POST /v1/activation/work-email:confirm proves it. Refused with 400 VALIDATION details.field email: details.reason FREE_MAIL_DOMAIN when the domain is on the maintained free-mail list (gmail.com, googlemail.com, the yahoo, outlook, hotmail and live domains, icloud.com, me.com, proton.me, protonmail.com, zoho.com, rediffmail.com, aol.com and gmx among them; the list is platform configuration, aikonic.activation.free-mail-domains [assumption], kept current by the platform and not part of the contract); details.reason DOMAIN_NOT_COMPANY when the profile has a website and the domain is neither the website's registrable domain nor a subdomain of it (with noWebsite true any domain not on the free-mail list is taken). The code lives 15 minutes and allows at most 5 attempts, and a new code voids the previous one, as the terms code; sentTo is the address masked as TermsCodeSent; resendAfter is 60 seconds after the send [assumption]. Rate limited per Owner as the identity code routes (IdentityRateLimited): 429 RATE_LIMITED details.reason SUBJECT_RATE_LIMITED, details.action work-email-code-send, with Retry-After. On the NINE_STEP flow 409 CONFLICT details.reason FLOW_NOT_CAPPED. Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required: a replay answers the first 202 and sends nothing. A deployment without an identity mail sender is 501 NOT_IMPLEMENTED details.reason NO_VERIFICATION_SENDER. The code is never logged, audited or returned; the send is audited as activation.work_email_code_sent with sentTo masked. Test mode (the sandbox twin and sandbox keys) is unaffected by the guardrail.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkEmailSendCodeRequest"},"examples":{"work":{"summary":"A work address on the website's domain","value":{"email":"arjun@sampletravels.co.uk"}}}}}},"responses":{"202":{"description":"Accepted — a code is on its way to the work address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkEmailCodeSent"},"examples":{"sent":{"summary":"202 — a code sent to the work address (masked), valid 15 minutes","value":{"sentTo":"ar***@sampletravels.co.uk","expiresAt":"2026-10-28T09:16:00Z","resendAfter":"2026-10-28T09:02:00Z"}}}}}},"400":{"description":"VALIDATION details.field email — FREE_MAIL_DOMAIN (a free-mail domain), DOMAIN_NOT_COMPANY (not on the website's domain), or not an e-mail address\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"freeMailDomain":{"summary":"400 VALIDATION FREE_MAIL_DOMAIN — a free-mail address cannot prove the business","value":{"code":"VALIDATION","message":"Use an address on your company's domain, not a free-mail address.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FREE_MAIL_DOMAIN","field":"email"}}},"domainNotCompany":{"summary":"400 VALIDATION DOMAIN_NOT_COMPANY — not on the domain of the profile's website","value":{"code":"VALIDATION","message":"Use an address on sampletravels.co.uk, the domain of your website.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"DOMAIN_NOT_COMPANY","field":"email"}}},"invalidEmail":{"summary":"400 VALIDATION — not an e-mail address","value":{"code":"VALIDATION","message":"email must be an e-mail address.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"email"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT FLOW_NOT_CAPPED — the tenant is on the nine-step rail; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"flowNotCapped":{"summary":"409 CONFLICT FLOW_NOT_CAPPED — served on the CAPPED flow only","value":{"code":"CONFLICT","message":"This tenant is on the nine-step activation; no work e-mail check is asked there.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FLOW_NOT_CAPPED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"description":"RATE_LIMITED SUBJECT_RATE_LIMITED (action work-email-code-send) — too soon after the last code, or too many codes","headers":{"Retry-After":{"description":"Whole seconds until another code may be sent; mirrored in details.retryAfterMs","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"resendTooSoon":{"summary":"429 RATE_LIMITED SUBJECT_RATE_LIMITED — a code was sent less than a minute ago","value":{"code":"RATE_LIMITED","message":"A code was sent a moment ago. Wait for the Retry-After interval before asking for another.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"SUBJECT_RATE_LIMITED","action":"work-email-code-send","retryAfterMs":42000}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED — CAPPED_FLOW_UNAVAILABLE (aikonic.activation.capped-flow.enabled is off) or NO_VERIFICATION_SENDER (no identity mail sender is deployed, OD-ENR-16). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}},"noVerificationSender":{"summary":"501 NOT_IMPLEMENTED NO_VERIFICATION_SENDER — no identity mail sender on this deployment","value":{"code":"NOT_IMPLEMENTED","message":"This deployment cannot send e-mail codes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NO_VERIFICATION_SENDER"}}}}}}}}}},"/v1/activation/work-email:confirm":{"post":{"operationId":"confirmWorkEmail","x-mcp":{"expose":"excluded","toolset":"public","name":"confirm_work_email","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b). Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Prove the work address with the e-mailed code — CAPPED flow only (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2, OD-ENR-32). email must be the address the latest code was sent to (400 VALIDATION details.field email otherwise) and code the 6 digits from that e-mail (400 VALIDATION details.field code when it is not six digits). A wrong, expired, voided or never-sent code is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and the 5th wrong attempt spends the code (STEP_UP_LOCKED until a new code is sent), as the terms code. On success the address is recorded as the tenant's verified work e-mail (ActivationState.workEmail.email and verifiedAt) and the guardrail is satisfied; the sign-in username does not change [assumption]. Confirming the address already verified is a 200 with the first verifiedAt. On the NINE_STEP flow 409 CONFLICT details.reason FLOW_NOT_CAPPED. Human session only; Owner only (403 OWNER_ONLY). Idempotency-Key required. Audited (activation.work_email_verified, the address masked).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkEmailConfirmRequest"},"examples":{"confirm":{"summary":"The address and the code from the e-mail","value":{"email":"arjun@sampletravels.co.uk","code":"482913"}}}}}},"responses":{"200":{"description":"The work e-mail, verified","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkEmail"},"examples":{"verified":{"summary":"200 — verified; the work-e-mail guardrail is satisfied","value":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-28T09:02:00Z"}}}}}},"400":{"description":"VALIDATION — email is not the address the latest code went to (details.field email), or code is not six digits (details.field code)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"emailNotSentTo":{"summary":"400 VALIDATION — no code was sent to this address","value":{"code":"VALIDATION","message":"Confirm the address the latest code was sent to.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"email"}}},"codeFormat":{"summary":"400 VALIDATION — code is not six digits","value":{"code":"VALIDATION","message":"code must be the six digits from the e-mail.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"code"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT FLOW_NOT_CAPPED — the tenant is on the nine-step rail; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"flowNotCapped":{"summary":"409 CONFLICT FLOW_NOT_CAPPED — served on the CAPPED flow only","value":{"code":"CONFLICT","message":"This tenant is on the nine-step activation; no work e-mail check is asked there.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FLOW_NOT_CAPPED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}}},"/v1/activation:go-live":{"post":{"operationId":"goLiveCapped","x-mcp":{"expose":"excluded","toolset":"public","name":"go_live_capped","class":"commit-money","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b, §18d). Asks for a tenant state change (opens the operator go-live review; an operator's approval turns the tenant from TRIAL to LIVE, tier LIVE_CAPPED): the Owner's own request and attestation, never an agent's. Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED (DR-39)."},"tags":["identity"],"summary":"Go live, capped — the Owner asks to go live and operators review it (FEAT-030 §18b, §18d)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2, OD-ENR-33). The Owner's explicit go-live on the CAPPED flow. It replaces the automatic promotion of 1.51.0: completing the last required step no longer activates the tenant by itself. When every required CAPPED step but go_live is done (verify_email, business_profile, business_verification, terms and money_model; a step in_review is not done) and the work e-mail is satisfied (ActivationState.workEmail.required false, or verifiedAt set), the tenant turns LIVE with tier LIVE_CAPPED and its daily cap (limits; GET /v1/activation/limits), actor the calling Owner, with no go-live signoff and no ops approval; step go_live then reads done, and the answer is the rail as GET /v1/activation reads it after the change. The lifecycle fact and events, and the tenant.live notification, are emitted as 1.51.0's promotion did. Refusals, all 409 CONFLICT and nothing changed: details.reason COUNTRY_NOT_LIVE (details.country; live bookings are not open for the tenant's country yet, ActivationState.liveAvailable false, checked first), then PREREQUISITES_OPEN (details.open lists the open step keys in rail order), WORK_EMAIL_REQUIRED (prove a work e-mail first, POST /v1/activation/work-email:send-code), ALREADY_LIVE (the tenant is LIVE already; a replay of the same Idempotency-Key answers the first answer instead) and FLOW_NOT_CAPPED on the nine-step rail (go live there with the signoffs of /admin/v1/go-live). Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY; role OWNER or CUSTOMER_ADMIN as OD-870). No request body (an empty JSON object is accepted and ignored). Idempotency-Key required. Audited (activation.went_live, with the tier and the cap). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): a business goes live ONLY once the verification provider's verdict is verified (ActivationState.businessVerification.status verified; business_verification done). While the case is not_submitted, in_progress, in_review, needs_more or rejected the refusal stays 409 CONFLICT PREREQUISITES_OPEN with business_verification in details.open (no new reason; a client reads businessVerification for why). Once verified, the Owner may ask to go live (the go-live review below; amended the same day, it replaces \"go-live is immediate\"). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review, OD-ENR-39; amended 2026-10-29, same version): going live goes to an operator review. The call no longer turns the tenant LIVE: it opens a go-live review (ActivationState.goLiveReview, GoLiveReview, status in_review) for the products asked for (optional body GoLiveRequest.products, canonical product codes; absent means every product of the tenant's sandbox capabilities) and files an operator approval of kind GO_LIVE_REVIEW (OpsApproval; scope.key the review id), then answers 202 Accepted with the rail as GET /v1/activation reads it: tenant still TRIAL (tier null, liveAt null), step go_live in_review, a golive.in_review notification. Each product carries its checks (GoLiveCheck): business_details, by operator, and test_calls, by system (the tenant's own sandbox search, reprice, orders and cancel calls for that product). The test calls are required for the approval, not for this call: the Owner may ask before finishing them, the review opens, and an operator cannot approve until test_calls is passed for every requested product (OD-ENR-41); goLiveReview shows what is missing per product (verbsSeen) and step sandbox_booking names it in needs[]. Operators decide through the existing approvals queue, with the TENANT_ACTIVATE approver permission and step-up: approving approves every requested product (OD-ENR-40 option (a), all or nothing), turns the tenant LIVE, tier LIVE_CAPPED, with its daily cap (actor the approving operator), sets go_live done and emits tenant.live and golive.approved; rejecting sets the review changes_requested with the operator's note (go_live needs_you) and the Owner may call this again, which opens a new review. expectedBy is the answer the platform aims for, \"usually within one business day\" [assumption, OD-ENR-42]; each decision is e-mailed to the Owner and to the tenant's operations contact e-mail (GET /admin/v1/contact-emails operations; the Owner's address when unset, then sent once); test mode keeps working throughout. Refusals as above, plus 409 CONFLICT GO_LIVE_IN_REVIEW while a review is in_review (checked after ALREADY_LIVE), and 400 VALIDATION (details.field products) for a duplicate or a product not in the tenant's sandbox capabilities (details.reason PRODUCT_NOT_AVAILABLE); nothing is filed on a refusal. A replay of the same Idempotency-Key with the same body answers the first 202; the same key with other products is 409 IDEMPOTENCY_CONFLICT. Where the operator approvals queue (FEAT-024) is not running on the deployment the call is 501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE and nothing is filed, as POST /v1/activation/limit-requests. The 200 answer of round 2 (tenant LIVE at once) is not produced any more. Audited (activation.go_live_requested with the products; activation.went_live with the tier, the cap and the approval id when approved).\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveRequest"},"examples":{"everyProduct":{"summary":"No products named — every product of the tenant's sandbox capabilities","value":{}},"flightsAndHotels":{"summary":"Go live for flights and hotels only","value":{"products":["FLT","HTL"]}}}}}},"responses":{"202":{"description":"The go-live review is open — the rail after the request: tenant still TRIAL, step go_live in_review, goLiveReview in_review with the requested products and their checks\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivationState"},"examples":{"inReview":{"summary":"202 — go-live under review for flights and hotels; the sandbox keeps working","value":{"tenantId":"TEN-SAMPLE","accountType":"BUSINESS","buildingIntent":"BOOKING_SITE","tier":null,"moneyModel":"CARD","firstSandboxCallAt":"2026-10-29T09:05:00Z","firstSandboxOrderAt":"2026-10-29T10:35:00Z","liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-29T10:20:00Z"},"flow":"CAPPED","country":"GB","registrationGstin":null,"registrationConfirmedAt":"2026-10-29T09:30:00Z","limits":null,"liveAvailable":true,"workEmail":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-29T09:02:00Z","required":true},"businessVerification":{"status":"verified","provider":"stripe_connect","submittedAt":"2026-10-29T09:44:00Z","decidedAt":"2026-10-29T09:58:00Z","outstanding":[],"rejectionReason":null},"goLiveReview":{"id":"GLR-01JB7Q2M4X","status":"in_review","submittedAt":"2026-10-29T11:00:00Z","decidedAt":null,"expectedBy":"2026-10-30T11:00:00Z","note":null,"products":[{"product":"FLT","status":"in_review","decidedAt":null,"note":null,"checks":[{"key":"business_details","status":"in_progress","by":"operator","at":"2026-10-29T11:00:00Z","detail":null},{"key":"test_calls","status":"in_progress","by":"system","at":"2026-10-29T10:20:00Z","detail":"Search and reprice seen in the sandbox; order and cancel not yet.","verbsSeen":["search","reprice"]}]},{"product":"HTL","status":"in_review","decidedAt":null,"note":null,"checks":[{"key":"business_details","status":"in_progress","by":"operator","at":"2026-10-29T11:00:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]}]},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-29T09:00:30Z","needs":[],"required":true},{"key":"business_profile","state":"done","updatedAt":"2026-10-29T09:20:00Z","needs":[],"required":true},{"key":"business_verification","state":"done","updatedAt":"2026-10-29T09:58:00Z","needs":[],"required":true},{"key":"terms","state":"done","updatedAt":"2026-10-29T10:20:00Z","needs":[],"required":true},{"key":"money_model","state":"done","updatedAt":"2026-10-29T10:10:00Z","needs":[],"required":true},{"key":"sandbox_booking","state":"in_progress","updatedAt":"2026-10-29T11:00:00Z","needs":["FLT.orders","FLT.cancel"],"required":true},{"key":"go_live","state":"in_review","updatedAt":"2026-10-29T11:00:00Z","needs":[],"required":true}]}}}}}},"400":{"description":"VALIDATION details.field products — a duplicate code, a malformed code, or a product not in the tenant's sandbox capabilities (details.reason PRODUCT_NOT_AVAILABLE); nothing is filed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"productNotAvailable":{"summary":"400 VALIDATION PRODUCT_NOT_AVAILABLE — a requested product is not in the tenant's sandbox capabilities","value":{"code":"VALIDATION","message":"CRU isn't one of your sandbox products. Choose from the products you tested.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"products","reason":"PRODUCT_NOT_AVAILABLE"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT COUNTRY_NOT_LIVE (details.country; checked before PREREQUISITES_OPEN), PREREQUISITES_OPEN (details.open), WORK_EMAIL_REQUIRED, ALREADY_LIVE, GO_LIVE_IN_REVIEW (a go-live review is open; v1.63.0 go-live review) or FLOW_NOT_CAPPED; or IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"countryNotLive":{"summary":"409 CONFLICT COUNTRY_NOT_LIVE — live bookings are not open for the tenant's country yet","value":{"code":"CONFLICT","message":"Live bookings aren't available in your country yet. Keep testing in the sandbox.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"COUNTRY_NOT_LIVE","country":"AE"}}},"prerequisitesOpen":{"summary":"409 CONFLICT PREREQUISITES_OPEN — required steps are still open","value":{"code":"CONFLICT","message":"Finish the open steps before going live.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PREREQUISITES_OPEN","open":["business_verification","money_model"]}}},"verificationNotVerified":{"summary":"409 CONFLICT PREREQUISITES_OPEN — the provider has not verified the business yet (v1.63.0 round 3)","value":{"code":"CONFLICT","message":"Your business is waiting to be verified. You can go live once it is.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PREREQUISITES_OPEN","open":["business_verification"]}}},"workEmailRequired":{"summary":"409 CONFLICT WORK_EMAIL_REQUIRED — prove a work e-mail first","value":{"code":"CONFLICT","message":"Prove an e-mail address on your company's domain before going live.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WORK_EMAIL_REQUIRED"}}},"alreadyLive":{"summary":"409 CONFLICT ALREADY_LIVE — the tenant is live already","value":{"code":"CONFLICT","message":"This account is live already.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ALREADY_LIVE"}}},"goLiveInReview":{"summary":"409 CONFLICT GO_LIVE_IN_REVIEW — a go-live review is already open (v1.63.0 go-live review)","value":{"code":"CONFLICT","message":"Your go-live request is being reviewed. We'll e-mail you when it's decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GO_LIVE_IN_REVIEW"}}},"flowNotCapped":{"summary":"409 CONFLICT FLOW_NOT_CAPPED — served on the CAPPED flow only","value":{"code":"CONFLICT","message":"This tenant is on the nine-step activation. Go live with the Owners' signoffs in the console.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"FLOW_NOT_CAPPED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}}},"/v1/activation/limits":{"get":{"operationId":"getActivationLimits","x-mcp":{"expose":"excluded","toolset":"public","name":"get_activation_limits","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): a read served only behind aikonic.activation.capped-flow.enabled and not offered to agents while OD-ENR-26 is undecided; exposing it later is additive (18 §1)."},"tags":["identity"],"summary":"The capped tier's daily limit — cap, spent today and when it resets (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-26). The tenant's LIVE tier and its daily spend cap, which applies to the orders of a LIVE_CAPPED tenant that is not a sandbox twin, on any rung: dailyCap (INR when the activation country is IN, else USD), usedToday (the tenant's non-cancelled orders since 00:00 UTC, converted to the cap currency at each order's FX snapshot) and resetAt (the next 00:00 UTC). An order that would take usedToday past dailyCap is refused 403 PERMISSION_DENIED details.reason TIER_CAP_REACHED (POST /v1/orders). The cap is set per deployment by currency (aikonic.activation.capped.daily-cap.INR and .USD, minor units) until OD-ENR-26 decides it, and raised by an approved limit request (POST /v1/activation/limit-requests). A tenant whose tier carries no daily cap (TRIAL, LIVE_STANDARD, LIVE_ENTERPRISE) is 409 CONFLICT details.reason TENANT_NOT_CAPPED. tier is x-extensible-enum. Needs tenant:read, as GET /v1/activation; a human session or an API key.\n","responses":{"200":{"description":"The tier and its daily limit","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierLimits"},"examples":{"capped":{"summary":"200 — LIVE_CAPPED, ₹4,00,000 a day, ₹1,25,000 spent today","value":{"tier":"LIVE_CAPPED","dailyCap":{"amount":40000000,"currency":"INR"},"usedToday":{"amount":12500000,"currency":"INR"},"resetAt":"2026-10-27T00:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT TENANT_NOT_CAPPED — the tenant's tier carries no daily cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"tenantNotCapped":{"summary":"409 CONFLICT TENANT_NOT_CAPPED — the tenant's tier has no daily cap","value":{"code":"CONFLICT","message":"This tenant's tier has no daily limit.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_NOT_CAPPED"}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}}},"/v1/activation/limit-requests":{"post":{"operationId":"createLimitRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"create_limit_request","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Ask for a higher daily limit — opens an ops approval (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-27). Files a request to raise the daily cap of a LIVE_CAPPED tenant and opens an operator approval of kind TIER_LIMIT_RAISE (OpsApproval, FEAT-024 §7; scope.tenantId the tenant, scope.key the request id, effect the requested and current caps). Approving it raises the cap to requestedCap and marks the request APPROVED; rejecting it marks it REJECTED; either emits a limit.request_decided notification. One open request at a time: another while one is PENDING is 409 CONFLICT details.reason LIMIT_REQUEST_OPEN (details.requestId names it). A tenant whose tier carries no daily cap is 409 CONFLICT details.reason TENANT_NOT_CAPPED. dailyCap must be in the current cap's currency (400 VALIDATION details.reason CURRENCY_MISMATCH) and above the current cap (400 VALIDATION details.field dailyCap.amount). Human session only; Owner only (403 PERMISSION_DENIED details.reason OWNER_ONLY). Idempotency-Key required. Audited (activation.limit_requested). 501 NOT_IMPLEMENTED details.reason CAPPED_FLOW_UNAVAILABLE also covers a deployment where the ops approval queue (FEAT-024) is not running, so no TIER_LIMIT_RAISE approval could be opened; nothing is filed.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LimitRequestCreate"},"examples":{"raise":{"summary":"Ask for ₹10,00,000 a day for the corporate travel season","value":{"dailyCap":{"amount":100000000,"currency":"INR"},"reason":"Corporate travel season"}}}}}},"responses":{"201":{"description":"The request, PENDING an operator's decision","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LimitRequest"},"examples":{"pending":{"summary":"201 — filed; an operator approval of kind TIER_LIMIT_RAISE is open","value":{"id":"LRQ-01JB2K7Q","status":"PENDING","requestedCap":{"amount":100000000,"currency":"INR"},"currentCap":{"amount":40000000,"currency":"INR"},"reason":"Corporate travel season","createdAt":"2026-10-26T11:00:00Z","decidedAt":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT LIMIT_REQUEST_OPEN or TENANT_NOT_CAPPED; or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"limitRequestOpen":{"summary":"409 CONFLICT LIMIT_REQUEST_OPEN — one request at a time","value":{"code":"CONFLICT","message":"A request for a higher limit is already waiting for a decision.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"LIMIT_REQUEST_OPEN","requestId":"LRQ-01JB2K7Q"}}},"tenantNotCapped":{"summary":"409 CONFLICT TENANT_NOT_CAPPED — the tenant's tier has no daily cap","value":{"code":"CONFLICT","message":"This tenant's tier has no daily limit to raise.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_NOT_CAPPED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}},"get":{"operationId":"listLimitRequests","x-mcp":{"expose":"excluded","toolset":"public","name":"list_limit_requests","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): a read served only behind aikonic.activation.capped-flow.enabled and not offered to agents while OD-ENR-27 is undecided; exposing it later is additive (18 §1)."},"tags":["identity"],"summary":"The tenant's requests for a higher daily limit, newest first (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-27). Every limit request with its status (PENDING, APPROVED, REJECTED; x-extensible-enum) and decision time, newest first. Cursor-paged as GET /v1/wallets/{currency}/transactions: pass nextCursor back as cursor; a cursor this route never issued is 400 VALIDATION (details.field cursor). Needs tenant:read; a human session or an API key.\n","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque; the nextCursor of the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20 [assumption]"}],"responses":{"200":{"description":"A page of limit requests, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LimitRequestPage"},"examples":{"page":{"summary":"200 — one approved request","value":{"items":[{"id":"LRQ-01JB2K7Q","status":"APPROVED","requestedCap":{"amount":100000000,"currency":"INR"},"currentCap":{"amount":40000000,"currency":"INR"},"reason":"Corporate travel season","createdAt":"2026-10-26T11:00:00Z","decidedAt":"2026-10-26T15:30:00Z"}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}}},"/v1/certification":{"get":{"operationId":"getCertification","x-mcp":{"expose":"resource","toolset":"public","name":"get_certification","class":"read","autonomy":"L0"},"tags":["identity"],"summary":"Passive certification — the six integration checks observed from sandbox traffic (FEAT-030 §5.6)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-3, published in 1.18.0). The integration cert suite of 11 §15, evaluated continuously against the tenant's sandbox-twin traffic: idempotent_retry, price_change_handled, async_respected, webhook_verified, ambiguity_rule, deprecation_logged. A check is passed only with the requestIds of the traffic that proved it (AC-030-10); failed names the request that broke the rule (for example a blind retry after a price change). Rail step certification is done when every check is passed. Needs tenant:read, which the Owner and Admin role bundles carry (OD-ENR-8); the users:manage fallback is retired; a human session or an API key. Check keys and states are x-extensible-enum.\n","responses":{"200":{"description":"Every check with its state and evidence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificationState"},"examples":{"fourOfSix":{"summary":"200 — four checks observed, two still missing","value":{"passed":4,"total":6,"checks":[{"key":"idempotent_retry","state":"passed","observedAt":"2026-10-09T09:12:03Z","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736"]},{"key":"price_change_handled","state":"passed","observedAt":"2026-10-09T09:13:30Z","requestIds":["0af7651916cd43dd8448eb211c80319c","b7ad6b7169203331b7ad6b7169203331"]},{"key":"async_respected","state":"passed","observedAt":"2026-10-09T09:05:44Z","requestIds":["5b8aa5a2d2c872e8321cf37308d69df2"]},{"key":"webhook_verified","state":"not_observed","observedAt":null,"requestIds":[]},{"key":"ambiguity_rule","state":"passed","observedAt":"2026-10-09T09:20:12Z","requestIds":["9c4f1b0e2d3a4c5b8e7f6a1b2c3d4e5f"]},{"key":"deprecation_logged","state":"not_observed","observedAt":null,"requestIds":[]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/certification:run-missing":{"post":{"operationId":"runMissingCertificationChecks","x-mcp":{"expose":"tool","toolset":"public","name":"run_missing_certification_checks","class":"commit-lite","autonomy":"L1"},"tags":["identity"],"summary":"Run the checks not yet observed, through the sandbox scenario catalogue (FEAT-030 §5.6)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-3, published in 1.18.0). Drives the sandbox scenario catalogue (the Test header scenarios, components/parameters/SandboxTest) on the caller's sandbox twin for every check not yet passed, and answers 202 at once; read GET /v1/certification for the results. Checks already passed are listed and not re-run. Sandbox only, the same gate as the Test header: accepted only from a mo_test_ API key (or a service token of one) on a rung where aikonic.sandbox.simulation is on; anything else, including a production key, is 400 VALIDATION details.reason SANDBOX_ONLY and runs nothing. A human session is not a sandbox key (security declares the key schemes only). Needs orders:write [assumption: the run places sandbox orders for the tenant]. Idempotency-Key required.\n","security":[{"apiKeyAuth":[]},{"oauthClientCredentials":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"202":{"description":"The run is scheduled; poll GET /v1/certification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificationRun"},"examples":{"scheduled":{"summary":"202 — two checks scheduled","value":{"runId":"CRUN-01JA9R2","scheduled":["webhook_verified","deprecation_logged"],"alreadyPassed":["idempotent_retry","price_change_handled","async_respected","ambiguity_rule"],"startedAt":"2026-10-09T10:00:00Z"}}}}}},"400":{"description":"VALIDATION SANDBOX_ONLY — not a sandbox key on a simulation rung; nothing was run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"sandboxOnly":{"summary":"400 VALIDATION SANDBOX_ONLY","value":{"code":"VALIDATION","message":"Certification checks run only from a sandbox (mo_test_) API key where simulation is on.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SANDBOX_ONLY"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/onboard":{"get":{"operationId":"getOnboard","x-mcp":{"expose":"tool","toolset":"public","name":"onboard","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.57.0, FEAT-034 §5.5, AC-034-05): served on /mcp/public only while the plane's AIKONIC_MCP_ONBOARD_ENABLED and the platform's aikonic.mcp.onboard.enabled are on; it returns exactly the GET /v1/onboard object."},"tags":["identity"],"summary":"Where this tenant is on the way to certified and LIVE, and the next call to make (FEAT-034 §5.5)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.mcp.onboard.enabled is on, else 501 NOT_IMPLEMENTED details.reason ONBOARD_UNAVAILABLE). One read for an agent: the onboarding manifest of GET /.well-known/aikonic.json without its signature (manifest), the tenant's origin and sign-up agent (tenant), the activation rail exactly as GET /v1/activation answers it (rail, ActivationState: each step todo, in_progress, needs_you, in_review, done or blocked with the exact missing item in needs[]), passive certification as GET /v1/certification answers it (certification, the advisory checks included), and nextCall: the one request that moves the tenant forward now (operationId, method, path, headers, body, why, docs), or null when nothing is left for an agent to do (the remaining steps need the Owner). nextCall never names a human-session route; for a step only a person can do it names no call and the step reads needs_you. It walks the quick-start in order — search, reprice an offer the tenant's last search returned, hold it, book it (POST /v1/orders), cancel the order — with the refs the tenant's own sandbox calls produced, then runMissingCertificationChecks until every check passes. The quick-start and certification run on the device sign-up's SYSTEM key, which the D-50 approval gate does not stop (DR-40); only after certification passes does nextCall offer the narrowing mint of an ai child for the agent's MCP use (POST /v1/keys, actorKind ai), because an ai key's order writes wait for a person's D-50 approval (AC-034-16) [assumption: the order]. Needs tenant:read; a human session or an API key. The MCP tool onboard on /mcp/public returns this object unchanged.\n","responses":{"200":{"description":"The manifest, the tenant's rail and certification, and the next call","headers":{"X-Principal":{"$ref":"#/components/headers/X-Principal"},"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardState"},"examples":{"quickstartNext":{"summary":"200 — signed up by an agent; the quick-start booking is next","value":{"manifest":{"manifestVersion":"2026-10-27.1","issuedAt":"2026-10-27T00:00:00Z","expiresAt":"2026-10-28T00:00:00Z","environments":{"sandbox":{"api":"https://apiq-sandbox.tripproagencysites.com","mcp":"https://mcp.example/mcp/public"},"live":{"api":"https://api.live.example","mcp":"https://mcp.live.example/mcp/public"}},"signup":{"device":"https://apiq-sandbox.tripproagencysites.com/v1/signup/device","deviceToken":"https://apiq-sandbox.tripproagencysites.com/v1/signup/device:token","agentPath":"Start POST /v1/signup/device, show the person user_code and verification_uri, poll deviceToken every interval seconds, then call GET /v1/onboard with the sandbox key it returns; the person signs in to the console themselves.","email":"https://apiq-sandbox.tripproagencysites.com/v1/signup","identities":["email_code"]},"keys":{"defaults":{"sandbox":{"writes":"book","products":null,"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":null},"liveCapped":{"writes":"book-requires-approval","products":null,"spendPerDay":null,"lookToBook":1500,"reqPerSec":10,"holdsPerHour":30,"cancelsPerDay":20,"expiresAt":null}},"lifetimeDays":{"sandbox":90,"liveCapped":30},"liveCappedSpendPercentOfTenantCap":20},"velocity":{"pauseOnSoftBreach":3,"softBreachWindowHours":24,"burstMultiplier":5,"identicalRequestsPerMinute":20,"holdsWithoutOutcomePer10Minutes":50,"callsAfterStopBeforeRevoke":100},"rail":["verify_email","sandbox_booking","business_profile","business_verification","money_model","fund_wallet","certification","terms","go_live"],"certification":{"checks":["idempotent_retry","price_change_handled","async_respected","webhook_verified","ambiguity_rule","deprecation_logged"],"advisory":["velocity_retry_honoured","stopped_on_pause"]},"scenarios":"https://apiq.example/developers/scenarios.json","products":["flights","hotels"],"pricing":"https://apiq.example/pricing","sdks":{"npm":"@mondee-one/connect-client","pypi":"mondee-connect-client","maven":"com.mondee.connect:connect-client"},"docs":{"llms":"https://apiq.example/llms.txt","openapi":"https://apiq.example/openapi.json","mcp":"https://apiq.example/developers/mcp"}},"tenant":{"tenantId":"TEN-EVAL-S5KH8Q2W","origin":"agent","signupAgent":"claude-code/2.4 (claude-fable-5-1)"},"rail":{"tenantId":"TEN-EVAL-S5KH8Q2W","buildingIntent":"AI_AGENT","tier":null,"moneyModel":null,"firstSandboxCallAt":"2026-10-27T09:13:02Z","firstSandboxOrderAt":null,"liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":null,"acceptedAt":null},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-27T09:12:00Z","needs":[]},{"key":"sandbox_booking","state":"in_progress","updatedAt":"2026-10-27T09:13:02Z","needs":["quickstart.book"]}]},"certification":{"passed":1,"total":6,"checks":[{"key":"idempotent_retry","state":"passed","observedAt":"2026-10-27T09:16:40Z","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736"],"evidence":[{"requestId":"4bf92f3577b34da6a3ce929d0e0e4736","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X"}]}],"advisory":[{"key":"velocity_retry_honoured","state":"not_observed","observedAt":null,"requestIds":[]},{"key":"stopped_on_pause","state":"not_observed","observedAt":null,"requestIds":[]}]},"nextCall":{"operationId":"search","method":"POST","path":"/v1/search","body":{"context":{"domain":"HTL","currency":"USD"},"message":{"intent":{"destination":{"placeId":"mondee:place:miami"},"stay":{"checkIn":"2026-11-10","checkOut":"2026-11-12"},"rooms":[{"adults":2}]}}},"why":"sandbox_booking needs the quick-start booking; search hotels first, then reprice an offer and book its repriceRef.","docs":"https://apiq.example/developers/quickstart#search"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED ONBOARD_UNAVAILABLE — the onboarding read is off on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"onboardUnavailable":{"summary":"501 NOT_IMPLEMENTED ONBOARD_UNAVAILABLE — read GET /v1/activation and GET /v1/certification","value":{"code":"NOT_IMPLEMENTED","message":"The onboarding read is not available on this deployment. Read GET /v1/activation and GET /v1/certification.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ONBOARD_UNAVAILABLE"}}}}}}}}}},"/v1/wallets":{"get":{"operationId":"listWallets","x-mcp":{"expose":"resource","toolset":"public","name":"list_wallets","class":"read","autonomy":"L0"},"tags":["wallets"],"summary":"The tenant's wallets, one per settlement currency (FEAT-030 §5.5)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-4, published in 1.18.0; sandbox twin only in this phase). The wallet is on where aikonic.wallet.enabled is: DEV and SANDBOX; off by default and refused at boot above SANDBOX. Where it is off the wallet routes still answer: an empty list here, 404 NOT_FOUND for one wallet, 501 WALLET_TOPUP_UNAVAILABLE on a top-up. Every amount is Money (integer minor units + ISO-4217). balance, held and available are sums of ledger legs (F-08), never a stored number: available = balance - held. environment selects the side of the tenant pair (07 §5); see components/parameters/WalletEnvironmentQuery. Needs finance:read [PROPOSED]; a human session or an API key (a key may hold finance:read). An empty list when no wallet exists (money model not WALLET).\n","parameters":[{"$ref":"#/components/parameters/WalletEnvironmentQuery"}],"responses":{"200":{"description":"The wallets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletList"},"examples":{"sandbox":{"summary":"200 — the sandbox wallet preloaded with 10,000.00 USD","value":{"wallets":[{"currency":"USD","environment":"SANDBOX","balance":{"amount":1000000,"currency":"USD"},"held":{"amount":45000,"currency":"USD"},"available":{"amount":955000,"currency":"USD"},"fundingInstructions":{"virtualAccount":"SIM-0000-0000-4417","reference":"AIK-TEN-ACME","note":"Simulated in sandbox; no money moves."},"lowBalanceThreshold":{"amount":100000,"currency":"USD"},"updatedAt":"2026-10-09T09:14:40Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/wallets/{currency}":{"get":{"operationId":"getWallet","x-mcp":{"expose":"resource","toolset":"public","name":"get_wallet","class":"read","autonomy":"L0"},"tags":["wallets"],"summary":"One wallet — balance, held, available, funding instructions (FEAT-030 §5.5)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-4, published in 1.18.0). As one row of GET /v1/wallets. No wallet in that currency is 404 NOT_FOUND. Needs finance:read [PROPOSED]; a human session or an API key.\n","parameters":[{"$ref":"#/components/parameters/WalletCurrencyPath"},{"$ref":"#/components/parameters/WalletEnvironmentQuery"}],"responses":{"200":{"description":"The wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"},"examples":{"sandbox":{"summary":"200 — the sandbox USD wallet","value":{"currency":"USD","environment":"SANDBOX","balance":{"amount":1000000,"currency":"USD"},"held":{"amount":45000,"currency":"USD"},"available":{"amount":955000,"currency":"USD"},"fundingInstructions":{"virtualAccount":"SIM-0000-0000-4417","reference":"AIK-TEN-ACME","note":"Simulated in sandbox; no money moves."},"lowBalanceThreshold":{"amount":100000,"currency":"USD"},"updatedAt":"2026-10-09T09:14:40Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/wallets/{currency}/transactions":{"get":{"operationId":"listWalletTransactions","x-mcp":{"expose":"tool","toolset":"public","name":"list_wallet_transactions","class":"read","autonomy":"L0"},"tags":["wallets"],"summary":"The wallet's movements, newest first — one row per ledger leg (FEAT-030 §5.5, F-08)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-4, published in 1.18.0). One row per ledger leg: WALLET_TOPUP (credit), WALLET_HOLD (moves funds to held at booking), WALLET_SETTLE (debit at ticketing or confirmation, clears the hold), WALLET_RELEASE (clears a hold when the booking fails) and WALLET_FEE (debit). Rows are never edited; a correction is a new leg. Newest first; cursor-paged as GET /v1/webhooks/{webhookId}/deliveries: pass nextCursor back as cursor with the same filters; a cursor this route never issued is 400 VALIDATION (details.field cursor). type is x-extensible-enum. Needs finance:read [PROPOSED]; a human session or an API key. No wallet in that currency is 404 NOT_FOUND.\n","parameters":[{"$ref":"#/components/parameters/WalletCurrencyPath"},{"$ref":"#/components/parameters/WalletEnvironmentQuery"},{"name":"type","in":"query","required":false,"schema":{"type":"string"},"description":"Only legs of this type (WALLET_TOPUP | WALLET_HOLD | WALLET_SETTLE | WALLET_RELEASE | WALLET_FEE)"},{"name":"orderRef","in":"query","required":false,"schema":{"type":"string"},"description":"Only the legs of this order"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque; the nextCursor of the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20 [assumption]"}],"responses":{"200":{"description":"A page of movements, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletTransactionPage"},"examples":{"page":{"summary":"200 — a hold for an order and the preload top-up","value":{"transactions":[{"transactionId":"led_01JA9K7H","type":"WALLET_HOLD","direction":"DEBIT","amount":{"amount":45000,"currency":"USD"},"orderRef":"MO-8KD-2FQ-9XT","topUpRef":null,"createdAt":"2026-10-09T09:14:38Z"},{"transactionId":"led_01JA9A01","type":"WALLET_TOPUP","direction":"CREDIT","amount":{"amount":1000000,"currency":"USD"},"orderRef":null,"topUpRef":"TU-SIM-PRELOAD","createdAt":"2026-10-09T09:00:31Z"}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/wallets/{currency}/alerts":{"put":{"operationId":"putWalletAlerts","x-mcp":{"expose":"excluded","toolset":"public","name":"put_wallet_alerts","class":"commit-lite","autonomy":"L1","reason":"Wallet low-balance threshold is a finance control on the tenant's money (FEAT-030); left to a human until the spec grants agent access."},"tags":["wallets"],"summary":"Set or clear the low-balance threshold (FEAT-030 §5.5)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-4, published in 1.18.0). lowBalanceThreshold is Money in the wallet's own currency (another currency is 400 VALIDATION details.reason CURRENCY_MISMATCH), or null to clear it. When available falls below it the platform e-mails the Owners and emits wallet.low_balance once per crossing. Needs finance:write [PROPOSED], which is in no key's default grant. No wallet in that currency is 404 NOT_FOUND. Idempotency-Key required. Audited.\n","parameters":[{"$ref":"#/components/parameters/WalletCurrencyPath"},{"$ref":"#/components/parameters/WalletEnvironmentQuery"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletAlertsRequest"},"examples":{"thousand":{"summary":"Alert below 1,000.00 USD","value":{"lowBalanceThreshold":{"amount":100000,"currency":"USD"}}}}}}},"responses":{"200":{"description":"The wallet with its new threshold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"},"examples":{"ok":{"summary":"200 — threshold set","value":{"currency":"USD","environment":"SANDBOX","balance":{"amount":1000000,"currency":"USD"},"held":{"amount":0,"currency":"USD"},"available":{"amount":1000000,"currency":"USD"},"fundingInstructions":{"virtualAccount":"SIM-0000-0000-4417","reference":"AIK-TEN-ACME","note":"Simulated in sandbox; no money moves."},"lowBalanceThreshold":{"amount":100000,"currency":"USD"},"updatedAt":"2026-10-09T09:40:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/wallets/{currency}/top-ups":{"post":{"operationId":"createWalletTopUp","x-mcp":{"expose":"excluded","toolset":"public","name":"create_wallet_top_up","class":"commit-money","autonomy":"L1","reason":"Credits the tenant's prepaid wallet by card (simulated in sandbox only today, FEAT-030 §5.5). It moves money, the proposal flow has no wallet action (only orders.create and orders.cancel, DR-44), and the route is human-session only."},"tags":["wallets"],"summary":"Top up the wallet by card — simulated in sandbox only in this phase (FEAT-030 §5.5)","description":"connect-v1 1.18.1 (served; FEAT-030 WP-4, published in 1.18.0). In this phase the top-up is SIMULATED: it is served only for the sandbox side of the tenant pair (environment SANDBOX) on a rung where aikonic.sandbox.simulation is on, credits the wallet at once with one WALLET_TOPUP leg (plus a WALLET_FEE leg when fee is non-zero) and answers simulated true; no card is charged and paymentMethod is not read. Anywhere else (environment PRODUCTION, or aikonic.wallet.enabled off) it is 501 NOT_IMPLEMENTED details.reason WALLET_TOPUP_UNAVAILABLE and nothing moves; real card top-ups through the payments path (OD-875 tokenisation) and bank transfers wait on OD-ENR-4. amount must be positive and in the wallet's currency (400 VALIDATION details.reason CURRENCY_MISMATCH otherwise), and at most the simulated maximum (default 1,000,000.00 in the wallet's currency, configurable per deployment): above it the answer is 400 VALIDATION details.field amount.amount, details.reason AMOUNT_TOO_LARGE, details.maximum (a Money) and nothing is credited. Human session only (an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED); needs finance:write [PROPOSED]. Idempotency-Key REQUIRED: a replay returns the original top-up and credits nothing twice.\n","x-aikonic-credential":"human-session","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/WalletCurrencyPath"},{"$ref":"#/components/parameters/WalletEnvironmentQuery"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletTopUpRequest"},"examples":{"simulated":{"summary":"Add 500.00 USD to the sandbox wallet","value":{"amount":{"amount":50000,"currency":"USD"}}}}}}},"responses":{"201":{"description":"Credited (simulated)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletTopUp"},"examples":{"simulated":{"summary":"201 — simulated top-up credited","value":{"topUpRef":"TU-SIM-01JA9S4","state":"SUCCEEDED","simulated":true,"amount":{"amount":50000,"currency":"USD"},"fee":{"amount":0,"currency":"USD"},"transactionIds":["led_01JA9S4A"],"createdAt":"2026-10-09T10:10:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/EnrollmentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"NOT_IMPLEMENTED WALLET_TOPUP_UNAVAILABLE — top-ups are simulated in sandbox only in this phase","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"topUpUnavailable":{"summary":"501 NOT_IMPLEMENTED WALLET_TOPUP_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Wallet top-ups are available only as a sandbox simulation for now.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WALLET_TOPUP_UNAVAILABLE","environment":"PRODUCTION"}}}}}}}}}},"/v1/devices":{"post":{"operationId":"registerDevice","x-mcp":{"expose":"excluded","toolset":"public","name":"register_device","class":"commit-lite","autonomy":"L1","reason":"Registers or removes a person's phone for push; a signed-in human's device, not an agent action."},"tags":["devices"],"summary":"Register this phone's push token for the signed-in user (FEAT-021 §18a)","description":"Human session only (the devices tag): an API key or a service token is 403 PERMISSION_DENIED, details.reason SESSION_REQUIRED. No permission is needed to register — what a device receives is decided when each push is sent: it gets an order's events only while its user holds orders:read (FEAT-021 §18a). Idempotent on (platform, token), so it takes no Idempotency-Key: registering a token this user already registered answers 200 with the same deviceId (appVersion refreshed); a token registered under another user, of this tenant or of another, moves to the caller — 201 with a new deviceId, and the earlier registration ends (its deviceId is a 404 to its former owner from then on). The token is stored, never logged and never returned: responses carry its last six characters only. A SUSPENDED tenant's users can still register, because push serves the post-booking servicing that suspension leaves up.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegistration"},"examples":{"iphone":{"summary":"An iPhone's APNs token","value":{"platform":"APNS","token":"00112233445566778899aabbccddeeff00112233445566778899aabbcc3e2f1a","appVersion":"1.2.0 (42)"}},"android":{"summary":"An Android phone's FCM registration token","value":{"platform":"FCM","token":"fcm-example-registration-token-not-a-real-one-0000000000AbCdEf","appVersion":"1.2.0"}}}}}},"responses":{"200":{"description":"This user had already registered the token — the same device, appVersion refreshed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Device"},"examples":{"alreadyRegistered":{"summary":"Re-registering on app launch returns the same deviceId","value":{"deviceId":"dev_7Qm2c9Lx4Rt8","platform":"APNS","tokenSuffix":"3e2f1a","appVersion":"1.2.0 (42)","createdAt":"2026-09-28T09:12:00Z","updatedAt":"2026-09-28T14:40:05Z"}}}}}},"201":{"description":"Registered for the caller — a new token, or one moved from another user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Device"},"examples":{"registered":{"summary":"A new registration","value":{"deviceId":"dev_7Qm2c9Lx4Rt8","platform":"APNS","tokenSuffix":"3e2f1a","appVersion":"1.2.0 (42)","createdAt":"2026-09-28T09:12:00Z","updatedAt":"2026-09-28T09:12:00Z"}}}}}},"400":{"description":"VALIDATION — platform is not APNS or FCM, or the token is empty or longer than 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — an unknown platform","value":{"code":"VALIDATION","message":"platform must be APNS or FCM","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"platform","accepted":["APNS","FCM"]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/DeviceForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listDevices","x-mcp":{"expose":"excluded","toolset":"public","name":"list_devices","class":"read","autonomy":"L0","reason":"Lists a signed-in person's own phones and push tokens; personal device data, not an agent read."},"tags":["devices"],"summary":"The signed-in user's own registered devices, newest first (FEAT-021 §18a)","description":"Only the caller's own devices — never a colleague's, whatever the caller's permissions. The token is shown as its last six characters only. Human session only (the devices tag).\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The caller's devices, newest first (an empty list when none)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceList"},"examples":{"twoPhones":{"summary":"An agent with an iPhone and an Android phone","value":{"devices":[{"deviceId":"dev_9Hk4v2Np6Ws1","platform":"FCM","tokenSuffix":"AbCdEf","appVersion":"1.2.0","createdAt":"2026-09-28T11:02:00Z","updatedAt":"2026-09-28T11:02:00Z"},{"deviceId":"dev_7Qm2c9Lx4Rt8","platform":"APNS","tokenSuffix":"3e2f1a","appVersion":"1.2.0 (42)","createdAt":"2026-09-28T09:12:00Z","updatedAt":"2026-09-28T14:40:05Z"}]}},"none":{"summary":"No device registered","value":{"devices":[]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/DeviceForbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/devices/{deviceId}":{"delete":{"operationId":"deleteDevice","x-mcp":{"expose":"excluded","toolset":"public","name":"delete_device","class":"commit-lite","autonomy":"L1","reason":"Registers or removes a person's phone for push; a signed-in human's device, not an agent action."},"tags":["devices"],"summary":"Stop pushing to one of the caller's devices — the apps call it on sign-out (FEAT-021 §18a)","description":"The device stops receiving pushes at once. A deviceId that is unknown, already deleted, moved to another user, or another user's is a 404, indistinguishable from none — an app signing out treats that 404 as done. No Idempotency-Key: deleting is idempotent in effect. Human session only (the devices tag).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/DeviceIdPath"}],"responses":{"204":{"description":"Deleted; nothing more is pushed to this device"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/DeviceForbidden"},"404":{"$ref":"#/components/responses/DeviceNotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/events":{"get":{"operationId":"listEvents","x-mcp":{"expose":"tool","toolset":"public","name":"list_events","class":"read","autonomy":"L0"},"tags":["events"],"summary":"Cursor-based event replay — the recover-from-missed-webhooks path (03 §3 #26)","parameters":[{"name":"cursor","description":"The nextCursor of the previous page; absent for the first page.","in":"query","schema":{"type":"string"}},{"name":"type","description":"Only events of this type, for example order.state_changed.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Event page with next cursor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventPage"},"examples":{"page":{"summary":"200 — cursor replay: the recover-from-missed-webhooks path (03 §3 #26)","value":{"events":[{"eventId":"ev_01J8Q2","type":"order.state_changed","occurredAt":"2026-09-14T17:06:04Z","tenant":"acme-travel","data":{"orderRef":"MO-8KD-2FQ-9XT","from":"BOOKED","to":"CONFIRMED"}},{"eventId":"ev_01J8Q3","type":"hold.expired","occurredAt":"2026-09-14T17:05:41Z","tenant":"acme-travel","data":{"holdRef":"HD-M-4419ab"}}],"nextCursor":"ZXZfMDFKOFEz"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/home":{"get":{"operationId":"getConsoleHome","x-mcp":{"expose":"resource","toolset":"console","name":"get_console_home","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Both console Homes' tiles in one read — ops (queues, at-risk money, supplier health) and analytical (funnel, money today, latency, volume) (09 §4, §6, §7; OD-81, OD-401)","description":"A read model projected server-side from the stores that exist (OrderStore, the F-10 event log, SearchRunStore, payments) — one console_projections entry per tenant and window (OD-401). Every money figure, INCLUDING every total, is a field computed here in integer minor units; the console renders and never adds a column up (web hard rule 1). dataThrough is the freshness stamp 09 §6 demands: a lagging projection says so rather than being silently stale. Requires a console session; any role that can see Orders or Analytics (09 §12).\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Both Homes' figures for the caller's tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleHome"},"examples":{"demoTenant":{"summary":"200 — the demo tenant at 09:30 UTC; one reconciliation and one on-request row need attention","value":{"generatedAt":"2026-09-27T09:30:04Z","dataThrough":"2026-09-27T09:29:00Z","currency":"USD","window":{"from":"2026-09-27T00:00:00Z","to":"2026-09-27T09:29:00Z"},"ops":{"queues":[{"queue":"ON_REQUEST_AGING","open":3,"claimed":1,"breached":0},{"queue":"RECONCILIATION","open":2,"claimed":1,"breached":1},{"queue":"DOC_GEN_FAILED","open":0,"claimed":0,"breached":0},{"queue":"SCHEDULE_CHANGES","open":1,"claimed":0,"breached":0}],"atRiskTotal":{"amount":194800,"currency":"USD"},"attention":[{"itemId":"qi_rec_01J8Q2","queue":"RECONCILIATION","orderRef":"MO-8KD-2FQ-9XT","domain":"HTL","supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","state":"PENDING_RECONCILIATION","enteredAt":"2026-09-27T08:41:10Z","slaDueAt":"2026-09-27T10:41:10Z","slaState":"WARNING","atRisk":{"amount":120400,"currency":"USD"},"attempt":3,"nextRetryAt":"2026-09-27T09:41:10Z","actions":["CLAIM","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL"]},{"itemId":"qi_onr_01J8Q7","queue":"ON_REQUEST_AGING","orderRef":"MO-3PL-8ZD-1QW","domain":"EXP","supplier":"EXAMPLEACTIVITIES","credentialSet":"tenant-acme-activities-01","state":"ON_REQUEST","enteredAt":"2026-09-26T19:02:00Z","slaDueAt":"2026-09-27T19:02:00Z","slaState":"WITHIN","atRisk":{"amount":74400,"currency":"USD"},"assignee":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Dana Ops","claimedAt":"2026-09-27T08:02:11Z"},"actions":["RELEASE"]}],"supplierHealth":[{"supplier":"EXAMPLEHOTELS","status":"LIVE","errorRateBps":40,"p95Ms":1210},{"supplier":"EXAMPLEACTIVITIES","status":"DEGRADED","errorRateBps":860,"p95Ms":2890}]},"analytics":{"funnel":[{"stage":"SEARCH","count":1840},{"stage":"DETAIL","count":612},{"stage":"REPRICE","count":188},{"stage":"ORDER","count":61},{"stage":"CONFIRMED","count":58}],"moneyToday":{"gross":{"amount":4821000,"currency":"USD"},"net":{"amount":4390000,"currency":"USD"},"margin":{"amount":431000,"currency":"USD"},"refunds":{"amount":68000,"currency":"USD"}},"latency":[{"supplier":"EXAMPLEHOTELS","p50Ms":640,"p95Ms":1210},{"supplier":"EXAMPLEACTIVITIES","p50Ms":1180,"p95Ms":2890}],"volume":[{"date":"2026-09-21","orders":44},{"date":"2026-09-22","orders":61},{"date":"2026-09-23","orders":52},{"date":"2026-09-24","orders":78},{"date":"2026-09-25","orders":66},{"date":"2026-09-26","orders":90},{"date":"2026-09-27","orders":58}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/team/members":{"get":{"operationId":"listTeamMembers","x-mcp":{"expose":"tool","toolset":"console","name":"list_team_members","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The tenant's human users with their roles and status (09 §10 Users & roles, FEAT-020 §15)","description":"Read-through over the identity store, never a second copy (WP consumes FEAT-020, it does not change it). Service principals are API keys and live under GET /v1/keys, so `kind` here is always HUMAN. Requires users:manage to list (Owner, Admin) or the Auditor role (09 §12).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["ACTIVE","DEACTIVATED","LOCKED"]},"description":"Filter by status (absent = every member)"},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Members, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleTeamMemberList"},"examples":{"twoMembers":{"summary":"200 — the demo tenant's owner and one ops agent","value":{"members":[{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","email":"owner@acme.example","displayName":"Ana Owner","kind":"HUMAN","roles":["OWNER"],"permissions":["keys:manage","users:manage","orders:read","orders:write","orders:cancel","orders:amend","rules:manage","webhooks:manage","search:hotels","search:flights"],"status":"ACTIVE","siteIds":["SITE-DEMO"],"createdAt":"2026-09-20T10:00:00Z","lastLoginAt":"2026-09-27T08:01:44Z"},{"userId":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","email":"dana@acme.example","displayName":"Dana Ops","kind":"HUMAN","roles":["OPS_AGENT"],"permissions":["orders:read","orders:write","orders:cancel","orders:amend","search:hotels","search:flights"],"status":"ACTIVE","siteIds":["SITE-DEMO"],"createdAt":"2026-09-22T14:30:00Z","lastLoginAt":"2026-09-27T08:02:11Z"}],"page":{"page":1,"pageSize":50,"total":2}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/team/members/{userId}":{"get":{"operationId":"getTeamMember","x-mcp":{"expose":"resource","toolset":"console","name":"get_team_member","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"One member — roles, resolved permissions, status (09 §10)","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/UserIdPath"}],"responses":{"200":{"description":"The member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleTeamMember"},"examples":{"opsAgent":{"summary":"200 — an active ops agent","value":{"userId":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","email":"dana@acme.example","displayName":"Dana Ops","kind":"HUMAN","roles":["OPS_AGENT"],"permissions":["orders:read","orders:write","orders:cancel","orders:amend","search:hotels","search:flights"],"status":"ACTIVE","siteIds":["SITE-DEMO"],"createdAt":"2026-09-22T14:30:00Z","lastLoginAt":"2026-09-27T08:02:11Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateTeamMember","x-mcp":{"expose":"excluded","toolset":"console","name":"update_team_member","class":"commit-lite","autonomy":"L1","reason":"Changes a member's roles or deactivates them (users:manage), which is role and permission state that an agent must never change."},"tags":["admin"],"x-audience":"console","summary":"Change a member's roles or deactivate / reactivate them (09 §10, §12; FEAT-020 §15)","description":"Requires users:manage. Roles are replaced as a set, never merged; `active: false` deactivates (sessions and refresh families revoked at once, keys the user minted are untouched — they are service principals). Refused 409 CONFLICT when the change would leave the tenant with no active OWNER (details.reason LAST_OWNER) or when a caller deactivates their own account (details.reason SELF). Audited as `user.roles_changed` / `user.deactivated` (13 §1.1). Every field is optional but at least one must be present.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/UserIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleTeamMemberUpdate"},"examples":{"promote":{"summary":"Promote an ops agent to Admin","value":{"roles":["ADMIN"],"reason":"Dana now runs the ops team"}},"deactivate":{"summary":"Deactivate a leaver","value":{"active":false,"reason":"Left the company 2026-09-26"}}}}}},"responses":{"200":{"description":"The member after the change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleTeamMember"},"examples":{"promoted":{"summary":"200 — roles replaced; permissions re-resolved from the role bundle","value":{"userId":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","email":"dana@acme.example","displayName":"Dana Ops","kind":"HUMAN","roles":["ADMIN"],"permissions":["keys:manage","users:manage","orders:read","orders:write","orders:cancel","orders:amend","rules:manage","webhooks:manage","search:hotels","search:flights"],"status":"ACTIVE","siteIds":["SITE-DEMO"],"createdAt":"2026-09-22T14:30:00Z","lastLoginAt":"2026-09-27T08:02:11Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/team/invites":{"get":{"operationId":"listTeamInvites","x-mcp":{"expose":"tool","toolset":"console","name":"list_team_invites","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Outstanding and recent invitations — never the token (FEAT-020 §5 flow 3, §7)","description":"The invite token went to the invitee alone and is not readable here (FEAT-020 §7). Requires users:manage or the Auditor role. Create an invite with POST /v1/invites.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"status","description":"Only invitations in this status.","in":"query","schema":{"type":"string","enum":["PENDING","ACCEPTED","EXPIRED","REVOKED"]}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Invitations, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleInviteList"},"examples":{"onePending":{"summary":"200 — one pending invitation","value":{"invites":[{"inviteId":"inv_01J8QK3","email":"colleague@acme.example","roles":["FINANCE"],"permissions":["orders:read"],"siteIds":["SITE-DEMO"],"invitedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","createdAt":"2026-09-26T10:00:00Z","expiresAt":"2026-10-03T10:00:00Z","status":"PENDING"}],"page":{"page":1,"pageSize":50,"total":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/team/invites/{inviteId}":{"delete":{"operationId":"revokeTeamInvite","x-mcp":{"expose":"excluded","toolset":"console","name":"revoke_team_invite","class":"commit-lite","autonomy":"L1","reason":"Requires users:manage and decides who may join the tenant, which is an access-control change that stays with a human."},"tags":["admin"],"x-audience":"console","summary":"Revoke a pending invitation; its token stops working at once (FEAT-020 §5 flow 3)","description":"Requires users:manage. Idempotent — revoking a revoked or expired invite is a 204; an accepted one is 409 CONFLICT (details.reason ALREADY_ACCEPTED).","security":[{"bearerAuth":[]}],"parameters":[{"name":"inviteId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Revoked"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/audit":{"get":{"operationId":"listAuditRecords","x-mcp":{"expose":"tool","toolset":"console","name":"list_audit_records","class":"read","autonomy":"L0","example":"{\"ref\":\"MO-8KD-2FQ\",\"from\":\"2026-10-01T00:00:00Z\",\"pageSize\":50}"},"tags":["admin"],"x-audience":"console","summary":"The tenant-visible slice of the immutable audit log, break-glass disclosures included (09 §10, 13 §1)","description":"13 §1.4's tenant slice: every state-changing action on this tenant's data, with actor.kind = ai badged on AI-initiated actions and every platform-side break-glass access rendered as a first-class row (who, why, TTL, approver — ADR-003). Filters are 09 §10's `actor`, `action`, `ref` plus a time window. before/after diffs are not returned here (13 §1.1 — drill-down by auditId is a later surface); `details` carries the code-specific keys. Requires the Owner, Admin or Auditor role (09 §12).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"actor","in":"query","schema":{"type":"string"},"description":"actor.id — a userId, agentId or `system`"},{"name":"action","in":"query","schema":{"type":"string"},"description":"Closed-registry verb, e.g. orders.create, config.ruleset.publish, breakglass.grant (13 §1.1)"},{"name":"ref","in":"query","schema":{"type":"string"},"description":"resource.ref — an orderRef, credential_set id, ruleset version …"},{"name":"from","description":"Only records at or after this time.","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"to","description":"Only records before this time.","in":"query","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Audit records, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAuditRecordList"},"examples":{"mixed":{"summary":"200 — a human publish, an AI proposal approval, and a break-glass disclosure","value":{"records":[{"auditId":"01J8QM2Z6X9K4R7T1V3W5Y8A0B","occurredAt":"2026-09-27T09:12:00Z","actor":{"kind":"user","id":"1e9bb43d-5e35-42ba-90f1-152117834fb9"},"tenantId":"TEN-DEMO","siteId":"SITE-DEMO","action":"config.ruleset.publish","resource":{"type":"ruleset","ref":"8"},"outcome":"SUCCESS","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","details":{"version":8,"rolledBackFrom":7}},{"auditId":"01J8QM4A1C2D3E4F5G6H7J8K9M","occurredAt":"2026-09-27T09:05:30Z","actor":{"kind":"ai","id":"agent-rebooking-ag22","agentId":"agent-rebooking-ag22","onBehalfOf":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f"},"tenantId":"TEN-DEMO","siteId":"SITE-DEMO","action":"orders.amend","resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"outcome":"SUCCESS","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","approvalTokenId":"apt_01J8QM3","details":{"proposalId":"prop_01J8QL9"}},{"auditId":"01J8QJ7P0Q1R2S3T4U5V6W7X8Y","occurredAt":"2026-09-26T22:41:00Z","actor":{"kind":"user","id":"platform-ops-0412"},"tenantId":"TEN-DEMO","siteId":"SITE-DEMO","action":"breakglass.grant","resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"outcome":"SUCCESS","traceId":"9c1a2b3c4d5e6f708192a3b4c5d6e7f8","breakGlass":{"reason":"INC-2291: supplier locator mismatch investigation","approver":"platform-ops-lead-0007","ttlUntil":"2026-09-27T00:41:00Z"}}],"page":{"page":1,"pageSize":50,"total":3}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/queues/{queue}":{"get":{"operationId":"getQueue","x-mcp":{"expose":"tool","toolset":"console","name":"get_queue","class":"read","autonomy":"L0","example":"{\"queue\":\"RECONCILIATION\",\"slaState\":\"BREACHED\",\"pageSize\":20}"},"tags":["admin"],"x-audience":"console","summary":"One of the four fulfilment queues — claimable, SLA-coloured rows (09 §4; 06 §8 step 4)","description":"Projected from OrderStore and the F-10 event log (OD-401). ON_REQUEST_AGING rows carry the supplier registry's confirmSlaHours as slaDueAt (08 §2); RECONCILIATION rows carry the retry schedule position (attempt, nextRetryAt — 06 §8 step 4); DOC_GEN_FAILED rows carry the template error in `reason`; SCHEDULE_CHANGES rows carry the normalised SupplierEvent id. `actions` lists exactly the verbs the caller may take on the row, so the console renders controls from the server's answer, never from a client-side rule. Owner, Admin and OpsAgent act; Auditor views (09 §12).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/QueuePath"},{"name":"assignee","in":"query","schema":{"type":"string"},"description":"`me` for the caller's claimed rows, a userId, or `unassigned`"},{"name":"slaState","in":"query","schema":{"$ref":"#/components/schemas/ConsoleSlaState"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"The queue's rows, most urgent first (breached, then soonest slaDueAt)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleQueue"},"examples":{"reconciliation":{"summary":"200 — the RECONCILIATION queue with one breached and one claimed row","value":{"queue":"RECONCILIATION","counts":{"open":2,"claimed":1,"breached":1},"items":[{"itemId":"qi_rec_01J8Q2","queue":"RECONCILIATION","orderRef":"MO-8KD-2FQ-9XT","domain":"HTL","supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","state":"PENDING_RECONCILIATION","enteredAt":"2026-09-27T08:41:10Z","slaDueAt":"2026-09-27T10:41:10Z","slaState":"WARNING","atRisk":{"amount":120400,"currency":"USD"},"attempt":3,"nextRetryAt":"2026-09-27T09:41:10Z","actions":["CLAIM","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL"]},{"itemId":"qi_rec_01J8P9","queue":"RECONCILIATION","orderRef":"MO-2AB-7CD-4EF","domain":"FLT","supplier":"EXAMPLEGDS","credentialSet":"MONDEE","state":"PENDING_RECONCILIATION","enteredAt":"2026-09-26T21:10:00Z","slaDueAt":"2026-09-26T23:10:00Z","slaState":"BREACHED","atRisk":{"amount":61200,"currency":"USD"},"assignee":{"userId":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","displayName":"Dana Ops","claimedAt":"2026-09-27T08:02:11Z"},"attempt":6,"nextRetryAt":null,"actions":["RELEASE","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL"]}],"page":{"page":1,"pageSize":50,"total":2}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/queues/{queue}/{itemId}:claim":{"post":{"operationId":"claimQueueItem","x-mcp":{"expose":"tool","toolset":"console","name":"claim_queue_item","class":"commit-lite","autonomy":"L1","example":"{\"queue\":\"ON_REQUEST_AGING\",\"itemId\":\"QI-7F2K9\",\"idempotencyKey\":\"6d2f8a41-3c9e-4b7a-8f15-2e9c7d4b1a03\"}"},"tags":["admin"],"x-audience":"console","summary":"Claim a queue row for the caller (09 §4)","description":"The ':claim' literal-suffix custom method, spelled like /v1/config/rules:simulate. Sets assignee to the caller; a row already claimed by someone else is 409 CONFLICT (details.assignee) — release it first. Claiming a row you already hold is a no-op 200. Posts a `queue.claimed` line into the order timeline. Requires orders:write (Owner, Admin, OpsAgent). connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/QueuePath"},{"$ref":"#/components/parameters/QueueItemIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The row, now assigned to the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleQueueItem"},"examples":{"claimed":{"summary":"200 — claimed; actions now offer RELEASE instead of CLAIM","value":{"itemId":"qi_rec_01J8Q2","queue":"RECONCILIATION","orderRef":"MO-8KD-2FQ-9XT","domain":"HTL","supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","state":"PENDING_RECONCILIATION","enteredAt":"2026-09-27T08:41:10Z","slaDueAt":"2026-09-27T10:41:10Z","slaState":"WARNING","atRisk":{"amount":120400,"currency":"USD"},"assignee":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Ana Owner","claimedAt":"2026-09-27T09:31:00Z"},"attempt":3,"nextRetryAt":"2026-09-27T09:41:10Z","actions":["RELEASE","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL"]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/queues/{queue}/{itemId}:release":{"post":{"operationId":"releaseQueueItem","x-mcp":{"expose":"tool","toolset":"console","name":"release_queue_item","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Release a claimed queue row back to the queue (09 §4)","description":"Clears assignee. Only the claimant, or an Owner/Admin, may release; anyone else is 409 CONFLICT (details.assignee). Releasing an unclaimed row is a no-op 200. Requires orders:write. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/QueuePath"},{"$ref":"#/components/parameters/QueueItemIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The row, unassigned again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleQueueItem"},"examples":{"released":{"summary":"200 — released","value":{"itemId":"qi_rec_01J8Q2","queue":"RECONCILIATION","orderRef":"MO-8KD-2FQ-9XT","domain":"HTL","supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","state":"PENDING_RECONCILIATION","enteredAt":"2026-09-27T08:41:10Z","slaDueAt":"2026-09-27T10:41:10Z","slaState":"WARNING","atRisk":{"amount":120400,"currency":"USD"},"attempt":3,"nextRetryAt":"2026-09-27T09:41:10Z","actions":["CLAIM","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL"]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/orders/{orderRef}/reconcile:retry":{"post":{"operationId":"retryReconciliation","x-mcp":{"expose":"excluded","toolset":"console","name":"retry_reconciliation","class":"commit-money","autonomy":"L1","reason":"A FOUND_CONFIRMED verdict confirms the order and charges per the money model (connect-v1 reconcile:retry), and the proposal flow carries only orders.create and orders.cancel (DR-44), so an agent must not trigger it (DR-38)."},"tags":["admin"],"x-audience":"console","summary":"Re-run the adapter's retrieve() now, out of the reconciliation schedule (09 §3; 06 §8 step 4)","description":"Wraps the existing reconciliation seam (W24 / Lane I own it; WP calls it). The order must be PENDING_RECONCILIATION, else 409 CONFLICT (details.state). The outcome is the retrieve's verdict: FOUND_CONFIRMED moves the order to CONFIRMED and charges per money model; FOUND_UNSETTLED (v1.6.6) means the supplier holds the booking but has not settled it (held, pending, a state that is not CONFIRMED): the order stays PENDING_RECONCILIATION, nothing is confirmed or compensated, and the reconcile can be retried; NOT_FOUND means the supplier does not have the booking; NOT_FOUND, FOUND_UNSETTLED and SUPPLIER_UNAVAILABLE leave the order PENDING_RECONCILIATION with the next scheduled attempt. The reason string is posted into the timeline (09 §4). Requires orders:write (Owner, Admin, OpsAgent — 09 §12 \"Reconciliation retry/adopt\").\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileRetryRequest"},"examples":{"retry":{"summary":"Retry after the supplier confirmed by phone that their outage is over","value":{"reason":"Example Hotels ops confirmed the booking API is back; retrieving out of schedule"}}}}}},"responses":{"200":{"description":"The retrieve ran; its outcome and the order's state after it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileOutcome"},"examples":{"foundConfirmed":{"summary":"200 FOUND_CONFIRMED — the supplier had it; the order is CONFIRMED","value":{"orderRef":"MO-8KD-2FQ-9XT","outcome":"FOUND_CONFIRMED","state":"CONFIRMED","attempt":4,"supplierLocator":"HB-88213Q","nextRetryAt":null,"performedAt":"2026-09-27T09:32:10Z"}},"foundUnsettled":{"summary":"200 FOUND_UNSETTLED — the supplier holds it but has not settled it; the order stays, retry later","value":{"orderRef":"MO-8KD-2FQ-9XT","outcome":"FOUND_UNSETTLED","state":"PENDING_RECONCILIATION","attempt":4,"supplierLocator":"HB-88213Q","nextRetryAt":"2026-09-27T10:32:10Z","performedAt":"2026-09-27T09:32:10Z"}},"notFound":{"summary":"200 NOT_FOUND — still nothing at the supplier; the schedule continues","value":{"orderRef":"MO-8KD-2FQ-9XT","outcome":"NOT_FOUND","state":"PENDING_RECONCILIATION","attempt":4,"supplierLocator":null,"nextRetryAt":"2026-09-27T10:32:10Z","performedAt":"2026-09-27T09:32:10Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/admin/v1/orders/{orderRef}/reconcile:adopt":{"post":{"operationId":"adoptReconciliationLocator","x-mcp":{"expose":"excluded","toolset":"console","name":"adopt_reconciliation_locator","class":"commit-money","autonomy":"L1","reason":"Adopting a supplier locator settles an order as booked, which fixes what the tenant pays; a desk decision with no proposal action to carry it."},"tags":["admin"],"x-audience":"console","summary":"Adopt a supplier locator found by desk / GDS lookup — outcome found-confirmed (09 §3)","description":"The human found the booking the platform could not: the locator is verified with one retrieve() by reference, adopted onto the order, the order proceeds to CONFIRMED and is charged per money model. A locator the supplier does not know is 409 CONFLICT (details.reason LOCATOR_NOT_FOUND); a locator the supplier holds but has not settled (held, pending, a state that is not CONFIRMED; v1.6.6) is also refused, as 409 CONFLICT (details.reason SUPPLIER_UNSETTLED), because adopting it would confirm an order the supplier has not: the order stays as it is and the reconcile can be retried (:retry answers FOUND_UNSETTLED while that holds); an order that is not PENDING_RECONCILIATION is 409 CONFLICT (details.state). Requires orders:write.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileAdoptRequest"},"examples":{"adopt":{"summary":"Adopt the locator the supplier's desk read out","value":{"supplierLocator":"HB-88213Q","reason":"Located via Example Hotels extranet; guest name and dates match"}}}}}},"responses":{"200":{"description":"Adopted; the order is CONFIRMED under the given locator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileOutcome"},"examples":{"adopted":{"summary":"200 FOUND_CONFIRMED — adopted","value":{"orderRef":"MO-8KD-2FQ-9XT","outcome":"FOUND_CONFIRMED","state":"CONFIRMED","attempt":4,"supplierLocator":"HB-88213Q","nextRetryAt":null,"performedAt":"2026-09-27T09:33:40Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/admin/v1/orders/{orderRef}/reconcile:fail":{"post":{"operationId":"failReconciliation","x-mcp":{"expose":"excluded","toolset":"console","name":"fail_reconciliation","class":"commit-money","autonomy":"L1","reason":"Marks the order failed and compensates (refund path, zero-residue money check); moves money and no proposal action carries it."},"tags":["admin"],"x-audience":"console","summary":"Mark failed & compensate — FAILED_COMPENSATED with the zero-residue check; reason + second approver (09 §3, ADR-003)","description":"A financial action: the payment is reversed per money model and the order lands FAILED_COMPENSATED only when the ledger shows zero residue (18 §10). Dual approval is the D-50 token: a SECOND human mints one at POST /admin/v1/approvals for scope orders:cancel, action reconcile.fail, resource the orderRef and the amount to compensate, and the caller presents it here. An expired or already-used token is 403 APPROVAL_TOKEN_EXPIRED / APPROVAL_TOKEN_USED; a token bound to another order or amount fails closed the same way as USED. A token minted by the caller themselves is refused 403 PERMISSION_DENIED (details.reason SELF_APPROVAL). Requires orders:cancel.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileFailRequest"},"examples":{"fail":{"summary":"Compensate after 72h with no locator anywhere","value":{"reason":"Supplier confirms no booking exists after 72h; guest rebooked elsewhere","approvalToken":"apt_01J8QN7.k3v9x2m8q4z1"}}}}}},"responses":{"200":{"description":"Compensated; the order is FAILED_COMPENSATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReconcileOutcome"},"examples":{"compensated":{"summary":"200 — FAILED_COMPENSATED, zero residue","value":{"orderRef":"MO-8KD-2FQ-9XT","outcome":"FAILED_COMPENSATED","state":"FAILED_COMPENSATED","attempt":6,"supplierLocator":null,"nextRetryAt":null,"performedAt":"2026-09-27T09:35:00Z","compensated":{"amount":120400,"currency":"USD"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/analytics/{report}":{"get":{"operationId":"getAnalyticsReport","x-mcp":{"expose":"tool","toolset":"console","name":"get_analytics_report","class":"read","autonomy":"L0","example":"{\"report\":\"l2b\",\"from\":\"2026-09-01\",\"to\":\"2026-09-30\",\"domain\":\"flights\"}"},"tags":["admin"],"x-audience":"console","summary":"One of the five v1 analytics reports — funnel · l2b · routes · supplier-mix · margin (09 §6)","description":"Read models over the stores that exist (OD-401), never the operational store on the hot path. Every report is one typed table: `columns` (at most four — OD-82) name and type the cells, `rows` carry cells aligned to them, and `totals` is the server's own total row — the console never sums a column (web hard rule 1). Money is integer minor units in the report's `currency`; ratios are basis points. Offline-channel orders are excluded from funnel and l2b denominators (09 §5). `dataThrough` is the freshness stamp. Margin splits by money model per 08 §7: markup lines (MERCHANT), commission lines (AGENCY), platform-fee lines (PASS_THROUGH / BYOC). Owner, Admin, OpsAgent, Finance, RevenueManager and Auditor view (09 §12).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"report","description":"The report to read: funnel, l2b, routes, supplier-mix or margin.","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ConsoleAnalyticsReportName"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"First day of the window (inclusive, tenant time zone)"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Last day of the window (inclusive); at most 366 days after `from`"},{"name":"domain","in":"query","schema":{"type":"string"},"description":"Restrict to one product (canonical code, 02 §1)"},{"name":"supplier","description":"Only the bookings of this supplier.","in":"query","schema":{"type":"string"}},{"name":"credentialSet","in":"query","schema":{"type":"string"},"description":"MONDEE or one of the tenant's own credential-set ids (08 §5 attribution)"},{"name":"pos","description":"Only the bookings of this point-of-sale market.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The report as a typed table","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAnalyticsReport"},"examples":{"funnel":{"summary":"200 funnel — five stages, count and step conversion","value":{"report":"funnel","window":{"from":"2026-09-01","to":"2026-09-27"},"dataThrough":"2026-09-27T09:00:00Z","currency":"USD","filters":{"domain":"HTL"},"columns":[{"key":"count","label":"Count","kind":"COUNT"},{"key":"stepConversion","label":"Step conversion","kind":"RATIO_BPS"}],"rows":[{"key":"SEARCH","label":"Search","cells":[{"kind":"COUNT","count":48210},{"kind":"RATIO_BPS","ratioBps":null}]},{"key":"DETAIL","label":"Detail","cells":[{"kind":"COUNT","count":15960},{"kind":"RATIO_BPS","ratioBps":3311}]},{"key":"REPRICE","label":"Reprice","cells":[{"kind":"COUNT","count":4870},{"kind":"RATIO_BPS","ratioBps":3051}]},{"key":"ORDER","label":"Order","cells":[{"kind":"COUNT","count":1612},{"kind":"RATIO_BPS","ratioBps":3310}]},{"key":"CONFIRMED","label":"Confirmed","cells":[{"kind":"COUNT","count":1544},{"kind":"RATIO_BPS","ratioBps":9578}]}]}},"margin":{"summary":"200 margin — one row per money model; totals are the server's","value":{"report":"margin","window":{"from":"2026-09-01","to":"2026-09-27"},"dataThrough":"2026-09-27T09:00:00Z","currency":"USD","filters":{},"columns":[{"key":"gross","label":"Gross","kind":"MONEY"},{"key":"net","label":"Net","kind":"MONEY"},{"key":"margin","label":"Margin / fee","kind":"MONEY"},{"key":"marginBps","label":"Margin","kind":"RATIO_BPS"}],"rows":[{"key":"MERCHANT","label":"Merchant (markup)","cells":[{"kind":"MONEY","money":{"amount":128400000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":116900000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":11500000,"currency":"USD"}},{"kind":"RATIO_BPS","ratioBps":896}]},{"key":"AGENCY","label":"Agency (commission)","cells":[{"kind":"MONEY","money":{"amount":41200000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":41200000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":3296000,"currency":"USD"}},{"kind":"RATIO_BPS","ratioBps":800}]},{"key":"PASS_THROUGH","label":"Pass-through (platform fee)","cells":[{"kind":"MONEY","money":{"amount":22750000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":22750000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":455000,"currency":"USD"}},{"kind":"RATIO_BPS","ratioBps":200}]}],"totals":{"key":"TOTAL","label":"Total","cells":[{"kind":"MONEY","money":{"amount":192350000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":180850000,"currency":"USD"}},{"kind":"MONEY","money":{"amount":15251000,"currency":"USD"}},{"kind":"RATIO_BPS","ratioBps":793}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbiddenTabDisabled"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/insights/summary":{"get":{"operationId":"getConsoleInsightsSummary","x-mcp":{"expose":"tool","toolset":"console","name":"get_console_insights_summary","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Activity summary (this tenant)","description":"connect-v1 1.9.0. As opsInsightsSummary, walled to the caller's OWN tenant pair: environment PRODUCTION reads the production tenant of the pair, SANDBOX its sandbox twin, ALL both. No parameter names another tenant; platform work is never included. `tenants` lists the tenant ids read. analytics:read (API-0012 §6).","security":[{"bearerAuth":[]}],"parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Activity summary (this tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleInsightsSummary"},"examples":{"ok":{"summary":"200 — Activity summary (this tenant)","value":{"environment":"PRODUCTION","tenants":["TEN-ACME"],"summary":{"asOf":"2026-10-06T12:00:00Z","from":"2026-09-29T12:00:00Z","to":"2026-10-06T12:00:00Z","totals":{"calls":1240,"succeeded":1180,"failed":52,"refused":8,"p50Ms":640,"p95Ms":2210,"orders":14,"ordersConfirmed":11,"ordersCancelled":1,"ordersPending":2,"truncated":false},"bySupplier":[{"supplier":"SUP-HTL-EXAMPLE","calls":610,"failed":12,"p50Ms":820,"p95Ms":2400,"orders":6,"lastCallAt":"2026-10-06T11:58:10Z"}],"byOperation":[{"supplier":"SUP-HTL-EXAMPLE","operation":"PROPERTY_PRICING","verb":"SEARCH","calls":480,"failed":9,"p50Ms":860,"p95Ms":2450,"lastCallAt":"2026-10-06T11:58:10Z"}],"byTenant":[{"tenantId":"TEN-ACME","calls":900,"failed":40,"orders":10}],"byOutcome":[{"key":"SUCCEEDED","count":1180},{"key":"TIMED_OUT","count":30}],"faults":[{"supplier":"SUP-FNB-EXAMPLE","operation":"AVAILABILITY","outcome":"FAILED_PROTOCOL","faultCode":"HTTP_403","count":22}],"perDay":[{"day":"2026-10-06","calls":210,"failed":9,"searches":180,"orders":3,"searchP50Ms":640,"searchP95Ms":2100,"bookP50Ms":1900,"bookP95Ms":4200}],"ordersByDomain":[{"key":"HTL","count":9}],"ordersByState":[{"key":"CONFIRMED","count":11}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/insights/calls":{"get":{"operationId":"listConsoleInsightsCalls","x-mcp":{"expose":"tool","toolset":"console","name":"list_console_insights_calls","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Supplier calls (this tenant)","description":"connect-v1 1.9.0. The newest supplier calls made for the caller's tenant pair (the environment picks the side). analytics:read.","security":[{"bearerAuth":[]}],"parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"operation","in":"query","required":false,"description":"Narrow to one supplier operation, as journalled.","schema":{"type":"string"}},{"name":"outcome","in":"query","required":false,"description":"Narrow to one call outcome.","schema":{"$ref":"#/components/schemas/SupplierCallOutcome"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 100).","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Supplier calls (this tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleInsightsCalls"},"examples":{"ok":{"summary":"200 — Supplier calls (this tenant)","value":{"environment":"PRODUCTION","calls":[{"at":"2026-10-06T11:58:10Z","tenantId":"TEN-ACME","supplier":"SUP-XFR-EXAMPLE","operation":"SEARCH","verb":"SEARCH","credentialSetId":"MONDEE","outcome":"SUCCEEDED","elapsedMs":1840,"parameters":["end_address","pickup_datetime","start_address"]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/insights/orders":{"get":{"operationId":"listConsoleInsightsOrders","x-mcp":{"expose":"tool","toolset":"console","name":"list_console_insights_orders","class":"read","autonomy":"L0","example":"{\"environment\":\"PRODUCTION\",\"state\":\"PENDING_RECONCILIATION\",\"limit\":50}"},"tags":["admin"],"x-audience":"console","summary":"Orders (this tenant)","description":"connect-v1 1.9.0. The newest orders' metadata for the caller's tenant pair. analytics:read.","security":[{"bearerAuth":[]}],"parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"state","in":"query","required":false,"description":"Narrow to one order state.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 100).","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Orders (this tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleInsightsOrders"},"examples":{"ok":{"summary":"200 — Orders (this tenant)","value":{"environment":"PRODUCTION","orders":[{"orderRef":"ORD-01JA7Q","tenantId":"TEN-ACME","supplier":"SUP-HTL-EXAMPLE","domain":"HTL","state":"CONFIRMED","createdAt":"2026-10-05T09:12:00Z","amountMinor":4250000,"currency":"INR","supplierReference":"SV-88213"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/statements":{"get":{"operationId":"getStatement","x-mcp":{"expose":"tool","toolset":"console","name":"get_statement","class":"read","autonomy":"L0","example":"{\"model\":\"MERCHANT\",\"period\":\"2026-09\"}"},"tags":["admin"],"x-audience":"console","summary":"A money-model statement for one period — DERIVED from orders + payments, not a ledger read (09 §7; OD-411)","description":"This run's finance cut (OD-411): statements are a derived view over OrderStore and the payments seam, split by the 08 §7 money-model columns — gross/net/margin per line (MERCHANT), commission (AGENCY), platform fee (PASS_THROUGH). Every line carries the `{moneyModel, credentialSet, fxSnapshotId}` stamp of its order line; a line whose order is PENDING_RECONCILIATION is `provisional: true` and excluded from `totals` (09 §7). Invoices, payouts, FX-snapshot viewer and remittance match need ledger data the platform does not hold and are not in this contract. Finance edits; Owner, Admin, RevenueManager, Auditor view.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"model","description":"The money model to report: MERCHANT (markup lines), AGENCY (commission lines) or PASS_THROUGH (platform-fee lines).","in":"query","required":true,"schema":{"$ref":"#/components/schemas/ConsoleMoneyModel"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9]{4}-(0[1-9]|1[0-2])$"},"description":"Calendar month, YYYY-MM (tenant time zone)"},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"The statement's lines and server-computed totals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleStatement"},"examples":{"merchantSeptember":{"summary":"200 — MERCHANT, September; one settled line and one provisional line","value":{"model":"MERCHANT","period":"2026-09","currency":"USD","derived":true,"dataThrough":"2026-09-27T09:00:00Z","lines":[{"orderRef":"MO-8KD-2FQ-9XT","itemRef":"OI-1","domain":"HTL","supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT","fxSnapshotId":"fx-2026-09-14T17:00Z-01","bookedAt":"2026-09-14T17:06:02Z","state":"CONFIRMED","provisional":false,"gross":{"amount":84210,"currency":"USD"},"net":{"amount":76555,"currency":"USD"},"margin":{"amount":7655,"currency":"USD"}},{"orderRef":"MO-2AB-7CD-4EF","itemRef":"OI-1","domain":"FLT","supplier":"EXAMPLEGDS","credentialSet":"MONDEE","moneyModel":"MERCHANT","fxSnapshotId":"fx-2026-09-26T21:00Z-02","bookedAt":"2026-09-26T21:09:40Z","state":"PENDING_RECONCILIATION","provisional":true,"gross":{"amount":61200,"currency":"USD"},"net":{"amount":58000,"currency":"USD"},"margin":{"amount":3200,"currency":"USD"}}],"totals":{"lines":1,"provisionalLines":1,"gross":{"amount":84210,"currency":"USD"},"net":{"amount":76555,"currency":"USD"},"margin":{"amount":7655,"currency":"USD"}},"page":{"page":1,"pageSize":50,"total":2}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/ledger:export":{"post":{"operationId":"exportLedger","x-mcp":{"expose":"excluded","toolset":"console","name":"export_ledger","class":"commit-lite","autonomy":"L1","reason":"Bulk finance export (finance:export) that leaves through a signed download link; a data-boundary decision left to a human."},"tags":["admin"],"x-audience":"console","summary":"Start a ledger export (CSV) for a period — answers with a job ref to poll (09 §7)","description":"Asynchronous because a month of lines outgrows a request: 202 with a ConsoleJob; poll GET /admin/v1/jobs/{jobId} until SUCCEEDED, then fetch result.downloadUrl (signed, short-lived). Rows are the same derived statement lines as GET /admin/v1/statements, every money model unless `model` narrows it. Requires the Finance role (09 §12; RevenueManager may export analytics, not the ledger).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleLedgerExportRequest"},"examples":{"september":{"summary":"Export September, every money model, as CSV","value":{"period":"2026-09","format":"CSV"}}}}}},"responses":{"202":{"description":"Export queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleJob"},"examples":{"queued":{"summary":"202 — poll the job","value":{"jobId":"job_01J8QP4","kind":"LEDGER_EXPORT","state":"QUEUED","createdAt":"2026-09-27T09:40:00Z","updatedAt":"2026-09-27T09:40:00Z","progressPct":0}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/jobs/{jobId}":{"get":{"operationId":"getJob","x-mcp":{"expose":"resource","toolset":"console","name":"get_job","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Poll a console job (ledger export today) until it settles","security":[{"bearerAuth":[]}],"parameters":[{"name":"jobId","description":"The job to read, as the call that started it returned it.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The job's current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleJob"},"examples":{"succeeded":{"summary":"200 SUCCEEDED — download link, expires in 15 minutes","value":{"jobId":"job_01J8QP4","kind":"LEDGER_EXPORT","state":"SUCCEEDED","createdAt":"2026-09-27T09:40:00Z","updatedAt":"2026-09-27T09:40:41Z","progressPct":100,"result":{"downloadUrl":"https://api.live.example/admin/v1/jobs/job_01J8QP4/download?sig=3f9a…","contentType":"text/csv","rows":1612,"expiresAt":"2026-09-27T09:55:41Z"}}},"running":{"summary":"200 RUNNING — keep polling","value":{"jobId":"job_01J8QP4","kind":"LEDGER_EXPORT","state":"RUNNING","createdAt":"2026-09-27T09:40:00Z","updatedAt":"2026-09-27T09:40:20Z","progressPct":45}},"failed":{"summary":"200 FAILED — the error is the support handle, not a retry hint","value":{"jobId":"job_01J8QP5","kind":"LEDGER_EXPORT","state":"FAILED","createdAt":"2026-09-27T09:40:00Z","updatedAt":"2026-09-27T09:41:02Z","progressPct":12,"error":{"code":"INTERNAL","message":"Projection read failed; quote the traceId to support","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/rules/versions":{"get":{"operationId":"listRuleSetVersions","x-mcp":{"expose":"tool","toolset":"console","name":"list_rule_set_versions","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The tenant's rule-set version history — immutable, newest first (FEAT-013 §5, §7; OD-408)","description":"Every version ever published, including the platform-seeded pass-through version 1. The currently served one is PUBLISHED; the rest read back RETIRED. Read one with GET /admin/v1/rules/versions/{version} (the same RuleSet shape as GET /v1/config/rules). Requires rules:manage or the Finance / Auditor view (09 §12).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Versions, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleRuleSetVersionList"},"examples":{"history":{"summary":"200 — version 8 is a rollback to 6's body; 7 is retired","value":{"current":8,"versions":[{"version":8,"state":"PUBLISHED","publishedAt":"2026-09-27T09:12:00Z","publishedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","note":"Rollback to v6: the 12% HTL markup floor-clamped every Example Hotels rate","simulationId":null,"ruleCount":2,"rolledBackFrom":7},{"version":7,"state":"RETIRED","publishedAt":"2026-09-25T09:40:00Z","publishedBy":"usr_rm_0142","note":"HTL markup 10% -> 12%","simulationId":"sim_8d20a1","ruleCount":2,"rolledBackFrom":null},{"version":6,"state":"RETIRED","publishedAt":"2026-09-20T10:12:00Z","publishedBy":"usr_rm_0142","note":"Example Hotels sell floor","simulationId":"sim_4c11e0","ruleCount":2,"rolledBackFrom":null}],"page":{"page":1,"pageSize":50,"total":8,"next":null}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/rules/versions/{version}":{"get":{"operationId":"getRuleSetVersion","x-mcp":{"expose":"resource","toolset":"console","name":"get_rule_set_version","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"One immutable rule-set version in full (FEAT-013 §5) — the RuleSet shape of GET /v1/config/rules","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/RuleSetVersionPath"}],"responses":{"200":{"description":"The version","headers":{"ETag":{"schema":{"type":"string"},"description":"\"rs-{version}\" — immutable, so this never changes"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSet"},"examples":{"retired":{"summary":"200 — retired version 7 (the 12% markup that was rolled back)","value":{"version":7,"state":"RETIRED","publishedAt":"2026-09-25T09:40:00Z","publishedBy":"usr_rm_0142","simulationId":"sim_8d20a1","note":"HTL markup 10% -> 12%","rules":[{"ruleId":"rule_platform_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0},{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL"},"action":{"kind":"MARKUP_PCT","value":1200},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/rules/versions/{version}:rollback":{"post":{"operationId":"rollbackRuleSet","x-mcp":{"expose":"excluded","toolset":"console","name":"rollback_rule_set","class":"commit-lite","autonomy":"L1","reason":"Publishes the tenant's markup pricing for every later sale (rules:manage); tenant state that changes what customers pay, and no proposal action carries it."},"tags":["admin"],"x-audience":"console","summary":"Roll back to a prior version — publishes ITS rules as a NEW version; history stays immutable (FEAT-013 §5; OD-408)","description":"Rollback = re-publish, never edit-in-place or re-point (OD-408): the target version's rule list is minted as version current+1 with `rolledBackFrom` naming the version it replaces and no simulationId (the body was published before, so simulate-before-publish is satisfied by construction — [PROPOSED]). Rolling back to the current version is 409 CONFLICT (details.reason ALREADY_CURRENT). Emits config.rules_published (FEAT-013 §8) and is audited as config.ruleset.publish with details.rolledBackFrom. A publish-class action, so `reason` is mandatory (09 §12). Requires rules:manage (Owner, Admin — 09 §12 \"publish\").\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/RuleSetVersionPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleRuleSetRollbackRequest"},"examples":{"rollback":{"summary":"Roll back to version 6","value":{"reason":"v7's 12% HTL markup floor-clamped every Example Hotels rate; restoring v6"}}}}}},"responses":{"201":{"description":"The new PUBLISHED version carrying the target's rules","headers":{"ETag":{"schema":{"type":"string"},"description":"\"rs-{version}\" of the new version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSet"},"examples":{"rolledBack":{"summary":"201 — version 8, body of version 6","value":{"version":8,"state":"PUBLISHED","publishedAt":"2026-09-27T09:12:00Z","publishedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","note":"Rollback to v6: the 12% HTL markup floor-clamped every Example Hotels rate","rules":[{"ruleId":"rule_platform_passthrough","layer":"PLATFORM","scope":{},"action":{"kind":"MARKUP_PCT","value":0},"priority":0},{"ruleId":"rule_tnt_htl_10pct","layer":"TENANT","scope":{"domain":"HTL"},"action":{"kind":"MARKUP_PCT","value":1000},"floor":{"kind":"SUPPLIER_SELL_FLOOR"},"priority":100}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/webhooks/{webhookId}/deliveries/{deliveryId}:replay":{"post":{"operationId":"replayWebhookDelivery","x-mcp":{"expose":"tool","toolset":"console","name":"replay_webhook_delivery","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Re-send one delivery, re-signed with the CURRENT secret; the new row is REPLAYED (09 §10)","description":"Queues one out-of-ladder attempt of the same event to the same endpoint and answers 202 with the new attempt row (`replayOf` names the original). The original row is untouched — the log is append-only. Replaying while the subscription is DISABLED is 409 CONFLICT (details.reason ENDPOINT_DISABLED). Requires webhooks:manage. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/WebhookIdPath"},{"name":"deliveryId","description":"The delivery to send again, as the subscription's delivery log lists it.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"202":{"description":"Replay queued; the new attempt row","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleWebhookDelivery"},"examples":{"replayed":{"summary":"202 — the replay attempt, signed with the current secret","value":{"deliveryId":"dl_01J8QR9","webhookId":"wh_7d31c0","eventId":"ev_01J8Q3","type":"hold.expired","attemptedAt":"2026-09-27T09:41:00Z","attempt":1,"httpStatus":null,"latencyMs":null,"outcome":"REPLAYED","responseSnippet":null,"payloadHash":"1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f809","nextAttemptAt":null,"replayOf":"dl_01J8QR0"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/webhooks/{webhookId}:test":{"post":{"operationId":"testWebhook","x-mcp":{"expose":"tool","toolset":"console","name":"test_webhook","class":"commit-lite","autonomy":"L1","example":"{\"webhookId\":\"WH-91XZ\",\"idempotencyKey\":\"a4e7c2d9-5b18-4f3a-9c6e-7d21b8f0e5a4\",\"body\":{\"type\":\"webhook.test\"}}"},"tags":["admin"],"x-audience":"console","summary":"Send a signed test event to the endpoint — proves signature verification end to end (09 §10)","description":"Delivers a `webhook.test` WebhookEnvelope (or a sample of another subscribed `type`, with fixture data and a `test: true` marker in data) through the real delivery subsystem, so the receiver exercises Mondee-Signature verification exactly as for production (12 §A17). Not retried: one attempt, answered 202 with the attempt row; poll the delivery log for the outcome. Requires webhooks:manage. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/WebhookIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleWebhookTestRequest"},"examples":{"default":{"summary":"The default test event","value":{"type":"webhook.test"}},"sampleOrderEvent":{"summary":"A fixture order.state_changed","value":{"type":"order.state_changed"}}}}}},"responses":{"202":{"description":"Test delivery queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleWebhookDelivery"},"examples":{"queued":{"summary":"202 — the test attempt","value":{"deliveryId":"dl_01J8QS2","webhookId":"wh_7d31c0","eventId":"ev_test_01J8QS2","type":"webhook.test","attemptedAt":"2026-09-27T09:42:00Z","attempt":1,"httpStatus":null,"latencyMs":null,"outcome":"RETRYING","responseSnippet":null,"payloadHash":"5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3f4e5d6c7b8a9f0e1d2c3b4a5f6e","nextAttemptAt":null,"replayOf":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/usage":{"get":{"operationId":"getUsage","x-mcp":{"expose":"tool","toolset":"console","name":"get_usage","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Usage vs quota per endpoint class, plotted against the limits the tenant sees in capabilities (09 §10; 12 §A4)","description":"Gateway counters (12 §A4) bucketed by `granularity` over the window: requests and 429s per endpoint class (search · money · reference · account) beside that class's sustained and burst limits, plus the monthly quotas (searches, orders) with their reset time. One truth for quota: the limits here are the ones GET /v1/capabilities.limits advertises. Owner, Admin, Developer, Finance, RevenueManager and Auditor view (09 §12). Since 1.58.0 the same counters also give the Usage tab's breakdowns, every one over the window: `summary` beside `previous` (the equal window before, absent past the meter's 35-day retention), `statuses`, `latencySeries`, the busiest `endpoints`, per-key `keys`, the 429 bursts in `rateLimitEvents`, the last 7 days by hour in `weekHours`, and the shopping `funnel`. All are sums of metered counts; latency percentiles are the upper bound of the latency bucket the percentile falls in (bounds 25, 50, 100, 250, 500, 1000, 2500, 5000 ms; above that, the slowest request seen). A minute metered before 1.58.0 counts in every total but carries no status, route or latency-bucket breakdown.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"granularity","description":"The width of each bucket in the series, an hour or a day.","in":"query","required":true,"schema":{"type":"string","enum":["hour","day"]}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Default — the last 24 hours (hour) or 30 days (day)"},{"name":"to","description":"End of the window; absent means now.","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Usage series and quotas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleUsage"},"examples":{"hourly":{"summary":"200 — two hourly buckets of the search class; searches quota at 61%","value":{"granularity":"hour","window":{"from":"2026-09-27T07:00:00Z","to":"2026-09-27T09:00:00Z"},"dataThrough":"2026-09-27T09:00:00Z","classes":[{"class":"search","sustainedRps":100,"burst":300,"peakRps":61.4,"points":[{"at":"2026-09-27T07:00:00Z","requests":18420,"rateLimited":0},{"at":"2026-09-27T08:00:00Z","requests":24110,"rateLimited":37}]},{"class":"money","sustainedRps":10,"burst":30,"peakRps":0.8,"points":[{"at":"2026-09-27T07:00:00Z","requests":610,"rateLimited":0},{"at":"2026-09-27T08:00:00Z","requests":744,"rateLimited":0}]}],"quotas":[{"metric":"searches","used":1221000,"limit":2000000,"resetsAt":"2026-10-01T00:00:00Z"},{"metric":"orders","used":1612,"limit":10000,"resetsAt":"2026-10-01T00:00:00Z"}],"summary":{"requests":43884,"rateLimited":37,"serverErrors":12,"meanLatencyMs":182,"p95LatencyMs":500,"maxLatencyMs":4210},"previous":{"requests":40112,"rateLimited":0,"serverErrors":9,"meanLatencyMs":175,"p95LatencyMs":500,"maxLatencyMs":3980},"latencySeries":[{"at":"2026-09-27T07:00:00Z","p95Ms":250},{"at":"2026-09-27T08:00:00Z","p95Ms":500}],"statuses":[{"status":200,"requests":1354},{"status":202,"requests":42431},{"status":404,"requests":50},{"status":429,"requests":37},{"status":500,"requests":12}],"endpoints":[{"method":"POST","path":"/v1/search","class":"search","requests":42530,"rateLimited":37,"serverErrors":8,"meanLatencyMs":190,"points":[{"at":"2026-09-27T07:00:00Z","requests":18420,"rateLimited":0},{"at":"2026-09-27T08:00:00Z","requests":24110,"rateLimited":37}]}],"keys":[{"keyId":"mo_live_7Hq2LxP4aZ9mK3bN","source":"api_key","requests":43120,"rateLimited":37,"serverErrors":12,"lastSeenAt":"2026-09-27T08:59:00Z"},{"keyId":"session","source":"session","requests":764,"rateLimited":0,"serverErrors":0,"lastSeenAt":"2026-09-27T08:41:00Z"}],"rateLimitEvents":[{"at":"2026-09-27T08:14:00Z","keyId":"mo_live_7Hq2LxP4aZ9mK3bN","class":"search","method":"POST","path":"/v1/search","count":37}],"weekHours":[{"at":"2026-09-27T07:00:00Z","requests":19030,"rateLimited":0},{"at":"2026-09-27T08:00:00Z","requests":24854,"rateLimited":37}],"funnel":{"searches":42431,"details":3120,"reprices":402,"holds":0,"orders":61}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/usage/detail":{"get":{"operationId":"getUsageDetail","x-mcp":{"expose":"tool","toolset":"console","name":"get_usage_detail","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"One endpoint, key, rate class or status code over a usage window — the Usage tab's detail (09 §10)","description":"Since 1.59.0. The same metered counters as GET /admin/v1/usage, narrowed to one `dimension` and its `id`: the window's summary beside the equal window before (absent past the meter's 35-day retention), the series by `granularity`, and what it breaks down into. What a dimension carries is what the meter holds for it: a key or a class is whole minutes (statuses, endpoints, a bucketed p95 and the slowest request); an endpoint is a route's own counts (requests, 429s, 5xx and an exact mean latency — no percentile, no status split); a status code is a count per minute (no latency). A figure a dimension cannot carry is absent, never estimated. An id with no traffic in the window answers zeros. Same readers as GET /admin/v1/usage (`usage:read`).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"dimension","description":"What `id` names, an endpoint, an API key, a rate class or a status code.","in":"query","required":true,"schema":{"type":"string","enum":["endpoint","key","class","status"]}},{"name":"id","in":"query","required":true,"schema":{"type":"string","maxLength":300},"description":"endpoint — `\"METHOD /route/template\"` (as ConsoleUsageEndpoint names it); key — the `keyId` of ConsoleUsageKey; class — search | money | reference | account; status — an HTTP status code\n"},{"name":"granularity","description":"The width of each bucket in the series, an hour or a day.","in":"query","required":true,"schema":{"type":"string","enum":["hour","day"]}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Default — the last 24 hours (hour) or 30 days (day)"},{"name":"to","description":"End of the window; absent means now.","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"The dimension's usage over the window","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleUsageDetail"},"examples":{"endpoint":{"summary":"200 — POST /v1/search over two hours, one burst of 429s","value":{"dimension":"endpoint","id":"POST /v1/search","granularity":"hour","window":{"from":"2026-09-27T07:00:00Z","to":"2026-09-27T09:00:00Z"},"dataThrough":"2026-09-27T09:00:00Z","summary":{"requests":42530,"rateLimited":37,"serverErrors":8,"meanLatencyMs":190},"previous":{"requests":40011,"rateLimited":0,"serverErrors":5,"meanLatencyMs":181},"points":[{"at":"2026-09-27T07:00:00Z","requests":18420,"rateLimited":0,"serverErrors":3,"meanLatencyMs":176},{"at":"2026-09-27T08:00:00Z","requests":24110,"rateLimited":37,"serverErrors":5,"meanLatencyMs":201}],"byKey":[{"keyId":"mo_live_7Hq2LxP4aZ9mK3bN","source":"api_key","requests":42500,"rateLimited":37,"serverErrors":8}],"rateLimitEvents":[{"at":"2026-09-27T08:14:00Z","keyId":"mo_live_7Hq2LxP4aZ9mK3bN","class":"search","method":"POST","path":"/v1/search","count":37}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/usage/rows":{"get":{"operationId":"listUsageRows","x-mcp":{"expose":"tool","toolset":"console","name":"list_usage_rows","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Every endpoint, key or 429 burst of a usage window — searchable, filterable, sorted and paged (09 §10)","description":"Since 1.59.0. The Usage tab's \"View all\" behind the top-ten lists of GET /admin/v1/usage: every row of one `dimension` over the window, `q` matched case-insensitively against the row's `id`, narrowed to one rate `class`, ordered by `sort` and paged with an opaque `cursor`. `classCounts` (endpoint and event) counts the searched rows per class before the class filter, for category tabs. Same readers as GET /admin/v1/usage (`usage:read`).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"dimension","description":"What each row is, an endpoint, an API key or a rate-limit event.","in":"query","required":true,"schema":{"type":"string","enum":["endpoint","key","event"]}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Default — the last 24 hours"},{"name":"to","description":"End of the window; absent means now.","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"q","description":"Only rows whose id matches this text, ignoring case.","in":"query","schema":{"type":"string","maxLength":200}},{"name":"class","description":"Only rows of this rate class.","in":"query","schema":{"type":"string","enum":["search","money","reference","account"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["requests","rateLimited","serverErrors","latency","recent"]},"description":"Default — requests (endpoint, key) or recent (event)"},{"name":"cursor","description":"The nextCursor of the previous page; absent for the first page.","in":"query","schema":{"type":"string"}},{"name":"limit","description":"How many rows to return per page.","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"One page of the dimension's rows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleUsageRows"},"examples":{"endpoints":{"summary":"200 — the first page of endpoints, busiest first","value":{"dimension":"endpoint","window":{"from":"2026-09-26T09:00:00Z","to":"2026-09-27T09:00:00Z"},"classCounts":{"search":3,"money":4,"reference":9,"account":2},"total":18,"rows":[{"id":"POST /v1/search","method":"POST","path":"/v1/search","class":"search","requests":42530,"rateLimited":37,"serverErrors":8,"meanLatencyMs":190,"lastSeenAt":"2026-09-27T08:59:00Z"}],"nextCursor":"25"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/branding":{"get":{"operationId":"getBranding","x-mcp":{"expose":"resource","toolset":"console","name":"get_branding","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The site's brand — names, logo refs, palette tokens; brand is data, not code (07 §6, 09 §9; OD-406)","description":"What the console shell applies as CSS variables after the contrast validator passes (OD-406). Scoped to the site the caller is signed in to (07 §1: site is the white-label unit). Colours are #RRGGBB tokens; logos are https URLs the platform serves. Every role reads it (the shell needs it); ContentEditor, Owner and Admin write (09 §12).\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The current brand","headers":{"ETag":{"schema":{"type":"string"},"description":"\"br-{version}\" — changes on every PUT"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleBranding"},"examples":{"acme":{"summary":"200 — the demo tenant's brand","value":{"siteId":"SITE-DEMO","version":3,"displayName":"Acme Travel","shortName":"Acme","logo":{"lightUrl":"https://cdn.example/tenants/TEN-DEMO/logo-light.svg","darkUrl":"https://cdn.example/tenants/TEN-DEMO/logo-dark.svg","markUrl":"https://cdn.example/tenants/TEN-DEMO/mark.svg"},"faviconUrl":"https://cdn.example/tenants/TEN-DEMO/favicon.png","palette":{"primary":"#1F3A5F","onPrimary":"#FFFFFF","accent":"#E8792B","background":"#FFFFFF","surface":"#F5F7FA","text":"#14213D"},"typography":{"fontFamily":"Inter"},"support":{"email":"help@acme.example","url":"https://help.acme.example"},"defaultLocale":"en","updatedAt":"2026-09-25T15:00:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateBranding","x-mcp":{"expose":"excluded","toolset":"console","name":"update_branding","class":"commit-lite","autonomy":"L1","reason":"Changes tenant state (activation, terms, money model, verification, brand); human-owned."},"tags":["admin"],"x-audience":"console","summary":"Replace the site's brand — full object, versioned, audited (07 §6, 09 §9; OD-406)","description":"A full replace (PUT), never a merge: send the whole brand. `version` is optimistic concurrency — it must equal the current version, else 409 CONFLICT (details.currentVersion). Malformed colours or non-https logo URLs are 400 VALIDATION (details.field). The platform's error semantics stay unbrandable (07 §6): only copy and look change here. Audited with before/after (13 §1.1 config change). Requires the ContentEditor, Owner or Admin role.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleBrandingUpdate"},"examples":{"newAccent":{"summary":"Change the accent colour","value":{"version":3,"displayName":"Acme Travel","shortName":"Acme","logo":{"lightUrl":"https://cdn.example/tenants/TEN-DEMO/logo-light.svg","darkUrl":"https://cdn.example/tenants/TEN-DEMO/logo-dark.svg","markUrl":"https://cdn.example/tenants/TEN-DEMO/mark.svg"},"faviconUrl":"https://cdn.example/tenants/TEN-DEMO/favicon.png","palette":{"primary":"#1F3A5F","onPrimary":"#FFFFFF","accent":"#C2410C","background":"#FFFFFF","surface":"#F5F7FA","text":"#14213D"},"typography":{"fontFamily":"Inter"},"support":{"email":"help@acme.example","url":"https://help.acme.example"},"defaultLocale":"en"}}}}}},"responses":{"200":{"description":"The brand after the change, version incremented","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleBranding"},"examples":{"updated":{"summary":"200 — version 4","value":{"siteId":"SITE-DEMO","version":4,"displayName":"Acme Travel","shortName":"Acme","logo":{"lightUrl":"https://cdn.example/tenants/TEN-DEMO/logo-light.svg","darkUrl":"https://cdn.example/tenants/TEN-DEMO/logo-dark.svg","markUrl":"https://cdn.example/tenants/TEN-DEMO/mark.svg"},"faviconUrl":"https://cdn.example/tenants/TEN-DEMO/favicon.png","palette":{"primary":"#1F3A5F","onPrimary":"#FFFFFF","accent":"#C2410C","background":"#FFFFFF","surface":"#F5F7FA","text":"#14213D"},"typography":{"fontFamily":"Inter"},"support":{"email":"help@acme.example","url":"https://help.acme.example"},"defaultLocale":"en","updatedAt":"2026-09-27T09:45:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/credential-sets":{"get":{"operationId":"listCredentialSets","x-mcp":{"expose":"tool","toolset":"console","name":"list_credential_sets","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The tenant's own (BYOC) credential sets — status, health, expiry; never a secret (08 §3, §6; 09 §8)","description":"The credential_set record of 08 §3 minus vaultRef: the console sees last-4 + fingerprint and the FSM status (PENDING_VALIDATION → CERTIFYING → ACTIVE; RATE_LIMITED / EXPIRING / REVOKED / FAILED_HEALTH as alert states). The platform's own sets are never listed — 08 §5's isolation law. Requires credentials:manage (Owner, Admin) or the Developer / Auditor view.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplier","description":"Only the credential sets of this supplier.","in":"query","schema":{"type":"string"}},{"name":"status","description":"Only credential sets in this status.","in":"query","schema":{"$ref":"#/components/schemas/ConsoleCredentialSetStatus"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Credential sets, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCredentialSetList"},"examples":{"oneActive":{"summary":"200 — one ACTIVE Example Activities key expiring in 26 days","value":{"credentialSets":[{"credentialSetId":"tenant-acme-activities-01","supplier":"EXAMPLEACTIVITIES","label":"Acme Example Activities production","products":["EXP"],"environment":"live","status":"EXPIRING","secret":{"kind":"API_KEY","last4":"7Q2f","fingerprint":"sha256:8c1f4a2e9b3d"},"rotation":{"policy":"MANUAL","lastRotatedAt":"2026-03-30T10:00:00Z","expiresAt":"2026-10-23T00:00:00Z"},"health":{"lastProbeAt":"2026-09-27T09:00:00Z","errorRateBps":12},"limits":{"rps":20,"l2bCap":400},"certification":{"state":"PASSED","runId":"cert_01J7ZZ1","completedAt":"2026-04-02T11:20:00Z"},"createdAt":"2026-03-30T10:00:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}],"page":{"page":1,"pageSize":50,"total":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createCredentialSet","x-mcp":{"expose":"excluded","toolset":"console","name":"create_credential_set","class":"commit-lite","autonomy":"L1","reason":"Credentials, keys, secrets, sessions or sign-in; an agent never handles authentication material."},"tags":["admin"],"x-audience":"console","summary":"Enter a BYOC key — write-only into the vault; the validation probe and cert suite start (08 §6, §8; 09 §8)","description":"Step 1 of 08 §8: the secret goes straight to the vault (KMS envelope, region-local key) and is never readable back — this response and every later read show last-4 + fingerprint only. The validation probe runs at once (status PENDING_VALIDATION), then the automated cert suite (CERTIFYING); read the report card at GET …/{credentialSetId}/certification. A set for the same supplier + environment that is not REVOKED is 409 CONFLICT (details.credentialSetId). A supplier that is not byocEligible (08 §2, CQ-03) is 400 VALIDATION (details.field supplier). Requires credentials:manage — never in a default role bundle (15 §1.3) — and a tenant that is not SUSPENDED (403 TENANT_SUSPENDED, 07 §5).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCredentialSetCreate"},"examples":{"viatorKey":{"summary":"A tenant's own Example Activities API key for the sandbox environment","value":{"supplier":"EXAMPLEACTIVITIES","label":"Acme Example Activities sandbox","products":["EXP"],"environment":"sandbox","secret":{"kind":"API_KEY","apiKey":"vtr-sbx-9f2a1cbb7e0d4c5e8a1f3b6d2c4e5f70"},"rotation":{"policy":"MANUAL","expiresAt":"2027-03-31T00:00:00Z"}}}}}}},"responses":{"201":{"description":"Vaulted; probe running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCredentialSet"},"examples":{"pendingValidation":{"summary":"201 — PENDING_VALIDATION; the secret is already unreadable","value":{"credentialSetId":"tenant-acme-activities-sbx-01","supplier":"EXAMPLEACTIVITIES","label":"Acme Example Activities sandbox","products":["EXP"],"environment":"sandbox","status":"PENDING_VALIDATION","secret":{"kind":"API_KEY","last4":"5f70","fingerprint":"sha256:2e9b3d8c1f4a"},"rotation":{"policy":"MANUAL","lastRotatedAt":"2026-09-27T09:50:00Z","expiresAt":"2027-03-31T00:00:00Z"},"health":{"lastProbeAt":null,"errorRateBps":null},"limits":{"rps":null,"l2bCap":null},"certification":{"state":"NOT_STARTED","runId":null,"completedAt":null},"createdAt":"2026-09-27T09:50:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/CredentialForbidden"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/credential-sets/{credentialSetId}":{"delete":{"operationId":"deleteCredentialSet","x-mcp":{"expose":"excluded","toolset":"console","name":"delete_credential_set","class":"commit-lite","autonomy":"L1","reason":"Credentials, keys, secrets, sessions or sign-in; an agent never handles authentication material."},"tags":["admin"],"x-audience":"console","summary":"Revoke and remove a credential set — publish-class, reason mandatory (08 §4 E4; 09 §12)","description":"Moves the set to REVOKED and shreds the vault entry. In-flight orders on it never fall back to another set (08 §4 E4) — they fail PROVIDER_DOWN if the supplier is called again — so a set with orders still PENDING_RECONCILIATION or ON_REQUEST is 409 CONFLICT (details.reason IN_FLIGHT_ORDERS, details.orderRefs) until they settle or `force=true` is given, which is itself audited. `reason` is mandatory: 09 §12 lists credential deletion among the publish-class actions. Idempotent — deleting a REVOKED set is a 204. Requires credentials:manage.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CredentialSetIdPath"},{"name":"reason","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":500},"description":"The ADR-003-style reason string recorded in the audit row"},{"name":"force","in":"query","schema":{"type":"boolean","default":false},"description":"Revoke even with in-flight orders on this set (their next supplier call fails PROVIDER_DOWN)"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Revoked and removed"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/credential-sets/{credentialSetId}/certification":{"get":{"operationId":"getCredentialSetCertification","x-mcp":{"expose":"resource","toolset":"console","name":"get_credential_set_certification","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The certification report card — the nine 08 §8 checks and the canary window (08 §8; 09 §8)","description":"The latest cert run of the set: one row per check of 08 §8 step 2 (auth, search shape, reprice honesty, hold/expiry, book-in-sandbox, cancel + penalty accuracy, retrieve-by-reference, idempotency echo, error taxonomy conformance), each with its status and, on failure, the redacted capture id (13 §2) the console links to. `canary` reports the first-N live bookings window that follows a pass (08 §8 step 3). Streams while RUNNING — poll. Requires credentials:manage or the Developer / Auditor view (09 §8 \"Certification report card — Developer\").\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CredentialSetIdPath"}],"responses":{"200":{"description":"The report card","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCertificationReport"},"examples":{"failedOnPenalty":{"summary":"200 FAILED — eight checks passed, cancel + penalty accuracy failed","value":{"credentialSetId":"tenant-acme-activities-sbx-01","runId":"cert_01J8QT4","state":"FAILED","startedAt":"2026-09-27T09:51:00Z","completedAt":"2026-09-27T10:03:12Z","checks":[{"check":"AUTH","status":"PASSED","durationMs":410,"message":null,"captureId":null},{"check":"SEARCH_SHAPE","status":"PASSED","durationMs":2210,"message":null,"captureId":null},{"check":"REPRICE_HONESTY","status":"PASSED","durationMs":3890,"message":null,"captureId":null},{"check":"HOLD_EXPIRY","status":"SKIPPED","durationMs":null,"message":"Supplier has no hold verb (capabilities NOT_APPLICABLE)","captureId":null},{"check":"BOOK_SANDBOX","status":"PASSED","durationMs":5120,"message":null,"captureId":null},{"check":"CANCEL_PENALTY","status":"FAILED","durationMs":2980,"message":"Quoted penalty 2500 USD, supplier charged 4000 USD (PENALTY_CHANGED not raised)","captureId":"cap_01J8QT9"},{"check":"RETRIEVE_BY_REFERENCE","status":"PASSED","durationMs":640,"message":null,"captureId":null},{"check":"IDEMPOTENCY_ECHO","status":"PASSED","durationMs":1210,"message":null,"captureId":null},{"check":"ERROR_TAXONOMY","status":"PASSED","durationMs":1850,"message":null,"captureId":null}]}},"passedInCanary":{"summary":"200 PASSED — in the canary window, 4 of 10 live bookings reviewed","value":{"credentialSetId":"tenant-acme-activities-01","runId":"cert_01J7ZZ1","state":"PASSED","startedAt":"2026-04-02T11:00:00Z","completedAt":"2026-04-02T11:20:00Z","checks":[{"check":"AUTH","status":"PASSED","durationMs":380,"message":null,"captureId":null},{"check":"SEARCH_SHAPE","status":"PASSED","durationMs":1990,"message":null,"captureId":null},{"check":"REPRICE_HONESTY","status":"PASSED","durationMs":3610,"message":null,"captureId":null},{"check":"HOLD_EXPIRY","status":"SKIPPED","durationMs":null,"message":"Supplier has no hold verb (capabilities NOT_APPLICABLE)","captureId":null},{"check":"BOOK_SANDBOX","status":"PASSED","durationMs":4870,"message":null,"captureId":null},{"check":"CANCEL_PENALTY","status":"PASSED","durationMs":2760,"message":null,"captureId":null},{"check":"RETRIEVE_BY_REFERENCE","status":"PASSED","durationMs":590,"message":null,"captureId":null},{"check":"IDEMPOTENCY_ECHO","status":"PASSED","durationMs":1180,"message":null,"captureId":null},{"check":"ERROR_TAXONOMY","status":"PASSED","durationMs":1720,"message":null,"captureId":null}],"canary":{"required":10,"completed":4,"flagged":0}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/suppliers":{"get":{"operationId":"listConsoleSuppliers","x-mcp":{"expose":"tool","toolset":"console","name":"list_console_suppliers","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The supplier directory — every supplier adapter the platform serves, as this tenant sees it (09 §8; OD-527)","description":"Added in connect-v1 v1.8.0 (OD-527 (a)). The read-only, tenant-filtered supplier directory of 09 §8, read from the platform's adapter registry: a supplier is listed once its adapter's registry descriptor passed the startup gate (F-04, F-05), so the directory is what the platform can actually call, not a catalogue of intentions. One row per supplier: the canonical domains it serves, the verbs and capability flags its descriptor declares, the currencies it prices in (empty = any), and the credential sets THIS tenant may use for it — its own sets (owner TENANT) and the platform's (owner PLATFORM) — each with its 08 §3 FSM status and certification state. Never a secret, a vault reference or another tenant's set (08 §5), and never the platform's contract terms. `status` is the tenant's standing on the supplier, derived from those sets: AVAILABLE (a set serves traffic), DEGRADED (no set serves traffic and one is out only transiently — RATE_LIMITED or FAILED_HEALTH), NO_CREDENTIAL (no set this tenant may use is in service). Regions are not carried: no registry source states them yet. Sorted by supplierId. Requires credentials:read or credentials:manage.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"domain","in":"query","required":false,"description":"Only suppliers serving this domain (canonical code or a registered alias, 02 §1)","schema":{"type":"string"}}],"responses":{"200":{"description":"The directory","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleSupplierDirectory"},"examples":{"twoSuppliers":{"summary":"200 — a dining supplier on the platform key and an experiences supplier on the tenant's own key","value":{"suppliers":[{"supplierId":"SUP-EXP-EXAMPLE","domains":["EXP"],"adapterVersion":"1.0.0","status":"AVAILABLE","verbs":["SEARCH","DETAIL","REPRICE","HOLD","ORDER","POST_BOOKING","RETRIEVE"],"flags":["SUPPORTS_HOLD","RETURNS_CANCEL_POLICY"],"pricingCurrencies":[],"credentialSets":[{"credentialSetId":"TEN-DEMO:SUP-EXP-EXAMPLE:own","owner":"TENANT","environment":"live","status":"ACTIVE","certification":{"state":"PASSED","runId":"cert_01J8QT4","completedAt":"2026-09-27T10:03:12Z"}},{"credentialSetId":"MONDEE","owner":"PLATFORM","environment":"live","status":"ACTIVE","certification":{"state":"NOT_STARTED"}}]},{"supplierId":"SUP-FNB-EXAMPLE","domains":["FNB"],"adapterVersion":"1.0.0","status":"AVAILABLE","verbs":["SEARCH","DETAIL","REPRICE","HOLD","ORDER","POST_BOOKING","RETRIEVE"],"flags":["SUPPORTS_HOLD","RETURNS_CANCEL_POLICY"],"pricingCurrencies":["USD"],"credentialSets":[{"credentialSetId":"MONDEE","owner":"PLATFORM","environment":"live","status":"ACTIVE","certification":{"state":"NOT_STARTED"}}]}]}},"empty":{"summary":"200 — a platform with no supplier adapter wired","value":{"suppliers":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/fare-source-config/{domain}":{"get":{"operationId":"getFareSourceConfig","x-mcp":{"expose":"resource","toolset":"console","name":"get_fare_source_config","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The tenant's fare-source resolution config for one product (08 §4; 09 §8; OD-528)","description":"Added in connect-v1 v1.8.0 (OD-528 (a)). 08 §4's `resolution` object for (tenant, domain): the strategy (PREFER_OWN | OWN_ONLY | MONDEE_ONLY | BLEND), the fallback when the own key cannot serve (MONDEE | NONE), blendDedupe (meaningful only where some effective rule is BLEND) and the perSupplier overrides, with its version. A tenant that never saved one reads the platform default — PREFER_OWN, fallback MONDEE, CHEAPEST_WINS, no overrides — at version 0 with updatedAt null. The fan-out planner reads this same object at plan time (\"a pure function of config + credential health\", 08 §4), so a saved change takes effect at the next search; a request-level fareSourcePolicy may still narrow it, never widen it (08 §4 step 4). An unknown domain is 400 VALIDATION (details.field domain). Requires credentials:read or credentials:manage.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/FareSourceDomainPath"}],"responses":{"200":{"description":"The config (the platform default at version 0 when none was saved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FareSourceConfig"},"examples":{"viatorOwnOnly":{"summary":"200 — experiences prefer the tenant's own keys; Example Activities is own-only","value":{"domain":"EXP","strategy":"PREFER_OWN","fallback":"MONDEE","blendDedupe":"CHEAPEST_WINS","perSupplier":{"SUP-EXP-EXAMPLE":{"strategy":"OWN_ONLY","fallback":"MONDEE"}},"version":3,"updatedAt":"2026-09-20T10:00:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"platformDefault":{"summary":"200 — nothing saved yet; the platform default at version 0","value":{"domain":"HTL","strategy":"PREFER_OWN","fallback":"MONDEE","blendDedupe":"CHEAPEST_WINS","perSupplier":{},"version":0,"updatedAt":null,"updatedBy":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateFareSourceConfig","x-mcp":{"expose":"excluded","toolset":"console","name":"update_fare_source_config","class":"commit-lite","autonomy":"L1","reason":"Requires credentials:manage and changes which supplier credential sets (own vs MONDEE) price every later search, so it is tenant credential and pricing state that a human owns."},"tags":["admin"],"x-audience":"console","summary":"Replace the tenant's fare-source config for one product — versioned, reason mandatory, audited (08 §4; 09 §8; OD-528)","description":"Added in connect-v1 v1.8.0 (OD-528 (a)). A full replace (PUT), never a merge: send the whole resolution object. `expectedVersion` is optimistic concurrency — it must equal the current version (0 for a tenant still on the platform default), else 409 CONFLICT with details.reason VERSION_MOVED and details.currentVersion: a stale editor never overwrites a newer config. `reason` is mandatory (09 §8: \"publishing a config change is audit-stamped\"). perSupplier may only name suppliers the platform serves for the domain (GET /admin/v1/suppliers?domain=), else 400 VALIDATION with details.field `perSupplier.<supplierId>`. Idempotency-Key is required; a replay answers the first outcome. Audited with before/after (13 §1.1 config change). The change takes effect at the next search's plan time. Requires credentials:manage.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/FareSourceDomainPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FareSourceConfigUpdate"},"examples":{"viatorOwnOnly":{"summary":"Make Example Activities own-key only, keep the rest on PREFER_OWN","value":{"strategy":"PREFER_OWN","fallback":"MONDEE","blendDedupe":"CHEAPEST_WINS","perSupplier":{"SUP-EXP-EXAMPLE":{"strategy":"OWN_ONLY","fallback":"MONDEE"}},"reason":"Our Example Activities contract rates only; no platform rates for Example Activities","expectedVersion":2}}}}}},"responses":{"200":{"description":"The config after the change, version incremented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FareSourceConfig"},"examples":{"published":{"summary":"200 — version 3","value":{"domain":"EXP","strategy":"PREFER_OWN","fallback":"MONDEE","blendDedupe":"CHEAPEST_WINS","perSupplier":{"SUP-EXP-EXAMPLE":{"strategy":"OWN_ONLY","fallback":"MONDEE"}},"version":3,"updatedAt":"2026-09-20T10:00:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"description":"CONFLICT VERSION_MOVED — the config moved on since the editor read it; re-read and resend","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionMoved":{"summary":"409 CONFLICT VERSION_MOVED","value":{"code":"CONFLICT","message":"The EXP fare-source config is at version 3; the request was authored against version 2","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERSION_MOVED","currentVersion":3}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getOAuthServerMetadata","x-mcp":{"expose":"excluded","toolset":"public","name":"get_oauth_server_metadata","class":"read","autonomy":"L0","reason":"Anonymous OAuth discovery document; read by MCP clients before any principal exists, not an agent tool."},"tags":["identity"],"summary":"Authorization server metadata (RFC 8414) for MCP clients (FEAT-031 §5.1, ADR-C10 §Phase 2 design D1)","description":"The discovery document of the Apiq authorization server, served at the API origin (the issuer, config aikonic.mcp.grants.issuer). An MCP client reads it after the AI plane's Protected Resource Metadata (RFC 9728, API-0011 §7) names this issuer. It advertises S256 as the only PKCE method, code as the only response type, Client ID Metadata Documents (client_id_metadata_document_supported, the registration the MCP authorization revision 2026-07-28 prefers) beside RFC 7591 dynamic registration (deprecated by that revision, served for older clients), and the RFC 9207 iss parameter on every authorization response. grant_types_supported lists authorization_code, refresh_token and the device_code grant; it deliberately omits client_credentials and token-exchange, which POST /v1/oauth/token serves to API keys and to the AI plane only. scopes_supported lists the scopes this rung admits: mcp:console:read and mcp:supplier:read wherever grants are on; mcp:console:write only where the platform write ceiling aikonic.mcp.grants.writes.enabled is on (ADR-C10 Phase 3, DR-68); mcp:operator:read only where operator grants are enabled and aikonic.ops.mfa.required is true (ADR-C10 Phase 4). Not envelope-shaped (see the identity tag). Where grants are off (aikonic.mcp.grants.enabled false: every rung above SANDBOX until OD-28 signs) the route answers 404 NOT_FOUND.\n","security":[],"responses":{"200":{"description":"The metadata document","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=300"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthServerMetadata"},"examples":{"sandbox":{"summary":"The SANDBOX authorization server","value":{"issuer":"https://apiq-sandbox.tripproagencysites.com","authorization_endpoint":"https://apiq-sandbox.tripproagencysites.com/v1/oauth/authorize","token_endpoint":"https://apiq-sandbox.tripproagencysites.com/v1/oauth/token","registration_endpoint":"https://apiq-sandbox.tripproagencysites.com/v1/oauth/register","device_authorization_endpoint":"https://apiq-sandbox.tripproagencysites.com/v1/oauth/device_authorization","revocation_endpoint":"https://apiq-sandbox.tripproagencysites.com/v1/oauth/revoke","scopes_supported":["mcp:console:read","mcp:console:write","mcp:supplier:read"],"response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code"],"token_endpoint_auth_methods_supported":["none"],"revocation_endpoint_auth_methods_supported":["none"],"code_challenge_methods_supported":["S256"],"client_id_metadata_document_supported":true,"authorization_response_iss_parameter_supported":true}},"dev":{"summary":"A DEV authorization server with console writes and operator grants on (connect-v1 1.26.0, ADR-C10 Phases 3 and 4)","value":{"issuer":"https://api.dev.example","authorization_endpoint":"https://api.dev.example/v1/oauth/authorize","token_endpoint":"https://api.dev.example/v1/oauth/token","registration_endpoint":"https://api.dev.example/v1/oauth/register","device_authorization_endpoint":"https://api.dev.example/v1/oauth/device_authorization","revocation_endpoint":"https://api.dev.example/v1/oauth/revoke","scopes_supported":["mcp:console:read","mcp:console:write","mcp:operator:read","mcp:supplier:read"],"response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code"],"token_endpoint_auth_methods_supported":["none"],"revocation_endpoint_auth_methods_supported":["none"],"code_challenge_methods_supported":["S256"],"client_id_metadata_document_supported":true,"authorization_response_iss_parameter_supported":true}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/aikonic.json":{"get":{"operationId":"getAgentManifest","x-mcp":{"expose":"excluded","toolset":"public","name":"get_agent_manifest","class":"read","autonomy":"L0","reason":"Unauthenticated discovery document (security: []) fetched by URL before any credential exists, not an agent tool; a tenant's agent reads the same document through the onboard tool (FEAT-034 §5.5)."},"tags":["discovery"],"summary":"The signed onboarding manifest — environments, sign-up, key defaults, velocity, rail, certification (FEAT-034 §5.5)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.manifest.enabled is on, else 404 NOT_FOUND). The one URL a coding agent is given: every environment's API and MCP endpoints, the sign-up routes and identities, the default key scopes for sandbox and LIVE_CAPPED agent keys, the velocity and loop thresholds (OD-AGT-6), the rail step keys, the certification checks (advisory ones apart), the scenario catalogue, products, the pricing summary, SDK package names and the llms.txt, openapi.json and MCP documentation URLs. Public data, no credential, Cache-Control public, max-age=3600. Signed (OD-AGT-7): jws is a detached-payload compact JWS (ES256) over the RFC 8785 canonical JSON of the document without jws, verifiable with the key of jws.kid at GET /.well-known/aikonic-jwks.json; an agent refuses a document that does not verify, and one past its expiresAt (issuedAt + 24 h [assumption]). The key set is served from the same origin, so it is no independent trust anchor: the kid thumbprints are also published out of band, in the SDKs and the docs, and an agent pins them (OD-AGT-7) [verify]. The signer is the platform KMS (aikonic.manifest.jws.signer kms; local is for DEV and SANDBOX and refused at boot above SANDBOX). If the signing key is unavailable the last signed document is served from cache and an alarm raised (FEAT-034 §12) until its expiresAt. Family API-0018. MCP: excluded.\n","security":[],"responses":{"200":{"description":"The signed manifest","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=3600"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentManifest"},"examples":{"sandbox":{"summary":"The manifest as served on SANDBOX, signed with kid aik-manifest-2026-10","value":{"manifestVersion":"2026-10-27.1","issuedAt":"2026-10-27T00:00:00Z","expiresAt":"2026-10-28T00:00:00Z","environments":{"sandbox":{"api":"https://apiq-sandbox.tripproagencysites.com","mcp":"https://mcp.example/mcp/public"},"live":{"api":"https://api.live.example","mcp":"https://mcp.live.example/mcp/public"}},"signup":{"device":"https://apiq-sandbox.tripproagencysites.com/v1/signup/device","deviceToken":"https://apiq-sandbox.tripproagencysites.com/v1/signup/device:token","agentPath":"Start POST /v1/signup/device, show the person user_code and verification_uri, poll deviceToken every interval seconds, then call GET /v1/onboard with the sandbox key it returns; the person signs in to the console themselves.","email":"https://apiq-sandbox.tripproagencysites.com/v1/signup","identities":["email_code"]},"keys":{"defaults":{"sandbox":{"writes":"book","products":null,"spendPerDay":null,"lookToBook":500,"reqPerSec":10,"holdsPerHour":60,"cancelsPerDay":50,"expiresAt":null},"liveCapped":{"writes":"book-requires-approval","products":null,"spendPerDay":null,"lookToBook":1500,"reqPerSec":10,"holdsPerHour":30,"cancelsPerDay":20,"expiresAt":null}},"lifetimeDays":{"sandbox":90,"liveCapped":30},"liveCappedSpendPercentOfTenantCap":20},"velocity":{"pauseOnSoftBreach":3,"softBreachWindowHours":24,"burstMultiplier":5,"identicalRequestsPerMinute":20,"holdsWithoutOutcomePer10Minutes":50,"callsAfterStopBeforeRevoke":100},"rail":["verify_email","sandbox_booking","business_profile","business_verification","money_model","fund_wallet","certification","terms","go_live"],"certification":{"checks":["idempotent_retry","price_change_handled","async_respected","webhook_verified","ambiguity_rule","deprecation_logged"],"advisory":["velocity_retry_honoured","stopped_on_pause"]},"scenarios":"https://apiq.example/developers/scenarios.json","products":["flights","hotels"],"pricing":"https://apiq.example/pricing","sdks":{"npm":"@mondee-one/connect-client","pypi":"mondee-connect-client","maven":"com.mondee.connect:connect-client"},"docs":{"llms":"https://apiq.example/llms.txt","openapi":"https://apiq.example/openapi.json","mcp":"https://apiq.example/developers/mcp"},"jws":{"kid":"aik-manifest-2026-10","alg":"ES256","signature":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImFpay1tYW5pZmVzdC0yMDI2LTEwIn0..-v4QAPhwTpJUF0s6xravqJaM2z1K6PVSzRSu-MhNx4y70ug1saRy71wabV4rvUssk1Z2Kkre3ZG-7v6vtX8FiA"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/aikonic-jwks.json":{"get":{"operationId":"getAgentManifestKeys","x-mcp":{"expose":"excluded","toolset":"public","name":"get_agent_manifest_keys","class":"read","autonomy":"L0","reason":"Unauthenticated discovery document (security: []) fetched by URL to verify the manifest signature, not an agent tool (FEAT-034 §5.5, OD-AGT-7)."},"tags":["discovery"],"summary":"The public keys that verify the onboarding manifest (JWKS, RFC 7517)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; served when aikonic.manifest.enabled is on, else 404 NOT_FOUND). The JSON Web Key Set holding the public half of every manifest signing key in use. During a rotation (90 days, OD-AGT-7 default) two kids are live: the new one signs, the old one still verifies cached documents. Public data, Cache-Control public, max-age=3600. Family API-0018. MCP: excluded.\n","security":[],"responses":{"200":{"description":"The key set","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=3600"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManifestJwks"},"examples":{"twoKeys":{"summary":"Two keys live during a rotation","value":{"keys":[{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0","kid":"aik-manifest-2026-10","use":"sig","alg":"ES256"},{"kty":"EC","crv":"P-256","x":"WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis","y":"y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE","kid":"aik-manifest-2026-07","use":"sig","alg":"ES256"}]}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/authorize":{"get":{"operationId":"oauthAuthorize","x-mcp":{"expose":"excluded","toolset":"public","name":"oauth_authorize","class":"commit-lite","autonomy":"L1","reason":"Browser redirect that starts a human consent. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["identity"],"summary":"Start an MCP grant — OAuth 2.1 authorization code with PKCE (FEAT-031 §5.1, ADR-C10 §Phase 2 design D1, D8)","description":"Opened in the human's browser by an MCP client. Checks run in this order. (1) client_id: a client registered at POST /v1/oauth/register, or an https URL naming a Client ID Metadata Document, which the platform fetches with an SSRF guard (https only, public addresses only, checked on the address actually connected to so DNS rebinding cannot reach an internal host, no redirects, at most 5 KB, 2 s, cached at most 24 h) and whose client_id must equal the URL. (2) redirect_uri: exact match against the registered or published list, except that for a registered http loopback IP literal (127.0.0.1 or [::1]) only the port may differ (RFC 8252 §7.3); scheme, host, path and query must still match, and the name localhost is never treated as loopback. If (1) or (2) fails the answer is a plain 400 and the browser is NEVER redirected. (3) response_type: code only. (4) code_challenge and code_challenge_method: S256 only; plain or absent is refused with reason PKCE_S256_REQUIRED. (5) resource (RFC 8707, required): exactly one of the configured MCP resources, the plane's /mcp/console, /mcp/supplier or /mcp/operator URL; any other value is invalid_target. The operator resource (ADR-C10 Phase 4) is invalid_target with reason OPERATOR_GRANTS_DISABLED where aikonic.mcp.grants.toolsets.operator.enabled is false and with reason OPERATOR_MFA_NOT_ENFORCED where aikonic.ops.mfa.required is false on the rung. (6) scope: absent (the resource's read scope), or a space-separated list from the resource's scopes. Console: mcp:console:read, plus mcp:console:write (ADR-C10 Phase 3, DR-68), which must come with mcp:console:read and is invalid_scope with reason WRITES_DISABLED where the platform write ceiling aikonic.mcp.grants.writes.enabled is false. Supplier: mcp:supplier:read only. Operator: mcp:operator:read only (reads only, ADR-C10 R3, OD-899). Any other scope, mcp:supplier:write and mcp:operator:write included, is invalid_scope. The write scope does not itself grant a write: the consent screen offers only the tools the tenant enabled and the human may use, every one unticked. (7) state: passed back unchanged. Refusals from (3) on are redirects to redirect_uri with error, error_description, state and iss (RFC 9207) only when that redirect_uri is a loopback IP literal or the client is a platform pre-registered (verified) client; for any other redirect_uri of an unverified client the refusal is a plain 400 with the same error and reason, so an anonymously registered client cannot turn this route into an open redirector to its own host (RFC 9700 §4.11.2). Requests are rate limited per client address (details.action oauth-authorize), which also bounds the metadata-document fetches an anonymous caller can cause. On success the platform records a pending grant request (10 minutes) and answers 302 to the consent screen of the realm the resource belongs to, {consent-base}/console/connect/authorize?request=<id>, {consent-base}/supplier/connect/authorize?request=<id> or {consent-base}/admin/connect/authorize?request=<id> (operator), where the signed-in human reads it (getMcpGrantRequest, supplierGetMcpGrantRequest or opsGetMcpGrantRequest) and approves or denies. Every client registered dynamically or by metadata document is UNVERIFIED on that screen. There is no implicit grant and no password grant. Off (404 NOT_FOUND) where aikonic.mcp.grants.enabled is false.\n","security":[],"parameters":[{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"client_id","in":"query","required":true,"description":"A registered client id, or the https URL of the client's metadata document","schema":{"type":"string","maxLength":2048}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri","maxLength":2048}},{"name":"code_challenge","in":"query","required":true,"description":"BASE64URL(SHA-256(code_verifier)), 43 characters (RFC 7636 §4.2)","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$"}},{"name":"code_challenge_method","in":"query","required":true,"schema":{"type":"string","enum":["S256"]}},{"name":"resource","in":"query","required":true,"description":"The canonical URL of the MCP endpoint the grant is for (RFC 8707)","schema":{"type":"string","format":"uri","maxLength":2048}},{"name":"scope","in":"query","required":false,"description":"Space-separated (RFC 6749 §3.3). mcp:console:read, optionally with mcp:console:write; mcp:supplier:read; or mcp:operator:read (connect-v1 1.26.0, ADR-C10 Phases 3 and 4)\n","schema":{"type":"string","maxLength":256,"pattern":"^mcp:[a-z]+:(read|write)( mcp:[a-z]+:(read|write))*$"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","maxLength":512}}],"responses":{"302":{"description":"To the realm's consent screen (success), or to redirect_uri with error, error_description, state and iss (a refusal after the client and redirect_uri checks, when redirect_uri is a loopback IP literal or the client is verified)\n","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"For example https://console.example/console/connect/authorize?request=mgr_01JAD4K7Q2W9XN5T3B8C6V1R0M, or http://127.0.0.1:53682/callback?error=invalid_request&error_description=PKCE%20S256%20is%20required.&state=af0ifjsldkj&iss=https%3A%2F%2Fapiq-sandbox.tripproagencysites.com\n"},"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}}},"400":{"description":"invalid_request (reason REDIRECT_URI_MISMATCH) or invalid_client (reason CLIENT_UNKNOWN or CLIENT_METADATA_INVALID): the client or the redirect_uri could not be trusted, so the browser is not redirected (RFC 6749 §4.1.2.1). Also any later refusal (checks 3 to 6) for an unverified client whose redirect_uri is not a loopback IP literal, answered here instead of by redirect so the route is never an open redirector (RFC 9700 §4.11.2)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"redirectMismatch":{"summary":"invalid_request — redirect_uri is not one the client registered; no redirect","value":{"error":"invalid_request","error_description":"The redirect_uri is not registered for this client.","reason":"REDIRECT_URI_MISMATCH"}},"clientMetadataInvalid":{"summary":"invalid_client — the metadata document could not be fetched or does not name its own URL","value":{"error":"invalid_client","error_description":"The client metadata document is not valid.","reason":"CLIENT_METADATA_INVALID"}},"operatorMfaNotEnforced":{"summary":"invalid_target, OPERATOR_MFA_NOT_ENFORCED — this rung does not require the operator second factor (connect-v1 1.26.0)","value":{"error":"invalid_target","error_description":"Operator agent access is not available in this environment.","reason":"OPERATOR_MFA_NOT_ENFORCED"}},"writesDisabled":{"summary":"invalid_scope, WRITES_DISABLED — the platform write ceiling is off on this rung (connect-v1 1.26.0)","value":{"error":"invalid_scope","error_description":"The requested scope is not available for this resource.","reason":"WRITES_DISABLED"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/register":{"post":{"operationId":"registerOAuthClient","x-mcp":{"expose":"excluded","toolset":"public","name":"register_oauth_client","class":"commit-lite","autonomy":"L1","reason":"Anonymous client registration with no principal for the autonomy ladder. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["identity"],"summary":"Dynamic client registration for MCP clients (RFC 7591; deprecated by MCP 2026-07-28, served for older clients)","description":"DEPRECATED by the MCP authorization specification revision 2026-07-28, which prefers Client ID Metadata Documents (pass the document's https URL as client_id instead); served for MCP clients that implement only RFC 7591. Public clients only: token_endpoint_auth_method is none and PKCE S256 is mandatory at authorize, so no client secret is ever issued. Every client registered here is UNVERIFIED on the consent screen (ADR-C10 §Decision item 4), and a client unused for 30 days is removed. redirect_uris must be https URLs or loopback IP literals (http://127.0.0.1 or http://[::1], any port, RFC 8252); a device-flow-only client (grant_types without authorization_code) may omit them. Rate limited per client address (details.action oauth-register). Not envelope-shaped (see the identity tag). Off (404 NOT_FOUND) where aikonic.mcp.grants.enabled is false.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientRegistrationRequest"},"examples":{"loopback":{"summary":"A desktop MCP client with a loopback redirect","value":{"client_name":"Example IDE Assistant","redirect_uris":["http://127.0.0.1/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}}}}}},"responses":{"201":{"description":"Registered","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientRegistrationResponse"},"examples":{"registered":{"summary":"201 — a public, unverified client","value":{"client_id":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","client_id_issued_at":1792058400,"client_name":"Example IDE Assistant","redirect_uris":["http://127.0.0.1/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none","aikonic_client_verified":false}}}}}},"400":{"description":"RFC 7591 §3.2.2 refusal — invalid_redirect_uri or invalid_client_metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"invalidRedirect":{"summary":"invalid_redirect_uri — neither https nor a loopback IP literal","value":{"error":"invalid_redirect_uri","error_description":"Each redirect URI must be https or a loopback IP literal."}},"invalidMetadata":{"summary":"invalid_client_metadata — a confidential client was requested","value":{"error":"invalid_client_metadata","error_description":"Only public clients (token_endpoint_auth_method none) are registered."}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/device_authorization":{"post":{"operationId":"startDeviceAuthorization","x-mcp":{"expose":"excluded","toolset":"public","name":"start_device_authorization","class":"commit-lite","autonomy":"L1","reason":"Starts a human consent on a device without a browser. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["identity"],"summary":"Start an MCP grant on a device without a browser (RFC 8628; console and supplier only)","description":"The fallback for an MCP client that cannot open a browser (FEAT-031 §5.2). The client sends its client_id, the resource (the plane's /mcp/console or /mcp/supplier URL) and optionally the resource's read scope, and receives a device_code to poll POST /v1/oauth/token with and a user_code for the human. The human opens verification_uri (the realm's Connect page, {consent-base}/console/connect or {consent-base}/supplier/connect), signs in and types the code; there is deliberately no verification_uri_complete, so the code is always typed (ADR-C10 T2). user_code is 8 letters of the RFC 8628 §6.1 base-20 alphabet shown as XXXX-XXXX; it lives 10 minutes and serves one approval, and the device_code is redeemed for tokens once (a later poll is invalid_grant). Poll no faster than interval (5 s); polling faster answers slow_down. The operator resource is refused 400 invalid_target with reason DEVICE_FLOW_NOT_ALLOWED: the operator realm never offers a device flow, on every setting. scope is the resource's read scope, or for console mcp:console:read with mcp:console:write (ADR-C10 Phase 3; invalid_scope reason WRITES_DISABLED where the platform write ceiling is off); every other scope is invalid_scope. Off (404 NOT_FOUND) where aikonic.mcp.grants.enabled or aikonic.mcp.grants.device-flow.enabled is false.\n","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationRequest"},"examples":{"supplier":{"summary":"A headless agent asks to read the supplier toolset","value":{"client_id":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","resource":"https://mcp.example/mcp/supplier","scope":"mcp:supplier:read"}}}}}},"responses":{"200":{"description":"The codes (RFC 8628 §3.2)","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationResponse"},"examples":{"issued":{"summary":"200 — the human types WDJB-MJHT at the supplier Connect page","value":{"device_code":"mcd_Gk3Lr9QzVb7XnM2pWc5Ht8Jd4Fs6Ay1E","user_code":"WDJB-MJHT","verification_uri":"https://console.example/supplier/connect","expires_in":600,"interval":5}}}}}},"400":{"description":"invalid_request (a missing or repeated parameter), invalid_scope (a scope the resource does not admit; reason WRITES_DISABLED for mcp:console:write with the write ceiling off) or invalid_target (an unknown resource; reason DEVICE_FLOW_NOT_ALLOWED for the operator resource)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"operatorRefused":{"summary":"invalid_target — the operator realm never offers a device flow","value":{"error":"invalid_target","error_description":"The device flow is not offered for this resource.","reason":"DEVICE_FLOW_NOT_ALLOWED"}},"writeScope":{"summary":"invalid_scope — a write scope the resource does not admit (mcp:supplier:write, mcp:operator:write)","value":{"error":"invalid_scope","error_description":"The requested scope is not available for this resource."}}}}}},"401":{"description":"invalid_client — the client_id is not a registered client or a valid metadata document","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"unknownClient":{"summary":"invalid_client — unknown client","value":{"error":"invalid_client","error_description":"The client is not known.","reason":"CLIENT_UNKNOWN"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/revoke":{"post":{"operationId":"revokeOAuthToken","x-mcp":{"expose":"excluded","toolset":"public","name":"revoke_oauth_token","class":"commit-lite","autonomy":"L1","reason":"Client-side revocation, called by the client outside the tool surface. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["identity"],"summary":"Revoke an MCP grant's refresh or access token (RFC 7009)","description":"The MCP client revokes the grant it holds by sending its refresh token (or a current access token) with its own client_id. Revoking either ends the WHOLE grant: its refresh token is refused at once and its access tokens within one access-token lifetime (10 minutes at most; the AI plane re-checks every token at most 60 s after its last check). The answer is 200 with an empty body whether or not the token was valid, already revoked or belongs to another client (RFC 7009 §2.2), so the route reveals nothing; a token of another client is not revoked. Only invalid_request (no token or no client_id) is refused. API keys and their client-credentials tokens are revoked through DELETE /v1/keys/{keyPrefix}, not here.\n","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthRevokeRequest"},"examples":{"refresh":{"summary":"The client disconnects and revokes its refresh token","value":{"token":"mcr_EXAMPLEONLYnotARealRefreshToken0000","token_type_hint":"refresh_token","client_id":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6"}}}}}},"responses":{"200":{"description":"Revoked, or nothing to revoke (always the same answer)"},"400":{"description":"invalid_request — the token or client_id parameter is missing or repeated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"missingToken":{"summary":"invalid_request — no token","value":{"error":"invalid_request","error_description":"The token parameter is required."}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/mcp/grant-requests/{requestId}":{"get":{"operationId":"getMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"get_mcp_grant_request","class":"read","autonomy":"L0","reason":"Consent screen data for a pending grant; human session only, an agent never reads its own consent."},"tags":["identity"],"summary":"The consent screen of a pending console MCP grant (FEAT-031 §5.1, ADR-C10 §Phase 2 design D7)","description":"HUMAN CONSOLE SESSION ONLY: an API key, a service token or an MCP grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED. What the console's Approve agent screen shows: the client (name; VERIFIED only for a client the platform pre-registered, otherwise UNVERIFIED; the redirect host), the toolset, the scope with a plain description, the read operations of the toolset's generated allow-list (the x-mcp tools and resources the grant can reach, all pre-ticked), the grant lifetime and, for a device flow, the requesting address's country. A request of the supplier realm, an unknown id and an expired request are all 404 NOT_FOUND. Not envelope-shaped (see the identity tag).\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"The pending request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequest"},"examples":{"authCode":{"summary":"200 — an unverified desktop client asks to read the console toolset","value":{"requestId":"mgr_01JAD4K7Q2W9XN5T3B8C6V1R0M","flow":"AUTH_CODE","realm":"TENANT","toolset":"console","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false,"registration":"METADATA_DOCUMENT","redirectHost":"127.0.0.1","logoUri":null},"scopes":[{"scope":"mcp:console:read","description":"Read your console data as you (queues, audit, team, usage, agent proposals). It cannot book, cancel, pay or approve."}],"operations":[{"name":"list_agent_proposals","kind":"tool","title":"The agent desk's inbox"},{"name":"get_queue","kind":"tool","title":"One of the fulfilment queues"},{"name":"get_console_home","kind":"resource","title":"Console home tiles"}],"lifetimeSeconds":28800,"requesterIpCountry":null,"writeOperations":[],"crossTenantReads":[],"stepUpRequired":false,"secondApprovalRequired":false,"approvalId":null,"redirectTo":null}},"writeOffered":{"summary":"200 — the client asked for mcp:console:write and the tenant enabled one write; the box is unticked (connect-v1 1.26.0)","value":{"requestId":"mgr_01JAE1V7J3N5QS9X1T3H5M7P0R","flow":"AUTH_CODE","realm":"TENANT","toolset":"console","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false,"registration":"METADATA_DOCUMENT","redirectHost":"127.0.0.1","logoUri":null},"scopes":[{"scope":"mcp:console:read","description":"Read your console data as you (queues, audit, team, usage, agent proposals). It cannot book, cancel, pay or approve."},{"scope":"mcp:console:write","description":"Make the changes you tick below as you. It still cannot book, cancel, pay or approve."}],"operations":[{"name":"get_queue","kind":"tool","title":"One of the fulfilment queues"}],"writeOperations":[{"name":"claim_queue_item","title":"Claim a queue row for you","permission":"orders:write"}],"crossTenantReads":[],"lifetimeSeconds":28800,"requesterIpCountry":null,"stepUpRequired":false,"secondApprovalRequired":false,"approvalId":null,"redirectTo":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/mcp/grant-requests:lookup":{"post":{"operationId":"lookupMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"lookup_mcp_grant_request","class":"read","autonomy":"L0","reason":"Consent screen data for a pending grant; human session only, an agent never reads its own consent."},"tags":["identity"],"summary":"Find a pending console device-flow grant by the code the human typed (RFC 8628 §3.3)","description":"HUMAN CONSOLE SESSION ONLY (a key, service token or grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED). The console's Connect page posts the user_code the human typed; the code travels in the body so it never reaches a URL or an access log. Case and the dash are ignored. Answers the same McpGrantRequest as getMcpGrantRequest, flow DEVICE_CODE, with requesterIpCountry. An unknown, expired, spent or supplier-realm code is 404 NOT_FOUND details.reason USER_CODE_UNKNOWN (one answer, so codes cannot be probed). Lookups are rate limited per session and per client address: 429 RATE_LIMITED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestLookup"},"examples":{"typed":{"summary":"The code from the agent's screen","value":{"userCode":"WDJB-MJHT"}}}}}},"responses":{"200":{"description":"The pending request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequest"},"examples":{"deviceCode":{"summary":"200 — a device-flow request; the human must pick the toolset by hand","value":{"requestId":"mgr_01JAD5B2H8M4QZ7T1W9C3V6R5N","flow":"DEVICE_CODE","realm":"TENANT","toolset":"console","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","name":"Example CLI Agent","verified":false,"registration":"DYNAMIC","redirectHost":null,"logoUri":null},"scopes":[{"scope":"mcp:console:read","description":"Read your console data as you (queues, audit, team, usage, agent proposals). It cannot book, cancel, pay or approve."}],"operations":[{"name":"list_audit_records","kind":"tool","title":"The tenant-visible audit log"}],"lifetimeSeconds":28800,"requesterIpCountry":"PT","writeOperations":[],"crossTenantReads":[],"stepUpRequired":false,"secondApprovalRequired":false,"approvalId":null,"redirectTo":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/McpUserCodeUnknown"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/mcp/grant-requests/{requestId}:approve":{"post":{"operationId":"approveMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"approve_mcp_grant_request","class":"commit-money","autonomy":"L1","reason":"Issues a credential (rule 6) and is a decision route (rule 8); human session only."},"tags":["identity"],"summary":"The signed-in human approves an MCP grant for themselves (FEAT-031 §5.1, §5.2; ADR-C10 §Phase 2 design D5, D6)","description":"HUMAN CONSOLE SESSION ONLY: an API key, a service token and an MCP grant-derived token are 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, so an agent can never approve its own grant. The grant is bound to the approving human and to the session (refresh family) the approval came from: logout, logout-all, a password change or reset, user disable, tenant SUSPENDED or OFFBOARDED and session reuse detection all end it. Its lifetime is the earlier of 8 hours and the bound session's expiry. toolset is required and must equal the request's (for a device flow the human selects it by hand); operations, if sent, narrows the grant to those x-mcp names, a non-empty subset of the request's operations (default: all of them); confirmOwnDevice true is required for a device flow. The tenant must be ACTIVE (a SUSPENDED tenant is 403 TENANT_SUSPENDED), the tenant's console toolset switch ON (getMcpSettings; OFF is 403 PERMISSION_DENIED details.reason TOOLSET_DISABLED) and grants enabled on the rung (else 403 PERMISSION_DENIED details.reason GRANTS_DISABLED). A request that is no longer PENDING is 409 CONFLICT details.reason WRONG_STATE. For an authorization-code request the answer carries redirectTo, the client's redirect_uri with code, state and iss, and the browser goes exactly there; the code is single-use and lives 60 s. For a device flow the answer is {approved: true} and the agent's next poll receives the tokens. A grant never reaches a route outside its toolset's read operations, and never one the human cannot reach now (ADR-C10 invariant 5). connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): writeOperations, a subset of the request's offered writeOperations, gives write consent for those console commit-lite tools; it needs the step-up proof (stepUpCode or stepUpAssertion; absent 403 STEP_UP_REQUIRED, wrong STEP_UP_FAILED). A ticked write runs only while the tenant keeps it ON, the platform and plane write ceilings are on and the human holds its permission; money and decisions stay unreachable. Emits mcp.grant.approved (internal event; a console (TENANT-realm) one is also delivered to its tenant as a webhook, 1.45.0, DR-78) and writes the audit row mcp.grant.approve (writeOperations, stepUp).\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestApproval"},"examples":{"allReads":{"summary":"Approve every read the request lists","value":{"toolset":"console"}},"narrowed":{"summary":"Approve two reads only","value":{"toolset":"console","operations":["list_agent_proposals","get_queue"]}},"device":{"summary":"A device flow — the toolset picked by hand and the own-device confirmation ticked","value":{"toolset":"console","confirmOwnDevice":true}},"withWrite":{"summary":"Approve the reads and one write, with a step-up code (connect-v1 1.26.0, ADR-C10 Phase 3)","value":{"toolset":"console","writeOperations":["claim_queue_item"],"stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Approved","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"redirect":{"summary":"200 — authorization code; the browser goes to redirectTo","value":{"redirectTo":"http://127.0.0.1:53682/callback?code=mca_EXAMPLEONLYcode0001&state=af0ifjsldkj&iss=https%3A%2F%2Fapiq-sandbox.tripproagencysites.com"}},"device":{"summary":"200 — device flow; the agent's next poll receives the tokens","value":{"approved":true}}}}}},"400":{"$ref":"#/components/responses/McpGrantApprovalInvalid"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpGrantApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/mcp/grant-requests/{requestId}:deny":{"post":{"operationId":"denyMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"public","name":"deny_mcp_grant_request","class":"commit-lite","autonomy":"L1","reason":"Decision route on a consent (rule 8); human session only."},"tags":["identity"],"summary":"The signed-in human denies a pending MCP grant","description":"HUMAN CONSOLE SESSION ONLY (a key, service token or grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED). For an authorization-code request the answer carries redirectTo, the client's redirect_uri with error=access_denied, state and iss; for a device flow it is {denied: true} and the agent's next poll answers access_denied. A request that is no longer PENDING is 409 CONFLICT details.reason WRONG_STATE. Emits mcp.grant.denied (internal event; a console (TENANT-realm) one is also delivered to its tenant as a webhook, 1.45.0, DR-78).\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"Denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"redirect":{"summary":"200 — authorization code; the browser goes to redirectTo","value":{"redirectTo":"http://127.0.0.1:53682/callback?error=access_denied&state=af0ifjsldkj&iss=https%3A%2F%2Fapiq-sandbox.tripproagencysites.com"}},"device":{"summary":"200 — device flow","value":{"denied":true}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grant-requests/{requestId}":{"get":{"operationId":"supplierGetMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_get_mcp_grant_request","class":"read","autonomy":"L0","reason":"Consent screen data for a pending grant; human session only, an agent never reads its own consent."},"tags":["supplier"],"x-audience":"supplier","summary":"The consent screen of a pending supplier MCP grant (FEAT-031 §5.1, ADR-C10 §Phase 2 design D7)","description":"The supplier realm's getMcpGrantRequest: the same McpGrantRequest, realm SUPPLIER, toolset supplier, read by a signed-in supplier user. Supplier sessions are realm tokens that /v1 cannot verify, so the consent routes exist once per realm. A supplier grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED. A console-realm request, an unknown id and an expired request are 404 NOT_FOUND.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"The pending request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequest"},"examples":{"authCode":{"summary":"200 — an unverified client asks to read the supplier toolset","value":{"requestId":"mgr_01JAD6C4J2P8RZ5T7W1C9V3R2Q","flow":"AUTH_CODE","realm":"SUPPLIER","toolset":"supplier","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false,"registration":"METADATA_DOCUMENT","redirectHost":"127.0.0.1","logoUri":null},"scopes":[{"scope":"mcp:supplier:read","description":"Read your supplier bookings, analytics, settlements and scorecards as you. It cannot confirm, decline or attest a booking."}],"operations":[{"name":"list_supplier_bookings","kind":"tool","title":"The bookings routed to this supplier"},{"name":"supplier_sla_scorecard","kind":"resource","title":"SLA scorecard"}],"lifetimeSeconds":28800,"requesterIpCountry":null,"writeOperations":[],"crossTenantReads":[],"stepUpRequired":false,"secondApprovalRequired":false,"approvalId":null,"redirectTo":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grant-requests:lookup":{"post":{"operationId":"supplierLookupMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_lookup_mcp_grant_request","class":"read","autonomy":"L0","reason":"Consent screen data for a pending grant; human session only, an agent never reads its own consent."},"tags":["supplier"],"x-audience":"supplier","summary":"Find a pending supplier device-flow grant by the code the human typed (RFC 8628 §3.3)","description":"The supplier realm's lookupMcpGrantRequest, for a signed-in supplier user. The code travels in the body, never a URL. An unknown, expired, spent or console-realm code is 404 NOT_FOUND details.reason USER_CODE_UNKNOWN; lookups are rate limited per session and per client address (429 RATE_LIMITED). A supplier grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestLookup"},"examples":{"typed":{"summary":"The code from the agent's screen","value":{"userCode":"WDJB-MJHT"}}}}}},"responses":{"200":{"description":"The pending request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequest"},"examples":{"deviceCode":{"summary":"200 — a supplier device-flow request","value":{"requestId":"mgr_01JAD7D5K3Q9SZ6T8W2C1V4R3P","flow":"DEVICE_CODE","realm":"SUPPLIER","toolset":"supplier","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","name":"Example CLI Agent","verified":false,"registration":"DYNAMIC","redirectHost":null,"logoUri":null},"scopes":[{"scope":"mcp:supplier:read","description":"Read your supplier bookings, analytics, settlements and scorecards as you. It cannot confirm, decline or attest a booking."}],"operations":[{"name":"supplier_settlements","kind":"resource","title":"Settlement statements"}],"lifetimeSeconds":28800,"requesterIpCountry":"PT","writeOperations":[],"crossTenantReads":[],"stepUpRequired":false,"secondApprovalRequired":false,"approvalId":null,"redirectTo":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/McpUserCodeUnknown"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grant-requests/{requestId}:approve":{"post":{"operationId":"supplierApproveMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_approve_mcp_grant_request","class":"commit-money","autonomy":"L1","reason":"Issues a credential (rule 6) and is a decision route (rule 8); human session only."},"tags":["supplier"],"x-audience":"supplier","summary":"The signed-in supplier user approves an MCP grant for themselves","description":"The supplier realm's approveMcpGrantRequest. SUPPLIER SESSION ONLY: a supplier grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, so an agent never approves its own grant. The grant binds to the approving supplier user and the realm session it came from; ending that session (logout, ending every session, reuse detection) or disabling the user ends the grant. Lifetime: the earlier of 8 hours and the session's expiry. toolset must be supplier; operations narrows as on the console route; a device flow needs confirmOwnDevice. The platform supplier switch (aikonic.mcp.grants.toolsets.supplier.enabled) off is 403 PERMISSION_DENIED details.reason TOOLSET_DISABLED; grants off on the rung is 403 PERMISSION_DENIED details.reason GRANTS_DISABLED. Not PENDING is 409 CONFLICT details.reason WRONG_STATE. The grant reads only the caller's own supplierCode.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestApproval"},"examples":{"allReads":{"summary":"Approve every read the request lists","value":{"toolset":"supplier"}},"device":{"summary":"A device flow","value":{"toolset":"supplier","confirmOwnDevice":true}}}}}},"responses":{"200":{"description":"Approved","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"redirect":{"summary":"200 — authorization code; the browser goes to redirectTo","value":{"redirectTo":"http://127.0.0.1:53682/callback?code=mca_EXAMPLEONLYcode0002&state=Zx81kQ&iss=https%3A%2F%2Fapiq-sandbox.tripproagencysites.com"}},"device":{"summary":"200 — device flow","value":{"approved":true}}}}}},"400":{"$ref":"#/components/responses/McpGrantApprovalInvalid"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpGrantApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grant-requests/{requestId}:deny":{"post":{"operationId":"supplierDenyMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_deny_mcp_grant_request","class":"commit-lite","autonomy":"L1","reason":"Decision route on a consent (rule 8); human session only."},"tags":["supplier"],"x-audience":"supplier","summary":"The signed-in supplier user denies a pending MCP grant","description":"The supplier realm's denyMcpGrantRequest: redirectTo with error=access_denied for an authorization-code request, {denied: true} for a device flow. SUPPLIER SESSION ONLY (a grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED). Not PENDING is 409 CONFLICT details.reason WRONG_STATE.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"Denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"device":{"summary":"200 — device flow","value":{"denied":true}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/mcp/grants":{"get":{"operationId":"listMcpGrants","x-mcp":{"expose":"excluded","toolset":"console","name":"list_mcp_grants","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read (FEAT-031 Q6 may reclass it as a resource later)."},"tags":["admin"],"x-audience":"console","summary":"Connected agents — the tenant's MCP grants, newest first (FEAT-031 §5.3)","description":"A console session sees its own grants; with users:manage it sees every grant in the tenant and may filter by userId (without users:manage, a userId other than the caller's is 403 PERMISSION_DENIED MISSING_PERMISSION). Filter by status; paged by an opaque cursor. List rows carry no recentCalls or refusalCounts (read one grant for those). An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED: an agent never lists credentials.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"userId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","REVOKED","EXPIRED"]}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of grants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantPage"},"examples":{"onePage":{"summary":"200 — one active console grant","value":{"items":[{"grantId":"mcg_01JAD4P2T6Y8RJ3W5K9H0Q2Z7N","realm":"TENANT","toolset":"console","boundUser":{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:console:read"],"operations":["list_agent_proposals","get_queue"],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:42:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T18:01:10Z","status":"ACTIVE","revokedAt":null,"revokedBy":null,"revokeReason":null,"writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"}]}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/mcp/grants/{grantId}":{"get":{"operationId":"getMcpGrant","x-mcp":{"expose":"excluded","toolset":"console","name":"get_mcp_grant","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read (FEAT-031 Q6 may reclass it as a resource later)."},"tags":["admin"],"x-audience":"console","summary":"One MCP grant with its last 50 calls and refusal counts (FEAT-031 §5.3)","description":"Own grant always; any grant in the tenant with users:manage. recentCalls lists at most 50 calls, newest first, with the tool, the outcome and the time only: never arguments or results. refusalCounts counts the grant's refused requests by reason (SESSION_REQUIRED is a request outside its toolset's read operations). A grant of another tenant is 404 NOT_FOUND. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"responses":{"200":{"description":"The grant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"detail":{"summary":"200 — an active grant and what it did","value":{"grantId":"mcg_01JAD4P2T6Y8RJ3W5K9H0Q2Z7N","realm":"TENANT","toolset":"console","boundUser":{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:console:read"],"operations":["list_agent_proposals","get_queue"],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:42:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T18:01:10Z","status":"ACTIVE","revokedAt":null,"revokedBy":null,"revokeReason":null,"writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"}],"recentCalls":[{"tool":"get_queue","outcome":"OK","at":"2026-10-15T10:42:31Z"},{"tool":"approve_agent_proposal","outcome":"REFUSED","at":"2026-10-15T10:40:02Z"}],"refusalCounts":{"SESSION_REQUIRED":1}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/mcp/grants/{grantId}:revoke":{"post":{"operationId":"revokeMcpGrant","x-mcp":{"expose":"excluded","toolset":"console","name":"revoke_mcp_grant","class":"commit-lite","autonomy":"L1","reason":"Revokes a grant bound to a person. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["admin"],"x-audience":"console","summary":"Revoke one MCP grant (FEAT-031 §5.3)","description":"Own grant always; any grant in the tenant with users:manage. The refresh token is refused at once; calls stop within one access-token lifetime (the AI plane re-checks at most 60 s after its last check). Idempotent: revoking a REVOKED or EXPIRED grant answers it unchanged, so no Idempotency-Key is needed. Emits mcp.grant.revoked with reason USER (own) or ADMIN (internal event; a console (TENANT-realm) one is also delivered to its tenant as a webhook, 1.45.0, DR-78). An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRevokeRequest"},"examples":{"withReason":{"summary":"Revoke with a reason for the audit","value":{"reason":"Laptop lost; disconnecting every agent"}}}}}},"responses":{"200":{"description":"The grant, REVOKED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"revoked":{"summary":"200 — revoked by an admin","value":{"grantId":"mcg_01JAD4P2T6Y8RJ3W5K9H0Q2Z7N","realm":"TENANT","toolset":"console","boundUser":{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:console:read"],"operations":["list_agent_proposals","get_queue"],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:42:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T18:01:10Z","status":"REVOKED","revokedAt":"2026-10-15T11:00:00Z","revokedBy":"7c1f9a2e-3b4d-4e5f-8a6b-9c0d1e2f3a4b","revokeReason":"Laptop lost; disconnecting every agent","writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/mcp/grants:revoke-all":{"post":{"operationId":"revokeAllMcpGrants","x-mcp":{"expose":"excluded","toolset":"console","name":"revoke_all_mcp_grants","class":"commit-lite","autonomy":"L1","reason":"Revokes every grant of one person. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["admin"],"x-audience":"console","summary":"Revoke every ACTIVE MCP grant of one user (FEAT-031 §5.3)","description":"Without userId, the caller's own grants; with another user's id, users:manage is required (else 403 PERMISSION_DENIED MISSING_PERMISSION). Answers how many grants were revoked (0 when none were active), so a repeat is harmless and no Idempotency-Key is needed. Each emits mcp.grant.revoked (internal event; a console (TENANT-realm) one is also delivered to its tenant as a webhook, 1.45.0, DR-78). An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRevokeAllRequest"},"examples":{"oneUser":{"summary":"An admin disconnects every agent of one user","value":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","reason":"Role change; reconnect after review"}}}}}},"responses":{"200":{"description":"How many grants were revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRevokeAllResult"},"examples":{"two":{"summary":"200 — two grants revoked","value":{"revoked":2}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/mcp/settings":{"get":{"operationId":"getMcpSettings","x-mcp":{"expose":"excluded","toolset":"console","name":"get_mcp_settings","class":"read","autonomy":"L0","reason":"Sets what agents may do for the tenant; an agent never widens its own autonomy."},"tags":["admin"],"x-audience":"console","summary":"The tenant's MCP toolset switch (FEAT-031 §10, AC-031-02)","description":"toolsets.console is ON (the default) or OFF. OFF refuses new console grants (approve 403 TOOLSET_DISABLED), their refresh and their use: the AI plane answers 503 AI_FEATURE_DISABLED reason TOOLSET_DISABLED within one access-token lifetime. Any console session may read it. The supplier toolset has no tenant switch (a supplier grant has no tenant). writesEnabled (connect-v1 1.26.0, ADR-C10 Phase 3, DR-68) is the per-tool console write opt-in, all OFF until an Owner turns a tool ON. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","responses":{"200":{"description":"The settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpSettings"},"examples":{"default":{"summary":"200 — the default, never changed; every write OFF","value":{"toolsets":{"console":"ON"},"writesEnabled":{"claim_queue_item":"OFF","release_queue_item":"OFF","test_webhook":"OFF","replay_webhook_delivery":"OFF","update_supplier_data_profile":"OFF","start_supplier_data_run":"OFF","cancel_supplier_data_run":"OFF","pause_supplier_data_run":"OFF","resume_supplier_data_run":"OFF","escalate_console_case":"OFF"},"updatedBy":null,"updatedAt":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateMcpSettings","x-mcp":{"expose":"excluded","toolset":"console","name":"update_mcp_settings","class":"commit-money","autonomy":"L1","reason":"Sets what agents may do for the tenant; an agent never widens its own autonomy."},"tags":["admin"],"x-audience":"console","summary":"Turn the tenant's console MCP toolset, or one console write, ON or OFF","description":"users:manage is required (403 PERMISSION_DENIED MISSING_PERMISSION). Turning the toolset ON also needs the Owner role (403 PERMISSION_DENIED details.reason OWNER_REQUIRED), because ON lets every user connect agents again. Turning OFF takes effect for new approvals at once and for running grants within one access-token lifetime; it does not revoke them, so turning ON again resumes them until they expire. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): the body is McpSettingsUpdate and changes only what it names. Turning a writesEnabled tool ON needs the Owner role (else OWNER_REQUIRED) and a step-up proof (absent: 403 PERMISSION_DENIED details.reason STEP_UP_REQUIRED; wrong or doubled: STEP_UP_FAILED; locked out: STEP_UP_LOCKED). Turning one OFF needs users:manage only and no proof, and refuses that tool to every grant at the platform on its next call and at the AI plane within 60 s. Setting a value it already has is a no-op 200, so no Idempotency-Key is needed. Every changed tool writes an audit row mcp.settings.update (tool, from, to, actor, stepUp) and emits mcp.settings.changed (internal event, also delivered to the tenant as a webhook, 1.45.0, DR-78). An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED: an agent never widens its own access.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpSettingsUpdate"},"examples":{"off":{"summary":"Switch the console toolset off for the whole tenant","value":{"toolsets":{"console":"OFF"}}},"writeOn":{"summary":"The Owner lets agents claim queue items, with a step-up code (connect-v1 1.26.0)","value":{"writesEnabled":{"claim_queue_item":"ON"},"stepUpCode":"000000"}},"writeOff":{"summary":"An Admin turns one write off; no step-up needed (connect-v1 1.26.0)","value":{"writesEnabled":{"claim_queue_item":"OFF"}}}}}}},"responses":{"200":{"description":"The settings as stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpSettings"},"examples":{"off":{"summary":"200 — switched off","value":{"toolsets":{"console":"OFF"},"writesEnabled":{"claim_queue_item":"OFF","release_queue_item":"OFF","test_webhook":"OFF","replay_webhook_delivery":"OFF","update_supplier_data_profile":"OFF","start_supplier_data_run":"OFF","cancel_supplier_data_run":"OFF","pause_supplier_data_run":"OFF","resume_supplier_data_run":"OFF","escalate_console_case":"OFF"},"updatedBy":"7c1f9a2e-3b4d-4e5f-8a6b-9c0d1e2f3a4b","updatedAt":"2026-10-15T11:05:00Z"}},"writeOn":{"summary":"200 — claim_queue_item ON (connect-v1 1.26.0)","value":{"toolsets":{"console":"ON"},"writesEnabled":{"claim_queue_item":"ON","release_queue_item":"OFF","test_webhook":"OFF","replay_webhook_delivery":"OFF","update_supplier_data_profile":"OFF","start_supplier_data_run":"OFF","cancel_supplier_data_run":"OFF","pause_supplier_data_run":"OFF","resume_supplier_data_run":"OFF","escalate_console_case":"OFF"},"updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","updatedAt":"2026-10-15T11:05:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — details.reason MISSING_PERMISSION (users:manage), OWNER_REQUIRED (turning the toolset or a write ON), STEP_UP_REQUIRED, STEP_UP_FAILED or STEP_UP_LOCKED (turning a write ON), or SESSION_REQUIRED (not a console session)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"ownerRequired":{"summary":"403 PERMISSION_DENIED, OWNER_REQUIRED — only an Owner turns a toolset back on","value":{"code":"PERMISSION_DENIED","message":"Only an Owner can turn agent access back on for the tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OWNER_REQUIRED"}}},"missingPermission":{"summary":"403 PERMISSION_DENIED, MISSING_PERMISSION","value":{"code":"PERMISSION_DENIED","message":"Changing agent access needs the users:manage permission (Owner or Admin).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"users:manage"}}},"stepUpRequired":{"summary":"403 PERMISSION_DENIED, STEP_UP_REQUIRED — turning a write ON needs a step-up proof (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"Confirm with your authenticator to let agents make this change.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_REQUIRED"}}},"stepUpFailed":{"summary":"403 PERMISSION_DENIED, STEP_UP_FAILED — the step-up proof is wrong (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"The verification code is not valid.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_FAILED","field":"stepUpCode"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grants":{"get":{"operationId":"supplierListMcpGrants","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_list_mcp_grants","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read (FEAT-031 Q6 may reclass it as a resource later)."},"tags":["supplier"],"x-audience":"supplier","summary":"Connected agents in the supplier console — the supplier's MCP grants (FEAT-031 §5.3)","description":"A supplier user sees their own grants; the supplier's Account manager sees every grant of the supplier and may filter by userId. Filter by status; paged by an opaque cursor. A supplier grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"userId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","REVOKED","EXPIRED"]}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of grants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantPage"},"examples":{"empty":{"summary":"200 — no grants yet","value":{"items":[],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grants/{grantId}":{"get":{"operationId":"supplierGetMcpGrant","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_get_mcp_grant","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read (FEAT-031 Q6 may reclass it as a resource later)."},"tags":["supplier"],"x-audience":"supplier","summary":"One supplier MCP grant with its last 50 calls and refusal counts","description":"Own grant always; any grant of the supplier for its Account manager. recentCalls carries the tool, the outcome and the time only. A grant of another supplier is 404 NOT_FOUND. A supplier grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"responses":{"200":{"description":"The grant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"detail":{"summary":"200 — a device-flow supplier grant","value":{"grantId":"mcg_01JAD7Q3W5E7RT9Y1U3I5O7P9A","realm":"SUPPLIER","toolset":"supplier","boundUser":{"id":"sup_usr_01JA9K2","displayName":"Marta Costa"},"client":{"clientId":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","name":"Example CLI Agent","verified":false},"flow":"DEVICE_CODE","scopes":["mcp:supplier:read"],"operations":["list_supplier_bookings","supplier_settlements"],"createdAt":"2026-10-15T09:00:00Z","approvedAt":"2026-10-15T09:02:00Z","lastUsedAt":"2026-10-15T09:30:00Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T17:02:00Z","status":"ACTIVE","revokedAt":null,"revokedBy":null,"revokeReason":null,"writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"sup_usr_01JA9K2","displayName":"Marta Costa"}],"recentCalls":[{"tool":"list_supplier_bookings","outcome":"OK","at":"2026-10-15T09:30:00Z"}],"refusalCounts":{}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/mcp/grants/{grantId}:revoke":{"post":{"operationId":"supplierRevokeMcpGrant","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_revoke_mcp_grant","class":"commit-lite","autonomy":"L1","reason":"Revokes a supplier grant bound to a person. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["supplier"],"x-audience":"supplier","summary":"Revoke one supplier MCP grant","description":"Own grant always; any grant of the supplier for its Account manager. The refresh token is refused at once; calls stop within one access-token lifetime. Idempotent on a REVOKED or EXPIRED grant, so no Idempotency-Key is needed. A supplier grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRevokeRequest"},"examples":{"withReason":{"summary":"Revoke with a reason","value":{"reason":"Agent no longer used"}}}}}},"responses":{"200":{"description":"The grant, REVOKED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"revoked":{"summary":"200 — revoked by its own user","value":{"grantId":"mcg_01JAD7Q3W5E7RT9Y1U3I5O7P9A","realm":"SUPPLIER","toolset":"supplier","boundUser":{"id":"sup_usr_01JA9K2","displayName":"Marta Costa"},"client":{"clientId":"mcpc_8Hn2kQ4rT7vW1xZ3bC5dF6","name":"Example CLI Agent","verified":false},"flow":"DEVICE_CODE","scopes":["mcp:supplier:read"],"operations":["list_supplier_bookings","supplier_settlements"],"createdAt":"2026-10-15T09:00:00Z","approvedAt":"2026-10-15T09:02:00Z","lastUsedAt":"2026-10-15T09:30:00Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T17:02:00Z","status":"REVOKED","revokedAt":"2026-10-15T09:45:00Z","revokedBy":"sup_usr_01JA9K2","revokeReason":"Agent no longer used","writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"sup_usr_01JA9K2","displayName":"Marta Costa"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grant-requests/{requestId}":{"get":{"operationId":"opsGetMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_mcp_grant_request","class":"read","autonomy":"L0","reason":"Consent screen data for a pending grant; human session only, an agent never reads its own consent."},"tags":["operator"],"x-audience":"operator","summary":"The consent screen of a pending operator MCP grant (FEAT-031 §5.1, ADR-C10 Phase 4)","description":"OPERATOR SESSION ONLY: a tenant or supplier session or an API key is 401 (TOKEN_WRONG_REALM), and an MCP grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED. What the operator console's Approve agent screen shows: the client (VERIFIED only for a platform pre-registered client), the scope mcp:operator:read, the operator toolset's read operations (pre-ticked), the five cross-tenant reads in crossTenantReads (UNTICKED), the lifetime (1 hour at most), stepUpRequired (always true) and secondApprovalRequired (true on a rung named in aikonic.mcp.operator.second-approver-rungs, default UAT and PROD). writeOperations is always empty: an operator grant is reads only (ADR-C10 R3, OD-899). Once the request leaves PENDING only the operator who approved it reads it (another operator gets 404 NOT_FOUND); while AWAITING_SECOND_APPROVAL it carries approvalId, and once the second approval executed it carries redirectTo, where the screen sends the browser. Refused with 403 PERMISSION_DENIED details.reason OPERATOR_MFA_NOT_ENFORCED where aikonic.ops.mfa.required is false and OPERATOR_GRANTS_DISABLED where operator grants are off. An unknown id, an expired request and a request of another realm are 404 NOT_FOUND.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"The request","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequest"},"examples":{"pending":{"summary":"200 — an operator request on a second-approver rung; the cross-tenant reads are unticked","value":{"requestId":"mgr_01JAE4Y0M6R8TV2A4W6K8Q0S3U","flow":"AUTH_CODE","realm":"OPERATOR","toolset":"operator","state":"PENDING","expiresAt":"2026-10-15T10:10:00Z","client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false,"registration":"METADATA_DOCUMENT","redirectHost":"127.0.0.1","logoUri":null},"scopes":[{"scope":"mcp:operator:read","description":"Read operator data as you for at most one hour. It cannot change, approve, switch or pay anything."}],"operations":[{"name":"operator_me","kind":"resource","title":"Your operator profile"},{"name":"ops_list_supplier_data_runs","kind":"tool","title":"Supplier data runs"}],"writeOperations":[],"crossTenantReads":[{"name":"ops_list_tenants","kind":"tool","title":"Every tenant and its status"},{"name":"ops_finance_summary","kind":"tool","title":"The finance summary across tenants"}],"lifetimeSeconds":3600,"requesterIpCountry":null,"stepUpRequired":true,"secondApprovalRequired":true,"approvalId":null,"redirectTo":null}},"awaitingSecondApproval":{"summary":"200 — approved by the first operator, waiting for an OPS_SECURITY operator","value":{"requestId":"mgr_01JAE4Y0M6R8TV2A4W6K8Q0S3U","flow":"AUTH_CODE","realm":"OPERATOR","toolset":"operator","state":"AWAITING_SECOND_APPROVAL","expiresAt":"2026-10-15T10:31:00Z","client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false,"registration":"METADATA_DOCUMENT","redirectHost":"127.0.0.1","logoUri":null},"scopes":[{"scope":"mcp:operator:read","description":"Read operator data as you for at most one hour. It cannot change, approve, switch or pay anything."}],"operations":[{"name":"operator_me","kind":"resource","title":"Your operator profile"}],"writeOperations":[],"crossTenantReads":[],"lifetimeSeconds":3600,"requesterIpCountry":null,"stepUpRequired":true,"secondApprovalRequired":true,"approvalId":"APR-0144","redirectTo":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grant-requests/{requestId}:approve":{"post":{"operationId":"opsApproveMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_approve_mcp_grant_request","class":"commit-money","autonomy":"L1","reason":"Issues a credential (ADR-C09 rule 6) and is a decision route (rule 8); human operator session with step-up only."},"tags":["operator"],"x-audience":"operator","summary":"The signed-in operator approves an operator MCP grant for themselves, with step-up (ADR-C10 Phase 4)","description":"OPERATOR SESSION ONLY (an MCP grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, so an agent never approves its own grant). Served only where aikonic.mcp.grants.toolsets.operator.enabled is true (else 403 OPERATOR_GRANTS_DISABLED) and aikonic.ops.mfa.required is true on the rung (else 403 OPERATOR_MFA_NOT_ENFORCED). The step-up proof is always required (stepUpCode or stepUpAssertion, exactly one; absent 403 PERMISSION_DENIED details.reason STEP_UP_REQUIRED, wrong or doubled STEP_UP_FAILED, locked STEP_UP_LOCKED). toolset must be operator; operations narrows the reads; crossTenantReads ticks any of the five cross-tenant reads the request offered (default none); writeOperations must be absent or empty (an operator grant is reads only, ADR-C10 R3, OD-899; anything else is 400 OPERATION_NOT_ALLOWED). The grant is bound to the approving operator and to that operator's session; it lives at most 1 hour from approval and never past the session (aikonic.mcp.grants.max-lifetime.operator, which cannot be set above PT1H). On a rung named in aikonic.mcp.operator.second-approver-rungs (default UAT and PROD) the answer is {awaitingSecondApproval: true, approvalId}: an MCP_OPERATOR_GRANT ops_approval is opened, no code is issued, and the request turns APPROVED only when a different OPS_SECURITY operator approves it with step-up at POST /ops/v1/approvals/{id}:approve (the requester deciding is 409 APPROVER_IS_REQUESTER); its expiry (aikonic.mcp.operator.approval-ttl, default 30 minutes) or a reject ends the request and the client receives access_denied. On any other rung the answer is redirectTo with the code. A request no longer PENDING is 409 CONFLICT WRONG_STATE. Emits mcp.grant.approved or mcp.grant.awaiting_second_approval (internal events) and writes the operator audit row mcp.grant.approve.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestApproval"},"examples":{"readsOnly":{"summary":"Approve the reads only, with a step-up code; no cross-tenant read","value":{"toolset":"operator","stepUpCode":"000000"}},"oneCrossTenant":{"summary":"Approve the reads and one cross-tenant read, with a step-up code","value":{"toolset":"operator","crossTenantReads":["ops_list_tenants"],"stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Approved, or waiting for the second approver","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"redirect":{"summary":"200 — a rung without a second approver; the browser goes to redirectTo","value":{"redirectTo":"http://127.0.0.1:53682/callback?code=mca_EXAMPLEONLYcode0002&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.dev.example"}},"awaitingSecondApproval":{"summary":"200 — a second-approver rung; an OPS_SECURITY operator must approve APR-0144","value":{"awaitingSecondApproval":true,"approvalId":"APR-0144"}}}}}},"400":{"$ref":"#/components/responses/McpGrantApprovalInvalid"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpGrantApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grant-requests/{requestId}:deny":{"post":{"operationId":"opsDenyMcpGrantRequest","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_deny_mcp_grant_request","class":"commit-lite","autonomy":"L1","reason":"Decision route on a consent (rule 8); human operator session only."},"tags":["operator"],"x-audience":"operator","summary":"The signed-in operator denies a pending operator MCP grant","description":"OPERATOR SESSION ONLY (an MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED). The answer carries redirectTo, the client's redirect_uri with error=access_denied, state and iss. A request no longer PENDING is 409 CONFLICT details.reason WRONG_STATE (a request AWAITING_SECOND_APPROVAL is ended by rejecting its MCP_OPERATOR_GRANT approval). No step-up is needed: refusing access must stay cheap. Emits mcp.grant.denied (internal event).\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/McpGrantRequestIdPath"}],"responses":{"200":{"description":"Denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRequestDecision"},"examples":{"redirect":{"summary":"200 — the browser goes to redirectTo with access_denied","value":{"redirectTo":"http://127.0.0.1:53682/callback?error=access_denied&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.dev.example"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/McpConsentForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/McpGrantRequestConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grants":{"get":{"operationId":"opsListMcpGrants","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_list_mcp_grants","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read."},"tags":["operator"],"x-audience":"operator","summary":"Operator Connected agents — MCP grants, newest first (ADR-C10 Phase 4)","description":"An operator sees their own operator grants. OPS_SECURITY (permission ops:mcp_grants:manage) sees every grant of every realm (TENANT, SUPPLIER, OPERATOR) and may filter by realm; without it, a realm other than OPERATOR is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. Filter by status; paged by an opaque cursor. List rows carry no recentCalls or refusalCounts. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"realm","in":"query","required":false,"schema":{"type":"string","enum":["TENANT","SUPPLIER","OPERATOR"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","REVOKED","EXPIRED"]}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of grants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantPage"},"examples":{"onePage":{"summary":"200 — one active operator grant approved by two operators","value":{"items":[{"grantId":"mcg_01JAE3X9L5Q7SU1Z3V5J7P9R2T","realm":"OPERATOR","toolset":"operator","boundUser":{"id":"op_3c9a71","displayName":"Rui Almeida"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:operator:read"],"operations":["operator_me","ops_list_supplier_data_runs","ops_list_tenants"],"writeOperations":[],"crossTenantReads":["ops_list_tenants"],"approvedBy":[{"id":"op_3c9a71","displayName":"Rui Almeida"},{"id":"op_51d2e0","displayName":"Ana Ferreira"}],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:12:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T11:01:10Z","status":"ACTIVE","revokedAt":null,"revokedBy":null,"revokeReason":null}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grants/{grantId}":{"get":{"operationId":"opsGetMcpGrant","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_mcp_grant","class":"read","autonomy":"L0","reason":"Lists credentials bound to people; credential metadata is not an agent read."},"tags":["operator"],"x-audience":"operator","summary":"One MCP grant with its last 50 calls and refusal counts (operator view)","description":"Own operator grant always; any grant of any realm with OPS_SECURITY (permission ops:mcp_grants:manage). recentCalls carries the tool, the outcome and the time only, never arguments or results. A grant the caller may not see is 404 NOT_FOUND. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"responses":{"200":{"description":"The grant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"detail":{"summary":"200 — an operator grant and its calls","value":{"grantId":"mcg_01JAE3X9L5Q7SU1Z3V5J7P9R2T","realm":"OPERATOR","toolset":"operator","boundUser":{"id":"op_3c9a71","displayName":"Rui Almeida"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:operator:read"],"operations":["operator_me","ops_list_supplier_data_runs","ops_list_tenants"],"writeOperations":[],"crossTenantReads":["ops_list_tenants"],"approvedBy":[{"id":"op_3c9a71","displayName":"Rui Almeida"}],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:12:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T11:01:10Z","status":"ACTIVE","revokedAt":null,"revokedBy":null,"revokeReason":null,"recentCalls":[{"tool":"ops_list_tenants","outcome":"OK","at":"2026-10-15T10:12:31Z"},{"tool":"ops_start_supplier_data_run","outcome":"REFUSED","at":"2026-10-15T10:10:02Z"}],"refusalCounts":{"SESSION_REQUIRED":1}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/mcp/grants/{grantId}:revoke":{"post":{"operationId":"opsRevokeMcpGrant","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_revoke_mcp_grant","class":"commit-lite","autonomy":"L1","reason":"Revokes an operator, tenant or supplier grant bound to a person. An OAuth or MCP grant credential route (ADR-C10, DR-67): an agent never issues, consents to, or revokes its own credential; classed commit-lite by the revokeApiKey precedent (FEAT-031 §6)."},"tags":["operator"],"x-audience":"operator","summary":"Revoke one MCP grant (own operator grant, or any grant with OPS_SECURITY)","description":"Own operator grant always; any grant of any realm with OPS_SECURITY (permission ops:mcp_grants:manage), the incident path of RB-17. The refresh token is refused at once; calls stop within one access-token lifetime (the AI plane re-checks at most 60 s after its last check). Idempotent on a REVOKED or EXPIRED grant, so no Idempotency-Key is needed and no step-up is required (revoking must stay cheap). Emits mcp.grant.revoked with reason OPERATOR (internal event; when the grant is a console (TENANT-realm) grant it is also delivered to that tenant as a webhook with actor kind system, id platform: the operator's identity is never sent, 1.45.0, DR-78) and writes an operator audit row. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/McpGrantIdPath"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrantRevokeRequest"},"examples":{"incident":{"summary":"OPS_SECURITY revokes a tenant user's grant during an incident","value":{"reason":"Incident SEC-0412; disconnecting the agent"}}}}}},"responses":{"200":{"description":"The grant, REVOKED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpGrant"},"examples":{"revoked":{"summary":"200 — a console grant revoked by OPS_SECURITY","value":{"grantId":"mcg_01JAD4P2T6Y8RJ3W5K9H0Q2Z7N","realm":"TENANT","toolset":"console","boundUser":{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"},"client":{"clientId":"https://assistant.example.com/oauth/client.json","name":"Example IDE Assistant","verified":false},"flow":"AUTH_CODE","scopes":["mcp:console:read"],"operations":["list_agent_proposals","get_queue"],"writeOperations":[],"crossTenantReads":[],"approvedBy":[{"id":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya Raman"}],"createdAt":"2026-10-15T10:00:00Z","approvedAt":"2026-10-15T10:01:10Z","lastUsedAt":"2026-10-15T10:42:31Z","lastUsedIpCountry":"PT","expiresAt":"2026-10-15T18:01:10Z","status":"REVOKED","revokedAt":"2026-10-15T11:00:00Z","revokedBy":"op_51d2e0","revokeReason":"Incident SEC-0412; disconnecting the agent"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/ai/search-intent":{"post":{"operationId":"consoleAiAssistSearchIntent","x-mcp":{"expose":"excluded","toolset":"console","name":"console_ai_assist_search_intent","class":"read","autonomy":"L0","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"Natural-language query to a validated, grounded search intent (FEAT-018 §5, AC-018-02; OD-848, OD-860)","description":"Forwarded by the backend to the AI plane's POST /ai/v1/p0/search-intent (OD-848 (a)); the browser never talks to the plane, and the plane learns tenant, actor and permissions only from the gateway-signed context. A console session only — an API key or OAuth token is 403 PERMISSION_DENIED, details.reason SESSION_REQUIRED — holding the permission ai:assist (agents:read or agents:act alone is 403 PERMISSION_DENIED, details.reason MISSING_PERMISSION). Only GROUNDED slots appear in `intent`: the destination must be one the tenant's golden index retrieved, dates must appear in the query in ISO form with checkIn before checkOut, and adults must be stated in the query. Every other slot is absent from `intent` and asked back as a clarification, never guessed. The answer is a suggestion for the search form, not a search: run POST /v1/search with it. Rate-limited per tenant (429 RATE_LIMITED, retryable, Retry-After). 503 PROVIDER_DOWN, details.reason AI_FEATURE_DISABLED, while the plane has no golden index or no model provider, or the AI kill switch is on.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistSearchIntentRequest"},"examples":{"datedQuery":{"summary":"A query that states destination, dates and party size","value":{"query":"Hotel in Lisbon 2026-11-12 to 2026-11-15 for 2 adults"}}}}}},"responses":{"200":{"description":"The validated intent, the clarifications for every slot that is not grounded, and this call's model-gateway flags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistSearchIntentResponse"},"examples":{"grounded":{"summary":"200 — every slot grounded, nothing to clarify","value":{"intent":{"destinationRef":"dst-lis-001","checkIn":"2026-11-12","checkOut":"2026-11-15","adults":2},"clarifications":[],"blockedToolCalls":0,"degraded":false,"injectionSuspected":false}},"needsClarification":{"summary":"200 — destination grounded; dates and party size asked back, never guessed","value":{"intent":{"destinationRef":"dst-lis-001"},"clarifications":["Which dates would you like to travel (check-in and check-out)?","How many adults are travelling?"],"blockedToolCalls":0,"degraded":false,"injectionSuspected":false}}}}}},"400":{"$ref":"#/components/responses/ConsoleAiAssistBadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleAiAssistForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/ConsoleAiAssistRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/ConsoleAiAssistUpstreamUnavailable"},"503":{"$ref":"#/components/responses/ConsoleAiAssistUnavailable"}}}},"/admin/v1/ai/autofill-validation":{"post":{"operationId":"consoleAiAssistAutofillValidation","x-mcp":{"expose":"excluded","toolset":"console","name":"console_ai_assist_autofill_validation","class":"read","autonomy":"L0","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"Check a traveler draft against an offer's requiredInfo — it flags, never fabricates (FEAT-018 §5, AC-018-03; OD-848, OD-860)","description":"Forwarded by the backend to the AI plane's POST /ai/v1/p0/autofill-validation (OD-848 (a)). Same posture as POST /admin/v1/ai/search-intent: a console session holding ai:assist, rate-limited per tenant. `findings` are deterministic (a required field missing, a nationality that differs from the document's issuing country, an age outside the traveler type's band: INF 0-1, CHD 2-11, ADT 12-120). The model sees structural signals only, never a traveler value, and may only suggest fields to CONFIRM: a suggestion survives only if its value is already in the draft, and the answer names the field, never the value. `traveler` is the 0-based index into draft.travelers. A draft outside the request schema's bounds is 400 VALIDATION. Nothing is written; the booking form stays the traveler's.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistAutofillRequest"},"examples":{"twoTravelers":{"summary":"requiredInfo from the offer and a two-traveler draft","value":{"requiredInfo":[{"field":"passportNumber","required":true},{"field":"dateOfBirth","required":true}],"draft":{"travelers":[{"type":"ADT","age":34,"nationality":"PT","documentCountry":"PT","passportNumber":"P1234567","dateOfBirth":"1992-03-04"},{"type":"CHD","age":14,"nationality":"PT","documentCountry":"ES","dateOfBirth":"2012-06-30"}]}}}}}}},"responses":{"200":{"description":"The deterministic findings, the fields worth confirming, and this call's model-gateway flags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistAutofillResponse"},"examples":{"findings":{"summary":"200 — the child's passport is missing, its document country and age band disagree","value":{"findings":[{"code":"MISSING","traveler":1,"field":"passportNumber"},{"code":"NATIONALITY_DOCUMENT_MISMATCH","traveler":1,"field":"documentCountry"},{"code":"AGE_BAND_MISMATCH","traveler":1,"field":"age"}],"suggestions":[{"traveler":0,"field":"dateOfBirth"}],"droppedSuggestions":1,"degraded":false,"injectionSuspected":false}},"clean":{"summary":"200 — nothing to flag","value":{"findings":[],"suggestions":[],"droppedSuggestions":0,"degraded":false,"injectionSuspected":false}}}}}},"400":{"$ref":"#/components/responses/ConsoleAiAssistBadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleAiAssistForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/ConsoleAiAssistRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/ConsoleAiAssistUpstreamUnavailable"},"503":{"$ref":"#/components/responses/ConsoleAiAssistUnavailable"}}}},"/admin/v1/ai/error-explain":{"post":{"operationId":"consoleAiAssistErrorExplain","x-mcp":{"expose":"excluded","toolset":"console","name":"console_ai_assist_error_explain","class":"read","autonomy":"L0","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"A platform error code and its trace to a plain, redacted narrative for the agent (FEAT-018 §5, AC-018-04; OD-848, OD-860)","description":"Forwarded by the backend to the AI plane's POST /ai/v1/p0/error-explain (OD-848 (a)). Same posture as POST /admin/v1/ai/search-intent: a console session holding ai:assist, rate-limited per tenant. `code` must have the shape of a platform error code (an ApiErrorResponse.code value such as PRICE_CHANGED), else 400 VALIDATION. `trace` is the error's context (typically the ApiErrorResponse's traceId and details), at most 16 KiB serialized and 6 levels deep. The trace is redacted before the model sees it and the narrative is redacted again on the way out. The narrative explains; it never retries or changes anything.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistErrorExplainRequest"},"examples":{"priceChanged":{"summary":"Explain a 409 PRICE_CHANGED from POST /v1/orders","value":{"code":"PRICE_CHANGED","trace":{"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","route":"POST /v1/orders","details":{"previousPrice":{"amount":84210,"currency":"USD"},"price":{"amount":88010,"currency":"USD"}}}}}}}}},"responses":{"200":{"description":"The code echoed back, the redacted narrative, and this call's model-gateway flags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAiAssistErrorExplainResponse"},"examples":{"explained":{"summary":"200 — a two-sentence narrative and the next step","value":{"code":"PRICE_CHANGED","narrative":"The supplier's price rose from 842.10 to 880.10 USD between the reprice and the booking, so the order was not placed. Show the traveler the new price and, if they accept, reprice and book again.","blockedToolCalls":0,"degraded":false,"injectionSuspected":false}}}}}},"400":{"$ref":"#/components/responses/ConsoleAiAssistBadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleAiAssistForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/ConsoleAiAssistRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/ConsoleAiAssistUpstreamUnavailable"},"503":{"$ref":"#/components/responses/ConsoleAiAssistUnavailable"}}}},"/admin/v1/agents":{"get":{"operationId":"listAgents","x-mcp":{"expose":"resource","toolset":"console","name":"list_agents","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The AI agents instantiated for this tenant, their autonomy level and pause state (FEAT-019 §5, §6; 12 §B2; OD-404)","description":"Proxied by the backend to the AI plane's GET /ai/v1/agents under the tenant's AccessContext (OD-404) — the browser never talks to the AI plane. Each agent is its own least-privilege service principal (FEAT-019 §9); `autonomyLevel` is its production rung on the FEAT-019 §5 ladder, and money-touching agents are capped at L1 forever. Owner, Admin and OpsAgent act; Auditor views (09 §12 \"agent desk\").\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Agents for the tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentList"},"examples":{"twoAgents":{"summary":"200 — AG-22 rebooking (L1) and AG-24 adoption advisor (L0)","value":{"agents":[{"agentId":"agent-rebooking-ag22","name":"Disruption rebooking assistant","useCase":"AG-22","journey":"J4","autonomyLevel":"L1","state":"ACTIVE","owner":"platform-ai","policyDocRef":"FEAT-019 §5 J4 AG-22","lastActiveAt":"2026-09-27T09:05:30Z","openProposals":1,"pausedAt":null,"pausedBy":null},{"agentId":"agent-adoption-ag24","name":"Usage & adoption advisor","useCase":"AG-24","journey":"J4","autonomyLevel":"L0","state":"PAUSED","owner":"platform-ai","policyDocRef":"FEAT-019 §5 J4 AG-24","lastActiveAt":"2026-09-01T02:00:00Z","openProposals":0,"pausedAt":"2026-09-20T08:00:00Z","pausedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/agents/{agentId}:pause":{"post":{"operationId":"pauseAgent","x-mcp":{"expose":"excluded","toolset":"console","name":"pause_agent","class":"commit-lite","autonomy":"L1","reason":"Kill switch; an agent must never pause or resume agents or flip switches."},"tags":["admin"],"x-audience":"console","summary":"The kill switch — pause one agent for this tenant; its OPEN proposals stay reviewable but it drafts nothing new (FEAT-019 §6; D-54)","description":"Proxied to the AI plane's POST /ai/v1/agents/{id}/pause (OD-404). Idempotent — pausing a PAUSED agent is a 200. Emits `agent.paused` with actor.kind user (FEAT-019 §8). Resume with POST /admin/v1/agents/{agentId}:resume (IMP-P3-032). Requires the Owner or Admin role.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentPauseRequest"},"examples":{"pause":{"summary":"Pause after a stream of poor proposals","value":{"reason":"Three rebooking proposals this morning quoted the wrong fare family"}}}}}},"responses":{"200":{"description":"The agent, PAUSED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgent"},"examples":{"paused":{"summary":"200 — paused","value":{"agentId":"agent-rebooking-ag22","name":"Disruption rebooking assistant","useCase":"AG-22","journey":"J4","autonomyLevel":"L1","state":"PAUSED","owner":"platform-ai","policyDocRef":"FEAT-019 §5 J4 AG-22","lastActiveAt":"2026-09-27T09:05:30Z","openProposals":1,"pausedAt":"2026-09-27T09:55:00Z","pausedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/agent-proposals":{"get":{"operationId":"listAgentProposals","x-mcp":{"expose":"tool","toolset":"console","name":"list_agent_proposals","class":"read","autonomy":"L0","example":"{\"state\":\"OPEN\",\"orderRef\":\"MO-8KD-2FQ\",\"pageSize\":25}"},"tags":["admin"],"x-audience":"console","summary":"The agent desk's inbox — every L1 proposal as one reviewable object (FEAT-019 §6, §7; OD-404)","description":"Proxied to the AI plane's GET /ai/v1/proposals (OD-404). A proposal is FEAT-019 §6's uniform approval envelope: evidence, the change, projected effect, undo plan. State is OPEN / EXECUTING / APPROVED / REJECTED / EXPIRED / EXECUTION_UNKNOWN; proposals expire (default 48h, capped at the idempotency window) so stale drafts never execute against moved state (§7). `requiresApprovalToken` is true on every money-touching proposal (12 §B2.5 — no tenant override).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"state","description":"Only proposals in this state.","in":"query","schema":{"$ref":"#/components/schemas/ConsoleAgentProposalState"}},{"name":"agentId","description":"Only the proposals of this agent.","in":"query","schema":{"type":"string"}},{"name":"orderRef","in":"query","schema":{"type":"string"},"description":"Proposals whose resource is this order"},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Proposals, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalList"},"examples":{"oneOpen":{"summary":"200 — one OPEN AG-22 re-accommodation proposal","value":{"proposals":[{"proposalId":"prop_01J8QL9","agentId":"agent-rebooking-ag22","type":"REBOOKING","title":"Re-accommodate MO-8KD-2FQ-9XT after schedule change: LIS-OPO 2026-10-14","summary":"Supplier cancelled the 07:10 departure. Best option keeps the arrival window within 40 minutes for +18.00 USD.","state":"OPEN","actor":{"kind":"ai","id":"agent-rebooking-ag22","agentId":"agent-rebooking-ag22"},"resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"requiresApprovalToken":true,"amount":{"amount":1800,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"ev_01J8QK1","label":"order.schedule_changed"},{"kind":"TRACE","ref":"4bf92f3577b34da6a3ce929d0e0e4736","label":"Options search"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-14T07:10:00+01:00","after":"2026-10-14T09:35:00+01:00"}],"projectedEffect":{"description":"Arrival 10:40 instead of 08:15; fare difference charged per money model","amountDelta":{"amount":1800,"currency":"USD"}},"undoPlan":"Amend back to any remaining seat on the original fare family within 24h; penalty-free per supplier waiver","createdAt":"2026-09-27T09:02:00Z","expiresAt":"2026-09-30T09:02:00Z","decidedBy":null,"decidedAt":null,"decisionReason":null}],"page":{"page":1,"pageSize":50,"total":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}},"post":{"operationId":"createAgentProposal","x-mcp":{"expose":"excluded","toolset":"console","name":"create_agent_proposal","class":"commit-lite","autonomy":"L1","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"Ask an agent to draft a proposal for a resource — e.g. AG-22 options for a disrupted order (FEAT-019 §6; OD-404)","description":"Proxied to the AI plane's POST /ai/v1/proposals (OD-404). The human names the agent, the proposal type and the resource; the agent drafts the proposal asynchronously and it appears OPEN in the inbox, actor.kind ai. A PAUSED agent is 409 CONFLICT (details.reason AGENT_PAUSED). Requires orders:write (Owner, Admin, OpsAgent).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalCreate"},"examples":{"rebooking":{"summary":"Ask AG-22 for re-accommodation options","value":{"agentId":"agent-rebooking-ag22","type":"REBOOKING","resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"instructions":"Keep the arrival before noon; the traveller has a 13:00 meeting"}}}}}},"responses":{"202":{"description":"Drafting; the proposal in its initial state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"drafting":{"summary":"202 — OPEN, still being drafted (title and changes fill in as the agent works)","value":{"proposalId":"prop_01J8QV2","agentId":"agent-rebooking-ag22","type":"REBOOKING","title":"Re-accommodate MO-8KD-2FQ-9XT","summary":"Drafting options…","state":"OPEN","actor":{"kind":"ai","id":"agent-rebooking-ag22","agentId":"agent-rebooking-ag22"},"resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"requiresApprovalToken":true,"evidence":[],"changes":[],"undoPlan":null,"createdAt":"2026-09-27T09:58:00Z","expiresAt":"2026-09-30T09:58:00Z","decidedBy":null,"decidedAt":null,"decisionReason":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/agent-proposals/{proposalId}:approve":{"post":{"operationId":"approveAgentProposal","x-mcp":{"expose":"excluded","toolset":"console","name":"approve_agent_proposal","class":"commit-money","autonomy":"L1","reason":"Approves or rejects; a human decision, never an agent's (DR-38: the plane never approves, rejects or commits)."},"tags":["admin"],"x-audience":"console","summary":"Approve a proposal — with the D-50 token when it moves money; the platform accepts the token once (12 §B2.4; FEAT-019 §5; OD-403)","description":"Approve-with-amount is two calls: the console mints the token at POST /admin/v1/approvals for exactly this proposal's scope, action, resource and amount, then presents it here; the AI plane executes the write as an ordinary API call with actor.kind ai and approvalTokenId stamped on the event and audit rows (12 §B2.4 step 3). The gateway verifies the binding: any drift — a proposal whose amount moved, a token for another resource — fails closed. An expired token is 403 APPROVAL_TOKEN_EXPIRED; a spent one 403 APPROVAL_TOKEN_USED — the reuse test of acceptance row AGENTS. A money proposal presented without a token is 400 VALIDATION (details.field approvalToken). A proposal that is not OPEN is 409 CONFLICT (details.state). Proposals can never be approved by an agent: a session principal only. An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39). Requires orders:write plus the verb the proposal executes (orders:amend for AG-22). An ambiguous execution outcome is 500 INTERNAL details.reason EXECUTION_UNKNOWN: the proposal is EXECUTION_UNKNOWN (terminal), the grant stays spent, nothing is retried, and agent.proposal.execution_unknown is emitted. Approve claims the proposal (OPEN to EXECUTING) and runs its verb once. A definitive failure proven NOT dispatched (PRICE_CHANGED, OFFER_EXPIRED, a 402 PAYMENT_FAILED before dispatch, a supplier refusal that was not sent) returns the proposal to OPEN, gives the D-50 grant back and answers the verb's own error. Any failure that may have dispatched makes it EXECUTION_UNKNOWN: terminal for the API, the grant stays spent, the verb is never retried, and the desk resolves it. A stale EXECUTING (a crash mid-run) is likewise resolved by the desk to EXECUTION_UNKNOWN. Expiry applies only to OPEN: an EXECUTING or EXECUTION_UNKNOWN proposal never expires. Any failure after the claim that is not proven undispatched answers 500 INTERNAL details.reason EXECUTION_UNKNOWN, never a retryable INTERNAL.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/ProposalIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalDecision"},"examples":{"approveWithToken":{"summary":"Approve the +18.00 USD re-accommodation","value":{"approvalToken":"apt_01J8QM3.v8n2k4x9q1z7","reason":"Traveller confirmed by phone"}}}}}},"responses":{"200":{"description":"APPROVED and executing; the event and audit rows carry approvalTokenId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"approved":{"summary":"200 — APPROVED","value":{"proposalId":"prop_01J8QL9","agentId":"agent-rebooking-ag22","type":"REBOOKING","title":"Re-accommodate MO-8KD-2FQ-9XT after schedule change: LIS-OPO 2026-10-14","summary":"Supplier cancelled the 07:10 departure. Best option keeps the arrival window within 40 minutes for +18.00 USD.","state":"APPROVED","actor":{"kind":"ai","id":"agent-rebooking-ag22","agentId":"agent-rebooking-ag22"},"resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"requiresApprovalToken":true,"amount":{"amount":1800,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"ev_01J8QK1","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-14T07:10:00+01:00","after":"2026-10-14T09:35:00+01:00"}],"projectedEffect":{"description":"Arrival 10:40 instead of 08:15; fare difference charged per money model","amountDelta":{"amount":1800,"currency":"USD"}},"undoPlan":"Amend back to any remaining seat on the original fare family within 24h; penalty-free per supplier waiver","createdAt":"2026-09-27T09:02:00Z","expiresAt":"2026-09-30T09:02:00Z","decidedBy":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","decidedAt":"2026-09-27T09:05:30Z","decisionReason":"Traveller confirmed by phone","approvalTokenId":"apt_01J8QM3"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/AgentProposalExecutionUnknown"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/agent-proposals/{proposalId}:reject":{"post":{"operationId":"rejectAgentProposal","x-mcp":{"expose":"excluded","toolset":"console","name":"reject_agent_proposal","class":"commit-money","autonomy":"L1","reason":"Approves or rejects; a human decision, never an agent's (DR-38: the plane never approves, rejects or commits)."},"tags":["admin"],"x-audience":"console","summary":"Reject a proposal with a reason — nothing executes; the overturn feeds the autonomy ladder (FEAT-019 §5)","description":"Proxied to the AI plane's POST /ai/v1/proposals/{id}/reject (OD-404). `reason` is mandatory: rejections are the overturn-rate signal that gates an agent's promotion from L1 (FEAT-019 §5). A proposal that is not OPEN is 409 CONFLICT (details.state). Requires orders:write. An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/ProposalIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposalRejection"},"examples":{"reject":{"summary":"Reject — the traveller prefers a refund","value":{"reason":"Traveller asked to cancel and rebook themselves; refund path instead"}}}}}},"responses":{"200":{"description":"REJECTED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"rejected":{"summary":"200 — REJECTED with the reason kept","value":{"proposalId":"prop_01J8QL9","agentId":"agent-rebooking-ag22","type":"REBOOKING","title":"Re-accommodate MO-8KD-2FQ-9XT after schedule change: LIS-OPO 2026-10-14","summary":"Supplier cancelled the 07:10 departure. Best option keeps the arrival window within 40 minutes for +18.00 USD.","state":"REJECTED","actor":{"kind":"ai","id":"agent-rebooking-ag22","agentId":"agent-rebooking-ag22"},"resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"requiresApprovalToken":true,"amount":{"amount":1800,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"ev_01J8QK1","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-14T07:10:00+01:00","after":"2026-10-14T09:35:00+01:00"}],"projectedEffect":{"description":"Arrival 10:40 instead of 08:15; fare difference charged per money model","amountDelta":{"amount":1800,"currency":"USD"}},"undoPlan":"Amend back to any remaining seat on the original fare family within 24h; penalty-free per supplier waiver","createdAt":"2026-09-27T09:02:00Z","expiresAt":"2026-09-30T09:02:00Z","decidedBy":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","decidedAt":"2026-09-27T09:06:00Z","decisionReason":"Traveller asked to cancel and rebook themselves; refund path instead"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/approvals":{"post":{"operationId":"mintApprovalToken","x-mcp":{"expose":"excluded","toolset":"console","name":"mint_approval_token","class":"commit-money","autonomy":"L1","reason":"Mints the single-use D-50 grant that authorises a money action, so an agent minting its own approval would defeat the human gate (DR-38, DR-39)."},"tags":["admin"],"x-audience":"console","summary":"Mint a D-50 approval token — server-minted, single-use, ~2 min, bound to scope + action + resource + amount (12 §B2.4; 15 §1.4; OD-403)","description":"The console is the ONLY minter (OD-403): the human confirms in our UI, out of band of any model's channel (12 §B2.4 step 2), and this route mints. The AI plane and the MCP server present a token, never mint one. The token value is returned once, here; approvalTokenId is what audit rows quote (13 §1.1 actor.onBehalfOf + approvalTokenId). Presenting it to the wrong action, resource or amount fails closed; presenting it twice is 403 APPROVAL_TOKEN_USED; after expiresAt it is 403 APPROVAL_TOKEN_EXPIRED. Only a session principal may mint — an API key is 403 PERMISSION_DENIED (details.reason SESSION_REQUIRED) — and the minter needs the permission named in `scope`. An MCP grant-derived token (connect-v1 1.26.0) is likewise 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant: an agent acting for a human is never that human's session (DR-38, DR-39).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleApprovalRequest"},"examples":{"approveProposal":{"summary":"Bind a token to approving one proposal for +18.00 USD","value":{"scope":"orders:amend","action":"agent-proposal.approve","resource":{"type":"agent-proposal","ref":"prop_01J8QL9"},"amount":{"amount":1800,"currency":"USD"}}},"secondApprover":{"summary":"A second approver binds a token to compensating one order","value":{"scope":"orders:cancel","action":"reconcile.fail","resource":{"type":"order","ref":"MO-8KD-2FQ-9XT"},"amount":{"amount":120400,"currency":"USD"}}}}}}},"responses":{"201":{"description":"Minted; the token value appears here and nowhere else","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleApprovalToken"},"examples":{"minted":{"summary":"201 — single-use, expires in 120 seconds","value":{"approvalTokenId":"apt_01J8QM3","token":"apt_01J8QM3.v8n2k4x9q1z7","scope":"orders:amend","action":"agent-proposal.approve","resource":{"type":"agent-proposal","ref":"prop_01J8QL9"},"amount":{"amount":1800,"currency":"USD"},"singleUse":true,"mintedBy":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","mintedAt":"2026-09-27T09:05:00Z","expiresAt":"2026-09-27T09:07:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/uploads":{"post":{"operationId":"createUpload","x-mcp":{"expose":"tool","toolset":"public","name":"create_upload","class":"commit-lite","autonomy":"L1"},"tags":["orders"],"summary":"Ask for a pre-signed upload slot for a FILE-typed requiredInfo answer (IMP-P2-077; 03 §10.4)","description":"The client PUTs the file to the returned url with exactly the returned headers, then references uploadId in requiredInfoAnswers (or Traveler.documents[].uploadId). Slots are single-use, tenant-walled, time-boxed and scanned; a file over maxBytes or outside the FileAnswerSpec.accept list is refused 400 VALIDATION details.field sizeBytes or contentType. The object's data class is pinned from purpose (DATA-GOVERNANCE §2) and it is deleted when no order references it past expiresAt. Never a public URL, never bytes in an order body.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"},"examples":{"passportScan":{"summary":"A passport scan for a VISA application (a visa supplier)","value":{"fileName":"passport.pdf","contentType":"application/pdf","sizeBytes":1834022,"purpose":"PASSPORT_SCAN","field":"passportScan","offerRef":"OF-V-2d81aa"}},"eticket":{"summary":"The airline e-ticket an airport-service handler needs (an airport-services supplier)","value":{"fileName":"eticket-LA8084.pdf","contentType":"application/pdf","sizeBytes":210334,"purpose":"ETICKET","field":"eticketFile"}}}}}},"responses":{"201":{"description":"Slot issued; PUT the bytes before expiresAt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"},"examples":{"slot":{"summary":"201 — a pre-signed PUT slot","value":{"uploadId":"up_01JA7F3K2Q","url":"https://uploads.example/t/acme-travel/up_01JA7F3K2Q?X-Amz-Signature=redacted","method":"PUT","headers":{"Content-Type":"application/pdf","Content-Length":"1834022"},"expiresAt":"2026-11-01T10:15:00Z","maxBytes":5242880,"state":"PENDING","dataClass":"C2"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/uploads/{uploadId}":{"get":{"operationId":"getUpload","x-mcp":{"expose":"resource","toolset":"public","name":"get_upload","class":"read","autonomy":"L0"},"tags":["orders"],"summary":"The state of an upload slot — whether the bytes landed and passed scanning","parameters":[{"name":"uploadId","description":"The upload slot to read, as the upload call returned it.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The slot; url is absent once the slot is spent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"},"examples":{"scanned":{"summary":"200 — received and clean; usable in an answer","value":{"uploadId":"up_01JA7F3K2Q","url":"https://uploads.example/t/acme-travel/up_01JA7F3K2Q","method":"PUT","expiresAt":"2026-11-01T10:15:00Z","maxBytes":5242880,"state":"SCANNED","dataClass":"C2"}},"rejected":{"summary":"200 — rejected by the scanner; upload again","value":{"uploadId":"up_01JA7F3K2Q","url":"https://uploads.example/t/acme-travel/up_01JA7F3K2Q","method":"PUT","expiresAt":"2026-11-01T10:15:00Z","state":"REJECTED","dataClass":"C2"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/hold/{holdRef}/extend":{"post":{"operationId":"extendHold","x-mcp":{"expose":"tool","toolset":"public","name":"extend_hold","class":"commit-lite","autonomy":"L1"},"tags":["price"],"summary":"Ask for more time on an ACTIVE hold where the supplier allows it (IMP-P2-083; an activity supplier)","description":"Answers the same HoldResponse with a later holdExpiresAt. The supplier decides the extension (OCTO extend; Example Activities re-hold); the platform never fakes one. A hold that cannot be extended is 409 CONFLICT details.reason EXTEND_NOT_SUPPORTED; an expired one is 409 HOLD_EXPIRED; same key, different body is 409 IDEMPOTENCY_CONFLICT. The new expiry is the min() of every supplier clock (CANONICAL-MODEL §2.7).\n","parameters":[{"name":"holdRef","description":"The hold to extend, as the hold call returned it.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldExtendRequest"},"examples":{"extend":{"summary":"Ask for 20 more minutes","value":{"context":{"domain":"EXP"},"message":{"intent":{"requestedUntil":"2026-09-14T17:35:00Z"}}}}}}}},"responses":{"200":{"description":"Extended; holdExpiresAt is the new expiry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldResponse"},"examples":{"extended":{"summary":"200 — extended by the supplier","value":{"context":{"domain":"EXP","action":"on_hold","version":"2026-09-30","timestamp":"2026-09-14T17:14:11Z","message_id":"msg_9f2a1d","took_ms":410,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"holdRef":"HD-E-9b1c22","holdExpiresAt":"2026-09-14T17:35:00Z","repriceRef":"RP-E-7c3d20","state":"ACTIVE"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ForbiddenOrSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"HOLD_EXPIRED | CONFLICT (details.reason EXTEND_NOT_SUPPORTED) | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"holdExpired":{"summary":"409 HOLD_EXPIRED — too late; reprice and hold again","value":{"code":"HOLD_EXPIRED","message":"holdRef HD-E-9b1c22 expired at 2026-09-14T17:15:00Z","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"holdRef":"HD-E-9b1c22","expiredAt":"2026-09-14T17:15:00Z"}}},"extendNotSupported":{"summary":"409 CONFLICT EXTEND_NOT_SUPPORTED — this supplier cannot extend a hold","value":{"code":"CONFLICT","message":"This hold cannot be extended; book before it expires or hold again","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXTEND_NOT_SUPPORTED","holdRef":"HD-E-9b1c22"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/enrichment/flight-emissions":{"post":{"operationId":"getFlightEmissions","x-mcp":{"expose":"tool","toolset":"public","name":"get_flight_emissions","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"CO2e per passenger for flights and typical emissions for markets (API-0008 §11; a data provider)","description":"POST because lists outgrow query strings (the /v1/orders/search precedent). Served from cache or the pre-warmed store first; a provider call runs only under the kind's budget. Cache-Control max-age follows the SPI §6 ladder (7 days; invalidated on a model-version change). Scope data:read.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightEmissionsRequest"},"examples":{"oneFlight":{"summary":"One leg, economy","value":{"flights":[{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"}],"cabin":"ECONOMY"}}}}}},"responses":{"200":{"description":"data is FlightEmissions[]","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"max-age per the enrichment TTL ladder, private"},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightEmissionsResponse"},"examples":{"emissions":{"summary":"200 — one leg, 8% under typical","value":{"requestId":"req_01JA7J1","data":[{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"perPax":{"ECONOMY":{"grams":306000,"kg":306,"lb":674.6}},"selectedCabin":"ECONOMY","typical":{"ECONOMY":332000},"deltaVsTypicalPercent":-8,"contrailsImpact":"MODERATE","methodology":{"source":"TIM","modelVersion":"2025.3"}}],"provenance":[{"kind":"FLIGHT_EMISSIONS","provider":"SUP-DAT-EXAMPLE2","modelVersion":"2025.3","retrievedAt":"2026-11-01T02:00:00Z","stale":false,"attribution":"Travel Impact Model, Google (CC BY-SA 4.0)"}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"}}}},"/v1/enrichment/flight-status":{"get":{"operationId":"getFlightStatus","x-mcp":{"expose":"tool","toolset":"public","name":"get_flight_status","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Operational status of a flight, one record per operated leg (API-0008 §11; a data provider, a data provider)","description":"Cache TTL is dynamic by proximity to departure (SPI §6). Never changes order state; the order-scoped view is GET /v1/orders/{orderRef}/enrichment. Scope data:read.\n","parameters":[{"name":"carrier","description":"The airline, as its two-character IATA code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"flightNumber","description":"The flight number without the airline code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"date","description":"The departure date, local to the departure airport.","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"origin","description":"The departure airport, as its IATA code, to narrow a multi-leg flight to one leg.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"data is FlightStatus[]","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightStatusResponse"},"examples":{"delayed":{"summary":"200 — delayed 45 minutes, gate known","value":{"requestId":"req_01JA7J2","data":[{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"status":"DELAYED","departure":{"airport":"GRU","terminal":"3","gate":"318","scheduled":"2026-11-10T23:50:00-03:00","estimated":"2026-11-11T00:35:00-03:00"},"arrival":{"airport":"MIA","terminal":"N","scheduled":"2026-11-11T07:10:00-05:00","estimated":"2026-11-11T07:50:00-05:00"},"delayMinutes":{"departure":45,"arrival":40},"travelDay":{"date":"2026-11-10","phase":"DAY_OF"},"lastUpdatedAt":"2026-11-10T21:02:00Z"}],"provenance":[{"kind":"FLIGHT_STATUS","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-10T21:02:30Z","stale":false}],"warnings":[]}},"unavailable":{"summary":"200 — no provider answered; empty data with a warning (enrichment never fails a verb)","value":{"requestId":"req_01JA7J3","data":[],"provenance":[],"warnings":[{"code":"ENRICHMENT_UNAVAILABLE","message":"No flight-status provider answered within budget","details":{"kind":"FLIGHT_STATUS"}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/flight-ratings":{"get":{"operationId":"getFlightRatings","x-mcp":{"expose":"tool","toolset":"public","name":"get_flight_ratings","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"On-time statistics for a flight number or a route (API-0008 §11)","parameters":[{"name":"carrier","description":"The airline, as its two-character IATA code.","in":"query","schema":{"type":"string"}},{"name":"flightNumber","description":"The flight number without the airline code; send it, or origin and destination for a route.","in":"query","schema":{"type":"string"}},{"name":"origin","description":"The departure airport, as its IATA code, for a route.","in":"query","schema":{"type":"string"}},{"name":"destination","description":"The arrival airport, as its IATA code, for a route.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"data is FlightRatings","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRatingsResponse"},"examples":{"route":{"summary":"200 — GRU-MIA on-time 81.5%","value":{"requestId":"req_01JA7J4","data":{"route":{"origin":"GRU","destination":"MIA"},"sample":{"observations":412},"onTimePercent":81.5,"distribution":{"onTime":81.5,"late15":9.2,"late30":4.1,"late45":3,"cancelled":1.4,"diverted":0.8},"delayMinutes":{"mean":11.4,"stdDev":22.1},"stars":{"overall":4.1}},"provenance":[{"kind":"FLIGHT_RATINGS","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-01T02:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/flight-amenities":{"get":{"operationId":"getFlightAmenities","x-mcp":{"expose":"tool","toolset":"public","name":"get_flight_amenities","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Cabin amenities of a flight, with confidence (API-0008 §11)","parameters":[{"name":"carrier","description":"The airline, as its two-character IATA code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"flightNumber","description":"The flight number without the airline code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"date","description":"The departure date, local to the departure airport.","in":"query","schema":{"type":"string","format":"date"}},{"name":"cabin","description":"Only this cabin, for example ECONOMY or BUSINESS; absent means every cabin.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"data is FlightAmenities[]","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightAmenitiesResponse"},"examples":{"economy":{"summary":"200 — predicted economy amenities","value":{"requestId":"req_01JA7J5","data":[{"cabin":"ECONOMY","seat":{"pitchInches":31,"type":"STANDARD"},"wifi":"PAID","power":"AVAILABLE","ife":"SEATBACK","confidence":"PREDICTED"}],"provenance":[{"kind":"FLIGHT_AMENITIES","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-01T02:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/flight-track":{"get":{"operationId":"getFlightTrack","x-mcp":{"expose":"tool","toolset":"public","name":"get_flight_track","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"The flown track of a flight, one record per operated leg that has taken off (API-0008 §11; a data provider)","description":"Time-ordered positions of the aircraft for each operated leg of the flight that has taken off, and its latest position while the leg is airborne, for a client to draw on its own map. A leg that has not taken off, is cancelled, or lies outside the provider's window has no track: data is empty and warnings carry ENRICHMENT_UNAVAILABLE. Cache TTL is short while a leg is airborne; a landed leg's track is final (SPI §6). No map image is served. Never changes order state. Scope data:read.\n","parameters":[{"name":"carrier","description":"The airline, as its two-character IATA code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"flightNumber","description":"The flight number without the airline code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"date","description":"The departure date, local to the departure airport.","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"origin","description":"The departure airport, as its IATA code, to narrow a multi-leg flight to one leg.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"data is FlightTrack[]","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightTrackResponse"},"examples":{"airborne":{"summary":"200 — airborne, three points so far","value":{"requestId":"req_01JA7J6","data":[{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"airborne":true,"points":[{"position":{"lat":-23.4356,"lon":-46.4731},"at":"2026-11-11T03:41:00Z","altitudeFeet":2500,"groundSpeedKnots":180,"headingDegrees":350,"source":"ADSB"},{"position":{"lat":-22.9071,"lon":-46.6345},"at":"2026-11-11T03:47:30Z","altitudeFeet":18000,"groundSpeedKnots":390,"headingDegrees":345,"source":"ADSB"},{"position":{"lat":-21.6014,"lon":-47.0102},"at":"2026-11-11T03:56:10Z","altitudeFeet":35000,"groundSpeedKnots":470,"headingDegrees":344,"source":"RADAR"}],"current":{"position":{"lat":-21.6014,"lon":-47.0102},"at":"2026-11-11T03:56:10Z","altitudeFeet":35000,"groundSpeedKnots":470,"headingDegrees":344,"source":"RADAR"},"lastUpdatedAt":"2026-11-11T03:56:10Z"}],"provenance":[{"kind":"FLIGHT_TRACK","provider":"SUP-DAT-EXAMPLE4","retrievedAt":"2026-11-11T03:57:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/route-estimates":{"post":{"operationId":"getRouteEstimates","x-mcp":{"expose":"tool","toolset":"public","name":"get_route_estimates","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Door-to-door time and distance per leg, or an origins × destinations matrix (API-0008 §11; a data provider)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteEstimatesRequest"},"examples":{"oneLeg":{"summary":"Airport to hotel by car, historical traffic","value":{"legs":[{"legRef":"OI-2/0","origin":{"iata":"GRU"},"destination":{"placeId":"mondee:place:sp-hotel-unique"}}],"modes":["CAR"],"departAt":"2026-11-11T08:10:00-03:00","traffic":"HISTORICAL"}}}}}},"responses":{"200":{"description":"data is RouteEstimate[] for legs, RouteMatrix for origins × destinations","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteEstimatesResponse"},"examples":{"legs":{"summary":"200 — 48 minutes with historical traffic","value":{"requestId":"req_01JA7J6","data":{"estimates":[{"legRef":"OI-2/0","mode":"CAR","distanceMeters":31200,"durationSeconds":2880,"trafficDelaySeconds":600,"noTrafficDurationSeconds":2280,"trafficModel":"HISTORICAL","departAt":"2026-11-11T08:10:00-03:00","arriveAt":"2026-11-11T08:58:00-03:00"}]},"provenance":[{"kind":"ROUTE_ESTIMATE","provider":"SUP-DAT-EXAMPLE7","retrievedAt":"2026-11-01T10:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/weather":{"get":{"operationId":"getWeather","x-mcp":{"expose":"tool","toolset":"public","name":"get_weather","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Current, forecast or historical weather at a place (API-0008 §11; a data provider)","parameters":[{"name":"place","in":"query","required":true,"schema":{"type":"string"},"description":"A mondee:place:… id or an IATA code"},{"name":"from","description":"First day of the forecast or history (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","description":"Last day of the forecast or history (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"name":"kind","description":"CURRENT for now, FORECAST for coming days or HISTORICAL for past days.","in":"query","schema":{"type":"string","enum":["CURRENT","FORECAST","HISTORICAL"]}}],"responses":{"200":{"description":"data is WeatherForecast","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"},"examples":{"forecast":{"summary":"200 — Miami, one day","value":{"requestId":"req_01JA7J7","data":{"place":{"iata":"MIA"},"kind":"FORECAST","days":[{"date":"2026-11-11","min":{"c":22,"f":71.6},"max":{"c":29,"f":84.2},"condition":"PARTLY_CLOUDY","conditionText":"Partly cloudy"}]},"provenance":[{"kind":"WEATHER_FORECAST","provider":"SUP-DAT-EXAMPLE5","retrievedAt":"2026-11-09T06:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/holidays":{"get":{"operationId":"getHolidays","x-mcp":{"expose":"tool","toolset":"public","name":"get_holidays","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Public holidays and long weekends for a country (API-0008 §11; a data provider)","parameters":[{"name":"country","description":"The country, as its ISO 3166-1 alpha-2 code.","in":"query","required":true,"schema":{"type":"string"}},{"name":"subdivision","description":"A state or region, as its ISO 3166-2 code, to add its own holidays.","in":"query","schema":{"type":"string"}},{"name":"from","description":"First day of the range (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","description":"Last day of the range (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"name":"include","in":"query","schema":{"type":"string"},"description":"LONG_WEEKENDS to add the platform-computed long weekends; CELLS (planned) to add one HolidayCalendar.cells[] entry per day"}],"responses":{"200":{"description":"data is HolidayCalendar","headers":{"Cache-Control":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidaysResponse"},"examples":{"brazilNovember":{"summary":"200 — Brazil, November, with long weekends","value":{"requestId":"req_01JA7J8","data":{"country":"BR","range":{"from":"2026-11-01","to":"2026-11-30"},"holidays":[{"date":"2026-11-15","name":"Proclamação da República","scope":"NATIONAL","public":true},{"date":"2026-11-20","name":"Dia da Consciência Negra","scope":"NATIONAL","public":true}],"longWeekends":[{"start":"2026-11-20","end":"2026-11-22","days":3,"holidayDates":["2026-11-20"],"bridgeDays":[]}]},"provenance":[{"kind":"HOLIDAYS","provider":"SUP-DAT-EXAMPLE8","retrievedAt":"2026-10-15T00:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enrichment/entry-requirements":{"get":{"operationId":"getEntryRequirements","x-mcp":{"expose":"tool","toolset":"public","name":"get_entry_requirements","class":"read","autonomy":"L0","example":"{\"origin\":\"US\",\"destination\":\"BR\",\"nationality\":\"US\",\"residence\":\"US\",\"purpose\":\"TOURISM\",\"stayDays\":10}"},"tags":["enrichment"],"summary":"Documents a traveller needs for a corridor; never an eligibility verdict (API-0008 §11; a data provider)","description":"Tenant-scoped, Cache-Control private, TTL at most 24 hours. nationality and residence are personal attributes: never logged beside an order ref. The answer's shownAt is what an order keeps as evidence (CANONICAL-MODEL §5 P4). Scope data:read.\n","parameters":[{"name":"origin","description":"Where the traveller departs from, as a country code (ISO 3166-1 alpha-2).","in":"query","required":true,"schema":{"type":"string"}},{"name":"destination","description":"Where the traveller enters, as a country code (ISO 3166-1 alpha-2).","in":"query","required":true,"schema":{"type":"string"}},{"name":"nationality","description":"The traveller's nationality, as a country code (ISO 3166-1 alpha-2).","in":"query","required":true,"schema":{"type":"string"}},{"name":"residence","description":"The traveller's country of residence, as a country code (ISO 3166-1 alpha-2), when it differs from the nationality.","in":"query","required":true,"schema":{"type":"string"}},{"name":"purpose","description":"The reason for the trip, for example TOURISM, BUSINESS, TRANSIT, STUDY or WORK.","in":"query","schema":{"type":"string"}},{"name":"stayDays","description":"How many days the traveller stays.","in":"query","schema":{"type":"integer"}},{"name":"age","description":"The traveller's age in years, where requirements differ for minors.","in":"query","schema":{"type":"integer"}},{"name":"transport","description":"How the traveller arrives at the destination.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"data is EntryRequirements","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"private, max-age at most 86400"},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryRequirementsResponse"},"examples":{"brToUs":{"summary":"200 — BR national resident in BR entering the US","value":{"requestId":"req_01JA7J9","data":{"query":{"origin":"BR","destination":"US","nationality":"BR","residence":"BR"},"groups":[{"kind":"ENTRY","country":"US","docsRequired":true,"profiles":[{"profileId":"default","documentOptions":[[{"name":"Visa B1/B2","type":"VISA","requirementLevel":"REQUIRED"}]]}]}],"verdict":"NOT_PROVIDED","shownAt":"2026-11-01T10:05:00Z","disclaimerText":"Requirements change; verify with the consulate before travel."},"provenance":[{"kind":"ENTRY_REQUIREMENTS","provider":"SUP-DAT-EXAMPLE3","retrievedAt":"2026-11-01T10:05:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/orders/{orderRef}/enrichment":{"get":{"operationId":"getOrderEnrichment","x-mcp":{"expose":"resource","toolset":"public","name":"get_order_enrichment","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"The trip-page aggregate for one order — flight status from the watch store, weather, entry requirements, route estimates (API-0008 §11)","description":"Same permission as the order read; flight status is read from the watch store, never a live provider call (SPI §4.1).","parameters":[{"$ref":"#/components/parameters/OrderRefPath"}],"responses":{"200":{"description":"data is OrderEnrichment","headers":{"Cache-Control":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEnrichmentResponse"},"examples":{"trip":{"summary":"200 — an air order two days out","value":{"requestId":"req_01JA7JA","data":{"flightStatus":[{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"status":"SCHEDULED","lastUpdatedAt":"2026-11-08T21:00:00Z"}],"weather":[{"place":{"iata":"MIA"},"kind":"FORECAST","days":[{"date":"2026-11-11","min":{"c":22,"f":71.6},"max":{"c":29,"f":84.2},"condition":"PARTLY_CLOUDY"}]}],"entryRequirements":[],"routeEstimates":[],"provenance":[{"kind":"FLIGHT_STATUS","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-08T21:00:30Z","stale":false}]},"provenance":[{"kind":"FLIGHT_STATUS","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-08T21:00:30Z","stale":false}],"warnings":[]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/sustainability/summary":{"get":{"operationId":"getCarbonSummary","x-mcp":{"expose":"tool","toolset":"public","name":"get_carbon_summary","class":"read","autonomy":"L0"},"tags":["enrichment"],"summary":"Account-level emissions and \"carbon saved\" over a period, from order-commit snapshots (API-0008 §11)","parameters":[{"name":"from","description":"First day of the period (inclusive).","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","description":"Last day of the period (inclusive).","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"data is CarbonSummary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarbonSummaryResponse"},"examples":{"october":{"summary":"200 — October, 17 t saved against typical","value":{"requestId":"req_01JA7JB","data":{"tenant":"acme-travel","period":{"from":"2026-10-01","to":"2026-10-31"},"flownSegments":1240,"totalGrams":384000000,"typicalGrams":401000000,"savedGrams":17000000,"kg":384000,"lb":846575},"provenance":[{"kind":"FLIGHT_EMISSIONS","provider":"SUP-DAT-EXAMPLE2","retrievedAt":"2026-11-01T02:00:00Z","stale":false}],"warnings":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/jobs/{jobId}/download":{"get":{"operationId":"getJobDownload","x-mcp":{"expose":"excluded","toolset":"console","name":"get_job_download","class":"read","autonomy":"L0","reason":"Mints a signed download link to the finance:export ledger artifact, which is the same data-boundary decision that keeps export_ledger with a human."},"tags":["admin"],"x-audience":"console","summary":"A signed, short-lived download link for a SUCCEEDED job's artifact (ledger export; BE-4)","description":"Answers a fresh ConsoleJobDownload each time it is called; the previous link stays valid until its own expiresAt. A job that is not SUCCEEDED is 409 CONFLICT details.reason JOB_NOT_READY (QUEUED or RUNNING) or JOB_FAILED. Requires finance:export.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The link","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleJobDownload"},"examples":{"csv":{"summary":"200 — a 15-minute link to the CSV","value":{"jobId":"job_01J8QP4","url":"https://exports.example/t/acme-travel/job_01J8QP4.csv?X-Amz-Signature=redacted","expiresAt":"2026-09-27T09:55:41Z","contentType":"text/csv","sizeBytes":184220,"rows":1612}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason JOB_NOT_READY | JOB_FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notReady":{"summary":"409 CONFLICT JOB_NOT_READY — still running; poll GET /admin/v1/jobs/{jobId}","value":{"code":"CONFLICT","message":"Job job_01J8QP4 is RUNNING; the artifact is not ready","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"JOB_NOT_READY","state":"RUNNING"}}},"failed":{"summary":"409 CONFLICT JOB_FAILED — nothing to download","value":{"code":"CONFLICT","message":"Job job_01J8QP5 FAILED; there is no artifact","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"JOB_FAILED","state":"FAILED"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agents/{agentId}:resume":{"post":{"operationId":"resumeAgent","x-mcp":{"expose":"excluded","toolset":"console","name":"resume_agent","class":"commit-lite","autonomy":"L1","reason":"Kill switch; an agent must never pause or resume agents or flip switches."},"tags":["admin"],"x-audience":"console","summary":"Resume a PAUSED agent — the kill switch's inverse (IMP-P3-032; FEAT-019 §6)","description":"Proxied to the AI plane's POST /ai/v1/agents/{id}/resume (OD-404). Idempotent — resuming an ACTIVE agent is a 200. Emits `agent.resumed` with actor.kind user and the reason. Requires the Owner or Admin role and an Idempotency-Key.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentResumeRequest"},"examples":{"resume":{"summary":"Resume after the fix shipped","value":{"reason":"Fare-family mapping fixed in the 09:40 deploy; proposals re-verified"}}}}}},"responses":{"200":{"description":"The agent, ACTIVE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgent"},"examples":{"resumed":{"summary":"200 — active again","value":{"agentId":"agent-rebooking-ag22","name":"Disruption rebooking assistant","useCase":"AG-22","journey":"J4","autonomyLevel":"L1","state":"ACTIVE","owner":"platform-ai","policyDocRef":"FEAT-019 §5 J4 AG-22","lastActiveAt":"2026-09-27T09:05:30Z","openProposals":1,"pausedAt":null,"pausedBy":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/agent-proposals/{proposalId}":{"get":{"operationId":"getConsoleAgentProposal","x-mcp":{"expose":"resource","toolset":"console","name":"get_console_agent_proposal","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Read one proposal by id for the agent desk's detail screen (IMP-P3-032; FEAT-019 §6)","description":"The same ConsoleAgentProposal object the list answers, fetched by id instead of by filtering page one (ProposalDetailScreen). Proxied to the AI plane's GET /ai/v1/proposals/{id}; states map as on the list. Requires agents:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/ProposalIdPath"}],"responses":{"200":{"description":"The proposal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleAgentProposal"},"examples":{"open":{"summary":"200 — OPEN, awaiting a human","value":{"proposalId":"prop_01J9ZK2","agentId":"agt_rebook_22","type":"REBOOKING","title":"Book the 18:40 departure for the disrupted traveller","summary":"The 14:10 was cancelled by the carrier; the 18:40 has the same fare family and two seats together.","state":"OPEN","actor":{"kind":"ai","id":"key_ai_plane_01","agentId":"agt_rebook_22"},"resource":{"type":"reprice","ref":"RP-F-7c3d20"},"requiresApprovalToken":true,"approvalAction":"orders.create","amount":{"amount":84210,"currency":"USD"},"evidence":[{"kind":"EVENT","ref":"evt_01J9ZJ4","label":"order.schedule_changed"}],"changes":[{"path":"items[0].segments[0].departure","before":"2026-10-02T14:10:00-05:00","after":"2026-10-02T18:40:00-05:00"}],"projectedEffect":{"description":"Card on file charged for the new booking","amountDelta":{"amount":84210,"currency":"USD"}},"undoPlan":"Cancel the new order inside the free-cancellation window","createdAt":"2026-09-29T15:00:00Z","expiresAt":"2026-10-01T15:00:00Z","decidedBy":null,"decidedAt":null,"decisionReason":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/supplier-data/datasets":{"get":{"operationId":"listSupplierDatasets","x-mcp":{"expose":"tool","toolset":"console","name":"list_supplier_datasets","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The supplier × dataset overview grid — freshness, counts, next run, errors, quota (FEAT-023 §5 item 1)","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"Only the datasets of this supplier.","in":"query","schema":{"type":"string"}},{"name":"dataset","description":"Only this dataset, across suppliers.","in":"query","schema":{"type":"string"}},{"name":"vertical","description":"Only the suppliers of this product, by its product code (for example HTL).","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"},"description":"FRESH | STALE | FAILING | PAUSED"},{"name":"credentialSet","description":"Only the datasets synced with this credential set; absent means your default set.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The grid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataDatasets"},"examples":{"grid":{"summary":"200 — two cells, one stale","value":{"cells":[{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","status":"FRESH","dataAsOf":"2026-10-01T08:15:00Z","ageSeconds":2700,"maxStaleness":"P1D","recordCount":184210,"deltaSinceLastRun":298,"errorsLast24h":0,"nextRunAt":"2026-10-01T10:15:00Z","quotaUsedToday":0.21,"currentSnapshotId":"snap_01JA7K9"},{"supplierId":"SUP-HTL-EXAMPLE4","dataset":"availability_calendar","credentialSet":"MONDEE","status":"STALE","dataAsOf":"2026-09-29T02:10:00Z","ageSeconds":197400,"maxStaleness":"P1D","recordCount":2210400,"errorsLast24h":3,"nextRunAt":"2026-10-02T02:00:00Z","quotaUsedToday":0}],"generatedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/profiles/{supplierId}/{dataset}":{"get":{"operationId":"getSupplierDataProfile","x-mcp":{"expose":"resource","toolset":"console","name":"get_supplier_data_profile","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Read one supplier × dataset sync profile; the ETag is the version PUT must present (FEAT-023 §6)","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"The supplier the profile syncs.","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","description":"The dataset the profile syncs, for example catalog_product.","in":"path","required":true,"schema":{"type":"string"}},{"name":"credentialSet","description":"The credential set the profile belongs to; absent means your default set.","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The profile","headers":{"ETag":{"schema":{"type":"string"},"description":"The profile version; send it back as If-Match on PUT"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfile"},"examples":{"viator":{"summary":"200 — Example Activities catalogue, weekly FULL + hourly INCREMENTAL","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"INCREMENTAL","cron":"15 * * * *"}],"limits":{"concurrency":4,"quotaShare":0.3},"freshness":{"ttl":"PT6H","maxStaleness":"P1D","staleWhileRevalidate":"PT1H","liveFallback":"STALE_MARKER"},"language":"en","currency":"USD","enabled":true,"version":7,"updatedAt":"2026-10-01T08:00:00Z","estimate":{"calls":1850,"durationSeconds":2400,"quotaShare":0.21}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateSupplierDataProfile","x-mcp":{"expose":"tool","toolset":"console","name":"update_supplier_data_profile","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Replace the editable part of a profile under optimistic concurrency (FEAT-023 §6; supplier_data:manage)","description":"If-Match must carry the ETag the last GET answered; a stale one is 409 CONFLICT details.reason VERSION_MISMATCH and nothing is written. The answer carries the new ETag. Audit-logged with actor and the fields changed. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"The supplier the profile syncs.","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","description":"The dataset the profile syncs, for example catalog_product.","in":"path","required":true,"schema":{"type":"string"}},{"name":"If-Match","description":"The ETag the last read of this profile answered; a stale value changes nothing and answers a conflict.","in":"header","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfileUpdate"},"examples":{"nightlyWindow":{"summary":"Move the schedules window to nightly, next 120 days","value":{"modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"WINDOW","cron":"0 2 * * *","window":{"fromOffsetDays":0,"toOffsetDays":120,"sliceDays":7}}],"enabled":true}}}}}},"responses":{"200":{"description":"Updated","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfile"},"examples":{"updated":{"summary":"200 — version 8","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"WINDOW","cron":"0 2 * * *","window":{"fromOffsetDays":0,"toOffsetDays":120,"sliceDays":7}}],"enabled":true,"version":8,"updatedAt":"2026-10-01T09:05:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason VERSION_MISMATCH (If-Match is stale) | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionMismatch":{"summary":"409 CONFLICT VERSION_MISMATCH — re-read and re-apply","value":{"code":"CONFLICT","message":"The profile changed since you read it (version 8, you sent 7)","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERSION_MISMATCH","currentVersion":8}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/runs":{"get":{"operationId":"listSupplierDataRuns","x-mcp":{"expose":"tool","toolset":"console","name":"list_supplier_data_runs","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Run history, newest first (FEAT-023 §5 item 4)","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"Only the runs of this supplier.","in":"query","schema":{"type":"string"}},{"name":"dataset","description":"Only the runs of this dataset.","in":"query","schema":{"type":"string"}},{"name":"status","description":"Only runs in this status: QUEUED, RUNNING, PAUSED, SUCCEEDED, PARTIAL, FAILED or CANCELLED.","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"A page of runs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRunList"},"examples":{"page":{"summary":"200 — one running, one succeeded","value":{"runs":[{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","startedAt":"2026-10-01T09:00:00Z"},{"runId":"run_01JA6ZQ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"FULL","trigger":"SCHEDULE","status":"SUCCEEDED","startedAt":"2026-09-27T03:00:00Z","endedAt":"2026-09-27T03:41:00Z","snapshotId":"snap_01JA6ZZ"}],"page":{"total":2}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"startSupplierDataRun","x-mcp":{"expose":"tool","toolset":"console","name":"start_supplier_data_run","class":"commit-lite","autonomy":"L1","example":"{\"idempotencyKey\":\"b8c1e5f2-9a34-4d6b-8e07-1f5a2c9d3b76\",\"body\":{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"mode\":\"INCREMENTAL\"}}"},"tags":["admin"],"x-audience":"console","summary":"Run now — FULL, INCREMENTAL or WINDOW with an optional narrower scope (FEAT-023 §6; supplier_data:run)","description":"Answers 202 with the run QUEUED; progress is read at GET /admin/v1/supplier-data/runs/{runId}. A run already RUNNING for the same profile is 409 CONFLICT details.reason RUN_IN_PROGRESS. Requires an Idempotency-Key. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRunRequest"},"examples":{"incremental":{"summary":"Incremental now","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","mode":"INCREMENTAL"}}}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"queued":{"summary":"202 — queued","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","triggeredBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","status":"QUEUED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason RUN_IN_PROGRESS | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"runInProgress":{"summary":"409 CONFLICT RUN_IN_PROGRESS","value":{"code":"CONFLICT","message":"A run is already in progress for SUP-EXP-EXAMPLE/catalog_product","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"RUN_IN_PROGRESS","runId":"run_01JA7K3"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/runs/{runId}":{"get":{"operationId":"getSupplierDataRun","x-mcp":{"expose":"resource","toolset":"console","name":"get_supplier_data_run","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"One run — status, progress and statistics (FEAT-023 §6)","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","description":"The run to read, as returned when it started or listed by the run history.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"running":{"summary":"200 — running, ETA 15 minutes","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","stats":{"pages":120,"records":{"added":310,"updated":4120,"removed":12},"quotaUsed":640,"errors":0,"etaSeconds":900},"startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/runs/{runId}:cancel":{"post":{"operationId":"cancelSupplierDataRun","x-mcp":{"expose":"tool","toolset":"console","name":"cancel_supplier_data_run","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Cancel a QUEUED, RUNNING or PAUSED run; what landed stays, no snapshot is cut (FEAT-023 §6)","description":"connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","description":"The run to cancel, as returned when it started or listed by the run history.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, CANCELLED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"cancelled":{"summary":"200 — cancelled","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"CANCELLED","startedAt":"2026-10-01T09:00:00Z","endedAt":"2026-10-01T09:12:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/runs/{runId}:pause":{"post":{"operationId":"pauseSupplierDataRun","x-mcp":{"expose":"tool","toolset":"console","name":"pause_supplier_data_run","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Pause a RUNNING run at its next page boundary (FEAT-023 §6)","description":"connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","description":"The run to pause, as returned when it started or listed by the run history.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, PAUSED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"paused":{"summary":"200 — paused","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"PAUSED","startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/runs/{runId}:resume":{"post":{"operationId":"resumeSupplierDataRun","x-mcp":{"expose":"tool","toolset":"console","name":"resume_supplier_data_run","class":"commit-lite","autonomy":"L1"},"tags":["admin"],"x-audience":"console","summary":"Resume a PAUSED run from its cursor (FEAT-023 §6)","description":"connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","description":"The paused run to resume, as returned when it started or listed by the run history.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, RUNNING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"resumed":{"summary":"200 — running again","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/snapshots/{supplierId}/{dataset}":{"get":{"operationId":"listSupplierDataSnapshots","x-mcp":{"expose":"tool","toolset":"console","name":"list_supplier_data_snapshots","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The versions of a dataset, newest first (FEAT-023 §5 item 5)","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"The supplier whose snapshots are listed.","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","description":"The dataset whose snapshots are listed, for example catalog_product.","in":"path","required":true,"schema":{"type":"string"}},{"name":"credentialSet","description":"Only the snapshots synced with this credential set; absent means your default set.","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"A page of snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataSnapshotList"},"examples":{"two":{"summary":"200 — current and its parent","value":{"snapshots":[{"snapshotId":"snap_01JA7K9","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-10-01T08:15:00Z","recordCount":184210,"checksum":"sha256:9f2a1c","mappingVersion":"2026-09-28.2","parentSnapshotId":"snap_01JA6ZZ","current":true},{"snapshotId":"snap_01JA6ZZ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-09-27T03:41:00Z","recordCount":183912,"checksum":"sha256:1c9f2a","mappingVersion":"2026-09-28.2","current":false}],"page":{"total":2}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/snapshots/{supplierId}/{dataset}/diff":{"get":{"operationId":"diffSupplierDataSnapshots","x-mcp":{"expose":"tool","toolset":"console","name":"diff_supplier_data_snapshots","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The difference between two snapshots — counts and a page of changed keys (FEAT-023 §5 item 5)","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","description":"The supplier whose dataset is compared.","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","description":"The dataset compared, for example catalog_product or property_content.","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","description":"The older snapshot (its snapshotId).","in":"query","required":true,"schema":{"type":"string"}},{"name":"to","description":"The newer snapshot (its snapshotId).","in":"query","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"The diff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataDiff"},"examples":{"diff":{"summary":"200 — 310 added, 4120 updated, 12 removed","value":{"from":"snap_01JA6ZZ","to":"snap_01JA7K9","added":310,"updated":4120,"removed":12,"changes":[{"supplierKey":"5010SYDNEY","change":"UPDATED","fields":["price_from","images"]}],"page":{"total":4442}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/snapshots/{supplierId}/{dataset}/{snapshotId}:rollback":{"post":{"operationId":"rollbackSupplierDataSnapshot","x-mcp":{"expose":"excluded","toolset":"console","name":"rollback_supplier_data_snapshot","class":"commit-lite","autonomy":"L1","reason":"On the PROD rung it needs a second approver's X-Approval-Token minted at POST /admin/v1/approvals, which an agent can never mint, and it re-runs the FEAT-014 matcher over live catalog data."},"tags":["admin"],"x-audience":"console","summary":"Make an earlier snapshot current (FEAT-023 §5 item 5; supplier_data:rollback). PROD needs a D-50 approval token.","description":"Reason-stamped and audit-logged. On the PROD rung a second approver's token minted at POST /admin/v1/approvals (action supplier-data.rollback, resource the snapshot) must ride in X-Approval-Token; without it 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED. Emits supplier_data.snapshot.rolled_back and re-runs the FEAT-014 matcher.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"snapshotId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRollbackRequest"},"examples":{"badDump":{"summary":"Roll back a truncated ingest","value":{"reason":"08:15 run ingested a truncated dump (record count fell 31%)"}}}}}},"responses":{"200":{"description":"The snapshot, now current","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataSnapshot"},"examples":{"rolledBack":{"summary":"200 — parent is current again","value":{"snapshotId":"snap_01JA6ZZ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-09-27T03:41:00Z","recordCount":183912,"checksum":"sha256:1c9f2a","mappingVersion":"2026-09-28.2","current":true}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleApprovalForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/purge":{"post":{"operationId":"purgeSupplierData","x-mcp":{"expose":"excluded","toolset":"console","name":"purge_supplier_data","class":"commit-lite","autonomy":"L1","reason":"Destructive cache purge across the tenant's datasets; not reversible by the agent, left to a human."},"tags":["admin"],"x-audience":"console","summary":"Purge a dataset, a scope or single keys from the cache layers (FEAT-023 §5 item 6; supplier_data:purge). PROD needs a D-50 approval token.","description":"Never touches the dataset store (L3) — a purge forces re-reads from it. Audit-logged with actor, reason and scope; on PROD a second approver's X-Approval-Token (action supplier-data.purge) is required, else 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ApprovalToken"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataPurgeRequest"},"examples":{"oneKey":{"summary":"Purge one product's cache entries","value":{"scope":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","supplierKeys":["5010SYDNEY"]},"reason":"Stale image set reported by the supplier"}}}}}},"responses":{"200":{"description":"Purged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataPurgeResult"},"examples":{"purged":{"summary":"200 — entries removed per layer","value":{"purged":{"L1":1,"L2":1,"CDN":14},"auditRef":"aud_01JA7M1"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleApprovalForbidden"},"409":{"$ref":"#/components/responses/ConsoleConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/login":{"post":{"operationId":"supplierLogin","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_login","class":"commit-money","autonomy":"L1","reason":"An anonymous username/password sign-in that mints a supplier session. Handling passwords and minting sessions is never an agent action, and there is no verified principal yet for the autonomy ladder."},"tags":["supplier"],"x-audience":"supplier","summary":"Supplier-realm sign-in — {username, password} → session, or an MFA challenge (IMP-P4-049)","description":"Same enumeration-safe 401 as the console login (details.reason LOGIN_FAILED). A user with MFA enrolled gets 202 SupplierMfaChallenge and completes it at /auth/mfa/verify. Plain JSON, not an envelope. Rate-limited per client and per subject as the identity routes are.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierLoginRequest"},"examples":{"supplierOps":{"summary":"A supplier ops user","value":{"username":"ops@villas.example","password":"correct-horse-battery-staple"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSessionResponse"},"examples":{"session":{"summary":"200 — a supplier session","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-01T09:20:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-31T09:05:00Z","context":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act","supplier_fulfilment:write"],"actorKind":"human"}}}}}}},"202":{"description":"MFA required; complete the challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierMfaChallenge"},"examples":{"totp":{"summary":"202 — TOTP challenge","value":{"challengeId":"mfa_01JA7N2","methods":["TOTP"],"expiresAt":"2026-10-01T09:10:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/invites/accept":{"post":{"operationId":"supplierAcceptInvite","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_accept_invite","class":"commit-money","autonomy":"L1","reason":"An anonymous route where the invite token is the credential and the caller sets the account password. That creates a credential, and there is no principal for the kill switch or autonomy ladder to check."},"tags":["supplier"],"x-audience":"supplier","summary":"Accept a supplier-user invite with a chosen password — once (v1.6.5; mirrors /v1/invites/accept)","description":"Added in v1.6.5; the backend has served it since the supplier realm landed. Anonymous: the token is the credential, carried in the BODY as on /v1/invites/accept. The password must be 12 to 1024 characters. A token that is unknown, used, expired or whose user is no longer INVITED is one and the same 400 VALIDATION details.reason INVITE_INVALID; a password outside the policy is 400 VALIDATION details.field password. The account becomes ACTIVE with that password; the token is spent. Plain JSON, not an envelope. Rate-limited per client and per invite token as the identity routes are (429 RATE_LIMITED).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAcceptRequest"},"examples":{"accept":{"summary":"The token from the invite, and a chosen password","value":{"token":"q8Zp3VxN6tYb1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","password":"another long passphrase"}}}}}},"responses":{"201":{"description":"The supplier user the invite activated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInviteAccepted"},"examples":{"accepted":{"summary":"201 — sign in at /supplier/v1/auth/login with this email and the chosen password","value":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","email":"ops@villas.example","role":"SupplierOps"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/verify":{"post":{"operationId":"supplierMfaVerify","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_verify","class":"commit-money","autonomy":"L1","reason":"An anonymous route that turns an MFA challenge and a human's second-factor code into a supplier session. That mints a credential, and an agent must never hold or relay the code."},"tags":["supplier"],"x-audience":"supplier","summary":"Complete an MFA challenge → session","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierMfaVerifyRequest"},"examples":{"code":{"summary":"The six-digit code","value":{"challengeId":"mfa_01JA7N2","code":"000000"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSessionResponse"},"examples":{"session":{"summary":"200 — a supplier session","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-01T09:20:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-31T09:05:00Z","context":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act"],"actorKind":"human"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/refresh":{"post":{"operationId":"supplierRefresh","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_refresh","class":"commit-money","autonomy":"L1","reason":"This rotates a supplier session's refresh token, which re-mints a credential. An agent never holds a human's refresh token."},"tags":["supplier"],"x-audience":"supplier","summary":"Rotate a supplier session's refresh token","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"},"examples":{"refresh":{"summary":"The current refresh token","value":{"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb"}}}}}},"responses":{"200":{"description":"A new session; the old refresh token is dead","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSessionResponse"},"examples":{"session":{"summary":"200 — rotated","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-01T09:35:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-31T09:20:00Z","context":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act"],"actorKind":"human"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/logout":{"post":{"operationId":"supplierLogout","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_logout","class":"commit-money","autonomy":"L1","reason":"This revokes the human supplier user's own session credential. Session lifecycle belongs to that user, not to an agent."},"tags":["supplier"],"x-audience":"supplier","summary":"End the supplier session","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"204":{"description":"Signed out"},"401":{"$ref":"#/components/responses/Unauthenticated"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/me":{"get":{"operationId":"supplierMe","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_me","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"The resolved supplier principal — supplier code, role, permissions","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"The caller","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAccessContext"},"examples":{"supplierOps":{"summary":"200 — a supplier ops user","value":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act","supplier_fulfilment:write"],"actorKind":"human"}},"opsOnBehalf":{"summary":"200 — a platform-ops session acting for Example Villas","value":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"PLATFORM_OPS","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act","supplier_fulfilment:write"],"actorKind":"human","onBehalf":{"supplierCode":"EXAMPLEVILLAS","reason":"Owner confirmed by phone at 09:12; portal access pending"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/users":{"get":{"operationId":"listSupplierUsers","x-mcp":{"expose":"resource","toolset":"supplier","name":"list_supplier_users","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"The supplier's users and roles (supplier_users:manage to see inactive users)","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Users","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierUserList"},"examples":{"two":{"summary":"200 — owner and ops","value":{"users":[{"userId":"su_0f11aa","email":"owner@villas.example","name":"R. Mehta","role":"SupplierOwner","active":true,"mfaEnrolled":true},{"userId":"su_8a21f0","email":"ops@villas.example","name":"Priya N","role":"SupplierOps","active":true,"mfaEnrolled":true}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"inviteSupplierUser","x-mcp":{"expose":"excluded","toolset":"supplier","name":"invite_supplier_user","class":"commit-money","autonomy":"L1","reason":"Inviting a user grants a new person a role inside the supplier realm (supplier_users:manage, SupplierOwner only). Access control is not an action the proposal flow can carry (DR-38, DR-44), so it stays with a human."},"tags":["supplier"],"x-audience":"supplier","summary":"Invite a user into the supplier's realm (SupplierOwner; supplier_users:manage)","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierUserInvite"},"examples":{"finance":{"summary":"Invite a finance user","value":{"email":"finance@villas.example","role":"SupplierFinance"}}}}}},"responses":{"202":{"description":"Invite sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityAccepted"},"examples":{"accepted":{"summary":"202 — the invite email is on its way","value":{"status":"ACCEPTED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/users/{userId}":{"patch":{"operationId":"updateSupplierUser","x-mcp":{"expose":"excluded","toolset":"supplier","name":"update_supplier_user","class":"commit-money","autonomy":"L1","reason":"This changes a supplier user's role or deactivates them (SupplierOwner; the last owner is protected). Role changes are access control, which the proposal flow cannot carry (DR-38, DR-44)."},"tags":["supplier"],"x-audience":"supplier","summary":"Change a user's role or deactivate them (SupplierOwner). The last active owner cannot be demoted or deactivated.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierUserUpdate"},"examples":{"viewer":{"summary":"Demote to viewer","value":{"role":"SupplierViewer"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierUser"},"examples":{"updated":{"summary":"200 — now a viewer","value":{"userId":"su_8a21f0","email":"ops@villas.example","name":"Priya N","role":"SupplierViewer","active":true,"mfaEnrolled":true}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason SELF_CHANGE | LAST_OWNER, or IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"lastOwner":{"summary":"409 CONFLICT LAST_OWNER","value":{"code":"CONFLICT","message":"The last active SupplierOwner cannot be demoted or deactivated","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"LAST_OWNER"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings":{"get":{"operationId":"listSupplierBookings","x-mcp":{"expose":"tool","toolset":"supplier","name":"list_supplier_bookings","class":"read","autonomy":"L0","example":"{\"state\":\"ON_REQUEST\",\"domain\":\"hotels\",\"from\":\"2026-10-01\",\"to\":\"2026-10-31\",\"pageSize\":25}"},"tags":["supplier"],"x-audience":"supplier","summary":"The bookings routed to this supplier — on-request first (IMP-P3-002; spec/10 §11)","description":"Filtered by the caller's supplierCode; another supplier's booking does not exist here. state filters; counts gives the inbox badges.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"state","description":"Only bookings in this state.","in":"query","schema":{"type":"string"}},{"name":"domain","description":"Only bookings of this product, by its product code (for example HTL).","in":"query","schema":{"type":"string"}},{"name":"from","description":"Start of the date range (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","description":"End of the date range (inclusive).","in":"query","schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"A page of bookings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBookingList"},"examples":{"inbox":{"summary":"200 — one on-request booking due tomorrow","value":{"bookings":[{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"ON_REQUEST","supplierBookingGroup":"sbg_01JA7P4","lines":[{"orderItemRef":"OI-1","domain":"VR","state":"PENDING_SUPPLIER"}],"product":{"name":"Casa Branca, Alibaug","checkIn":"2026-11-10","checkOut":"2026-11-14","unit":"WHOLE_PROPERTY","guests":6},"supplierPrice":{"amount":9600000,"currency":"INR"},"requestedAt":"2026-10-01T08:40:00Z","slaDueAt":"2026-10-02T08:40:00Z","actions":["confirm","decline"]}],"page":{"total":1},"counts":{"ON_REQUEST":1,"CONFIRMED":14}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings/{bookingRef}":{"get":{"operationId":"getSupplierBooking","x-mcp":{"expose":"resource","toolset":"supplier","name":"get_supplier_booking","class":"read","autonomy":"L0","example":"{\"bookingRef\":\"SB-7Q2-4KD-9ZT\"}"},"tags":["supplier"],"x-audience":"supplier","summary":"One booking as the supplier sees it","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"bookingRef","description":"The booking to read, as the supplier's booking list names it.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBooking"},"examples":{"onRequest":{"summary":"200 — on request, with the party","value":{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"ON_REQUEST","supplierBookingGroup":"sbg_01JA7P4","lines":[{"orderItemRef":"OI-1","domain":"VR","state":"PENDING_SUPPLIER"}],"product":{"name":"Casa Branca, Alibaug","checkIn":"2026-11-10","checkOut":"2026-11-14","unit":"WHOLE_PROPERTY","guests":6},"travelers":[{"firstName":"Ana","lastName":"Silva","phoneNumber":"+5511999990000","lead":true}],"supplierPrice":{"amount":9600000,"currency":"INR"},"requestedAt":"2026-10-01T08:40:00Z","slaDueAt":"2026-10-02T08:40:00Z","actions":["confirm","decline"]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings/{bookingRef}:confirm":{"post":{"operationId":"confirmSupplierBooking","x-mcp":{"expose":"excluded","toolset":"supplier","name":"confirm_supplier_booking","class":"commit-money","autonomy":"L1","reason":"Confirming moves the tenant's order line from PENDING_SUPPLIER to CONFIRMED and generates travel documents, which commits a booking. The proposal flow carries only orders.create and orders.cancel (DR-38, DR-44), and the route accepts only a supplier-session or a step-up platform-ops session (DR-53)."},"tags":["supplier"],"x-audience":"supplier","summary":"Confirm an on-request booking with the supplier's reference (IMP-P3-002; supplier_bookings:act)","description":"The order line moves PENDING_SUPPLIER → CONFIRMED, order.state_changed fires to the tenant, and documents are generated. A booking not ON_REQUEST is 409 CONFLICT details.reason WRONG_STATE. A platform-ops session must send onBehalf with a reason (8 to 500 characters) and the step-up proof, stepUpCode or stepUpAssertion (v1.6.7, DR-53); a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and STEP_UP_LOCKED when the operator is locked out.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"bookingRef","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierConfirmRequest"},"examples":{"confirm":{"summary":"Confirmed with the supplier's reference","value":{"supplierReference":"SV-2026-118822","notes":"Early check-in agreed, 12:00"}},"opsOnBehalf":{"summary":"Ops confirms for the supplier after a phone call (reason-stamped)","value":{"supplierReference":"SV-2026-118822","onBehalf":{"supplierCode":"EXAMPLEVILLAS","reason":"Owner confirmed by phone at 09:12; portal access pending","stepUpCode":"000000"}}}}}}},"responses":{"200":{"description":"Confirmed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBooking"},"examples":{"confirmed":{"summary":"200 — CONFIRMED","value":{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"CONFIRMED","supplierReference":"SV-2026-118822","lines":[{"orderItemRef":"OI-1","domain":"VR","state":"CONFIRMED"}],"requestedAt":"2026-10-01T08:40:00Z","decidedAt":"2026-10-01T09:14:00Z","decidedBy":"su_8a21f0","actions":["attest","fulfil"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason WRONG_STATE | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"wrongState":{"summary":"409 CONFLICT WRONG_STATE — already decided","value":{"code":"CONFLICT","message":"Booking SB-7Q2-4KD-9ZT is CONFIRMED; it cannot be confirmed again","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WRONG_STATE","state":"CONFIRMED"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings/{bookingRef}:decline":{"post":{"operationId":"declineSupplierBooking","x-mcp":{"expose":"excluded","toolset":"supplier","name":"decline_supplier_booking","class":"commit-money","autonomy":"L1","reason":"Declining fails the order line (order.item_failed) and makes the tenant's order compensate per its atomicity, which can refund or reverse money. No proposal action carries it (only orders.create and orders.cancel, DR-38, DR-44), and the route is supplier-session only."},"tags":["supplier"],"x-audience":"supplier","summary":"Decline an on-request booking (IMP-P3-002; supplier_bookings:act)","description":"The order line fails (order.item_failed) and the order compensates per its atomicity; the tenant is told the reason code, never free text. A platform-ops session must send onBehalf with a reason (8 to 500 characters) and the step-up proof, stepUpCode or stepUpAssertion (v1.6.7, DR-53); a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and STEP_UP_LOCKED when the operator is locked out.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"bookingRef","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDeclineRequest"},"examples":{"unavailable":{"summary":"Dates blocked","value":{"reasonCode":"UNAVAILABLE","reason":"Owner blocked the dates for maintenance"}}}}}},"responses":{"200":{"description":"Declined","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBooking"},"examples":{"declined":{"summary":"200 — DECLINED","value":{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"DECLINED","lines":[{"orderItemRef":"OI-1","domain":"VR","state":"FAILED"}],"requestedAt":"2026-10-01T08:40:00Z","decidedAt":"2026-10-01T09:14:00Z","decidedBy":"su_8a21f0","actions":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings/{bookingRef}:attest":{"post":{"operationId":"attestSupplierBooking","x-mcp":{"expose":"excluded","toolset":"supplier","name":"attest_supplier_booking","class":"commit-money","autonomy":"L1","reason":"The attested state is what reconciliation adopts as the booking's truth where no supplier API exists (OD-534), so it drives settlement and order state. No proposal action carries it (DR-38, DR-44), and it needs a supplier-session or a step-up platform-ops session (DR-53)."},"tags":["supplier"],"x-audience":"supplier","summary":"Attest the booking's actual state with evidence — what reconciliation adopts where no supplier API exists (IMP-P3-002; OD-534)","description":"A platform-ops session must send onBehalf with a reason (8 to 500 characters) and the step-up proof, stepUpCode or stepUpAssertion (v1.6.7, DR-53); a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and STEP_UP_LOCKED when the operator is locked out.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"bookingRef","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAttestRequest"},"examples":{"cancelled":{"summary":"The guest cancelled by email","value":{"attestedState":"CANCELLED","supplierReference":"SV-2026-118822","evidence":[{"kind":"EMAIL","ref":"msg_91ab","label":"Guest cancellation email"}],"note":"Cancelled inside the free window; no penalty"}}}}}},"responses":{"200":{"description":"Recorded; the booking carries the attestation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBooking"},"examples":{"attested":{"summary":"200 — attestation recorded","value":{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"CANCELLED","supplierReference":"SV-2026-118822","requestedAt":"2026-10-01T08:40:00Z","attestations":[{"attestationId":"att_01JA7Q1","attestedState":"CANCELLED","attestedAt":"2026-10-03T07:00:00Z","attestedBy":"su_8a21f0","evidence":[{"kind":"EMAIL","ref":"msg_91ab","label":"Guest cancellation email"}]}],"actions":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/bookings/{bookingRef}/fulfilment":{"post":{"operationId":"fulfilSupplierBooking","x-mcp":{"expose":"excluded","toolset":"supplier","name":"fulfil_supplier_booking","class":"commit-money","autonomy":"L1","reason":"This pushes traveller documents, dispatch updates and redemptions for a booking. That is document issuance, which the proposal flow cannot carry (only orders.create and orders.cancel, DR-38, DR-44), and the route is supplier-session only, with step-up for onBehalf (DR-53)."},"tags":["supplier"],"x-audience":"supplier","summary":"Push documents, a dispatch update, a redemption or a status note for a booking (IMP-P3-002; supplier_fulfilment:write)","description":"A platform-ops session must send onBehalf with a reason (8 to 500 characters) and the step-up proof, stepUpCode or stepUpAssertion (v1.6.7, DR-53); a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and STEP_UP_LOCKED when the operator is locked out.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"bookingRef","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierFulfilmentRequest"},"examples":{"voucher":{"summary":"A voucher uploaded through POST /v1/uploads","value":{"kind":"DOCUMENT","documents":[{"uploadId":"up_01JA7R7","type":"VOUCHER","orderItemRef":"OI-1","scope":"PER_BOOKING"}]}},"dispatch":{"summary":"Driver assigned (ground)","value":{"kind":"DISPATCH","dispatch":{"state":"DRIVER_ASSIGNED","driver":{"name":"Carlos","phone":"+5511988880000"},"vehicle":{"make":"Toyota","model":"Corolla","plate":"ABC1D23"}}}}}}}},"responses":{"202":{"description":"Accepted for processing; the matching order event follows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierFulfilmentAccepted"},"examples":{"accepted":{"summary":"202 — one item accepted","value":{"bookingRef":"SB-7Q2-4KD-9ZT","accepted":1,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/cases":{"post":{"operationId":"openConsoleCase","x-mcp":{"expose":"excluded","toolset":"console","name":"open_console_case","class":"commit-money","autonomy":"L1","reason":"Opens desk work orders (OFFLINE_SUPPLIER_ACTION CANCEL/AMEND, REQUEST_TO_BOOK CONFIRM) that a human then executes at the supplier, which is a way round the orders.create/orders.cancel proposal path (DR-38, DR-44)."},"tags":["admin"],"x-audience":"console","summary":"Open an ops-desk case — an offline supplier action, a request-to-book, a voucher re-issue (IMP-P3-014, IMP-P3-060; BE-4)","description":"Opens one case on the desk. The row is read, claimed and released through GET /admin/v1/queues/{queue} (OFFLINE_SUPPLIER_ACTION, REQUEST_TO_BOOK, VOUCHER_REISSUE); the order's owner sees its tenant-visible status on GET /v1/orders/{orderRef} message.serviceCases[]. `action` must fit `type`: OFFLINE_SUPPLIER_ACTION takes CANCEL | AMEND | ENDORSE | VERIFY (VERIFY since v1.7.0), REQUEST_TO_BOOK takes CONFIRM, VOUCHER_REISSUE takes REISSUE (both case-insensitive). The SLA defaults to 24 h (OFFLINE_SUPPLIER_ACTION; REQUEST_TO_BOOK uses the supplier's confirmSlaHours when the registry names one) or 4 h (VOUCHER_REISSUE); slaHours > 0 overrides it. Requires queues:work and an Idempotency-Key (a replay answers the stored 201 with Idempotency-Replayed). Audited as cases.open. No Location header and no ETag.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCaseOpenRequest"},"examples":{"offlineAmend":{"summary":"Example Airport Services amends by email — the desk does it","value":{"type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","reason":"Example Airport Services amends by email only","publicMessage":"Your change request is with the supplier; we will confirm within 24 hours."}}}}}},"responses":{"201":{"description":"The case, OPEN","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCase"},"examples":{"opened":{"summary":"201 — open, the tenant sees PENDING_SUPPLIER_ACTION","value":{"caseId":"case_9f2a1c33b0d4e871","tenantId":"TEN-ACME","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","domain":"APTSVC","supplier":"EXAMPLEAIRPORT","status":"OPEN","tenantStatus":"PENDING_SUPPLIER_ACTION","tenantMessage":"Your change request is with the supplier; we will confirm within 24 hours.","reason":"Example Airport Services amends by email only","openedAt":"2026-10-01T10:00:00Z","slaDueAt":"2026-10-02T10:00:00Z","updatedAt":"2026-10-01T10:00:00Z","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","escalations":0,"stateVersion":0}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/cases/{caseId}:mark-done":{"post":{"operationId":"markConsoleCaseDone","x-mcp":{"expose":"excluded","toolset":"console","name":"mark_console_case_done","class":"commit-lite","autonomy":"L1","reason":"Attests that an offline supplier action (cancel, amend, confirm, reissue) actually happened and rewrites the customer-visible serviceCases[] message, which is a desk attestation that stays with a human."},"tags":["admin"],"x-audience":"console","summary":"Close a case as done — the queue row's MARK_DONE verb (IMP-P3-014; BE-4)","description":"status DONE, tenantStatus COMPLETED. publicMessage, when sent, replaces the sentence the tenant sees on serviceCases[].message; otherwise the previous one stays. Any open case of any type accepts it. A closed case never reopens (open a new one). Requires queues:work and an Idempotency-Key; audited as cases.mark_done.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CaseIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCaseCloseRequest"},"examples":{"done":{"summary":"The supplier confirmed","value":{"note":"Example Airport Services confirmed the amendment by email","publicMessage":"Your change is confirmed by the supplier."}}}}}},"responses":{"200":{"description":"The case, DONE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCase"},"examples":{"done":{"summary":"200 — DONE, the tenant sees COMPLETED","value":{"caseId":"case_9f2a1c33b0d4e871","tenantId":"TEN-ACME","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","status":"DONE","tenantStatus":"COMPLETED","tenantMessage":"Your change is confirmed by the supplier.","reason":"Example Airport Services amends by email only","note":"Example Airport Services confirmed the amendment by email","openedAt":"2026-10-01T10:00:00Z","slaDueAt":"2026-10-02T10:00:00Z","updatedAt":"2026-10-01T14:20:00Z","closedAt":"2026-10-01T14:20:00Z","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","closedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","escalations":0,"stateVersion":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleCaseConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/cases/{caseId}:escalate":{"post":{"operationId":"escalateConsoleCase","x-mcp":{"expose":"tool","toolset":"console","name":"escalate_console_case","class":"commit-lite","autonomy":"L1","example":"{\"caseId\":\"CASE-3QX81\",\"idempotencyKey\":\"c3a9f4e1-7d2b-4e85-a6f0-9b1d3c8e2f57\",\"body\":{\"note\":\"No reply from supplier desk after two emails\",\"slaHours\":2}}"},"tags":["admin"],"x-audience":"console","summary":"Escalate a case — stays OPEN with a re-based SLA (IMP-P3-014; BE-4)","description":"The case stays OPEN and the tenant still sees PENDING_SUPPLIER_ACTION. slaDueAt becomes now + slaHours (default 4 h), escalations goes up by one and escalatedAt is stamped. There is no publicMessage on escalate. Requires queues:work and an Idempotency-Key; audited as cases.escalate. connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68): an MCP grant reaches it only with write consent (ticked at consent with a step-up) on a tenant that has the tool ON in writesEnabled, with the platform write ceiling on and under the approving human's live permission; any other grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CaseIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCaseEscalateRequest"},"examples":{"escalate":{"summary":"No answer from the supplier desk","value":{"note":"No reply from Example Airport Services after two emails; desk lead to phone","slaHours":2}}}}}},"responses":{"200":{"description":"The case, still OPEN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCase"},"examples":{"escalated":{"summary":"200 — escalated once, SLA re-based","value":{"caseId":"case_9f2a1c33b0d4e871","tenantId":"TEN-ACME","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","status":"OPEN","tenantStatus":"PENDING_SUPPLIER_ACTION","reason":"Example Airport Services amends by email only","note":"No reply from Example Airport Services after two emails; desk lead to phone","openedAt":"2026-10-01T10:00:00Z","slaDueAt":"2026-10-01T16:00:00Z","updatedAt":"2026-10-01T14:00:00Z","escalatedAt":"2026-10-01T14:00:00Z","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","escalations":1,"stateVersion":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleCaseConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/cases/{caseId}:fail":{"post":{"operationId":"failConsoleCase","x-mcp":{"expose":"excluded","toolset":"console","name":"fail_console_case","class":"commit-lite","autonomy":"L1","reason":"Closes a desk case as FAILED for good (a closed case never reopens) and publishes a customer-visible message, which is a desk attestation that stays with a human."},"tags":["admin"],"x-audience":"console","summary":"Close a case as failed (IMP-P3-014; BE-4)","description":"status FAILED, tenantStatus FAILED; publicMessage as on :mark-done. Requires queues:work and an Idempotency-Key; audited as cases.fail.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CaseIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCaseCloseRequest"},"examples":{"failed":{"summary":"The supplier cannot do it","value":{"note":"Example Airport Services cannot amend inside 24 h of service","publicMessage":"The supplier could not make this change; you may cancel instead."}}}}}},"responses":{"200":{"description":"The case, FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleCase"},"examples":{"failed":{"summary":"200 — FAILED, the tenant sees FAILED","value":{"caseId":"case_9f2a1c33b0d4e871","tenantId":"TEN-ACME","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","status":"FAILED","tenantStatus":"FAILED","tenantMessage":"The supplier could not make this change; you may cancel instead.","reason":"Example Airport Services amends by email only","note":"Example Airport Services cannot amend inside 24 h of service","openedAt":"2026-10-01T10:00:00Z","slaDueAt":"2026-10-02T10:00:00Z","updatedAt":"2026-10-01T15:00:00Z","closedAt":"2026-10-01T15:00:00Z","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","closedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","escalations":0,"stateVersion":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConsoleCaseConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/jobs/{jobId}/artifact":{"get":{"operationId":"getJobArtifact","x-mcp":{"expose":"excluded","toolset":"console","name":"get_job_artifact","class":"read","autonomy":"L0","reason":"Signed-URL download edge (sig and expiry in the query); bytes for a browser, not an agent-readable resource."},"tags":["admin"],"x-audience":"console","summary":"The bytes behind a ConsoleJobDownload.url the platform signed itself (BE-4)","description":"Not a link to build: it is what ConsoleJobDownload.url points at when the export blob store does not presign (the default; an S3-compatible store answers its own presigned URL instead). It is NOT a bearer link — the console session and finance:export are still required; `exp` (epoch seconds) and `sig` (base64url HMAC-SHA256 over tenant, job and exp, keyed with the job's single-purpose download token) are checked on top. A wrong signature, a past exp, an artifact past its 24 h TTL, a job that is not SUCCEEDED and another tenant's job are all the same 404.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}},{"name":"exp","in":"query","required":false,"description":"Link expiry, epoch seconds (a missing value is the 404)","schema":{"type":"integer","format":"int64"}},{"name":"sig","in":"query","required":false,"description":"The link signature, base64url without padding","schema":{"type":"string"}}],"responses":{"200":{"description":"The CSV","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"},"Content-Disposition":{"schema":{"type":"string"},"description":"attachment; filename=\"{jobId}.csv\""}},"content":{"text/csv":{"schema":{"type":"string"},"examples":{"csv":{"summary":"200 — the ledger export","value":"orderRef,bookedAt,model,currency,sell,net,margin\nMO-8KD-2FQ-9XT,2026-09-02T10:14:00Z,MERCHANT,USD,1840.00,1655.00,185.00\n"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/password/change":{"post":{"operationId":"changePassword","x-mcp":{"expose":"excluded","toolset":"public","name":"change_password","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Change the signed-in user's password; every OTHER session of the user is revoked (IMP-P2-002; BE-5)","description":"Console session only (an access token): an API key is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED. The credential is checked before the body is parsed. newPassword follows the password policy (12 to 1024 characters). A wrong currentPassword, or a locked account, is the same 403 PERMISSION_DENIED details.reason CURRENT_PASSWORD_INVALID, and a wrong currentPassword counts toward the login lockout (5 in 15 minutes). A newPassword equal to the current one is 400 VALIDATION details.reason PASSWORD_UNCHANGED. On success every other session family of the user is revoked (reason PASSWORD_CHANGED; their refresh is 401 SESSION_REVOKED) and the caller's own session stays. Rate-limited 60 per client IP and 10 per user. No Idempotency-Key. Plain JSON, not an envelope.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChangeRequest"},"examples":{"change":{"summary":"A new passphrase","value":{"currentPassword":"correct-horse-battery-staple","newPassword":"a brand new passphrase 2026"}}}}}},"responses":{"204":{"description":"Changed; other sessions revoked","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}}},"400":{"description":"VALIDATION — the policy (details.field newPassword, minLength) or PASSWORD_UNCHANGED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"tooShort":{"summary":"400 VALIDATION — newPassword under 12 characters","value":{"code":"VALIDATION","message":"newPassword must be 12 to 1024 characters long.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"newPassword","minLength":12}}},"unchanged":{"summary":"400 VALIDATION PASSWORD_UNCHANGED — the same password again","value":{"code":"VALIDATION","message":"The new password must differ from the current one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"newPassword","reason":"PASSWORD_UNCHANGED"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — CURRENT_PASSWORD_INVALID, or ACCESS_TOKEN_REQUIRED for an API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"currentPasswordInvalid":{"summary":"403 PERMISSION_DENIED CURRENT_PASSWORD_INVALID — wrong, or the account is locked","value":{"code":"PERMISSION_DENIED","message":"The current password is not correct.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"currentPassword","reason":"CURRENT_PASSWORD_INVALID"}}},"accessTokenRequired":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — an API key presented","value":{"code":"PERMISSION_DENIED","message":"This operation needs a console session (an access token), not an API key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ACCESS_TOKEN_REQUIRED"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/password/set":{"post":{"operationId":"setFirstPassword","x-mcp":{"expose":"excluded","toolset":"public","name":"set_first_password","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Set the first password of an account that has none; every OTHER session of the user is revoked (FEAT-030, OD-ENR-15)","description":"connect-v1 1.29.0. For an account created by FEAT-030 passwordless signup (E-17; OD-ENR-15 option a), which has no password until it sets one here; GET /v1/me answers passwordSet false for it. Console session only (an access token): an API key is 403 PERMISSION_DENIED details.reason ACCESS_TOKEN_REQUIRED. The credential is checked before the body is parsed. An account that already has a password is 403 PERMISSION_DENIED details.reason PASSWORD_ALREADY_SET; it uses POST /v1/auth/password/change. Proof: when the user has an MFA factor enrolled, exactly one of stepUpCode and stepUpAssertion is required (DR-53, DR-54); neither, both or a wrong one is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a user locked out of step-up is STEP_UP_LOCKED. With no factor enrolled the session must be recent: signed in no more than 15 minutes ago (the session's sign-in time; a refresh does not renew it), otherwise 403 PERMISSION_DENIED details.reason STEP_UP_REQUIRED, whose message tells the person to use forgot and reset (POST /v1/auth/password/forgot). newPassword follows the password policy (12 to 1024 characters). On success every other session family of the user is revoked (reason PASSWORD_SET; their refresh is 401 SESSION_REVOKED) and the caller's own session stays; POST /v1/auth/login and /v1/auth/sign-in then accept the password. Rate-limited 60 per client IP and 10 per user, as change is. No Idempotency-Key. Plain JSON, not an envelope. No new error code (DR-20): PASSWORD_ALREADY_SET and STEP_UP_REQUIRED are new details.reason values of PERMISSION_DENIED.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordSetRequest"},"examples":{"recentSession":{"summary":"No factor enrolled, signed in within 15 minutes","value":{"newPassword":"a brand new passphrase 2026"}},"withStepUp":{"summary":"A factor is enrolled — its current code as the proof","value":{"newPassword":"a brand new passphrase 2026","stepUpCode":"000000"}}}}}},"responses":{"204":{"description":"Set; other sessions revoked","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}}},"400":{"description":"VALIDATION — the policy (details.field newPassword, minLength) or a malformed body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"tooShort":{"summary":"400 VALIDATION — newPassword under 12 characters","value":{"code":"VALIDATION","message":"newPassword must be 12 to 1024 characters long.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"newPassword","minLength":12}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"PERMISSION_DENIED — ACCESS_TOKEN_REQUIRED for an API key, PASSWORD_ALREADY_SET, STEP_UP_REQUIRED (no factor and a session older than 15 minutes), STEP_UP_FAILED or STEP_UP_LOCKED\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"accessTokenRequired":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — an API key presented","value":{"code":"PERMISSION_DENIED","message":"This operation needs a console session (an access token), not an API key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ACCESS_TOKEN_REQUIRED"}}},"passwordAlreadySet":{"summary":"403 PERMISSION_DENIED PASSWORD_ALREADY_SET — the account has a password","value":{"code":"PERMISSION_DENIED","message":"This account already has a password; change it with POST /v1/auth/password/change.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PASSWORD_ALREADY_SET"}}},"stepUpRequired":{"summary":"403 PERMISSION_DENIED STEP_UP_REQUIRED — no factor, and the session is older than 15 minutes","value":{"code":"PERMISSION_DENIED","message":"Sign in again to set a password, or use forgot password to set one by e-mail.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_REQUIRED"}}},"stepUpFailed":{"summary":"403 PERMISSION_DENIED STEP_UP_FAILED — missing, wrong or doubled step-up proof","value":{"code":"PERMISSION_DENIED","message":"The step-up code is not correct.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"stepUpCode","reason":"STEP_UP_FAILED"}}},"stepUpLocked":{"summary":"403 PERMISSION_DENIED STEP_UP_LOCKED — too many wrong step-up proofs","value":{"code":"PERMISSION_DENIED","message":"Step-up is locked for now; try again later.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_LOCKED"}}}}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/flags":{"get":{"operationId":"getFeatureFlags","x-mcp":{"expose":"resource","toolset":"console","name":"get_feature_flags","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"The tenant's console feature flags for one environment (IMP-P2-148; BE-5)","description":"Any console session of the tenant may read (no permission row); an API key is 403 SESSION_REQUIRED. `environment` defaults to PRODUCTION on the PROD rung and SANDBOX elsewhere. An absent flag and false mean the same thing (everything is off by default); the console owns the flag names. version 0 means never written (updatedAt and updatedBy null). No ETag.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/FlagEnvironmentQuery"}],"responses":{"200":{"description":"The flags","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlags"},"examples":{"sandbox":{"summary":"200 — two flags on in SANDBOX","value":{"tenantId":"TEN-ACME","environment":"SANDBOX","flags":{"supplierData":true,"verticalCruise":true},"version":3,"updatedAt":"2026-10-01T09:12:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"neverWritten":{"summary":"200 — never written, everything off","value":{"tenantId":"TEN-ACME","environment":"PRODUCTION","flags":{},"version":0,"updatedAt":null,"updatedBy":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateFeatureFlags","x-mcp":{"expose":"excluded","toolset":"console","name":"update_feature_flags","class":"commit-lite","autonomy":"L1","reason":"Feature flags switch tenant features on and off (flags:manage); tenant state, kill-switch adjacent."},"tags":["admin"],"x-audience":"console","summary":"Merge flag values into the tenant's set for one environment (IMP-P2-148; BE-5)","description":"A partial merge: the named flags are set, the others keep their value. 1 to 200 flags per update and at most 200 stored per environment; names match ^[a-zA-Z][a-zA-Z0-9]{0,63}$ and values are booleans (null is refused). Requires flags:manage (Owner and Admin bundles; never on an API key) and an Idempotency-Key. Concurrency is a server-side compare-and-set — there is no If-Match; five lost races in a row is 409 CONFLICT details.reason CONCURRENT_UPDATE. Audited as config.flags.update (resource feature_flags).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlagsUpdate"},"examples":{"enable":{"summary":"Turn two console surfaces on in SANDBOX","value":{"environment":"SANDBOX","flags":{"supplierData":true,"verticalCruise":true}}}}}}},"responses":{"200":{"description":"The merged flags","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlags"},"examples":{"updated":{"summary":"200 — merged, version 4","value":{"tenantId":"TEN-ACME","environment":"SANDBOX","flags":{"supplierData":true,"verticalCruise":true},"version":4,"updatedAt":"2026-10-02T08:00:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"description":"CONFLICT CONCURRENT_UPDATE, or an Idempotency-Key reused with another body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"concurrentUpdate":{"summary":"409 CONFLICT CONCURRENT_UPDATE — five compare-and-set losses in a row","value":{"code":"CONFLICT","message":"The flags changed under five attempts in a row; retry.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"CONCURRENT_UPDATE"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/copilot/autonomy":{"get":{"operationId":"getCopilotAutonomy","x-mcp":{"expose":"excluded","toolset":"console","name":"get_copilot_autonomy","class":"read","autonomy":"L0","reason":"The spec refuses any X-Actor-Kind ai request with 403 SESSION_REQUIRED (human console session only), so an agent call can never be served."},"tags":["admin"],"x-audience":"console","summary":"The tenant's effective Copilot autonomy level (BE-6; FEAT-019)","description":"Proxied to the AI plane. Requires agents:read and a human console session (an X-Actor-Kind ai request is 403 SESSION_REQUIRED). No ETag.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The effective autonomy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotAutonomy"},"examples":{"l1":{"summary":"200 — the default, suggest only","value":{"tenant":"TEN-ACME","level":"L1","meaning":"suggest","ceiling":"L2","enabled":true,"disabledBy":null,"source":"default","changedBy":null,"reason":null,"levels":{"L1":"suggest","L2":"propose","L3":"propose+auto-file"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}},"put":{"operationId":"updateCopilotAutonomy","x-mcp":{"expose":"excluded","toolset":"console","name":"update_copilot_autonomy","class":"commit-lite","autonomy":"L1","reason":"Sets the Copilot's autonomy level; an agent must never change its own place on the ladder (DR-60)."},"tags":["admin"],"x-audience":"console","summary":"Set the Copilot autonomy level or switch it off for the tenant (BE-6)","description":"Send level and/or enabled (neither is 400 VALIDATION). A level above the ceiling is 400 VALIDATION with details.ceiling. Requires agents:act, the Owner or Admin role (details.reason ROLE_NOT_PERMITTED otherwise), a human session and an Idempotency-Key (required, not forwarded; there is no replay store, so a duplicate PUT runs again). When the global floor or the environment has the feature off the answer is 503 with details.reason AI_FEATURE_DISABLED (retryable false). No If-Match.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotAutonomyUpdate"},"examples":{"pilot":{"summary":"Move to L2 for a pilot","value":{"level":"L2","reason":"pilot"}}}}}},"responses":{"200":{"description":"The effective autonomy after the change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotAutonomy"},"examples":{"l2":{"summary":"200 — L2, set at runtime","value":{"tenant":"TEN-ACME","level":"L2","meaning":"propose","ceiling":"L2","enabled":true,"disabledBy":null,"source":"runtime","changedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","reason":"pilot","levels":{"L1":"suggest","L2":"propose","L3":"propose+auto-file"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/copilot/sessions":{"post":{"operationId":"createCopilotSession","x-mcp":{"expose":"excluded","toolset":"console","name":"create_copilot_session","class":"commit-lite","autonomy":"L1","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"Start a Copilot session (BE-6)","description":"The body is ignored (send {} or nothing). Requires agents:read and an Idempotency-Key (required; not forwarded, no replay store). 201 with the session; the plane's Location header is not relayed. 503 AI_FEATURE_DISABLED when the Copilot is off for the tenant.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"examples":{"empty":{"summary":"Nothing to send","value":{}}}}}},"responses":{"201":{"description":"The new session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotSession"},"examples":{"created":{"summary":"201 — empty session at L1","value":{"id":"CPS-6f1d2c9e-3b7a-4f0e-9a51-1c2b3d4e5f60","tenant":"TEN-ACME","userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","createdAt":"2026-10-01T09:00:00+00:00","level":"L1","tools":["get_order","list_proposals","capabilities","adoption_advice"],"turns":[],"drafts":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/copilot/sessions/{sessionId}":{"get":{"operationId":"getCopilotSession","x-mcp":{"expose":"excluded","toolset":"console","name":"get_copilot_session","class":"read","autonomy":"L0","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"A Copilot session with its turns and drafts (BE-6)","description":"Requires agents:read. Another tenant's session is the same 404 as an unknown one.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CopilotSessionIdPath"}],"responses":{"200":{"description":"The session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotSession"},"examples":{"oneTurn":{"summary":"200 — one answered turn with a draft","value":{"id":"CPS-6f1d2c9e-3b7a-4f0e-9a51-1c2b3d4e5f60","tenant":"TEN-ACME","userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","createdAt":"2026-10-01T09:00:00+00:00","level":"L2","tools":["get_order","list_proposals","capabilities","propose_rebooking","adoption_advice"],"turns":[{"id":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d","at":"2026-10-01T09:01:10+00:00","question":"Rebook MO-8KD-2FQ-9XT, the LA8084 was cancelled","answer":"I drafted a rebooking proposal onto LA8086 the next morning.","intent":"rebook","tools":[{"name":"get_order","ok":true}],"suggestion":null,"draft":{"id":"CPD-5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","agentId":"AG-22","input":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT","travelers":[]},"summary":"Rebook onto LA8086 2026-11-11","turnId":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"},"proposal":null,"refusals":[],"model":"copilot-default","actor":{"kind":"ai","agentId":"COPILOT"}}],"drafts":[{"id":"CPD-5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","agentId":"AG-22","input":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT","travelers":[]},"summary":"Rebook onto LA8086 2026-11-11","turnId":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/copilot/sessions/{sessionId}/messages":{"post":{"operationId":"sendCopilotMessage","x-mcp":{"expose":"excluded","toolset":"console","name":"send_copilot_message","class":"commit-lite","autonomy":"L1","reason":"The AI plane's own feature; an MCP tool calling it would route the plane back into itself. Served to the console directly, not as an agent tool."},"tags":["admin"],"x-audience":"console","summary":"Ask the Copilot — streamed (text/event-stream, the default) or one JSON turn (BE-6)","description":"JSON only when Accept contains application/json and not text/event-stream; otherwise (absent Accept, */*, or SSE) the answer streams. SSE frames are `event: <name>` + `data: <json>` (no id or retry lines; the data is the payload alone): tool_call {name, arguments}, tool_result {name, ok} or {name, error {code}}, delta {text}, suggestion, draft (CopilotDraft), proposal (the plane's proposal), refusal {code, message}, error {code, message} on a mid-stream failure, and done (the full CopilotTurn) last. The stream carries X-Accel-Buffering no, is relayed as it arrives, and is cut at 16 MiB; the budget is 120 s. Errors before the first event (400, 404, 503) are ApiErrorResponse; after it the stream just ends. Requires agents:read and an Idempotency-Key (required; not forwarded).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CopilotSessionIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotMessageRequest"},"examples":{"rebook":{"summary":"Ask about one order","value":{"text":"Rebook MO-8KD-2FQ-9XT, the LA8084 was cancelled","context":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT"}}}}}}},"responses":{"200":{"description":"The turn (JSON) or its event stream (SSE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotTurn"},"examples":{"turn":{"summary":"200 — one finished turn (Accept application/json)","value":{"id":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d","at":"2026-10-01T09:01:10+00:00","question":"Rebook MO-8KD-2FQ-9XT, the LA8084 was cancelled","answer":"I drafted a rebooking proposal onto LA8086 the next morning.","intent":"rebook","tools":[{"name":"get_order","ok":true}],"suggestion":null,"draft":{"id":"CPD-5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","agentId":"AG-22","input":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT","travelers":[]},"summary":"Rebook onto LA8086 2026-11-11","turnId":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"},"proposal":null,"refusals":[],"model":"copilot-default","actor":{"kind":"ai","agentId":"COPILOT"}}}}},"text/event-stream":{"schema":{"type":"string"},"examples":{"stream":{"summary":"200 — the same turn, streamed","value":"event: tool_call\ndata: {\"name\":\"get_order\",\"arguments\":{\"orderRef\":\"MO-8KD-2FQ-9XT\"}}\n\nevent: tool_result\ndata: {\"name\":\"get_order\",\"ok\":true}\n\nevent: delta\ndata: {\"text\":\"I drafted a rebooking proposal\"}\n\nevent: done\ndata: {\"id\":\"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d\",\"intent\":\"rebook\"}\n\n"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/admin/v1/copilot/sessions/{sessionId}/drafts/{draftId}/file":{"post":{"operationId":"fileCopilotDraft","x-mcp":{"expose":"excluded","toolset":"console","name":"file_copilot_draft","class":"commit-lite","autonomy":"L1","reason":"Files a Copilot draft as an agent proposal; a human files it (DR-60), and agent filings go through book_offer and cancel_with_quote."},"tags":["admin"],"x-audience":"console","summary":"File a Copilot draft as an agent proposal (BE-6; D-50 still gates its execution)","description":"The body is ignored. A draft files once (a second file is 404). Requires agents:act, a human session (not X-Actor-Kind ai) and an Idempotency-Key. 201 with the plane's proposal object (CopilotProposal — see its description for the as-served id/state vocabulary). A paused agent is 409 CONFLICT details.reason AGENT_PAUSED.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CopilotSessionIdPath"},{"name":"draftId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"examples":{"empty":{"summary":"Nothing to send","value":{}}}}}},"responses":{"201":{"description":"The filed proposal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotProposal"},"examples":{"filed":{"summary":"201 — PROPOSED, awaiting a person","value":{"id":"PRP-1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","agentId":"AG-22","tenant":"TEN-ACME","createdAt":"2026-10-01T09:02:00+00:00","updatedAt":"2026-10-01T09:02:00+00:00","expiresAt":"2026-10-01T13:02:00+00:00","state":"PROPOSED","autonomyLevel":"L2","summary":"Rebook onto LA8086 2026-11-11","requires":["APPROVAL"],"actor":{"kind":"ai","agentId":"AG-22"},"input":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT AGENT_PAUSED — the drafting agent is paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentPaused":{"summary":"409 CONFLICT AGENT_PAUSED","value":{"code":"CONFLICT","message":"Agent AG-22 is paused; nothing was filed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_PAUSED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/AiPlaneUnavailable"}}}},"/v1/hold/{holdRef}/release":{"post":{"operationId":"releaseHold","x-mcp":{"expose":"excluded","toolset":"public","name":"release_hold","class":"commit-lite","autonomy":"L1","reason":"No MCP decision has been taken for releasing a hold early; a hold an agent does not book lapses at its expiry."},"tags":["price"],"summary":"Release a hold before it expires — free the inventory now","description":"Releasing an unexpired hold the tenant owns gives the inventory back now, frees the RP- for a new hold and emits hold.expired with data.releaseOutcome. The supplier is told once, where it supports a release; otherwise the hold is released on the platform side and the answer says so. Idempotent: releasing a released or expired hold is a 200 with its state unchanged. A booked hold is 409 CONFLICT details.reason HOLD_SPENT. Idempotency-Key required.\n","parameters":[{"name":"holdRef","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldReleaseRequest"},"examples":{"release":{"summary":"The traveller changed plans","value":{"context":{"domain":"EXP"},"message":{"intent":{"reason":"Traveller changed plans"}}}}}}}},"responses":{"200":{"description":"The hold, released","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldReleaseResponse"},"examples":{"released":{"summary":"200 — released at the supplier","value":{"context":{"domain":"EXP","action":"on_hold_release","version":"2026-09-30","timestamp":"2026-09-14T17:10:00Z","message_id":"msg_7c1a90","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"holdRef":"HD-E-9b1c22","state":"RELEASED","releasedAt":"2026-09-14T17:10:00Z","releaseOutcome":"RELEASED"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT HOLD_SPENT — the hold was booked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"holdSpent":{"summary":"409 CONFLICT HOLD_SPENT — booked; cancel the order instead","value":{"code":"CONFLICT","message":"Hold HD-E-9b1c22 was booked as MO-8KD-2FQ-9XT; cancel the order instead.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"HOLD_SPENT","holdRef":"HD-E-9b1c22"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"423":{"$ref":"#/components/responses/KeyPaused"},"429":{"$ref":"#/components/responses/VelocityLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/sessions":{"get":{"operationId":"listSessions","x-mcp":{"expose":"excluded","toolset":"public","name":"list_sessions","class":"read","autonomy":"L0","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"The caller's own sessions (OD-IMP-P4-037)","description":"Every signed-in session (refresh family) of the caller, current first. A console session only; an API key is 403 ACCESS_TOKEN_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","responses":{"200":{"description":"The sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionList"},"examples":{"ok":{"summary":"200 — The sessions","value":{"sessions":[{"sessionId":"ses_01JA9K2","current":true,"createdAt":"2026-10-01T08:00:00Z"},{"sessionId":"ses_01JA2P7","current":false,"createdAt":"2026-09-28T19:10:00Z","device":"Safari on iOS"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/sessions/{sessionId}":{"delete":{"operationId":"endSession","x-mcp":{"expose":"excluded","toolset":"public","name":"end_session","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"End one of the caller's sessions","description":"Ends that refresh family only (there is no bulk delete; POST /v1/auth/logout-all signs out everywhere). Ending the current session is a sign-out. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Ended"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa":{"get":{"operationId":"mfaStatus","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_status","class":"read","autonomy":"L0","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"The caller's MFA enrolment","description":"Factors and recovery-code count.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","responses":{"200":{"description":"Enrolment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaStatus"},"examples":{"ok":{"summary":"200 — Enrolment","value":{"enrolled":true,"required":true,"factors":[{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"}],"recoveryCodesRemaining":9}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/totp":{"post":{"operationId":"mfaStartTotp","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_start_totp","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Start a TOTP enrolment","description":"Idempotency-Key required; the factor is pending until verified.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpStartRequest"},"examples":{"request":{"summary":"Request","value":{"name":"Authenticator app"}}}}}},"responses":{"201":{"description":"Pending factor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpEnrolment"},"examples":{"ok":{"summary":"201 — Pending factor","value":{"factorId":"fac_01JA9M1","otpauthUri":"otpauth://totp/Apiq:ops%40acme.example?secret=JBSWY3DPEHPK3PXPJBSWY3DP&issuer=Apiq&digits=6&period=30&algorithm=SHA1","expiresAt":"2026-10-01T08:15:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/totp/{factorId}:verify":{"post":{"operationId":"mfaConfirmTotp","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_confirm_totp","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Confirm a pending TOTP factor with one code","description":"Idempotency-Key required. The first factor also answers the recovery codes, once.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaCodeRequest"},"examples":{"request":{"summary":"Request","value":{"code":"000000"}}}}}},"responses":{"200":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"200 — Enrolled","value":{"factor":{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"},"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/webauthn/registration-options":{"post":{"operationId":"mfaWebauthnOptions","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_webauthn_options","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"WebAuthn registration options","description":"Send {}. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"Options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnOptions"},"examples":{"ok":{"summary":"200 — Options","value":{"registrationId":"reg_01JA9N4","publicKey":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rp":{"name":"Apiq"},"user":{"id":"dXNyXzhhMjFmMA","name":"ops@acme.example","displayName":"Ops"},"pubKeyCredParams":[{"type":"public-key","alg":-7}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/webauthn":{"post":{"operationId":"mfaRegisterWebauthn","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_register_webauthn","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Register a security key (WebAuthn)","description":"Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnRegisterRequest"},"examples":{"request":{"summary":"Request","value":{"registrationId":"reg_01JA9N4","name":"YubiKey","credential":{"id":"AbC1","rawId":"AbC1","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZQ"}}}}}}}},"responses":{"201":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"201 — Enrolled","value":{"factor":{"factorId":"fac_01JA9Q3","type":"WEBAUTHN","name":"YubiKey","createdAt":"2026-10-01T08:20:00Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/factors/{factorId}":{"delete":{"operationId":"mfaRemoveFactor","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_remove_factor","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Remove a factor","description":"Idempotency-Key required. Removing the last factor where MFA is required is 409 CONFLICT details.reason MFA_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT MFA_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"mfaRequired":{"summary":"409 CONFLICT MFA_REQUIRED","value":{"code":"CONFLICT","message":"MFA is required for this account; enrol another factor first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MFA_REQUIRED"}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/recovery-codes":{"post":{"operationId":"mfaRegenerateRecoveryCodes","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_regenerate_recovery_codes","class":"commit-lite","autonomy":"L1","reason":"Human session only (x-aikonic-credential: human-session); an AI-kind key or service token is refused with 403 SESSION_REQUIRED, so no agent tool can serve it (DR-39)."},"tags":["identity"],"summary":"Regenerate recovery codes","description":"Send {}. The previous set stops working. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"New codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaRecoveryCodes"},"examples":{"ok":{"summary":"200 — New codes","value":{"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA","C8RW-5HNE"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/mfa/verify":{"post":{"operationId":"mfaVerify","x-mcp":{"expose":"excluded","toolset":"public","name":"mfa_verify","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"Complete a sign-in challenge → session","description":"One proof: code, recoveryCode or webauthn. A wrong proof is 401 UNAUTHENTICATED details.reason MFA_FAILED; an expired challenge 401 MFA_CHALLENGE_EXPIRED.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaVerifyRequest"},"examples":{"request":{"summary":"Request","value":{"challengeId":"mfa_01JA9P0","code":"000000"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"},"examples":{"ok":{"summary":"200 — Signed in","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-02T08:25:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-11-01T08:10:00Z","context":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","tenantId":"TEN-DEMO","siteId":"SITE-DEMO","segment":null,"permissions":["orders:read"],"tenantStatus":"TRIAL","productionWithheld":false}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/auth/sso/discover":{"post":{"operationId":"ssoDiscover","x-mcp":{"expose":"excluded","toolset":"public","name":"sso_discover","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["identity"],"summary":"SSO discovery — email → the tenant's OIDC/SAML start URL (IMP-P4-067)","description":"Server-side only (the web's sign-in action calls it). Enumeration-safe: an unknown domain is {mode: PASSWORD}. Rate-limited as the identity routes are.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoDiscoverRequest"},"examples":{"request":{"summary":"Request","value":{"email":"agent@acme-travel.example"}}}}}},"responses":{"200":{"description":"Where to sign in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoDiscovery"},"examples":{"ok":{"summary":"200 — Where to sign in","value":{"mode":"SSO","protocol":"OIDC","startUrl":"https://login.acme-travel.example/oauth2/authorize?client_id=apiq","displayName":"Acme Travel SSO"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/sessions":{"get":{"operationId":"supplierListSessions","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_list_sessions","class":"read","autonomy":"L0","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["supplier"],"x-audience":"supplier","summary":"The caller's own sessions (OD-IMP-P4-037)","description":"Every signed-in session (refresh family) of the caller, current first. A supplier session only.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"The sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionList"},"examples":{"ok":{"summary":"200 — The sessions","value":{"sessions":[{"sessionId":"ses_01JA9K2","current":true,"createdAt":"2026-10-01T08:00:00Z"},{"sessionId":"ses_01JA2P7","current":false,"createdAt":"2026-09-28T19:10:00Z","device":"Safari on iOS"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/sessions/{sessionId}":{"delete":{"operationId":"supplierEndSession","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_end_session","class":"commit-money","autonomy":"L1","reason":"This revokes one of the caller's session refresh families, and ending the current one signs the user out. Session credentials belong to the human user."},"tags":["supplier"],"x-audience":"supplier","summary":"End one of the caller's sessions","description":"Ends that refresh family only (there is no bulk delete; end the others one by one). Ending the current session is a sign-out. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Ended"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa":{"get":{"operationId":"supplierMfaStatus","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_status","class":"read","autonomy":"L0","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["supplier"],"x-audience":"supplier","summary":"The caller's MFA enrolment","description":"Factors and recovery-code count.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Enrolment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaStatus"},"examples":{"ok":{"summary":"200 — Enrolment","value":{"enrolled":true,"required":true,"factors":[{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"}],"recoveryCodesRemaining":9}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/totp":{"post":{"operationId":"supplierMfaStartTotp","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_start_totp","class":"commit-money","autonomy":"L1","reason":"This starts enrolling a TOTP second factor and returns its shared secret. An agent must never see or hold an MFA secret."},"tags":["supplier"],"x-audience":"supplier","summary":"Start a TOTP enrolment","description":"Idempotency-Key required; the factor is pending until verified.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpStartRequest"},"examples":{"request":{"summary":"Request","value":{"name":"Authenticator app"}}}}}},"responses":{"201":{"description":"Pending factor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpEnrolment"},"examples":{"ok":{"summary":"201 — Pending factor","value":{"factorId":"fac_01JA9M1","otpauthUri":"otpauth://totp/Apiq:ops%40acme.example?secret=JBSWY3DPEHPK3PXPJBSWY3DP&issuer=Apiq&digits=6&period=30&algorithm=SHA1","expiresAt":"2026-10-01T08:15:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/totp/{factorId}:verify":{"post":{"operationId":"supplierMfaConfirmTotp","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_confirm_totp","class":"commit-money","autonomy":"L1","reason":"This activates a TOTP factor and, on the first factor, returns the recovery codes. Both are credentials an agent must never see."},"tags":["supplier"],"x-audience":"supplier","summary":"Confirm a pending TOTP factor with one code","description":"Idempotency-Key required. The first factor also answers the recovery codes, once.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaCodeRequest"},"examples":{"request":{"summary":"Request","value":{"code":"000000"}}}}}},"responses":{"200":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"200 — Enrolled","value":{"factor":{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"},"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/webauthn/registration-options":{"post":{"operationId":"supplierMfaWebauthnOptions","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_webauthn_options","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["supplier"],"x-audience":"supplier","summary":"WebAuthn registration options","description":"Send {}. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"Options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnOptions"},"examples":{"ok":{"summary":"200 — Options","value":{"registrationId":"reg_01JA9N4","publicKey":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rp":{"name":"Apiq"},"user":{"id":"dXNyXzhhMjFmMA","name":"ops@acme.example","displayName":"Ops"},"pubKeyCredParams":[{"type":"public-key","alg":-7}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/webauthn":{"post":{"operationId":"supplierMfaRegisterWebauthn","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_register_webauthn","class":"commit-money","autonomy":"L1","reason":"This registers a hardware security key for a human user. It needs the physical authenticator and creates a credential."},"tags":["supplier"],"x-audience":"supplier","summary":"Register a security key (WebAuthn)","description":"Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnRegisterRequest"},"examples":{"request":{"summary":"Request","value":{"registrationId":"reg_01JA9N4","name":"YubiKey","credential":{"id":"AbC1","rawId":"AbC1","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZQ"}}}}}}}},"responses":{"201":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"201 — Enrolled","value":{"factor":{"factorId":"fac_01JA9Q3","type":"WEBAUTHN","name":"YubiKey","createdAt":"2026-10-01T08:20:00Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/factors/{factorId}":{"delete":{"operationId":"supplierMfaRemoveFactor","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_remove_factor","class":"commit-money","autonomy":"L1","reason":"This removes an MFA factor and weakens the account's authentication. A security-credential change stays with the human user."},"tags":["supplier"],"x-audience":"supplier","summary":"Remove a factor","description":"Idempotency-Key required. Removing the last factor where MFA is required is 409 CONFLICT details.reason MFA_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT MFA_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"mfaRequired":{"summary":"409 CONFLICT MFA_REQUIRED","value":{"code":"CONFLICT","message":"MFA is required for this account; enrol another factor first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MFA_REQUIRED"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/auth/mfa/recovery-codes":{"post":{"operationId":"supplierMfaRegenerateRecoveryCodes","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_mfa_regenerate_recovery_codes","class":"commit-money","autonomy":"L1","reason":"This invalidates the old recovery codes and returns new secret ones. An agent must never see recovery codes."},"tags":["supplier"],"x-audience":"supplier","summary":"Regenerate recovery codes","description":"Send {}. The previous set stops working. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"New codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaRecoveryCodes"},"examples":{"ok":{"summary":"200 — New codes","value":{"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA","C8RW-5HNE"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/volume":{"get":{"operationId":"supplierAnalyticsVolume","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_volume","class":"read","autonomy":"L0","example":"{\"from\":\"2026-09-01\",\"to\":\"2026-09-30\"}"},"tags":["supplier"],"x-audience":"supplier","summary":"Booking volume","description":"connect-v1 1.9.0 (served). Bookings naming this supplier, per day and by product, from the write-ahead orders. byRegion is empty: orders carry no region. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Booking volume","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsVolume"},"examples":{"ok":{"summary":"200 — Booking volume","value":{"asOf":"2026-10-02T06:00:00Z","perDay":[{"date":"2026-10-01","bookings":41}],"byProduct":[{"domain":"HTL","bookings":312}],"byRegion":[{"region":"EU-SOUTH","bookings":120}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/conversion":{"get":{"operationId":"supplierAnalyticsConversion","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_conversion","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Conversion funnel","description":"connect-v1 1.9.0 (served). OFFERS_SHOWN counts the searches this supplier answered and OFFERS_SELECTED the detail and reprice calls it answered (the platform keeps no per-offer impression count); BOOKED the orders. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Conversion funnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsConversion"},"examples":{"ok":{"summary":"200 — Conversion funnel","value":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"stages":[{"key":"OFFERS_SHOWN","count":18420},{"key":"OFFERS_SELECTED","count":1310},{"key":"BOOKED","count":100}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/response-times":{"get":{"operationId":"supplierAnalyticsResponseTimes","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_response_times","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Response times","description":"connect-v1 1.9.0 (served). Per-day p50/p95 of this supplier's SEARCH and ORDER calls, from the supplier call log. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Response times","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsResponseTimes"},"examples":{"ok":{"summary":"200 — Response times","value":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"points":[{"day":"2026-10-01","searchP50Ms":640,"searchP95Ms":1810,"bookP50Ms":2100,"bookP95Ms":5400,"breakerOpen":false}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/errors":{"get":{"operationId":"supplierAnalyticsErrors","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_errors","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Error classes","description":"connect-v1 1.9.0 (served). Failed calls classed by 06 §8's taxonomy, with the rate per 10k calls; refusals before anything was sent are the platform's own and are not counted. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Error classes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsErrors"},"examples":{"ok":{"summary":"200 — Error classes","value":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"classes":[{"cls":"TIMEOUT","count":42,"ratePer10k":12.5}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/cancellations":{"get":{"operationId":"supplierAnalyticsCancellations","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_cancellations","class":"read","autonomy":"L0","example":"{\"from\":\"2026-07-01\",\"to\":\"2026-09-30\"}"},"tags":["supplier"],"x-audience":"supplier","summary":"Cancellations and declines","description":"connect-v1 1.9.0 (served). Cancelled (and refunded) orders over all orders, and supplier-console declines over decided on-request bookings, in basis points. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Cancellations and declines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsCancellations"},"examples":{"ok":{"summary":"200 — Cancellations and declines","value":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"cancellationRateBps":420,"declineRateBps":180,"declineReasons":[{"code":"SOLD_OUT","count":7}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/l2b":{"get":{"operationId":"supplierAnalyticsL2B","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_l2b","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Look-to-book","description":"connect-v1 1.9.0 (served). Searches per booking per day. No look-to-book cap is contracted yet, so l2bCap is 0 (none) and shedding is false. Scoped to the caller's supplierCode (the supplier wall); no tenant, credential set or price is shown. supplier_analytics:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Look-to-book","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsL2b"},"examples":{"ok":{"summary":"200 — Look-to-book","value":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"l2bCap":500,"points":[{"day":"2026-10-01","l2b":410,"shedding":false}],"sheddingSince":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/analytics/api-calls":{"get":{"operationId":"supplierAnalyticsApiCalls","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_analytics_api_calls","class":"read","autonomy":"L0","example":"{\"from\":\"2026-10-05\",\"to\":\"2026-10-12\",\"operation\":\"SEARCH\",\"outcome\":\"TIMED_OUT\",\"limit\":50}"},"tags":["supplier"],"x-audience":"supplier","summary":"API traffic","description":"connect-v1 1.9.0. The platform's calls to THIS supplier's API (the caller's supplierCode), every tenant's and the platform's own: per-operation counts, failures and latency, outcomes, and the newest calls. No tenant, credential set or price is shown (spec/10 data minimisation); parameters are names only. supplier_analytics:read (every supplier role).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}},{"name":"operation","in":"query","required":false,"description":"Narrow to one supplier operation, as journalled.","schema":{"type":"string"}},{"name":"outcome","in":"query","required":false,"description":"Narrow to one call outcome.","schema":{"$ref":"#/components/schemas/SupplierCallOutcome"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 100).","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"API traffic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierAnalyticsApiCalls"},"examples":{"ok":{"summary":"200 — API traffic","value":{"asOf":"2026-10-06T12:00:00Z","from":"2026-09-29T12:00:00Z","to":"2026-10-06T12:00:00Z","calls":610,"failed":12,"p50Ms":820,"p95Ms":2400,"bookings":6,"operations":[{"operation":"PROPERTY_PRICING","verb":"SEARCH","calls":480,"failed":9,"p50Ms":860,"p95Ms":2450,"lastCallAt":"2026-10-06T11:58:10Z"}],"outcomes":[{"key":"SUCCEEDED","count":598}],"recent":[{"at":"2026-10-06T11:58:10Z","operation":"PROPERTY_PRICING","verb":"SEARCH","outcome":"SUCCEEDED","elapsedMs":812,"parameters":["checkin","checkout","property_id"]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/feeds":{"get":{"operationId":"supplierFeeds","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_feeds","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Feed health","description":"connect-v1 1.11.0 (served, OD-878). The catalogue syncs the platform runs against this supplier (lastDeltaAt = the last complete pass, rowsIngested = active rows). drift carries FEED_STALE when the last complete pass is older than the feed's window or the last run failed; publication is never paused by it. A supplier the platform syncs nothing from has no feeds. supplier_feeds:read (SupplierOwner, SupplierOps). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Feed health","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierFeeds"},"examples":{"ok":{"summary":"200 — Feed health","value":{"asOf":"2026-10-02T06:00:00Z","feeds":[{"feedId":"feed-rates","name":"Rates","scheduleWindowHours":6,"lastRunAt":"2026-10-02T05:00:00Z","lastDeltaAt":"2026-10-02T05:00:00Z","rowsIngested":18210,"rowsRejected":3,"rejections":[{"reason":"MALFORMED","count":3,"sampleRowRef":"row 2210"}]}],"drift":[{"feedId":"feed-rates","kind":"IDENTITY_DRIFT","detectedAt":"2026-10-02T05:30:00Z","detail":"12 properties changed name","publicationPaused":true,"reviewItemRef":"REV-2291"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/contract":{"get":{"operationId":"supplierContract","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_contract","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Contract","description":"connect-v1 1.11.0 (served, OD-878). The contract finance has loaded for this supplier. None loaded yet: 404 with details.reason NO_CONTRACT_ON_FILE — never a sample. supplier_contract:read (SupplierOwner, SupplierFinance). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierContract"},"examples":{"ok":{"summary":"200 — Contract","value":{"contractRef":"CTR-FIXTURE-2026","registryStatus":"ACTIVE","moneyModels":["MERCHANT"],"effectiveFrom":"2026-01-01","commissionSchedule":[{"domain":"HTL","commissionBps":1200}],"l2bCap":500,"sla":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/sla/scorecard":{"get":{"operationId":"supplierSlaScorecard","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_sla_scorecard","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"SLA scorecard","description":"connect-v1 1.11.0 (served, OD-878). The current and two previous calendar months (UTC). searchP95Ms and bookP95Ms are nearest-rank p95 of the supplier call log's SEARCH and ORDER/HOLD calls; availabilityBps is answered calls over attempted (a refusal before anything was sent is the platform's and not counted); confirmSlaHours is the p95 request-to-decision time of on-request bookings decided in the month. target comes from the contract and is omitted when none is on file (breach is then false); eventRefs names the breaker openings in a breached cell's window. A metric with nothing to measure has no cell. supplier_sla:read (SupplierOwner, SupplierOps). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"SLA scorecard","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSlaScorecard"},"examples":{"ok":{"summary":"200 — SLA scorecard","value":{"asOf":"2026-10-02T06:00:00Z","periods":[{"period":"2026-09","cells":[{"metric":"bookP95Ms","target":6000,"measured":6400,"breach":true,"window":"2026-09-01/2026-09-28","eventRefs":["brk_01J9"]}]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/sla/breaker-events":{"get":{"operationId":"supplierBreakerEvents","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_breaker_events","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Breaker events","description":"connect-v1 1.11.0 (served, OD-878). The last 90 days of circuit-breaker openings on this supplier's call paths, recorded from the breakers' own transitions (CLOSED to OPEN opens one, back to CLOSED closes it). chargedMinutes runs to closedAt, or to now while open; backoffMinutes is the breaker's wait in OPEN. supplier_sla:read. Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Breaker events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierBreakerEvents"},"examples":{"ok":{"summary":"200 — Breaker events","value":{"events":[{"eventId":"brk_01J9","openedAt":"2026-09-12T10:00:00Z","closedAt":"2026-09-12T10:20:00Z","trigger":{"errorClass":"TIMEOUT","failures":20,"windowSeconds":60},"chargedMinutes":20,"backoffMinutes":5}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/integration/health":{"get":{"operationId":"supplierIntegrationHealth","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_integration_health","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Integration health","description":"connect-v1 1.11.0 (served, OD-878). breakerState is OPEN while any of the supplier's breakers is not closed. verbs: per supplier verb over the last 7 days, failed calls classed by 06 §8 with the rate per 10k calls. driftAlerts is empty: no response-contract drift capture runs yet. supplier_integration:read (SupplierOwner). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Integration health","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierIntegrationHealth"},"examples":{"ok":{"summary":"200 — Integration health","value":{"asOf":"2026-10-02T06:00:00Z","breakerState":"CLOSED","verbs":[{"verb":"search","errors":[{"cls":"TIMEOUT","ratePer10k":4.1}]}],"driftAlerts":[]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/integration/cert":{"get":{"operationId":"supplierCertStatus","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_cert_status","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Certification status","description":"connect-v1 1.11.0 (served, OD-878). One entry per platform credential set for the supplier: CERTIFIED from a passing certification report, EXPIRING in its last 14 days, EXPIRED 90 days after the pass (dueReason NINETY_DAYS); no passing report is PENDING with certifiedAt, reCertDueAt and dueReason omitted. supplier_integration:read. Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Certification status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierCertStatus"},"examples":{"ok":{"summary":"200 — Certification status","value":{"asOf":"2026-10-02T06:00:00Z","versions":[{"apiVersion":"v3","certState":"EXPIRING","certifiedAt":"2026-07-01T00:00:00Z","reCertDueAt":"2026-10-10T00:00:00Z","dueReason":"NINETY_DAYS"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/integration/sandbox":{"get":{"operationId":"supplierSandboxInfo","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_sandbox_info","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Sandbox","description":"connect-v1 1.11.0 (served, OD-878). The sandbox endpoint the deployment provisions for the supplier and the platform's sandbox credential sets, masked (kind, last4) — never a secret. fixtures is empty until fixtures are published. No sandbox provisioned: 404 with details.reason SANDBOX_NOT_PROVISIONED. supplier_integration:read. Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSandboxInfo"},"examples":{"ok":{"summary":"200 — Sandbox","value":{"endpoint":"https://supplier-sim.example/EXAMPLEVILLAS","credentials":[{"keyId":"sk_01","kind":"API_KEY","last4":"9f2a","createdAt":"2026-09-01T00:00:00Z","expiresAt":null}],"fixtures":[{"fixtureId":"fx-onrequest","name":"On-request booking","description":"A booking that waits for confirm"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/settlements":{"get":{"operationId":"supplierSettlements","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_settlements","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Settlement statements","description":"connect-v1 1.11.0 (served, OD-878). Statements finance has published, newest period first; none yet is an empty list. supplier_settlement:read (SupplierOwner, SupplierFinance). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Settlement statements","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierSettlements"},"examples":{"ok":{"summary":"200 — Settlement statements","value":{"asOf":"2026-10-02T06:00:00Z","statements":[{"statementId":"ST-2608-M","period":"2026-08","moneyModel":"MERCHANT","direction":"REMITTANCE_TO_SUPPLIER","status":"PUBLISHED","remittanceTotal":{"amount":48200,"currency":"EUR"},"lines":[{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false}],"payoutHold":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/settlements/{statementId}/reconcile":{"post":{"operationId":"supplierReconcileStatement","x-mcp":{"expose":"tool","toolset":"supplier","name":"supplier_reconcile_statement","class":"read","autonomy":"L0","example":"{\"statementId\":\"ST-2026-09\",\"idempotencyKey\":\"d7e2b6a3-1f48-4c9d-b5e0-6a3c8f1d2e94\",\"body\":{\"lines\":[{\"lineId\":\"L-2608-003\",\"supplierBookingRef\":\"SB-7Q2-4KD-9ZT\",\"currency\":\"EUR\",\"amount\":48200}]}}"},"tags":["supplier"],"x-audience":"supplier","summary":"Reconcile a statement against the supplier's lines","description":"connect-v1 1.11.0 (served, OD-878). Pure comparison; changes nothing. Lines are matched on supplierBookingRef (and lineId when sent): equal amount and currency is matched, otherwise amountMismatch; the statement's lines the supplier did not send are missingTheirs, the supplier's lines the statement lacks missingOurs, and provisional lines are excluded. supplier_settlement:reconcile (SupplierFinance). Idempotency-Key required. A platform-ops session may act onBehalf {supplierCode, reason (8-500)} with the step-up proof (DR-53); the act is audited in the platform slice and in the supplier's own.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"statementId","description":"The settlement statement to reconcile against.","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierReconcileRequest"},"examples":{"request":{"summary":"Request","value":{"lines":[{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}]}}}}}},"responses":{"200":{"description":"Buckets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierReconcileResult"},"examples":{"ok":{"summary":"200 — Buckets","value":{"matched":[{"ours":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false},"theirs":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}}],"missingOurs":[],"missingTheirs":[],"amountMismatch":[],"provisionalExcluded":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/disputes":{"get":{"operationId":"supplierDisputes","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_disputes","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Disputes","description":"connect-v1 1.11.0 (served, OD-878). Every dispute thread, newest first. An author is shown as SUPPLIER (the user's name, resolved when read) or PLATFORM; no operator is ever named. supplier_disputes:read (SupplierOwner, SupplierOps, SupplierFinance). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Disputes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDisputeList"},"examples":{"ok":{"summary":"200 — Disputes","value":{"disputes":[{"disputeId":"DSP-0412","subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"status":"OPEN","openedAt":"2026-09-05T10:00:00Z","messages":[{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"supplierOpenDispute","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_open_dispute","class":"commit-lite","autonomy":"L1","reason":"This opens an audited settlement dispute with the platform in the supplier's name (supplier_disputes:write). The route is supplier-session only, and the spec gives no agent access to supplier writes."},"tags":["supplier"],"x-audience":"supplier","summary":"Open a dispute","description":"connect-v1 1.11.0 (served, OD-878). Opens a thread (status OPEN) with its first message. Audited. supplier_disputes:write (SupplierOwner, SupplierFinance). Idempotency-Key required. A platform-ops session may act onBehalf {supplierCode, reason (8-500)} with the step-up proof (DR-53); the act is audited in the platform slice and in the supplier's own.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDisputeCreate"},"examples":{"request":{"summary":"Request","value":{"subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"firstMessage":"We expected EUR 482.00 net."}}}}}},"responses":{"201":{"description":"The dispute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDispute"},"examples":{"ok":{"summary":"201 — The dispute","value":{"disputeId":"DSP-0412","subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"status":"OPEN","openedAt":"2026-09-05T10:00:00Z","messages":[{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/disputes/{disputeId}/messages":{"post":{"operationId":"supplierAddDisputeMessage","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_add_dispute_message","class":"commit-lite","autonomy":"L1","reason":"This posts an audited message into a settlement dispute in the supplier's name (supplier_disputes:write). The route is supplier-session only, and the spec gives no agent access."},"tags":["supplier"],"x-audience":"supplier","summary":"Add a message to a dispute","description":"connect-v1 1.11.0 (served, OD-878). Adds a message and answers the updated thread; a thread that moved on meanwhile is 409 VERSION_MOVED. Audited. supplier_disputes:write. Idempotency-Key required. A platform-ops session may act onBehalf {supplierCode, reason (8-500)} with the step-up proof (DR-53); the act is audited in the platform slice and in the supplier's own.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"name":"disputeId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDisputeMessage"},"examples":{"request":{"summary":"Request","value":{"text":"Attached our remittance advice."}}}}}},"responses":{"200":{"description":"The dispute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDispute"},"examples":{"ok":{"summary":"200 — The dispute","value":{"disputeId":"DSP-0412","subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"status":"OPEN","openedAt":"2026-09-05T10:00:00Z","messages":[{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/notifications":{"get":{"operationId":"supplierNotificationPrefs","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_notification_prefs","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Notification preferences","description":"connect-v1 1.11.0 (served, OD-878). The caller's own channels per event; an event never saved reads {email: false, webhook: false, console: true}. Any supplier role.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Notification preferences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierNotificationPrefs"},"examples":{"ok":{"summary":"200 — Notification preferences","value":{"onrequest.sla_warning":{"email":true,"webhook":false,"console":true},"settlement.published":{"email":true,"webhook":true,"console":true}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"supplierUpdateNotificationPrefs","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_update_notification_prefs","class":"commit-lite","autonomy":"L1","reason":"This replaces the human caller's own notification channels (audited, for themselves only). It is a personal preference of a supplier-session user, not an agent action."},"tags":["supplier"],"x-audience":"supplier","summary":"Replace notification preferences","description":"connect-v1 1.11.0 (served, OD-878). Replaces the caller's own map; events not sent fall back to the default. An unknown event, or channels that are not three booleans, is 400. Idempotency-Key required. Audited. Any supplier role, for themselves only.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierNotificationPrefs"},"examples":{"request":{"summary":"Request","value":{"onrequest.sla_warning":{"email":true,"webhook":false,"console":true},"settlement.published":{"email":true,"webhook":true,"console":true}}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierNotificationPrefs"},"examples":{"ok":{"summary":"200 — Saved","value":{"onrequest.sla_warning":{"email":true,"webhook":false,"console":true},"settlement.published":{"email":true,"webhook":true,"console":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/supplier/v1/compliance/documents":{"get":{"operationId":"supplierComplianceDocuments","x-mcp":{"expose":"resource","toolset":"supplier","name":"supplier_compliance_documents","class":"read","autonomy":"L0"},"tags":["supplier"],"x-audience":"supplier","summary":"Compliance documents","description":"connect-v1 1.11.0 (served, OD-878). Documents on file, newest first; a document past expiresOn reads EXPIRED whatever its review said. supplier_compliance:read (SupplierOwner, SupplierFinance). Scoped to the caller's supplierCode (the supplier wall); another supplier's row is 404, never 403.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","responses":{"200":{"description":"Compliance documents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierComplianceDocumentList"},"examples":{"ok":{"summary":"200 — Compliance documents","value":{"documents":[{"documentId":"DOC-2003","type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31","reviewStatus":"REJECTED","rejection":{"reasonCode":"ILLEGIBLE_SCAN","detail":"The account number is unreadable."}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"supplierSubmitComplianceDocument","x-mcp":{"expose":"excluded","toolset":"supplier","name":"supplier_submit_compliance_document","class":"commit-lite","autonomy":"L1","reason":"This files a compliance document as SUBMITTED for review in the supplier's name (supplier_compliance:write, SupplierFinance). It is a legal attestation that stays with a human."},"tags":["supplier"],"x-audience":"supplier","summary":"Submit a compliance document","description":"connect-v1 1.11.0 (served, OD-878). Records the document's metadata as SUBMITTED for review (the file itself goes through POST /v1/uploads; uploadId links it). Audited. supplier_compliance:write (SupplierFinance). Idempotency-Key required. A platform-ops session may act onBehalf {supplierCode, reason (8-500)} with the step-up proof (DR-53); the act is audited in the platform slice and in the supplier's own.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"supplier-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierComplianceDocumentCreate"},"examples":{"request":{"summary":"Request","value":{"type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31"}}}}}},"responses":{"201":{"description":"Submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierComplianceDocument"},"examples":{"ok":{"summary":"201 — Submitted","value":{"documentId":"DOC-2004","type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31","reviewStatus":"SUBMITTED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/go-live":{"get":{"operationId":"getGoLive","x-mcp":{"expose":"resource","toolset":"console","name":"get_go_live","class":"read","autonomy":"L0"},"tags":["admin"],"x-audience":"console","summary":"Go-live sign-offs and promotion state (OD-870)","description":"connect-v1 1.10.0 (served). Needs one of tenant:activate, users:manage or audit:read (the Auditor sees the state and acts on nothing). requiredSignoffs is 2; promotedAt is null until a promotion is recorded. connect-v1 1.47.0 (FEAT-033): for an INDIVIDUAL tenant (AccessContext.accountType) one Owner's sign-off is enough and requiredSignoffs is 1; a BUSINESS tenant keeps 2. The checklist itself is computed from GET /v1/me and the credential-set certification reports.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"State","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveState"},"examples":{"ok":{"summary":"200 — State","value":{"signoffs":[{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya (Owner)","signedAt":"2026-10-02T07:00:00Z","reason":"Live cert passed; canary clean"}],"requiredSignoffs":2,"promotedAt":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/go-live:signoff":{"post":{"operationId":"signOffGoLive","x-mcp":{"expose":"excluded","toolset":"console","name":"sign_off_go_live","class":"commit-lite","autonomy":"L1","reason":"Go-live sign-off is a named owner's attestation toward promoting the tenant to production; tenant state (OD-870)."},"tags":["admin"],"x-audience":"console","summary":"Sign off go-live (one of two owners; the one Owner of an INDIVIDUAL tenant) (OD-870)","description":"connect-v1 1.10.0 (served). Needs tenant:activate or users:manage, and the caller must be an Owner (role OWNER or CUSTOMER_ADMIN, an active human user of the tenant); a non-owner is 403 PERMISSION_DENIED details.reason OWNER_ONLY. A second sign-off by the same user is 409 CONFLICT details.reason ALREADY_SIGNED. Audited (go_live.signoff). connect-v1 1.47.0 (FEAT-033): an INDIVIDUAL tenant needs this one sign-off only (GoLiveState.requiredSignoffs 1).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveReason"},"examples":{"request":{"summary":"Request","value":{"reason":"Live cert passed; canary clean"}}}}}},"responses":{"201":{"description":"Signed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveSignoff"},"examples":{"ok":{"summary":"201 — Signed","value":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya (Owner)","signedAt":"2026-10-02T07:00:00Z","reason":"Live cert passed; canary clean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"description":"CONFLICT ALREADY_SIGNED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"alreadySigned":{"summary":"409 CONFLICT ALREADY_SIGNED","value":{"code":"CONFLICT","message":"You already signed off; a second owner must sign.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ALREADY_SIGNED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/go-live:promote":{"post":{"operationId":"promoteGoLive","x-mcp":{"expose":"excluded","toolset":"console","name":"promote_go_live","class":"commit-lite","autonomy":"L1","reason":"Promotes the tenant to production; tenant state (OD-870)."},"tags":["admin"],"x-audience":"console","summary":"Promote the tenant to production — two distinct owners' sign-offs required (OD-870)","description":"connect-v1 1.10.0 (served). Needs tenant:activate or users:manage, and the caller must be an Owner (role OWNER or CUSTOMER_ADMIN); a non-owner is 403 PERMISSION_DENIED details.reason OWNER_ONLY. Fewer than two distinct sign-offs by users who are still Owners is 409 CONFLICT details.reason SIGNOFFS_MISSING (details.have, details.need). connect-v1 1.47.0 (FEAT-033): for an INDIVIDUAL tenant one Owner's sign-off is enough (details.need 1, GoLiveState.requiredSignoffs 1); a BUSINESS tenant keeps two. A sandbox twin is 409 CONFLICT details.reason SANDBOX_TWIN (promote the contracting tenant); a tenant neither TRIAL nor LIVE is 409 CONFLICT details.reason ILLEGAL_TRANSITION (details.status). A deployment that cannot read the tenant's lifecycle or has no D-60 activation queue is 501 NOT_IMPLEMENTED details.reason ACTIVATION_UNAVAILABLE. X-Approval-Token (D-50) is required on PROD, minted at POST /admin/v1/approvals for scope users:manage, action go-live.promote, resource the caller's tenantId; the minter must not be the promoter (403 PERMISSION_DENIED details.reason SECOND_APPROVER_REQUIRED). A missing token is 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED with details {scope, action, resourceRef}, exactly what to mint. Below PROD the token is optional, but one that is presented is still verified. Promotion does not make the tenant LIVE by itself: it opens the D-60 TENANT_ACTIVATE ops approval (or finds the one already pending), records promotedAt and returns its id as activationApprovalId; tenantStatus stays TRIAL until an operator approves it, after which the tenant is LIVE and GET /v1/me reports productionWithheld false. A tenant already LIVE answers LIVE with no activationApprovalId. Audited (go_live.promote).\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/ApprovalToken"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveReason"},"examples":{"request":{"summary":"Request","value":{"reason":"Both owners signed; promote"}}}}}},"responses":{"200":{"description":"Promoted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLivePromotion"},"examples":{"ok":{"summary":"200 — Promoted; the D-60 activation awaits an operator","value":{"tenantStatus":"TRIAL","promotedAt":"2026-10-02T09:00:00Z","activationApprovalId":"apr_01JA9GL4QZ"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"description":"CONFLICT SIGNOFFS_MISSING | SANDBOX_TWIN | ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"signoffsMissing":{"summary":"409 CONFLICT SIGNOFFS_MISSING","value":{"code":"CONFLICT","message":"Two distinct owners must sign off before promotion.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SIGNOFFS_MISSING","have":1,"need":2}}},"sandboxTwin":{"summary":"409 CONFLICT SANDBOX_TWIN","value":{"code":"CONFLICT","message":"This is a sandbox tenant; it never goes live. Promote the contracting tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SANDBOX_TWIN"}}},"illegalTransition":{"summary":"409 CONFLICT ILLEGAL_TRANSITION","value":{"code":"CONFLICT","message":"A tenant in SUSPENDED cannot be promoted to production.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ILLEGAL_TRANSITION","status":"SUSPENDED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/admin/v1/invoices":{"get":{"operationId":"listInvoices","x-mcp":{"expose":"excluded","toolset":"console","name":"list_invoices","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): a console read served only behind aikonic.invoices.gst.enabled and not offered to agents while OD-ENR-28 is undecided; exposing it later is additive (18 §1) and leaves the console tool budget (ADR-C09 §3) unchanged now."},"tags":["admin"],"x-audience":"console","summary":"The tenant's GST invoices, newest first (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.invoices.gst.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-28). One GST invoice is issued for each confirmed order of a tenant with a confirmed GSTIN (POST /v1/activation/business-registration:confirm): billedTo is the registration's legal name and GSTIN, amount the order total, tax the GST on it at taxRateBps (aikonic.invoices.gst.rate-bps, default 500 [assumption, OD-ENR-28]). An order cancelled after issue voids its invoice (status VOID; x-extensible-enum); the number is never reused. from and to filter on issuedAt (dates, tenant time zone [assumption: UTC]). Newest first, cursor-paged as GET /v1/wallets/{currency}/transactions: pass nextCursor back as cursor; a cursor this route never issued is 400 VALIDATION (details.field cursor). Where the flag is off the route is 501 NOT_IMPLEMENTED details.reason INVOICES_UNAVAILABLE. Console session; needs finance:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Issued on or after this date"},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Issued on or before this date"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque; the nextCursor of the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20 [assumption]"}],"responses":{"200":{"description":"A page of invoices, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicePage"},"examples":{"page":{"summary":"200 — one issued invoice","value":{"items":[{"id":"INV-01JB3A1F","number":"AIK/2627/0000001","orderRef":"MO-8KD-2FQ-9XT","issuedAt":"2026-10-26T12:05:00Z","billedTo":{"legalName":"SAMPLE TRAVELS PRIVATE LIMITED","gstin":"29AABCS1234F1Z5"},"amount":{"amount":2500000,"currency":"INR"},"tax":{"amount":125000,"currency":"INR"},"taxRateBps":500,"status":"ISSUED"}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/InvoicesUnavailable"}}}},"/admin/v1/invoices/{invoiceId}":{"get":{"operationId":"getInvoice","x-mcp":{"expose":"excluded","toolset":"console","name":"get_invoice","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): a console read served only behind aikonic.invoices.gst.enabled and not offered to agents while OD-ENR-28 is undecided; exposing it later is additive (18 §1) and leaves the console tool budget (ADR-C09 §3) unchanged now."},"tags":["admin"],"x-audience":"console","summary":"One GST invoice (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.invoices.gst.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-28). An unknown id and another tenant's id are the same 404 NOT_FOUND. Where the flag is off the route is 501 NOT_IMPLEMENTED details.reason INVOICES_UNAVAILABLE. Console session; needs finance:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/InvoiceIdPath"}],"responses":{"200":{"description":"The invoice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"},"examples":{"issued":{"summary":"200 — an issued invoice for a confirmed order","value":{"id":"INV-01JB3A1F","number":"AIK/2627/0000001","orderRef":"MO-8KD-2FQ-9XT","issuedAt":"2026-10-26T12:05:00Z","billedTo":{"legalName":"SAMPLE TRAVELS PRIVATE LIMITED","gstin":"29AABCS1234F1Z5"},"amount":{"amount":2500000,"currency":"INR"},"tax":{"amount":125000,"currency":"INR"},"taxRateBps":500,"status":"ISSUED"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/InvoicesUnavailable"}}}},"/admin/v1/invoices/{invoiceId}/document":{"get":{"operationId":"getInvoiceDocument","x-mcp":{"expose":"excluded","toolset":"console","name":"get_invoice_document","class":"read","autonomy":"L0","reason":"An HTML document rendered for a browser to view or print, not an agent-readable resource (as get_job_artifact); and a PROPOSAL (connect-v1 1.51.0) served only behind aikonic.invoices.gst.enabled."},"tags":["admin"],"x-audience":"console","summary":"The GST invoice as a printable HTML document (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.invoices.gst.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-28). The invoice rendered as a self-contained HTML page (no external assets) for the console to show and the browser to print or save as PDF. Errors are JSON as on every route: an unknown id and another tenant's id are the same 404 NOT_FOUND; where the flag is off, 501 NOT_IMPLEMENTED details.reason INVOICES_UNAVAILABLE. Console session; needs finance:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/InvoiceIdPath"}],"responses":{"200":{"description":"The invoice document","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"text/html":{"schema":{"type":"string"},"examples":{"document":{"summary":"200 — the invoice page (abridged)","value":"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Tax invoice AIK/2627/0000001</title></head><body><h1>Tax invoice</h1><p>Billed to SAMPLE TRAVELS PRIVATE LIMITED, GSTIN 29AABCS1234F1Z5</p><p>Order MO-8KD-2FQ-9XT, taxable value INR 25,000.00, GST 5% INR 1,250.00</p></body></html>"}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/InvoicesUnavailable"}}}},"/admin/v1/notifications":{"get":{"operationId":"listNotifications","x-mcp":{"expose":"excluded","toolset":"console","name":"list_notifications","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): the console bell's own feed, served only behind aikonic.notifications.enabled and not offered to agents while OD-ENR-29 is undecided; exposing it later is additive (18 §1) and leaves the console tool budget (ADR-C09 §3) unchanged now."},"tags":["admin"],"x-audience":"console","summary":"The tenant's notifications feed for the console bell, newest first (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.notifications.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-29). One feed per tenant: activation.step_done, tenant.live, order.confirmed, order.cancelled, refund.issued, team.invite_sent, team.member_joined, limit.request_decided and webhook.test_delivered (kind is x-extensible-enum: render the kinds you know, skip the rest). params carries the kind's display values as strings (for example orderRef, step, amount); link is a console path, never an absolute URL. refund.issued is derived from the payment.refunded event: its params carry paymentRef, and orderRef only when the refunded payment is known to belong to an order. webhook.test_delivered is emitted when the signed test event is queued for delivery to the endpoint (POST /admin/v1/webhooks/{webhookId}:test), not when the endpoint answers. Read state is per signed-in user [assumption]: readAt is when the caller marked it read, and unread counts the caller's unread items in the whole feed, not the page. unreadOnly filters to them. Newest first, cursor-paged as GET /v1/wallets/{currency}/transactions: pass nextCursor back as cursor; a cursor this route never issued is 400 VALIDATION (details.field cursor). Items older than 90 days are dropped [assumption]. Where the flag is off the route is 501 NOT_IMPLEMENTED details.reason NOTIFICATIONS_UNAVAILABLE. Console session; needs tenant:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"unreadOnly","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Only the caller's unread notifications"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque; the nextCursor of the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Page size; default 20 [assumption]"}],"responses":{"200":{"description":"A page of notifications, newest first, and the caller's unread count","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPage"},"examples":{"page":{"summary":"200 — live, a confirmed order, two unread","value":{"items":[{"id":"NTF-01JB3B7K","kind":"order.confirmed","params":{"orderRef":"MO-8KD-2FQ-9XT","amount":"INR 25,000.00"},"link":"/console/orders/MO-8KD-2FQ-9XT","createdAt":"2026-10-26T12:05:00Z","readAt":null},{"id":"NTF-01JB3A2C","kind":"tenant.live","params":{"tier":"LIVE_CAPPED"},"link":"/console/activate","createdAt":"2026-10-26T10:31:00Z","readAt":null},{"id":"NTF-01JB39QZ","kind":"activation.step_done","params":{"step":"business_verification"},"link":"/console/activate","createdAt":"2026-10-26T09:14:00Z","readAt":"2026-10-26T09:20:00Z"}],"nextCursor":null,"unread":2}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotificationsUnavailable"}}}},"/admin/v1/notifications:mark-read":{"post":{"operationId":"markNotificationsRead","x-mcp":{"expose":"excluded","toolset":"console","name":"mark_notifications_read","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.51.0, FEAT-030 §18b): the console bell's own read state, served only behind aikonic.notifications.enabled and not offered to agents while OD-ENR-29 is undecided; exposing it later is additive (18 §1) and leaves the console tool budget (ADR-C09 §3) unchanged now."},"tags":["admin"],"x-audience":"console","summary":"Mark notifications read for the caller — some, or all (FEAT-030 §18b)","description":"connect-v1 1.51.0 (PROPOSAL; served when aikonic.notifications.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b, OD-ENR-29). The ':mark-read' literal-suffix custom method. Exactly one of ids (at most 100) or all true; both or neither is 400 VALIDATION. Marks them read for the caller only and answers how many changed and the caller's unread count after. An id that is unknown, another tenant's, or already read is skipped, not an error. Where the flag is off the route is 501 NOT_IMPLEMENTED details.reason NOTIFICATIONS_UNAVAILABLE. Console session; needs tenant:read. Idempotency-Key required.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationsMarkRead"},"examples":{"some":{"summary":"Mark two notifications read","value":{"ids":["NTF-01JB3B7K","NTF-01JB3A2C"]}},"all":{"summary":"Mark the whole feed read","value":{"all":true}}}}}},"responses":{"200":{"description":"How many changed, and the caller's unread count after","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationsMarkReadResult"},"examples":{"marked":{"summary":"200 — two marked; nothing left unread","value":{"marked":2,"unread":0}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotificationsUnavailable"}}}},"/admin/v1/contact-emails":{"get":{"operationId":"getContactEmails","x-mcp":{"expose":"excluded","toolset":"console","name":"get_contact_emails","class":"read","autonomy":"L0","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b): a console setting served only behind aikonic.activation.capped-flow.enabled and not offered to agents while the proposal is undecided; exposing it later is additive (18 §1) and leaves the console tool budget (ADR-C09 §3) unchanged now."},"tags":["admin"],"x-audience":"console","summary":"Where the tenant's billing, technical and operations e-mails go (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2). The three contact addresses of the tenant: billing (invoices and payment notices), technical (API deprecations, incidents, webhook failures) and operations (booking and supplier issues). Each defaults to the Owner's e-mail (the verified work e-mail when there is one) until set; updatedAt is null until a PUT. Console session; needs tenant:read.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The contact addresses, or the defaults","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEmails"},"examples":{"defaults":{"summary":"200 — never set; every address is the Owner's","value":{"billing":"arjun@sampletravels.co.uk","technical":"arjun@sampletravels.co.uk","operations":"arjun@sampletravels.co.uk","updatedAt":null}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}},"put":{"operationId":"putContactEmails","x-mcp":{"expose":"excluded","toolset":"console","name":"put_contact_emails","class":"commit-lite","autonomy":"L1","reason":"PROPOSAL (connect-v1 1.63.0, FEAT-030 §18b): a console setting served only behind aikonic.activation.capped-flow.enabled and not offered to agents while the proposal is undecided; where the tenant's notices go stays with a human."},"tags":["admin"],"x-audience":"console","summary":"Set the tenant's billing, technical and operations e-mails (FEAT-030 §18b)","description":"connect-v1 1.63.0 (PROPOSAL; served when aikonic.activation.capped-flow.enabled is on — off by default, on at DEV and SANDBOX; FEAT-030 §18b round 2). Replaces the three addresses (all three are sent; updatedAt is read-only and ignored). An address that is not an e-mail is 400 VALIDATION details.field naming it. The addresses are not verified [assumption]: notices go to them as set. Setting the values already stored is a no-op 200, so no Idempotency-Key is needed (idempotent by state, as PUT /admin/v1/mcp/settings). Console session; needs users:manage (403 PERMISSION_DENIED details.reason MISSING_PERMISSION). Audited (tenant.contact_emails_updated).\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEmails"},"examples":{"set":{"summary":"Billing to accounts, technical to the developer, operations to the desk","value":{"billing":"accounts@sampletravels.co.uk","technical":"dev@sampletravels.co.uk","operations":"desk@sampletravels.co.uk"}}}}}},"responses":{"200":{"description":"The addresses as stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEmails"},"examples":{"set":{"summary":"200 — the three addresses stored","value":{"billing":"accounts@sampletravels.co.uk","technical":"dev@sampletravels.co.uk","operations":"desk@sampletravels.co.uk","updatedAt":"2026-10-28T11:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/CappedFlowUnavailable"}}}},"/admin/v1/tickets":{"get":{"operationId":"listTickets","x-mcp":{"expose":"excluded","toolset":"console","name":"list_tickets","class":"read","autonomy":"L0","reason":"planned, not served (x-status: planned)"},"tags":["admin"],"x-audience":"console","x-status":"planned","summary":"Support tickets","description":"PLANNED — no backend serves this route yet (CON-2 change note lists the owning lane). The tenant's tickets.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tickets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketList"},"examples":{"ok":{"summary":"200 — Tickets","value":{"tickets":[{"ticketId":"TCK-1037","category":"ORDER_ISSUE","subject":"Voucher missing","state":"OPEN","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","openedAt":"2026-10-02T07:30:00Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"openTicket","x-mcp":{"expose":"excluded","toolset":"console","name":"open_ticket","class":"commit-lite","autonomy":"L1","reason":"planned, not served (x-status: planned)"},"tags":["admin"],"x-audience":"console","x-status":"planned","summary":"Open a support ticket","description":"PLANNED — no backend serves this route yet (CON-2 change note lists the owning lane). From the error panel the traceId is prefilled.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketCreate"},"examples":{"request":{"summary":"Request","value":{"category":"ORDER_ISSUE","subject":"Voucher missing","description":"The voucher never arrived for this stay.","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX"}}}}}},"responses":{"201":{"description":"Opened","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicket"},"examples":{"ok":{"summary":"201 — Opened","value":{"ticketId":"TCK-1037","category":"ORDER_ISSUE","subject":"Voucher missing","state":"OPEN","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","openedAt":"2026-10-02T07:30:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/orders/manual":{"post":{"operationId":"createManualOrder","x-mcp":{"expose":"excluded","toolset":"console","name":"create_manual_order","class":"commit-money","autonomy":"L1","reason":"planned, not served (x-status: planned). When served it records an order by hand, a money record the proposal flow cannot carry (only orders.create and orders.cancel, DR-44)."},"tags":["admin"],"x-audience":"console","x-status":"planned","summary":"Record an offline order by hand (OD-P5-20)","description":"PLANNED — no backend serves this route yet (CON-2 change note lists the owning lane). Requires orders:write. Audited.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualOrderRequest"},"examples":{"request":{"summary":"Request","value":{"channel":"OFFLINE","domain":"HTL","supplier":"EXAMPLEHOTELS","supplierLocator":"HB-118822","traveler":{"name":"Ana Souza"},"service":{"start":"2026-11-10","end":"2026-11-14"},"price":{"amount":184000,"currency":"USD"},"moneyModel":"AGENCY","reason":"Booked by phone during the outage"}}}}}},"responses":{"201":{"description":"Recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualOrderResult"},"examples":{"ok":{"summary":"201 — Recorded","value":{"orderRef":"MO-7RT-2KQ-8ZD","channel":"OFFLINE","supplierLocator":"HB-118822","price":{"amount":184000,"currency":"USD"},"moneyModel":"AGENCY"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"409":{"description":"DUPLICATE_ORDER — that supplier and locator are already recorded (details.orderRef)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"duplicateOrder":{"summary":"409 DUPLICATE_ORDER","value":{"code":"DUPLICATE_ORDER","message":"EXAMPLEHOTELS HB-118822 is already recorded.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"orderRef":"MO-7RT-2KQ-8ZD"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/supplier-data/alerts/{supplierId}/{dataset}":{"get":{"operationId":"getSupplierDataAlerts","x-mcp":{"expose":"excluded","toolset":"console","name":"get_supplier_data_alerts","class":"read","autonomy":"L0","reason":"planned, not served (x-status: planned)"},"tags":["admin"],"x-audience":"console","x-status":"planned","summary":"Alert settings for one dataset","description":"PLANNED — no backend serves this route yet (CON-2 change note lists the owning lane). supplier_data:read.","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataAlertSettings"},"examples":{"ok":{"summary":"200 — Settings","value":{"stalenessOverMax":true,"failureStreak":3,"quotaAbovePct":80,"recordDropPct":20,"mappingErrors":true}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateSupplierDataAlerts","x-mcp":{"expose":"excluded","toolset":"console","name":"update_supplier_data_alerts","class":"commit-lite","autonomy":"L1","reason":"planned, not served (x-status: planned)"},"tags":["admin"],"x-audience":"console","x-status":"planned","summary":"Replace alert settings for one dataset","description":"PLANNED — no backend serves this route yet (CON-2 change note lists the owning lane). supplier_data:manage.","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataAlertSettings"},"examples":{"request":{"summary":"Request","value":{"stalenessOverMax":true,"failureStreak":3,"quotaAbovePct":80,"recordDropPct":20,"mappingErrors":true}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataAlertSettings"},"examples":{"ok":{"summary":"200 — Saved","value":{"stalenessOverMax":true,"failureStreak":3,"quotaAbovePct":80,"recordDropPct":20,"mappingErrors":true}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/login":{"post":{"operationId":"operatorLogin","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_login","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["operator"],"x-audience":"operator","summary":"Operator sign-in — always answers an MFA challenge (FEAT-024 §6)","description":"MFA is mandatory for every operator: the password step answers 202 MfaChallenge; complete it at /ops/v1/auth/mfa/verify. First sign-in forces enrolment. Same enumeration-safe 401 as the other realms. A tenant or supplier token is never accepted on /ops/v1 (401, audited).","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorLoginRequest"},"examples":{"request":{"summary":"Request","value":{"username":"priya@mondee.example","password":"correct-horse-battery-staple"}}}}}},"responses":{"200":{"description":"connect-v1 1.12.0 (OD-879): the deployment does not require an operator second factor (aikonic.ops.mfa.required false) — the password opened the session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorSessionResponse"},"examples":{"ok":{"summary":"200 — password sign-in (no operator second factor required)","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-02T08:25:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-03T08:10:00Z","context":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}}}}}}},"202":{"description":"MFA required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaChallenge"},"examples":{"ok":{"summary":"202 — MFA required","value":{"challengeId":"mfa_01JA9R5","methods":["TOTP","WEBAUTHN","RECOVERY"],"expiresAt":"2026-10-02T08:10:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa":{"get":{"operationId":"operatorMfaStatus","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_status","class":"read","autonomy":"L0","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"The caller's MFA enrolment","description":"Factors and recovery-code count.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","responses":{"200":{"description":"Enrolment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaStatus"},"examples":{"ok":{"summary":"200 — Enrolment","value":{"enrolled":true,"required":true,"factors":[{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"}],"recoveryCodesRemaining":9}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/totp":{"post":{"operationId":"operatorMfaStartTotp","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_start_totp","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"Start a TOTP enrolment","description":"Idempotency-Key required; the factor is pending until verified.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpStartRequest"},"examples":{"request":{"summary":"Request","value":{"name":"Authenticator app"}}}}}},"responses":{"201":{"description":"Pending factor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTotpEnrolment"},"examples":{"ok":{"summary":"201 — Pending factor","value":{"factorId":"fac_01JA9M1","otpauthUri":"otpauth://totp/Apiq:ops%40acme.example?secret=JBSWY3DPEHPK3PXPJBSWY3DP&issuer=Apiq&digits=6&period=30&algorithm=SHA1","expiresAt":"2026-10-01T08:15:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/totp/{factorId}:verify":{"post":{"operationId":"operatorMfaConfirmTotp","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_confirm_totp","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"Confirm a pending TOTP factor with one code","description":"Idempotency-Key required. The first factor also answers the recovery codes, once.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaCodeRequest"},"examples":{"request":{"summary":"Request","value":{"code":"000000"}}}}}},"responses":{"200":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"200 — Enrolled","value":{"factor":{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"},"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/step-up/webauthn-options":{"post":{"operationId":"operatorMfaStepUpWebauthnOptions","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_step_up_webauthn_options","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"WebAuthn assertion options for a step-up (v1.6.7, DR-54)","description":"Added in v1.6.7 (DR-54, IMP-P2-196). Operator realm only. Send {}. Answers the PublicKeyCredentialRequestOptions (userVerification preferred) for a security key the operator has registered; run navigator.credentials.get with it and send the result as stepUpAssertion on the step-up-guarded request (entitlements publish, approvals approve and reject, switch flips, entitlement rollback, operator invite, supplier on-behalf). The options are for ONE assertion. An operator with no registered security key is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED. Idempotency-Key required, as on the registration options.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"Options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebAuthnStepUpOptions"},"examples":{"ok":{"summary":"200 — Options","value":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rpId":"ops.mondee.example","timeout":60000,"allowCredentials":[{"type":"public-key","id":"AaB3x9Zq1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0"}],"userVerification":"preferred"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/invites/accept":{"post":{"operationId":"operatorAcceptInvite","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_accept_invite","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["operator"],"x-audience":"operator","summary":"Accept an operator invite with a chosen password — once (v1.6.7; mirrors /supplier/v1/auth/invites/accept)","description":"Added in v1.6.7 (IMP-P2-160, DR-55). Anonymous: the token is the credential, carried in the BODY as on /v1/invites/accept. The password must be 12 to 1024 characters. A token that is unknown, used, expired or whose operator is no longer INVITED is one and the same 400 VALIDATION details.reason INVITE_INVALID; a password outside the policy is the same code with details.field password. Accepting makes the operator ACTIVE (status INVITED to ACTIVE) with that password; the token is spent. Plain JSON, not an envelope. The invitee's first sign-in answers 202 MfaChallenge carrying `enrolment`; no session is issued until a factor is verified (operators always have MFA). OperatorMe.mfaEnrolled reports the state afterwards. Rate-limited per client and per invite token as the identity routes are (429 RATE_LIMITED).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAcceptRequest"},"examples":{"accept":{"summary":"The token from the invite, and a chosen password","value":{"token":"q8Zp3VxN6tYb1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","password":"another long passphrase"}}}}}},"responses":{"201":{"description":"The operator the invite activated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorInviteAccepted"},"examples":{"accepted":{"summary":"201 — sign in at /ops/v1/auth/login with this email and the chosen password","value":{"operatorId":"op_8d3f21","email":"new.engineer@mondee.example","roles":["OPS_ENGINEER"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/webauthn/registration-options":{"post":{"operationId":"operatorMfaWebauthnOptions","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_webauthn_options","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"WebAuthn registration options","description":"Send {}. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"Options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnOptions"},"examples":{"ok":{"summary":"200 — Options","value":{"registrationId":"reg_01JA9N4","publicKey":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rp":{"name":"Apiq"},"user":{"id":"dXNyXzhhMjFmMA","name":"ops@acme.example","displayName":"Ops"},"pubKeyCredParams":[{"type":"public-key","alg":-7}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/webauthn":{"post":{"operationId":"operatorMfaRegisterWebauthn","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_register_webauthn","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"Register a security key (WebAuthn)","description":"Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaWebauthnRegisterRequest"},"examples":{"request":{"summary":"Request","value":{"registrationId":"reg_01JA9N4","name":"YubiKey","credential":{"id":"AbC1","rawId":"AbC1","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZQ"}}}}}}}},"responses":{"201":{"description":"Enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaFactorAdded"},"examples":{"ok":{"summary":"201 — Enrolled","value":{"factor":{"factorId":"fac_01JA9Q3","type":"WEBAUTHN","name":"YubiKey","createdAt":"2026-10-01T08:20:00Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/factors/{factorId}":{"delete":{"operationId":"operatorMfaRemoveFactor","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_remove_factor","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"Remove a factor","description":"Idempotency-Key required. Removing the last factor where MFA is required is 409 CONFLICT details.reason MFA_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"factorId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT MFA_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"mfaRequired":{"summary":"409 CONFLICT MFA_REQUIRED","value":{"code":"CONFLICT","message":"MFA is required for this account; enrol another factor first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MFA_REQUIRED"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/recovery-codes":{"post":{"operationId":"operatorMfaRegenerateRecoveryCodes","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_regenerate_recovery_codes","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"Regenerate recovery codes","description":"Send {}. The previous set stops working. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"examples":{"request":{"summary":"Request","value":{}}}}}},"responses":{"200":{"description":"New codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaRecoveryCodes"},"examples":{"ok":{"summary":"200 — New codes","value":{"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA","C8RW-5HNE"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/mfa/verify":{"post":{"operationId":"operatorMfaVerify","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_mfa_verify","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["operator"],"x-audience":"operator","summary":"Complete a sign-in challenge → session","description":"One proof: code, recoveryCode or webauthn. A wrong proof is 401 UNAUTHENTICATED details.reason MFA_FAILED; an expired challenge 401 MFA_CHALLENGE_EXPIRED.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaVerifyRequest"},"examples":{"request":{"summary":"Request","value":{"challengeId":"mfa_01JA9P0","code":"000000"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorSessionResponse"},"examples":{"ok":{"summary":"200 — Signed in","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-02T08:25:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-03T08:10:00Z","context":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/refresh":{"post":{"operationId":"operatorRefresh","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_refresh","class":"commit-lite","autonomy":"L1","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["operator"],"x-audience":"operator","summary":"Rotate an operator session's refresh token","description":"The old refresh token is dead.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"},"examples":{"request":{"summary":"Request","value":{"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb"}}}}}},"responses":{"200":{"description":"Rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorSessionResponse"},"examples":{"ok":{"summary":"200 — Rotated","value":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-02T08:25:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-03T08:10:00Z","context":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/IdentityRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/logout":{"post":{"operationId":"operatorLogout","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_logout","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"End the operator session","description":"Bearer only, no body.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","responses":{"204":{"description":"Signed out"},"401":{"$ref":"#/components/responses/Unauthenticated"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/sessions":{"get":{"operationId":"operatorListSessions","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_list_sessions","class":"read","autonomy":"L0","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"The caller's own sessions (OD-IMP-P4-037)","description":"Every signed-in session (refresh family) of the caller, current first. An operator session only.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","responses":{"200":{"description":"The sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionList"},"examples":{"ok":{"summary":"200 — The sessions","value":{"sessions":[{"sessionId":"ses_01JA9K2","current":true,"createdAt":"2026-10-01T08:00:00Z"},{"sessionId":"ses_01JA2P7","current":false,"createdAt":"2026-09-28T19:10:00Z","device":"Safari on iOS"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/auth/sessions/{sessionId}":{"delete":{"operationId":"operatorEndSession","x-mcp":{"expose":"excluded","toolset":"operator","name":"operator_end_session","class":"commit-lite","autonomy":"L1","reason":"Authentication self-service (sessions, MFA, sign-in) of a human user; not an agent surface."},"tags":["operator"],"x-audience":"operator","summary":"End one of the caller's sessions","description":"Ends that refresh family only (there is no bulk delete; end the others one by one). Ending the current session is a sign-out. Idempotency-Key required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Ended"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/me":{"get":{"operationId":"operatorMe","x-mcp":{"expose":"resource","toolset":"operator","name":"operator_me","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"The signed-in operator","description":"From the credential, never a body.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","responses":{"200":{"description":"The operator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorAccessContext"},"examples":{"ok":{"summary":"200 — The operator","value":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants":{"get":{"operationId":"opsListTenants","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_tenants","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"Tenants (platform metadata)","description":"Any operator role.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"status","in":"query","required":false,"description":"Filter by status","schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"Tenants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsTenantList"},"examples":{"ok":{"summary":"200 — Tenants","value":{"tenants":[{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","tier":"GROWTH","entitlementVersion":7,"createdAt":"2026-03-02T10:00:00Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/insights/environments":{"get":{"operationId":"opsInsightsEnvironments","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_insights_environments","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"Tenants per environment","description":"connect-v1 1.9.0. Every tenant on each side of the sandbox/production pairing (07 §5): status, region and entitled features. Platform metadata (FEAT-024); any operator role (ops:read).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[],"responses":{"200":{"description":"Tenants per environment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsInsightsEnvironments"},"examples":{"ok":{"summary":"200 — Tenants per environment","value":{"asOf":"2026-10-06T12:00:00Z","environments":[{"environment":"PRODUCTION","tenants":1,"byStatus":{"LIVE":1},"rows":[{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","pairedTenantId":"TEN-ACME-SBX","dataRegion":"ap-south","features":["HTL","XFR"],"createdAt":"2026-03-02T10:00:00Z"}]},{"environment":"SANDBOX","tenants":1,"byStatus":{"LIVE":1},"rows":[{"tenantId":"TEN-ACME-SBX","displayName":"Acme Travel (sandbox)","status":"LIVE","pairedTenantId":"TEN-ACME","dataRegion":"ap-south","features":["HTL","XFR"],"createdAt":"2026-03-02T10:00:00Z"}]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/insights/summary":{"get":{"operationId":"opsInsightsSummary","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_insights_summary","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenant) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"Platform activity summary","description":"connect-v1 1.9.0. Supplier calls (from the supplier call log, one row per attempt) and orders (write-ahead order metadata) for the window, by supplier, operation, tenant, outcome and day. Platform metadata in FEAT-024's sense — counts, timings, outcomes; no traveller or payment data. With environment=ALL and no tenant, calls no tenant made (background catalogue syncs) are included. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"tenant","in":"query","required":false,"description":"Narrow to one tenant id — the tenant an operator reads platform metadata OF; never resolved as the caller's tenant. A tenant outside `environment` reads nothing.","schema":{"type":"string"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Platform activity summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsInsightsSummary"},"examples":{"ok":{"summary":"200 — Platform activity summary","value":{"environment":"ALL","summary":{"asOf":"2026-10-06T12:00:00Z","from":"2026-09-29T12:00:00Z","to":"2026-10-06T12:00:00Z","totals":{"calls":1240,"succeeded":1180,"failed":52,"refused":8,"p50Ms":640,"p95Ms":2210,"orders":14,"ordersConfirmed":11,"ordersCancelled":1,"ordersPending":2,"truncated":false},"bySupplier":[{"supplier":"SUP-HTL-EXAMPLE","calls":610,"failed":12,"p50Ms":820,"p95Ms":2400,"orders":6,"lastCallAt":"2026-10-06T11:58:10Z"}],"byOperation":[{"supplier":"SUP-HTL-EXAMPLE","operation":"PROPERTY_PRICING","verb":"SEARCH","calls":480,"failed":9,"p50Ms":860,"p95Ms":2450,"lastCallAt":"2026-10-06T11:58:10Z"}],"byTenant":[{"tenantId":"TEN-ACME","calls":900,"failed":40,"orders":10}],"byOutcome":[{"key":"SUCCEEDED","count":1180},{"key":"TIMED_OUT","count":30}],"faults":[{"supplier":"SUP-FNB-EXAMPLE","operation":"AVAILABILITY","outcome":"FAILED_PROTOCOL","faultCode":"HTTP_403","count":22}],"perDay":[{"day":"2026-10-06","calls":210,"failed":9,"searches":180,"orders":3,"searchP50Ms":640,"searchP95Ms":2100,"bookP50Ms":1900,"bookP95Ms":4200}],"ordersByDomain":[{"key":"HTL","count":9}],"ordersByState":[{"key":"CONFIRMED","count":11}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/insights/calls":{"get":{"operationId":"opsInsightsCalls","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_insights_calls","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenant) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"Supplier calls","description":"connect-v1 1.9.0. The newest supplier call attempts: supplier, operation, verb, credential set, outcome, elapsed time, fault code and parameter NAMES (never a value). ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"tenant","in":"query","required":false,"description":"Narrow to one tenant id — the tenant an operator reads platform metadata OF; never resolved as the caller's tenant. A tenant outside `environment` reads nothing.","schema":{"type":"string"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"operation","in":"query","required":false,"description":"Narrow to one supplier operation, as journalled.","schema":{"type":"string"}},{"name":"outcome","in":"query","required":false,"description":"Narrow to one call outcome.","schema":{"$ref":"#/components/schemas/SupplierCallOutcome"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 100).","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Supplier calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsInsightsCalls"},"examples":{"ok":{"summary":"200 — Supplier calls","value":{"environment":"ALL","calls":[{"at":"2026-10-06T11:58:10Z","tenantId":"TEN-ACME","supplier":"SUP-XFR-EXAMPLE","operation":"SEARCH","verb":"SEARCH","credentialSetId":"MONDEE","outcome":"SUCCEEDED","elapsedMs":1840,"parameters":["end_address","pickup_datetime","start_address"]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/insights/orders":{"get":{"operationId":"opsInsightsOrders","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_insights_orders","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenant) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"Orders (metadata)","description":"connect-v1 1.9.0. The newest orders' metadata: reference, tenant, supplier, domain, state, created, charged (else quoted) amount and the supplier's reference. No traveller, contact or payment instrument — those stay behind a FEAT-017 grant. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pairing to read (07 §5). Default ALL.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"tenant","in":"query","required":false,"description":"Narrow to one tenant id — the tenant an operator reads platform metadata OF; never resolved as the caller's tenant. A tenant outside `environment` reads nothing.","schema":{"type":"string"}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE.","schema":{"type":"string"}},{"name":"state","in":"query","required":false,"description":"Narrow to one order state.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"Start of the window: an ISO date (UTC day) or instant. Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive): an ISO date (the whole UTC day) or instant. Default: now. At most 92 days after `from`.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 100).","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Orders (metadata)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsInsightsOrders"},"examples":{"ok":{"summary":"200 — Orders (metadata)","value":{"environment":"ALL","orders":[{"orderRef":"ORD-01JA7Q","tenantId":"TEN-ACME","supplier":"SUP-HTL-EXAMPLE","domain":"HTL","state":"CONFIRMED","createdAt":"2026-10-05T09:12:00Z","amountMinor":4250000,"currency":"INR","supplierReference":"SV-88213"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-calls":{"get":{"operationId":"opsSupplierCalls","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_supplier_calls","class":"read","autonomy":"L0","reason":"Raw supplier payloads; never to the AI plane."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"The supplier calls behind one trace or ref, metadata only (v1.25.0)","description":"connect-v1 1.25.0 (OD-898). Every supplier call attempt journalled for EXACTLY ONE selector: traceId, searchRef, offerRef, repriceRef, holdRef or orderRef. No selector is 400 VALIDATION details.reason SELECTOR_REQUIRED; more than one is 400 VALIDATION details.reason SELECTORS_EXCLUSIVE (details.fields names them). The platform resolves the selector to its traces and sibling refs (an orderRef reaches its offer, reprice and hold refs and the search that produced them) and answers every call on them, newest first, narrowed by supplier, verb and outcome. An unknown or expired ref answers 200 with an empty list, never 404, so the route does not confirm whether a ref exists. Rows are metadata only (supplier, operation, verb, outcome, fault, status, timing, method, path without its query string, byte counts and the capture state); a body is never in this answer and needs POST /ops/v1/supplier-calls:unlock. The rows may span tenants and are platform metadata, as on GET /ops/v1/insights/calls. capture says how this rung captures (OD-898 BODIES on DEV, SANDBOX and UAT; METADATA_ONLY on PROD until OD-898 decides PROD capture) and for how long (7 days). Needs ops:read. Cache-Control no-store.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"traceId","in":"query","required":false,"description":"Selector. A W3C trace id (32 lower-case hex), the support handle of an error body.","schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}},{"name":"searchRef","in":"query","required":false,"description":"Selector. A searchRef (SR-…).","schema":{"type":"string","maxLength":128}},{"name":"offerRef","in":"query","required":false,"description":"Selector. An offerRef (OF-…).","schema":{"type":"string","maxLength":128}},{"name":"repriceRef","in":"query","required":false,"description":"Selector. A repriceRef.","schema":{"type":"string","maxLength":128}},{"name":"holdRef","in":"query","required":false,"description":"Selector. A holdRef.","schema":{"type":"string","maxLength":128}},{"name":"orderRef","in":"query","required":false,"description":"Selector. An orderRef (MO-…).","schema":{"type":"string","maxLength":128}},{"name":"supplier","in":"query","required":false,"description":"Narrow to one supplier code, e.g. SUP-HTL-EXAMPLE2.","schema":{"type":"string"}},{"name":"verb","in":"query","required":false,"description":"Narrow to one adapter verb, e.g. SEARCH, PRICE, BOOK.","schema":{"type":"string"}},{"name":"outcome","in":"query","required":false,"description":"Narrow to one call outcome.","schema":{"$ref":"#/components/schemas/SupplierCallOutcome"}},{"name":"limit","in":"query","required":false,"description":"At most this many rows, newest first (1..500, default 200).","schema":{"type":"integer","minimum":1,"maximum":500,"default":200}}],"responses":{"200":{"description":"The calls behind the selector, possibly none","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierCallList"},"examples":{"byOrder":{"summary":"200 — the price and book calls behind one order, on SANDBOX (synthetic)","value":{"query":{"kind":"orderRef","value":"MO-SYN-0000-0001"},"resolved":{"traceIds":["0af7651916cd43dd8448eb211c80319c","4bf92f3577b34da6a3ce929d0e0e4736"],"searchRefs":["SR-H-00000001"],"refs":["OF-H-00000001","RP-H-00000001","MO-SYN-0000-0001"]},"capture":{"mode":"BODIES","rung":"SANDBOX","retentionDays":7},"calls":[{"callId":"SC-00000000000000000002","at":"2026-10-16T12:00:05Z","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","tenantId":"TEN-SYN-0001","environment":"SANDBOX","supplier":"SUP-HTL-EXAMPLE2","credentialSetId":"MONDEE","operation":"BOOK","verb":"BOOK","outcome":"SUCCEEDED","faultCode":null,"httpStatus":200,"elapsedMs":2140,"method":"POST","path":"/hotels/v1/book","requestBytes":3120,"responseBytes":2088,"capture":"BODIES","truncated":false,"refs":{"searchRef":"SR-H-00000001","offerRef":"OF-H-00000001","repriceRef":"RP-H-00000001","orderRef":"MO-SYN-0000-0001"}},{"callId":"SC-00000000000000000001","at":"2026-10-16T11:59:40Z","traceId":"0af7651916cd43dd8448eb211c80319c","tenantId":"TEN-SYN-0001","environment":"SANDBOX","supplier":"SUP-HTL-EXAMPLE2","credentialSetId":"MONDEE","operation":"PRICE","verb":"PRICE","outcome":"SUCCEEDED","faultCode":null,"httpStatus":200,"elapsedMs":880,"method":"POST","path":"/hotels/v1/rooms/price","requestBytes":512,"responseBytes":6410,"capture":"BODIES","truncated":false,"refs":{"searchRef":"SR-H-00000001","offerRef":"OF-H-00000001","repriceRef":"RP-H-00000001"}}]}},"unknownRef":{"summary":"200 — an unknown or expired ref answers an empty list","value":{"query":{"kind":"holdRef","value":"HD-SYN-UNKNOWN"},"resolved":{"traceIds":[],"searchRefs":[],"refs":[]},"capture":{"mode":"BODIES","rung":"SANDBOX","retentionDays":7},"calls":[]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-calls:unlock":{"post":{"operationId":"opsUnlockSupplierCalls","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_unlock_supplier_calls","class":"read","autonomy":"L0","reason":"Raw supplier payloads; never to the AI plane."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Unlock the raw wire for one trace or ref, step-up, 15 minutes, this session only (v1.25.0)","description":"connect-v1 1.25.0 (OD-898). Mints a wire grant for one scope (the same six selector kinds as GET /ops/v1/supplier-calls) that lets THIS operator session read call detail and bodies in that scope for 15 minutes. The grant is bound to the session: another session or another operator cannot use it, and logout or session revocation ends it. Needs ops:supplier_wire:read and step-up (stepUpCode or stepUpAssertion, exactly one). 403 PERMISSION_DENIED details.reason: MISSING_PERMISSION without ops:supplier_wire:read; STEP_UP_FAILED for a missing, wrong or doubled proof; STEP_UP_LOCKED when the operator is locked out; BREAK_GLASS_REQUIRED (details.tenantId) when the scope reaches a call of a PROD-environment tenant and the operator holds no ACTIVE FEAT-017 break-glass grant for that tenant. 400 VALIDATION details.field reason (8 to 500 characters), ticketRef (at most 100) or scope. A scope with no calls still mints a grant (it unlocks nothing), so the route does not confirm whether a ref exists. Each call mints a new grant; earlier grants stay valid until they expire. Audit supplier_wire.unlock records the operator, the session, the scope, the reason, the ticketRef and the grant id. Cache-Control no-store.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierWireUnlockRequest"},"examples":{"request":{"summary":"Request — unlock one order's calls for a support ticket","value":{"scope":{"kind":"orderRef","value":"MO-SYN-0000-0001"},"reason":"Customer reports a price change at book; compare the price and book bodies","ticketRef":"AIK-0000","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"The grant","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierWireGrant"},"examples":{"granted":{"summary":"200 — a 15-minute grant for one order","value":{"grantId":"SWG-00000000000000000001","expiresAt":"2026-10-16T12:15:00Z","scope":{"kind":"orderRef","value":"MO-SYN-0000-0001"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-calls/{callId}":{"get":{"operationId":"opsGetSupplierCall","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_supplier_call","class":"read","autonomy":"L0","reason":"Raw supplier payloads; never to the AI plane."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"One supplier call with its masked request and response (v1.25.0)","description":"connect-v1 1.25.0 (OD-898). The summary row plus the masked wire: the request (method, URL with credentials masked, headers, body) and the response (status, headers, body decoded from its content encoding), the timing split and the redaction stamp. Needs ops:supplier_wire:read AND an active wire grant of this session whose scope covers the call: without the permission 403 PERMISSION_DENIED details.reason MISSING_PERMISSION; without a covering grant 403 PERMISSION_DENIED details.reason UNLOCK_REQUIRED (unlock with POST /ops/v1/supplier-calls:unlock). 404 NOT_FOUND for an unknown callId and for one past retention. A body is inline up to 2 MB of stored text; a longer body is cut there with truncated true, and GET /ops/v1/supplier-calls/{callId}/body/{side} serves it whole. Masking is applied before the bytes are stored, never at read time (13 §2.2): credentials (authorization and API-key headers, secret query values, password and token fields of a body) are dropped and shown as a masked placeholder; a card number becomes {bin, last4} and a CVV is dropped; strong PII, and weak PII inside known traveller containers, become tenant-scoped HMAC pseudonyms, so two calls of one booking still join; supplier shopping tokens (the offer, rate and session keys the supplier issued) are KEPT, because they are what the operator compares. A capture state other than BODIES carries no request.body or response.body. Audit supplier_wire.view records the operator, the grant and the callId. Cache-Control no-store.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/SupplierCallId"}],"responses":{"200":{"description":"The call","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierCallDetail"},"examples":{"priceCall":{"summary":"200 — one masked PRICE call (synthetic)","value":{"callId":"SC-00000000000000000001","at":"2026-10-16T11:59:40Z","traceId":"0af7651916cd43dd8448eb211c80319c","tenantId":"TEN-SYN-0001","environment":"SANDBOX","supplier":"SUP-HTL-EXAMPLE2","credentialSetId":"MONDEE","operation":"PRICE","verb":"PRICE","outcome":"SUCCEEDED","faultCode":null,"httpStatus":200,"elapsedMs":880,"method":"POST","path":"/hotels/v1/rooms/price","requestBytes":512,"responseBytes":6410,"capture":"BODIES","truncated":false,"refs":{"searchRef":"SR-H-00000001","offerRef":"OF-H-00000001","repriceRef":"RP-H-00000001"},"request":{"method":"POST","url":"https://hotels.example.com/hotels/v1/rooms/price?apiKey=***","headers":[{"name":"Content-Type","value":"application/json","masked":false},{"name":"Authorization","value":"***","masked":true}],"contentType":"application/json","bodyFormat":"JSON","body":"{\"rateKey\":\"SYN-RATE-KEY-0001\",\"customer\":{\"name\":\"hmac:3f9a0c21\",\"email\":\"hmac:7b1e44d0\"}}","bodyBytes":512,"storedBytes":101,"truncated":false},"response":{"status":200,"headers":[{"name":"Content-Type","value":"application/json","masked":false}],"contentType":"application/json","contentEncoding":"gzip","bytesOnWire":1904,"decoded":true,"bodyFormat":"JSON","body":"{\"rateKey\":\"SYN-RATE-KEY-0001\",\"total\":{\"amount\":\"842.10\",\"currency\":\"USD\"},\"refundable\":true}","bodyBytes":6410,"truncated":false},"timing":{"connectMs":40,"supplierMs":790,"mappingMs":30,"platformMs":20},"redaction":{"policyVersion":"wire-redaction/1","maskedCount":3,"unknownDropped":0}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-calls/{callId}/body/{side}":{"get":{"operationId":"opsDownloadSupplierCallBody","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_download_supplier_call_body","class":"read","autonomy":"L0","reason":"Raw supplier payloads; never to the AI plane."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Download one call's full masked request or response body (v1.25.0)","description":"connect-v1 1.25.0 (OD-898). The whole stored body of one side, masked exactly as in GET /ops/v1/supplier-calls/{callId} but not cut at 2 MB, as an attachment named {callId}-{side}.json, .xml or .txt after bodyFormat. The same guard as the detail route: ops:supplier_wire:read and an active wire grant of this session covering the call (403 PERMISSION_DENIED details.reason MISSING_PERMISSION or UNLOCK_REQUIRED). 404 NOT_FOUND for an unknown or expired callId and for a side with no stored body (capture not BODIES). Audit supplier_wire.download records the operator, the grant, the callId and the side. Cache-Control no-store.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/SupplierCallId"},{"name":"side","in":"path","required":true,"description":"Which body, request or response.","schema":{"type":"string","enum":["request","response"]}}],"responses":{"200":{"description":"The masked body bytes","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"},"Content-Disposition":{"schema":{"type":"string"},"description":"attachment; filename=\"{callId}-{side}.json\" (.xml or .txt after bodyFormat)"}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants/{tenantId}":{"get":{"operationId":"opsGetTenant","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_tenant","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenantId) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"One tenant (platform metadata)","description":"Tenant data only under an ACTIVE break-glass grant (FEAT-017).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsTenantDetail"},"examples":{"ok":{"summary":"200 — The tenant","value":{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","tier":"GROWTH","entitlementVersion":7,"createdAt":"2026-03-02T10:00:00Z","standingReason":null,"approvalRef":"APR-0091","usage":{"period":"2026-09","searches":182000,"orders":912,"apiCalls":410000},"breakGlassActive":false}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants/{tenantId}:activate":{"post":{"operationId":"opsActivateTenant","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_activate_tenant","class":"commit-lite","autonomy":"L1","reason":"Changes tenant state (D-60 activation to LIVE) for a caller-chosen tenantId and needs a second OPS_ADMIN approval; tenant state is never an agent action and the tenant must come only from the credential."},"tags":["operator"],"x-audience":"operator","summary":"Ask for a tenant's activation (D-60; a second OPS_ADMIN approves)","description":"Added in v1.6.5; the backend has served it since the operator realm landed. Requests activation, it does not activate. The operator needs ops:tenants:write (OPS_ENGINEER or OPS_ADMIN). The body is a reason (8 to 500 characters); Idempotency-Key is required. Answers 202 with the id of a new PENDING TENANT_ACTIVATE approval (see /ops/v1/approvals); a second OPS_ADMIN approves it at /ops/v1/approvals/{id}:approve with step-up MFA, and the lifecycle change records the approver's operator id (FEAT-024 AC-024-04). This request carries no stepUpCode and no recent-MFA check; the second factor is on the approval. A missing or short reason is 400 VALIDATION details.field reason; a ticketRef over 100 characters is 400 VALIDATION details.field ticketRef (since backend !126); an unknown tenant is 404 NOT_FOUND details.reason TENANT_NOT_FOUND. Audited as ops.approval.request in the platform and tenant slices.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsReasonRequest"},"examples":{"request":{"summary":"Request","value":{"reason":"Pilot passed certification; go live Monday","ticketRef":"OPS-2301"}}}}}},"responses":{"202":{"description":"Accepted — an approval is waiting for a second OPS_ADMIN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApprovalRequested"},"examples":{"pending":{"summary":"202 — activation approval APR-0095","value":{"approvalId":"APR-0095"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/operators:invite":{"post":{"operationId":"opsInviteOperator","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_invite_operator","class":"commit-lite","autonomy":"L1","reason":"Changes users, invitations, roles, permissions or entitlements; access control stays with a human."},"tags":["operator"],"x-audience":"operator","summary":"Invite an operator (a second OPS_ADMIN approves; v1.6.7)","description":"Added in v1.6.7 (IMP-P2-160, DR-55; operator provisioning on UAT and PROD). OPS_ADMIN. Requests an invite, it does not send one. The body is the e-mail, the roles, a reason (8 to 500 characters), an optional ticketRef and the step-up proof (stepUpCode or stepUpAssertion, exactly one; a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, STEP_UP_LOCKED when locked out); Idempotency-Key is required. Answers 202 with the id of a new PENDING approval of kind OPERATOR_INVITE (see /ops/v1/approvals); a second OPS_ADMIN approves it at /ops/v1/approvals/{id}:approve with step-up MFA, and approval issues the invite, which the invitee accepts at POST /ops/v1/auth/invites/accept. An e-mail that already belongs to an operator is 409 CONFLICT with details.reason OPERATOR_EXISTS (v1.6.8); that check runs only AFTER the step-up proof has succeeded, so only a re-verified admin can learn that an address belongs to an operator (validation 400 first, then 403 step-up, then 409). Audited as ops.approval.request, and the audit row carries no address.\n","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsOperatorInviteRequest"},"examples":{"request":{"summary":"Request (step-up required)","value":{"email":"new.engineer@mondee.example","roles":["OPS_ENGINEER"],"reason":"New platform engineer joins the on-call rota","ticketRef":"OPS-2330","stepUpCode":"000000"}}}}}},"responses":{"202":{"description":"Accepted — an approval is waiting for a second OPS_ADMIN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApprovalRequested"},"examples":{"pending":{"summary":"202 — invite approval APR-0096","value":{"approvalId":"APR-0096"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"CONFLICT with details.reason OPERATOR_EXISTS — the e-mail already belongs to an operator (v1.6.8). Answered only after a successful step-up, never before it.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"operatorExists":{"summary":"409 CONFLICT — an operator with this e-mail exists","value":{"code":"CONFLICT","message":"An operator with this email exists.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OPERATOR_EXISTS"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/operators":{"get":{"operationId":"opsListOperators","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_list_operators","class":"read","autonomy":"L0","reason":"Operator roster with personal emails, grantable roles and per-account second-factor enrolment status; access-control data that stays with human operators and must not enter an agent context."},"tags":["operator"],"x-audience":"operator","summary":"Operators","description":"connect-v1 1.12.0 (OD-880; FEAT-024 §5.8). Every operator: email, roles, status, whether a second factor is enrolled, last sign-in. `roles` lists the roles that can be granted. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[],"responses":{"200":{"description":"Operators","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsOperatorList"},"examples":{"ok":{"summary":"200 — Operators","value":{"operators":[{"operatorId":"op_7vhjysunadvojubvx72lgswx","email":"asha@ops.example","roles":["OPS_ADMIN"],"status":"ACTIVE","mfaEnrolled":false,"lastLoginAt":"2026-10-06T22:40:00Z","createdAt":"2026-10-01T09:00:00Z","disabledAt":null}],"roles":["OPS_VIEWER","OPS_ENGINEER","OPS_ADMIN","OPS_SECURITY","OPS_FINANCE"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/operators/{operatorId}/roles":{"put":{"operationId":"opsSetOperatorRoles","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_set_operator_roles","class":"commit-lite","autonomy":"L1","reason":"Changes users, invitations, roles, permissions or entitlements; access control stays with a human."},"tags":["operator"],"x-audience":"operator","summary":"Change an operator's roles","description":"connect-v1 1.12.0. Replaces the operator's roles (one or more of the five). Never your own (403 SELF_ACTION); a concurrent change is 409 VERSION_MOVED. ops:operators:manage (OPS_ADMIN). Idempotency-Key required; `reason` 8-500 characters; audited in the platform slice.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"operatorId","in":"path","required":true,"description":"The operator acted on — never the caller (403 SELF_ACTION).","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsOperatorRolesChange"}}}},"responses":{"200":{"description":"Change an operator's roles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsOperator"},"examples":{"ok":{"summary":"200 — Change an operator's roles","value":{"operatorId":"op_7vhjysunadvojubvx72lgswx","email":"asha@ops.example","roles":["OPS_ADMIN"],"status":"ACTIVE","mfaEnrolled":false,"lastLoginAt":"2026-10-06T22:40:00Z","createdAt":"2026-10-01T09:00:00Z","disabledAt":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/operators/{operatorId}:disable":{"post":{"operationId":"opsDisableOperator","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_disable_operator","class":"commit-lite","autonomy":"L1","reason":"Changes users, invitations, roles, permissions or entitlements; access control stays with a human."},"tags":["operator"],"x-audience":"operator","summary":"Disable an operator","description":"connect-v1 1.12.0. The operator can no longer sign in, and every session it holds ends. Never your own. ops:operators:manage (OPS_ADMIN). Idempotency-Key required; `reason` 8-500 characters; audited in the platform slice.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"operatorId","in":"path","required":true,"description":"The operator acted on — never the caller (403 SELF_ACTION).","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsReason"}}}},"responses":{"200":{"description":"Disable an operator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsOperator"},"examples":{"ok":{"summary":"200 — Disable an operator","value":{"operatorId":"op_7vhjysunadvojubvx72lgswx","email":"asha@ops.example","roles":["OPS_ADMIN"],"status":"DISABLED","mfaEnrolled":false,"lastLoginAt":"2026-10-06T22:40:00Z","createdAt":"2026-10-01T09:00:00Z","disabledAt":"2026-10-07T03:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/operators/{operatorId}/sessions:revoke":{"post":{"operationId":"opsRevokeOperatorSessions","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_revoke_operator_sessions","class":"commit-lite","autonomy":"L1","reason":"Credentials, keys, secrets, sessions or sign-in; an agent never handles authentication material."},"tags":["operator"],"x-audience":"operator","summary":"End an operator's sessions","description":"connect-v1 1.12.0. Ends every session the operator holds; it can sign in again. Never your own (use /ops/v1/auth/sessions). ops:operators:sessions (OPS_ADMIN, OPS_SECURITY). Idempotency-Key required; `reason` 8-500 characters; audited in the platform slice.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"operatorId","in":"path","required":true,"description":"The operator acted on — never the caller (403 SELF_ACTION).","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsReason"}}}},"responses":{"200":{"description":"End an operator's sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSessionsRevoked"},"examples":{"ok":{"summary":"200 — End an operator's sessions","value":{"operatorId":"op_7vhj","revoked":2}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/finance/summary":{"get":{"operationId":"opsFinanceSummary","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_finance_summary","class":"read","autonomy":"L0","example":"{\"environment\":\"PRODUCTION\",\"from\":\"2026-09-30\",\"to\":\"2026-10-07\"}"},"tags":["operator"],"x-audience":"operator","summary":"Order value by customer","description":"connect-v1 1.12.0. Over the window and environment (default PRODUCTION, 7 days): orders and the value of confirmed orders per customer and currency, from the order metadata OD-877 reads (integer minor units, never converted), and every supplier contract on file. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"environment","in":"query","required":false,"description":"Which side of the pairing (07 §5). Default PRODUCTION.","schema":{"$ref":"#/components/schemas/InsightsEnvironment"}},{"name":"from","in":"query","required":false,"description":"Start of the window (ISO date or instant). Default: seven days before `to`.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the window (exclusive). Default: now.","schema":{"type":"string"}}],"responses":{"200":{"description":"Order value by customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsFinanceSummary"},"examples":{"ok":{"summary":"200 — Order value by customer","value":{"environment":"PRODUCTION","from":"2026-09-30T00:00:00Z","to":"2026-10-07T00:00:00Z","totals":[{"currency":"INR","orders":14,"valueMinor":4250000}],"byCustomer":[{"tenantId":"TEN-ACME","currency":"INR","orders":14,"confirmed":11,"valueMinor":4250000}],"truncated":false,"contracts":[{"supplierCode":"SUP-HTL-EXAMPLE","contractRef":"CTR-SV-2026","registryStatus":"ACTIVE","moneyModels":["MERCHANT"],"effectiveFrom":"2026-01-01","commissionSchedule":[{"domain":"HTL","commissionBps":1200}],"l2bCap":500,"sla":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24},"updatedAt":"2026-10-07T03:00:00Z","updatedBy":"ops:op_7vhj"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/suppliers/{supplierCode}/contract":{"get":{"operationId":"opsGetSupplierContract","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_get_supplier_contract","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"Supplier contract","description":"connect-v1 1.12.0. The contract on file; none is 404 NO_CONTRACT_ON_FILE. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"supplierCode","in":"path","required":true,"description":"A supplier registry code, SUP-<DOMAIN>-<NAME>.","schema":{"type":"string"}}],"responses":{"200":{"description":"Supplier contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierContractRecord"},"examples":{"ok":{"summary":"200 — Supplier contract","value":{"supplierCode":"SUP-HTL-EXAMPLE","contractRef":"CTR-SV-2026","registryStatus":"ACTIVE","moneyModels":["MERCHANT"],"effectiveFrom":"2026-01-01","commissionSchedule":[{"domain":"HTL","commissionBps":1200}],"l2bCap":500,"sla":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24},"updatedAt":"2026-10-07T03:00:00Z","updatedBy":"ops:op_7vhj"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"opsLoadSupplierContract","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_load_supplier_contract","class":"commit-money","autonomy":"L1","reason":"Sets the supplier's commercial terms that settlement is computed from; the proposal flow accepts only orders.create and orders.cancel at submit (DR-44), so no proposal can carry it."},"tags":["operator"],"x-audience":"operator","summary":"Load a supplier contract","description":"connect-v1 1.12.0 (OD-880). Loads (replaces) the supplier's contract: what the supplier console's Commercial area shows and what its SLA scorecard measures against. Also audited in the supplier's own slice. ops:finance:write (OPS_FINANCE). Idempotency-Key required; `reason` 8-500 characters; audited in the platform slice.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"supplierCode","in":"path","required":true,"description":"A supplier registry code, SUP-<DOMAIN>-<NAME>.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierContractLoad"}}}},"responses":{"200":{"description":"Load a supplier contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierContractRecord"},"examples":{"ok":{"summary":"200 — Load a supplier contract","value":{"supplierCode":"SUP-HTL-EXAMPLE","contractRef":"CTR-SV-2026","registryStatus":"ACTIVE","moneyModels":["MERCHANT"],"effectiveFrom":"2026-01-01","commissionSchedule":[{"domain":"HTL","commissionBps":1200}],"l2bCap":500,"sla":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24},"updatedAt":"2026-10-07T03:00:00Z","updatedBy":"ops:op_7vhj"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/suppliers/{supplierCode}/cancellations":{"get":{"operationId":"opsListSupplierCancellations","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_supplier_cancellations","class":"read","autonomy":"L0","example":"{\"supplierCode\":\"SUP-BUS-EXAMPLE\",\"offset\":0,\"limit\":25}"},"tags":["operator"],"x-audience":"operator","summary":"Supplier cancellations, read live (v1.16.0)","description":"connect-v1 1.16.0. The cancellations the supplier holds for the platform's own credential set (MONDEE), read live, newest first as the supplier lists it, each joined to the Apiq order and tenant that hold the booking when there is one — refunds reconciled against the supplier's books. No personal data. ops:read. offset from 0, limit 1-100 (default 25); nextOffset is absent on the last page. 404 NOT_FOUND details.reason SUPPLIER_NOT_DEPLOYED; 409 CONFLICT REPORT_NOT_SUPPORTED or CREDENTIAL_SET_NOT_SERVING. First supplier: SUP-BUS-EXAMPLE (GET /cancellations).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"supplierCode","in":"path","required":true,"description":"A supplier registry code, SUP-<DOMAIN>-<NAME>.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"One page of the supplier's cancellations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierCancellationsPage"},"examples":{"ok":{"summary":"200 — Example Bus cancellations","value":{"supplierCode":"SUP-BUS-EXAMPLE","credentialSet":"MONDEE","cancellations":[{"cancellationId":"103633","supplierLocator":"8657007","state":"success","method":"api","createdAt":"2026-10-10T00:55:32.094Z","charged":{"amount":2807,"currency":"USD"},"orderRef":"MO-sUuNU4-MLN9gIzvV_favRA","tenantId":"TEN-4417"}],"nextOffset":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/suppliers/{supplierCode}/statements":{"get":{"operationId":"opsListSupplierStatements","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_list_supplier_statements","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"Supplier statements","description":"connect-v1 1.12.0. The supplier's published statements, newest period first. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"supplierCode","in":"path","required":true,"description":"A supplier registry code, SUP-<DOMAIN>-<NAME>.","schema":{"type":"string"}}],"responses":{"200":{"description":"Supplier statements","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierStatementList"},"examples":{"ok":{"summary":"200 — Supplier statements","value":{"supplierCode":"SUP-HTL-EXAMPLE","statements":[{"statementId":"ST-EXAMPLEVILLAS-2026-08","supplierCode":"SUP-HTL-EXAMPLE","period":"2026-08","moneyModel":"MERCHANT","direction":"REMITTANCE_TO_SUPPLIER","status":"PUBLISHED","remittanceTotal":{"amount":49200,"currency":"EUR"},"lines":[{"lineId":"L-2608-001","supplierBookingRef":"SB-1","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR"},"provisional":false}],"payoutHold":null,"publishedAt":"2026-10-07T03:00:00Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"opsPublishSupplierStatement","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_publish_supplier_statement","class":"commit-money","autonomy":"L1","reason":"Publishes a settlement statement the supplier is paid against; the proposal flow accepts only orders.create and orders.cancel at submit (DR-44), so no proposal can carry it."},"tags":["operator"],"x-audience":"operator","summary":"Publish a settlement statement","description":"connect-v1 1.12.0 (OD-880). Publishes a statement the supplier console's Settlement area reads and reconciles. One currency per statement; remittanceTotal is computed here from the non-provisional lines, never sent. A published statement is never rewritten: the same statementId again is 409 STATEMENT_EXISTS. statementId defaults to ST-<NAME>-<period>. ops:finance:write (OPS_FINANCE). Idempotency-Key required; `reason` 8-500 characters; audited in the platform slice.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"supplierCode","in":"path","required":true,"description":"A supplier registry code, SUP-<DOMAIN>-<NAME>.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierStatementPublish"}}}},"responses":{"201":{"description":"Publish a settlement statement","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierStatement"},"examples":{"ok":{"summary":"201 — Publish a settlement statement","value":{"statementId":"ST-EXAMPLEVILLAS-2026-08","supplierCode":"SUP-HTL-EXAMPLE","period":"2026-08","moneyModel":"MERCHANT","direction":"REMITTANCE_TO_SUPPLIER","status":"PUBLISHED","remittanceTotal":{"amount":49200,"currency":"EUR"},"lines":[{"lineId":"L-2608-001","supplierBookingRef":"SB-1","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR"},"provisional":false}],"payoutHold":null,"publishedAt":"2026-10-07T03:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/content/feeds":{"get":{"operationId":"opsContentFeeds","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_content_feeds","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"Supplier content feeds","description":"connect-v1 1.12.0. Every supplier the platform ingests content from, with each feed's last run, last complete pass, rows and status (the catalogue syncs). ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[],"responses":{"200":{"description":"Supplier content feeds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsContentFeeds"},"examples":{"ok":{"summary":"200 — Supplier content feeds","value":{"asOf":"2026-10-07T03:00:00Z","suppliers":[{"supplierCode":"SUP-FNB-EXAMPLE","feeds":[{"feedId":"dining-directory","name":"Restaurant directory","scheduleWindowHours":12,"lastRunAt":"2026-10-06T18:59:31Z","lastDeltaAt":"2026-10-06T19:00:44Z","rowsIngested":66117,"status":"COMPLETE"}]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/developer/overview":{"get":{"operationId":"opsDeveloperOverview","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_developer_overview","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","summary":"API surface and platform credential sets","description":"connect-v1 1.12.0. The routes this deployment serves per API family (read from the running handler mappings: reads are GET, writes the rest) and the platform's own credential set per served supplier with its status and certification. No tenant's set, no secret. ops:read.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[],"responses":{"200":{"description":"API surface and platform credential sets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsDeveloperOverview"},"examples":{"ok":{"summary":"200 — API surface and platform credential sets","value":{"asOf":"2026-10-07T03:00:00Z","routes":[{"family":"/v1","routes":96,"reads":41,"writes":55}],"credentialSets":[{"supplierCode":"SUP-XFR-EXAMPLE","credentialSetId":"MONDEE","environment":"live","status":"ACTIVE","adapterVersion":"1.0.0","certState":"PENDING"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants/{tenantId}/entitlements":{"get":{"operationId":"opsGetEntitlements","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_entitlements","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenantId) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"A tenant's current entitlements","description":"The latest version (what /v1/capabilities is generated from).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementSet"},"examples":{"ok":{"summary":"200 — Current","value":{"tenantId":"TEN-ACME","version":7,"products":{"HTL":{"search":true,"reprice":true,"orders":true,"cancel":true},"FLT":{"search":true,"orders":false}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot","previousVersion":6}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"opsPublishEntitlements","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_publish_entitlements","class":"commit-lite","autonomy":"L1","reason":"Publishes a new entitlement version (the capabilities a tenant may use) for a caller-chosen tenantId under mandatory operator step-up MFA; permission/tenant-state writes stay with a human."},"tags":["operator"],"x-audience":"operator","summary":"Publish a new entitlement version","description":"OPS_ADMIN; step-up MFA (stepUpCode, or since v1.6.7 stepUpAssertion; exactly one, DR-54). A stale baseVersion is 409 CONFLICT details.reason VERSION_MISMATCH. Emits ops.entitlements.published and tenant.entitlements.changed.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementPublishRequest"},"examples":{"request":{"summary":"Request","value":{"baseVersion":7,"products":{"HTL":{"search":true,"orders":true}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"reason":"Flights search for the pilot","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementSet"},"examples":{"ok":{"summary":"200 — Published","value":{"tenantId":"TEN-ACME","version":8,"products":{"HTL":{"search":true,"reprice":true,"orders":true,"cancel":true},"FLT":{"search":true,"orders":false}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot","previousVersion":7}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT VERSION_MISMATCH","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionMismatch":{"summary":"409 CONFLICT VERSION_MISMATCH","value":{"code":"CONFLICT","message":"Entitlements moved to version 8; re-read and edit again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERSION_MISMATCH","currentVersion":8}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants/{tenantId}/entitlements/versions":{"get":{"operationId":"opsListEntitlementVersions","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_list_entitlement_versions","class":"read","autonomy":"L0","reason":"Takes a tenant argument (tenantId) from the caller; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"Entitlement version history","description":"Newest first.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Versions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementVersionList"},"examples":{"ok":{"summary":"200 — Versions","value":{"versions":[{"version":7,"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/tenants/{tenantId}/entitlements/versions/{version}:rollback":{"post":{"operationId":"opsRollbackEntitlements","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_rollback_entitlements","class":"commit-lite","autonomy":"L1","reason":"Re-publishes an earlier entitlement version for a caller-chosen tenantId under mandatory operator step-up MFA; permission/tenant-state writes stay with a human."},"tags":["operator"],"x-audience":"operator","summary":"Roll back to a version (publishes it as a new version)","description":"OPS_ADMIN; step-up MFA re-verification is required (stepUpCode or, since v1.6.7, stepUpAssertion, exactly one, DR-54; required since v1.6.4; FEAT-024 §3 goal 2, plan row IMP-P2-191). A missing, wrong or doubled proof is 403 PERMISSION_DENIED with details.reason STEP_UP_FAILED, the same as entitlements publish, approvals approve and switch flips. A ticketRef over 100 characters is 400. Emits ops.entitlements.rolled_back.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsRollbackRequest"},"examples":{"request":{"summary":"Request (step-up required)","value":{"reason":"Version 7 broke the pilot's hotel flow","ticketRef":"OPS-2210","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Rolled back","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementSet"},"examples":{"ok":{"summary":"200 — Rolled back","value":{"tenantId":"TEN-ACME","version":9,"products":{"HTL":{"search":true,"reprice":true,"orders":true,"cancel":true},"FLT":{"search":true,"orders":false}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Version 7 broke the pilot's hotel flow","previousVersion":8}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/approvals":{"get":{"operationId":"opsListApprovals","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_approvals","class":"read","autonomy":"L0","example":"{\"state\":\"PENDING\",\"kind\":\"PROD_ROLLBACK\"}"},"tags":["operator"],"x-audience":"operator","summary":"The approval queue","description":"Filter by state and kind.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"state","in":"query","required":false,"description":"PENDING | APPROVED | REJECTED | EXPIRED | EXECUTED","schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"description":"An ops_approval kind","schema":{"type":"string"}}],"responses":{"200":{"description":"Approvals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApprovalList"},"examples":{"ok":{"summary":"200 — Approvals","value":{"approvals":[{"id":"APR-0091","kind":"TENANT_ACTIVATE","requesterId":"op_51d2e0","scope":{"tenantId":"TEN-ACME"},"reason":"Live cert passed; D-60 activation","ticketRef":"OPS-2210","state":"PENDING","approverId":null,"decidedAt":null,"requestedAt":"2026-10-01T15:00:00Z","expiresAt":"2026-10-03T15:00:00Z","effect":{"tenantStatus":"LIVE"}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/approvals/{id}:approve":{"post":{"operationId":"opsApprove","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_approve","class":"commit-money","autonomy":"L1","reason":"Approves or rejects; a human decision, never an agent's (DR-38: the plane never approves, rejects or commits)."},"tags":["operator"],"x-audience":"operator","summary":"Approve — approver ≠ requester, the kind's role, step-up MFA","description":"OPS_SECURITY alone approves BREAK_GLASS; OPS_ADMIN activation, offboarding and OPERATOR_INVITE (v1.6.7; approving issues the invite); OPS_FINANCE PROD purge and rollback. Step-up MFA is required (stepUpCode or stepUpAssertion, exactly one; v1.6.7, DR-54); a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED. A decided or expired item is 409 CONFLICT details.reason NOT_PENDING; the requester deciding is 409 APPROVER_IS_REQUESTER. MCP_OPERATOR_GRANT (connect-v1 1.26.0, ADR-C10 Phase 4, DR-68) is approved by OPS_SECURITY alone (permission ops:approvals:mcp_grant); approving activates the operator MCP grant request, whose client receives its code through the first operator's consent screen; it expires with the request (aikonic.mcp.operator.approval-ttl, default 30 minutes). An MCP grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED. GO_LIVE_REVIEW (connect-v1 1.63.0, PROPOSAL, FEAT-030 §18d; amended 2026-10-29, same version) is approved by the TENANT_ACTIVATE approver; approving re-checks the tenant's go-live prerequisites (409 CONFLICT PREREQUISITES_OPEN, details.open) and the test calls (409 CONFLICT GO_LIVE_TEST_CALLS_MISSING, details.missingTestCalls, until the test_calls check is passed for every requested product); on either the approval stays PENDING and nothing changes. Rejecting it (:reject) asks the tenant for changes.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApproveRequest"},"examples":{"request":{"summary":"Request","value":{"comment":"Checked the cert report","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Approved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApproval"},"examples":{"ok":{"summary":"200 — Approved","value":{"id":"APR-0091","kind":"TENANT_ACTIVATE","requesterId":"op_51d2e0","scope":{"tenantId":"TEN-ACME"},"reason":"Live cert passed; D-60 activation","ticketRef":"OPS-2210","state":"EXECUTED","approverId":"op_3c9a71","decidedAt":"2026-10-02T08:00:00Z","requestedAt":"2026-10-01T15:00:00Z","expiresAt":"2026-10-03T15:00:00Z","effect":{"tenantStatus":"LIVE"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT APPROVER_IS_REQUESTER; for a GO_LIVE_REVIEW (v1.63.0 go-live review) GO_LIVE_TEST_CALLS_MISSING (details.missingTestCalls) or PREREQUISITES_OPEN (details.open)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"approverIsRequester":{"summary":"409 CONFLICT APPROVER_IS_REQUESTER","value":{"code":"CONFLICT","message":"You requested this; another operator must decide.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVER_IS_REQUESTER"}}},"goLiveTestCallsMissing":{"summary":"409 CONFLICT GO_LIVE_TEST_CALLS_MISSING — the tenant has not made every test call for every requested product (v1.63.0 go-live review)","value":{"code":"CONFLICT","message":"The tenant has not finished its sandbox test calls; approve once they are all seen.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GO_LIVE_TEST_CALLS_MISSING","missingTestCalls":[{"product":"FLT","verbs":["orders","cancel"]}]}}},"goLivePrerequisitesOpen":{"summary":"409 CONFLICT PREREQUISITES_OPEN — a go-live prerequisite no longer holds (v1.63.0 go-live review)","value":{"code":"CONFLICT","message":"The tenant's go-live prerequisites are no longer met; reject to ask for changes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PREREQUISITES_OPEN","open":["business_verification"]}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/approvals/{id}:reject":{"post":{"operationId":"opsReject","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_reject","class":"commit-money","autonomy":"L1","reason":"Approves or rejects; a human decision, never an agent's (DR-38: the plane never approves, rejects or commits)."},"tags":["operator"],"x-audience":"operator","summary":"Reject with a reason and step-up MFA","description":"Approver ≠ requester as on :approve. Since v1.6.7 (DR-53, IMP-P2-195) the body is OpsRejectRequest, which carries the step-up proof as :approve does (stepUpCode or stepUpAssertion, exactly one). A missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED (details.field stepUpCode); an operator locked out of step-up is STEP_UP_LOCKED. A ticketRef over 100 characters is 400. :activate keeps OpsReasonRequest. Rejecting an MCP_OPERATOR_GRANT (connect-v1 1.26.0) ends the operator MCP grant request DENIED and the client receives access_denied. An MCP grant-derived token is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsRejectRequest"},"examples":{"request":{"summary":"Request (step-up required)","value":{"reason":"No live cert report attached","ticketRef":"OPS-2210","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApproval"},"examples":{"ok":{"summary":"200 — Rejected","value":{"id":"APR-0091","kind":"TENANT_ACTIVATE","requesterId":"op_51d2e0","scope":{"tenantId":"TEN-ACME"},"reason":"Live cert passed; D-60 activation","ticketRef":"OPS-2210","state":"REJECTED","approverId":"op_3c9a71","decidedAt":"2026-10-02T08:00:00Z","requestedAt":"2026-10-01T15:00:00Z","expiresAt":"2026-10-03T15:00:00Z","effect":{"tenantStatus":"LIVE"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT APPROVER_IS_REQUESTER","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"approverIsRequester":{"summary":"409 CONFLICT APPROVER_IS_REQUESTER","value":{"code":"CONFLICT","message":"You requested this; another operator must decide.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVER_IS_REQUESTER"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/switches":{"get":{"operationId":"opsListSwitches","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_list_switches","class":"read","autonomy":"L0","reason":"The scope filter takes 'GLOBAL or a tenantId' from the caller, so it reads another tenant's kill-switch state by argument; the tenant must come only from the verified credential."},"tags":["operator"],"x-audience":"operator","summary":"Kill switches","description":"Filter by scope.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"scope","in":"query","required":false,"description":"GLOBAL or a tenantId","schema":{"type":"string"}}],"responses":{"200":{"description":"Switches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSwitchList"},"examples":{"ok":{"summary":"200 — Switches","value":{"switches":[{"key":"supplier.example-hotels","state":"OFF","scope":"GLOBAL","reason":null,"changedBy":null,"changedAt":null,"requiresApproval":false}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/switches/{key}":{"put":{"operationId":"opsFlipSwitch","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_flip_switch","class":"commit-lite","autonomy":"L1","reason":"Flips a runtime kill switch (the first gate every MCP call walks) under mandatory operator step-up MFA; an agent must never change the switch that governs agents."},"tags":["operator"],"x-audience":"operator","summary":"Flip a kill switch (audited, reversible)","description":"Applied now (200 PlatformSwitch) or, for a PROD money-path switch, parked for a second approver (202 PlatformSwitchPending). Read through a shared cache with a 5-second TTL. Step-up MFA is required (stepUpCode or stepUpAssertion, exactly one; v1.6.7, DR-54); otherwise 403 PERMISSION_DENIED details.reason STEP_UP_FAILED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSwitchFlip"},"examples":{"request":{"summary":"Request","value":{"state":"ON","scope":"GLOBAL","reason":"Example Hotels book timeouts; take it out of fan-out","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSwitch"},"examples":{"ok":{"summary":"200 — Applied","value":{"key":"supplier.example-hotels","state":"ON","scope":"GLOBAL","reason":"Example Hotels book timeouts; take it out of fan-out","changedBy":"op_3c9a71","changedAt":"2026-10-02T08:05:00Z","requiresApproval":false}}}}}},"202":{"description":"Parked for a second approver","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSwitchPending"},"examples":{"pending":{"summary":"202 — money-path switch, approval APR-0094","value":{"approvalId":"APR-0094"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/platform-suppliers":{"get":{"operationId":"opsListPlatformSuppliers","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_list_platform_suppliers","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Every deployed supplier adapter and its platform credential set (v1.17.0)","description":"connect-v1 1.17.0 (OD-883). One row per registered supplier adapter: the material a landing must carry, the platform set's status, whether it serves traffic, where it was declared (deployment config or this console), the landed material's field names and version, and the endpoint hosts it may bind to on this rung. Never a secret value. Needs ops:read.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The platform suppliers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierList"},"examples":{"ok":{"summary":"200 — Example Bus landed, not yet serving","value":{"suppliers":[{"supplierCode":"SUP-BUS-EXAMPLE","domains":["BUS"],"materialFields":[{"name":"endpoint.host","required":true,"secret":false},{"name":"public_token","required":true,"secret":true},{"name":"private_token","required":true,"secret":true}],"status":"CERTIFYING","serving":false,"source":"CONSOLE","material":{"fields":["private_token","public_token"],"version":1,"landedAt":"2026-10-09T10:15:00Z","landedBy":"op_7vhjysun"},"endpointHosts":["api.bus-supplier.example"]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/platform-suppliers/{supplierCode}":{"get":{"operationId":"opsGetPlatformSupplier","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_get_platform_supplier","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"One platform supplier (v1.17.0)","description":"connect-v1 1.17.0 (OD-883). 404 NOT_FOUND when no adapter of this supplier is deployed. Needs ops:read.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PlatformSupplierCode"}],"responses":{"200":{"description":"The platform supplier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplier"},"examples":{"ok":{"summary":"200 — Example Bus serving","value":{"supplierCode":"SUP-BUS-EXAMPLE","domains":["BUS"],"materialFields":[{"name":"endpoint.host","required":true,"secret":false},{"name":"public_token","required":true,"secret":true},{"name":"private_token","required":true,"secret":true}],"status":"ACTIVE","serving":true,"source":"CONSOLE","material":{"fields":["private_token","public_token"],"version":1,"landedAt":"2026-10-09T10:15:00Z","landedBy":"op_7vhjysun"},"endpointHosts":["api.bus-supplier.example"],"updatedAt":"2026-10-09T10:16:00Z","updatedBy":"op_7vhjysun"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/platform-suppliers/{supplierCode}/credentials":{"put":{"operationId":"opsLandPlatformCredentials","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_land_platform_credentials","class":"commit-lite","autonomy":"L1","reason":"Credentials, keys, secrets, sessions or sign-in; an agent never handles authentication material."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Land the platform's credential material and endpoint host for a supplier (v1.17.0)","description":"connect-v1 1.17.0 (OD-883). Writes the material, with endpoint.host = endpointHost and endpoint.scheme = https, to the secret store at aikonic/platform/{supplierCode}/MONDEE (a new version) and adds the host to this supplier's endpoint allowlist on this rung. A supplier at NOT_CONFIGURED becomes CERTIFYING (landed, not serving); an ACTIVE set stays ACTIVE and its next call reads the new material (rotation). Needs ops:suppliers:credentials (OPS_ENGINEER, OPS_ADMIN) and step-up (stepUpCode or stepUpAssertion, exactly one; otherwise 403 PERMISSION_DENIED details.reason STEP_UP_FAILED). 400 VALIDATION details.reason: MATERIAL_FIELD_MISSING | MATERIAL_FIELD_UNKNOWN (details.field), ENDPOINT_IN_MATERIAL, ENDPOINT_HOST_INVALID, REASON_INVALID. 409 CONFLICT details.reason FIELDS_UNKNOWN when the platform does not know this adapter's material fields. The values are never echoed, logged, audited or stored outside the secret store; audit ops.platform_supplier.credentials_land records field names, the version and the host.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PlatformSupplierCode"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformCredentialsLanding"}}}},"responses":{"200":{"description":"Landed; the supplier as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplier"},"examples":{"landed":{"summary":"200 — landed, CERTIFYING until activated","value":{"supplierCode":"SUP-BUS-EXAMPLE","domains":["BUS"],"materialFields":[{"name":"endpoint.host","required":true,"secret":false},{"name":"public_token","required":true,"secret":true},{"name":"private_token","required":true,"secret":true}],"status":"CERTIFYING","serving":false,"source":"CONSOLE","material":{"fields":["private_token","public_token"],"version":1,"landedAt":"2026-10-09T10:15:00Z","landedBy":"op_7vhjysun"},"endpointHosts":["api.bus-supplier.example"],"updatedAt":"2026-10-09T10:16:00Z","updatedBy":"op_7vhjysun"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/platform-suppliers/{supplierCode}/status":{"put":{"operationId":"opsSetPlatformSupplierStatus","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_set_platform_supplier_status","class":"commit-lite","autonomy":"L1","reason":"Puts the platform credential set in or out of the supply pool for every tenant; needs ops:suppliers:credentials plus step-up and on PROD opens a two-person PLATFORM_SUPPLIER_ACTIVATE approval."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Activate a platform supplier, or take it out of service (v1.17.0)","description":"connect-v1 1.17.0 (OD-883). status ACTIVE puts the platform set in the pool; CERTIFYING takes it out of service at once. ACTIVE needs landed material and at least one endpoint host, else 409 CONFLICT details.reason NOT_READY with details.missing MATERIAL | ENDPOINT_HOST. On a rung listed in aikonic.ops.platform-suppliers.approval-rungs (default PROD) ACTIVE opens a PLATFORM_SUPPLIER_ACTIVATE approval (202) that a DIFFERENT operator holding ops:approvals:money approves at POST /ops/v1/approvals/{id}:approve. Needs ops:suppliers:credentials and step-up. Audit ops.platform_supplier.status.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PlatformSupplierCode"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierStatusChange"}}}},"responses":{"200":{"description":"Applied; the supplier as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplier"},"examples":{"active":{"summary":"200 — activated on SANDBOX","value":{"supplierCode":"SUP-BUS-EXAMPLE","domains":["BUS"],"materialFields":[{"name":"endpoint.host","required":true,"secret":false},{"name":"public_token","required":true,"secret":true},{"name":"private_token","required":true,"secret":true}],"status":"ACTIVE","serving":true,"source":"CONSOLE","material":{"fields":["private_token","public_token"],"version":1,"landedAt":"2026-10-09T10:15:00Z","landedBy":"op_7vhjysun"},"endpointHosts":["api.bus-supplier.example"],"updatedAt":"2026-10-09T10:16:00Z","updatedBy":"op_7vhjysun"}}}}}},"202":{"description":"Parked for a second approver","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierPending"},"examples":{"pending":{"summary":"202 — PROD activation, approval APR-0131","value":{"approvalId":"APR-0131"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/platform-suppliers/{supplierCode}/account-profile":{"get":{"operationId":"opsGetPlatformSupplierAccountProfile","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_get_platform_supplier_account_profile","class":"read","autonomy":"L0","reason":"The account profile carries the agency's contact PII (customer and user objects); it is never handed to an agent."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Read the account profile the platform sends to a supplier (v1.20.0)","description":"connect-v1 1.20.0. The account profile is the JSON block (customer and user objects) by which an upstream supplier (a hotel supplier, a flight supplier) identifies the agency in each upstream request body. It is a platform-level record per supplier, stored in MongoDB collection platform_supplier_account_profile, and at call time the backend merges it into the supplier's credential material under the field name the adapter reads. The profile is NOT secret: it is stored in plain MongoDB and readable with ops:read. It contains contact PII, so it is never logged; the response is Cache-Control no-store. 404 NOT_FOUND details.reason SUPPLIER_NOT_DEPLOYED when no adapter of this supplier is deployed, ACCOUNT_PROFILE_NOT_SET when none has been set (the adapter then uses any account profile carried in the credential material). Needs ops:read.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PlatformSupplierCode"}],"responses":{"200":{"description":"The supplier's account profile","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierAccountProfile"},"examples":{"tripPro":{"summary":"200 — the Example Air flights account profile (synthetic values)","value":{"supplierCode":"SUP-AIR-EXAMPLE","profile":{"Customer":{"customerId":"00000000-0000-4000-8000-000000000001","dkNumber":"T000000001","consolidatorId":1000,"siteId":"00000000-0000-4000-8000-000000000002","currency":"USD","userType":"Agent"},"UserInfo":{"userId":"00000000-0000-4000-8000-000000000003","userType":"Agent","platform":"Example Air"},"SearchUserInfo":{"LoggedInUserId":"00000000-0000-4000-8000-000000000003"},"OtherInfo":{"DkNumber":"T000000001","ConsolidatorId":1000,"AgentLocation":"US"}},"version":2,"updatedAt":"2026-10-16T09:30:00Z","updatedBy":"op_00000001"}},"hotels":{"summary":"200 — a hotel supplier's account profile (synthetic values)","value":{"supplierCode":"SUP-HTL-EXAMPLE2","profile":{"customer":{"customerId":"00000000-0000-4000-8000-000000000011","dkNumber":"T000000002","name":"Example Travel Agency","email":"agency@example.com","phone":"+10000000000","currency":"USD"},"user":{"userId":"00000000-0000-4000-8000-000000000012","firstName":"Test","lastName":"Agent","email":"agent@example.com"},"UserInfo":{"userId":"00000000-0000-4000-8000-000000000012","userType":"Agent","platform":"ExamplePlatform"}},"version":1,"updatedAt":"2026-10-16T09:00:00Z","updatedBy":"op_00000001"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"NOT_FOUND — no adapter of this supplier is deployed, or no account profile is set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"supplierNotDeployed":{"summary":"404 NOT_FOUND SUPPLIER_NOT_DEPLOYED","value":{"code":"NOT_FOUND","message":"No adapter of SUP-AIR-EXAMPLE is deployed on this rung","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_DEPLOYED","supplierCode":"SUP-AIR-EXAMPLE"}}},"accountProfileNotSet":{"summary":"404 NOT_FOUND ACCOUNT_PROFILE_NOT_SET","value":{"code":"NOT_FOUND","message":"No account profile is set for SUP-HTL-EXAMPLE2","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"ACCOUNT_PROFILE_NOT_SET","supplierCode":"SUP-HTL-EXAMPLE2"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"opsSetPlatformSupplierAccountProfile","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_set_platform_supplier_account_profile","class":"commit-lite","autonomy":"L1","reason":"Rewrites the agency identity every upstream request of this supplier carries, for every tenant, and the profile holds contact PII; needs ops:suppliers:credentials plus step-up."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Set the account profile the platform sends to a supplier (v1.20.0)","description":"connect-v1 1.20.0. Replaces the supplier's account profile (a new version) in MongoDB collection platform_supplier_account_profile. The profile is NOT secret: it is stored in plain MongoDB and readable with ops:read. It contains contact PII, so it is never logged, and the audit record ops.platform_supplier.account_profile.set carries the supplier, the version, the reason and the ticket, never the profile. It takes effect within the platform-supplier refresh interval (about 15 s). When set, it overrides any account profile carried in the credential material. It changes identity data, not money, so it never parks for a second approver. Needs ops:suppliers:credentials (OPS_ENGINEER, OPS_ADMIN) and step-up (stepUpCode or stepUpAssertion, exactly one; otherwise 403 PERMISSION_DENIED details.reason STEP_UP_FAILED). 400 VALIDATION details.field profile or reason, details.reason PROFILE_TOO_LARGE (over 16384 bytes serialised), PROFILE_SHAPE (a hotel supplier needs a customer object; a flight supplier needs a Customer object with a non-blank customerId) or REASON_INVALID. 404 NOT_FOUND details.reason SUPPLIER_NOT_DEPLOYED. The same Idempotency-Key with a different body is 409 IDEMPOTENCY_CONFLICT.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PlatformSupplierCode"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierAccountProfileUpdate"}}}},"responses":{"200":{"description":"Set; the account profile as it now stands","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierAccountProfile"},"examples":{"set":{"summary":"200 — the Example Air flights account profile set (synthetic values)","value":{"supplierCode":"SUP-AIR-EXAMPLE","profile":{"Customer":{"customerId":"00000000-0000-4000-8000-000000000001","dkNumber":"T000000001","consolidatorId":1000,"siteId":"00000000-0000-4000-8000-000000000002","currency":"USD","userType":"Agent"},"UserInfo":{"userId":"00000000-0000-4000-8000-000000000003","userType":"Agent","platform":"Example Air"},"SearchUserInfo":{"LoggedInUserId":"00000000-0000-4000-8000-000000000003"},"OtherInfo":{"DkNumber":"T000000001","ConsolidatorId":1000,"AgentLocation":"US"}},"version":2,"updatedAt":"2026-10-16T09:30:00Z","updatedBy":"op_00000001"}}}}}},"400":{"description":"VALIDATION — the profile is too large or the wrong shape, or the reason is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"profileTooLarge":{"summary":"400 VALIDATION PROFILE_TOO_LARGE — over 16384 bytes serialised","value":{"code":"VALIDATION","message":"The account profile is over 16384 bytes serialised","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"profile","reason":"PROFILE_TOO_LARGE"}}},"profileShape":{"summary":"400 VALIDATION PROFILE_SHAPE — SUP-AIR-EXAMPLE without a Customer.customerId","value":{"code":"VALIDATION","message":"SUP-AIR-EXAMPLE needs a Customer object with a non-blank customerId","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"profile","reason":"PROFILE_SHAPE"}}},"reasonInvalid":{"summary":"400 VALIDATION REASON_INVALID — reason under 8 or over 500 characters","value":{"code":"VALIDATION","message":"reason must be 8 to 500 characters","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"reason","reason":"REASON_INVALID"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"NOT_FOUND SUPPLIER_NOT_DEPLOYED — no adapter of this supplier is deployed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"supplierNotDeployed":{"summary":"404 NOT_FOUND SUPPLIER_NOT_DEPLOYED","value":{"code":"NOT_FOUND","message":"No adapter of SUP-AIR-EXAMPLE is deployed on this rung","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_DEPLOYED","supplierCode":"SUP-AIR-EXAMPLE"}}}}}}},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/datasets":{"get":{"operationId":"opsListSupplierDatasets","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_supplier_datasets","class":"read","autonomy":"L0","example":"{\"status\":\"STALE\",\"vertical\":\"experiences\",\"credentialSet\":\"MONDEE\"}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"The supplier × dataset overview grid — freshness, counts, next run, errors, quota (FEAT-023 §5 item 1)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/datasets — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"query","schema":{"type":"string"}},{"name":"dataset","in":"query","schema":{"type":"string"}},{"name":"vertical","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"},"description":"FRESH | STALE | FAILING | PAUSED"},{"name":"credentialSet","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The grid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataDatasets"},"examples":{"grid":{"summary":"200 — two cells, one stale","value":{"cells":[{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","status":"FRESH","dataAsOf":"2026-10-01T08:15:00Z","ageSeconds":2700,"maxStaleness":"P1D","recordCount":184210,"deltaSinceLastRun":298,"errorsLast24h":0,"nextRunAt":"2026-10-01T10:15:00Z","quotaUsedToday":0.21,"currentSnapshotId":"snap_01JA7K9"},{"supplierId":"SUP-HTL-EXAMPLE4","dataset":"availability_calendar","credentialSet":"MONDEE","status":"STALE","dataAsOf":"2026-09-29T02:10:00Z","ageSeconds":197400,"maxStaleness":"P1D","recordCount":2210400,"errorsLast24h":3,"nextRunAt":"2026-10-02T02:00:00Z","quotaUsedToday":0}],"generatedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/profiles/{supplierId}/{dataset}":{"get":{"operationId":"opsGetSupplierDataProfile","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_get_supplier_data_profile","class":"read","autonomy":"L0","example":"{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"credentialSet\":\"MONDEE\"}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Read one supplier × dataset sync profile; the ETag is the version PUT must present (FEAT-023 §6)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/profiles/{supplierId}/{dataset} — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"credentialSet","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The profile","headers":{"ETag":{"schema":{"type":"string"},"description":"The profile version; send it back as If-Match on PUT"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfile"},"examples":{"viator":{"summary":"200 — Example Activities catalogue, weekly FULL + hourly INCREMENTAL","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"INCREMENTAL","cron":"15 * * * *"}],"limits":{"concurrency":4,"quotaShare":0.3},"freshness":{"ttl":"PT6H","maxStaleness":"P1D","staleWhileRevalidate":"PT1H","liveFallback":"STALE_MARKER"},"language":"en","currency":"USD","enabled":true,"version":7,"updatedAt":"2026-10-01T08:00:00Z","estimate":{"calls":1850,"durationSeconds":2400,"quotaShare":0.21}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"opsUpdateSupplierDataProfile","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_update_supplier_data_profile","class":"commit-lite","autonomy":"L1","example":"{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"ifMatch\":\"\\\"7\\\"\",\"idempotencyKey\":\"8f1c2a9e-4b6d-4a1f-9e3c-5d7b2a8c6f10\",\"body\":{\"modes\":[{\"mode\":\"FULL\",\"cron\":\"0 3 * * 0\"},{\"mode\":\"WINDOW\",\"cron\":\"0 2 * * *\",\"window\":{\"fromOffsetDays\":0,\"toOffsetDays\":120,\"sliceDays\":7}}],\"enabled\":true}}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Replace the editable part of a profile under optimistic concurrency (FEAT-023 §6; supplier_data:manage)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of PUT /admin/v1/supplier-data/profiles/{supplierId}/{dataset} — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:manage: OPS_ENGINEER or OPS_ADMIN; a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM. Idempotency-Key is REQUIRED (03 §7); a missing one is 400 VALIDATION. Audited as supplier_data.profile.update in the PLATFORM slice (actor kind operator, the operator id). If-Match must carry the ETag the last GET answered; a stale one is 409 CONFLICT details.reason VERSION_MISMATCH and nothing is written. The answer carries the new ETag. Audit-logged with actor and the fields changed.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"If-Match","in":"header","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfileUpdate"},"examples":{"nightlyWindow":{"summary":"Move the schedules window to nightly, next 120 days","value":{"modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"WINDOW","cron":"0 2 * * *","window":{"fromOffsetDays":0,"toOffsetDays":120,"sliceDays":7}}],"enabled":true}}}}}},"responses":{"200":{"description":"Updated","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataProfile"},"examples":{"updated":{"summary":"200 — version 8","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"WINDOW","cron":"0 2 * * *","window":{"fromOffsetDays":0,"toOffsetDays":120,"sliceDays":7}}],"enabled":true,"version":8,"updatedAt":"2026-10-01T09:05:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason VERSION_MISMATCH (If-Match is stale) | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionMismatch":{"summary":"409 CONFLICT VERSION_MISMATCH — re-read and re-apply","value":{"code":"CONFLICT","message":"The profile changed since you read it (version 8, you sent 7)","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERSION_MISMATCH","currentVersion":8}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/runs":{"get":{"operationId":"opsListSupplierDataRuns","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_supplier_data_runs","class":"read","autonomy":"L0","example":"{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"status\":\"FAILED\",\"page\":1,\"pageSize\":20}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Run history, newest first (FEAT-023 §5 item 4)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/runs — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"query","schema":{"type":"string"}},{"name":"dataset","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"A page of runs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRunList"},"examples":{"page":{"summary":"200 — one running, one succeeded","value":{"runs":[{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","startedAt":"2026-10-01T09:00:00Z"},{"runId":"run_01JA6ZQ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"FULL","trigger":"SCHEDULE","status":"SUCCEEDED","startedAt":"2026-09-27T03:00:00Z","endedAt":"2026-09-27T03:41:00Z","snapshotId":"snap_01JA6ZZ"}],"page":{"total":2}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"opsStartSupplierDataRun","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_start_supplier_data_run","class":"commit-lite","autonomy":"L1","example":"{\"idempotencyKey\":\"3b7e9d14-6c2a-4f8b-a1e5-0d9c7b3f2a68\",\"body\":{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"mode\":\"INCREMENTAL\"}}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Run now — FULL, INCREMENTAL or WINDOW with an optional narrower scope (FEAT-023 §6; supplier_data:run)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/runs — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:run: OPS_ENGINEER or OPS_ADMIN; a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM. Idempotency-Key is REQUIRED (03 §7); a missing one is 400 VALIDATION. Audited as supplier_data.run.start in the PLATFORM slice (actor kind operator, the operator id). Answers 202 with the run QUEUED; progress is read at GET /admin/v1/supplier-data/runs/{runId}. A run already RUNNING for the same profile is 409 CONFLICT details.reason RUN_IN_PROGRESS. Requires an Idempotency-Key.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRunRequest"},"examples":{"incremental":{"summary":"Incremental now","value":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","mode":"INCREMENTAL"}}}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"queued":{"summary":"202 — queued","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","triggeredBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","status":"QUEUED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT with details.reason RUN_IN_PROGRESS | IDEMPOTENCY_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"runInProgress":{"summary":"409 CONFLICT RUN_IN_PROGRESS","value":{"code":"CONFLICT","message":"A run is already in progress for SUP-EXP-EXAMPLE/catalog_product","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"RUN_IN_PROGRESS","runId":"run_01JA7K3"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/runs/{runId}":{"get":{"operationId":"opsGetSupplierDataRun","x-mcp":{"expose":"resource","toolset":"operator","name":"ops_get_supplier_data_run","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"One run — status, progress and statistics (FEAT-023 §6)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/runs/{runId} — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"running":{"summary":"200 — running, ETA 15 minutes","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","stats":{"pages":120,"records":{"added":310,"updated":4120,"removed":12},"quotaUsed":640,"errors":0,"etaSeconds":900},"startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/runs/{runId}:cancel":{"post":{"operationId":"opsCancelSupplierDataRun","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_cancel_supplier_data_run","class":"commit-lite","autonomy":"L1"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Cancel a QUEUED, RUNNING or PAUSED run; what landed stays, no snapshot is cut (FEAT-023 §6)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/runs/{runId}:cancel — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:run: OPS_ENGINEER or OPS_ADMIN; a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM. Idempotency-Key is REQUIRED (03 §7); a missing one is 400 VALIDATION. Audited as supplier_data.run.cancel in the PLATFORM slice (actor kind operator, the operator id).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, CANCELLED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"cancelled":{"summary":"200 — cancelled","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"CANCELLED","startedAt":"2026-10-01T09:00:00Z","endedAt":"2026-10-01T09:12:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/runs/{runId}:pause":{"post":{"operationId":"opsPauseSupplierDataRun","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_pause_supplier_data_run","class":"commit-lite","autonomy":"L1","example":"{\"runId\":\"run_01JA7K3\",\"idempotencyKey\":\"c41d8e27-9f3b-4a6c-b2d5-7e1a0f4c9b83\"}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Pause a RUNNING run at its next page boundary (FEAT-023 §6)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/runs/{runId}:pause — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:run: OPS_ENGINEER or OPS_ADMIN; a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM. Idempotency-Key is REQUIRED (03 §7); a missing one is 400 VALIDATION. Audited as supplier_data.run.pause in the PLATFORM slice (actor kind operator, the operator id).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, PAUSED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"paused":{"summary":"200 — paused","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"PAUSED","startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/runs/{runId}:resume":{"post":{"operationId":"opsResumeSupplierDataRun","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_resume_supplier_data_run","class":"commit-lite","autonomy":"L1"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Resume a PAUSED run from its cursor (FEAT-023 §6)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/runs/{runId}:resume — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:run: OPS_ENGINEER or OPS_ADMIN; a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM. Idempotency-Key is REQUIRED (03 §7); a missing one is 400 VALIDATION. Audited as supplier_data.run.resume in the PLATFORM slice (actor kind operator, the operator id).\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The run, RUNNING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataRun"},"examples":{"resumed":{"summary":"200 — running again","value":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","status":"RUNNING","startedAt":"2026-10-01T09:00:00Z"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/snapshots/{supplierId}/{dataset}":{"get":{"operationId":"opsListSupplierDataSnapshots","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_supplier_data_snapshots","class":"read","autonomy":"L0"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"The versions of a dataset, newest first (FEAT-023 §5 item 5)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/snapshots/{supplierId}/{dataset} — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"credentialSet","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"A page of snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataSnapshotList"},"examples":{"two":{"summary":"200 — current and its parent","value":{"snapshots":[{"snapshotId":"snap_01JA7K9","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-10-01T08:15:00Z","recordCount":184210,"checksum":"sha256:9f2a1c","mappingVersion":"2026-09-28.2","parentSnapshotId":"snap_01JA6ZZ","current":true},{"snapshotId":"snap_01JA6ZZ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-09-27T03:41:00Z","recordCount":183912,"checksum":"sha256:1c9f2a","mappingVersion":"2026-09-28.2","current":false}],"page":{"total":2}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/snapshots/{supplierId}/{dataset}/diff":{"get":{"operationId":"opsDiffSupplierDataSnapshots","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_diff_supplier_data_snapshots","class":"read","autonomy":"L0","example":"{\"supplierId\":\"SUP-EXP-EXAMPLE\",\"dataset\":\"catalog_product\",\"from\":\"snap_01JA6ZZ\",\"to\":\"snap_01JA7K9\",\"page\":1,\"pageSize\":50}"},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"The difference between two snapshots — counts and a page of changed keys (FEAT-023 §5 item 5)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of GET /admin/v1/supplier-data/snapshots/{supplierId}/{dataset}/diff — same parameters and shapes, acting on PLATFORM credential sets only (a tenant's BYOC set answers 404, never 403). Needs supplier_data:read: every operator role (OPS_VIEWER, OPS_ENGINEER, OPS_ADMIN, OPS_SECURITY, OPS_FINANCE); a missing permission is 403 PERMISSION_DENIED details.reason MISSING_PERMISSION. A tenant-realm token is 401 UNAUTHENTICATED details.reason TOKEN_WRONG_REALM.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":true,"schema":{"type":"string"}},{"name":"to","in":"query","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/PageSizeQuery"}],"responses":{"200":{"description":"The diff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataDiff"},"examples":{"diff":{"summary":"200 — 310 added, 4120 updated, 12 removed","value":{"from":"snap_01JA6ZZ","to":"snap_01JA7K9","added":310,"updated":4120,"removed":12,"changes":[{"supplierKey":"5010SYDNEY","change":"UPDATED","fields":["price_from","images"]}],"page":{"total":4442}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/snapshots/{supplierId}/{dataset}/{snapshotId}:rollback":{"post":{"operationId":"opsRollbackSupplierDataSnapshot","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_rollback_supplier_data_snapshot","class":"commit-lite","autonomy":"L1","reason":"Needs a fresh human step-up proof (stepUpCode or stepUpAssertion, DR-54) in the body, refuses an AI-marked session outright, and on PROD opens a two-person PROD_ROLLBACK ops approval, so an agent cannot carry it."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Make an earlier snapshot of a PLATFORM credential set current; on PROD, parked for a second operator (202)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/snapshots/{supplierId}/{dataset}/{snapshotId}:rollback, on PLATFORM credential sets only (a tenant's BYOC set answers 404). Needs supplier_data:rollback (OPS_ENGINEER or OPS_ADMIN). A fresh step-up proof is required in the body (stepUpCode or stepUpAssertion, exactly one; DR-54), otherwise 403 PERMISSION_DENIED details.reason STEP_UP_FAILED; an AI-marked session is refused. Two-person rule (FEAT-023 §9, FEAT-024 §9): on a rung that needs a second approver (PROD, or an undeclared rung) nothing is rolled back — the request opens a PROD_ROLLBACK ops approval (scope.key the profile id, supplierId|dataset|credentialSet) and answers 202 OpsApprovalRequested. A DIFFERENT operator holding ops:approvals:money (OPS_FINANCE) approves it at POST /ops/v1/approvals/{id}:approve (step-up again) and the rollback runs then, stamped with the approval id; the requester deciding is 409 APPROVER_IS_REQUESTER. Below PROD the rollback runs at once (200). There is no X-Approval-Token here: the operator realm's own queue is the second pair of hands. Idempotency-Key is REQUIRED. Audited as supplier_data.snapshot.rollback (and ops.approval.request when parked) in the PLATFORM slice.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataset","in":"path","required":true,"schema":{"type":"string"}},{"name":"snapshotId","in":"path","required":true,"schema":{"type":"string"}},{"name":"credentialSet","in":"query","schema":{"type":"string"},"description":"The platform credential set; may be left out when the supplier × dataset has exactly one"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierDataRollbackRequest"},"examples":{"badDump":{"summary":"Roll back a truncated ingest","value":{"reason":"08:15 run ingested a truncated dump (record count fell 31%)","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Rolled back now (a rung that needs no second approver)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataSnapshot"},"examples":{"rolledBack":{"summary":"200 — parent is current again","value":{"snapshotId":"snap_01JA6ZZ","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-09-27T03:41:00Z","recordCount":183912,"checksum":"sha256:1c9f2a","mappingVersion":"2026-09-28.2","current":true}}}}}},"202":{"description":"Parked for a second operator (a PROD_ROLLBACK approval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApprovalRequested"},"examples":{"parked":{"summary":"202 — PROD, approval opened","value":{"approvalId":"apr_7QK2M4XW9TBA3RCDJ5LN6PYE"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/supplier-data/purge":{"post":{"operationId":"opsPurgeSupplierData","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_purge_supplier_data","class":"commit-lite","autonomy":"L1","reason":"Purges cache layers for a platform credential set; needs a fresh human step-up proof (DR-54), refuses an AI-marked session, and on PROD opens a two-person PROD_PURGE approval."},"tags":["operator"],"x-audience":"operator","x-aikonic-credential":"operator-session","summary":"Purge cache layers for a PLATFORM credential set; on PROD, parked for a second operator (202)","description":"connect-v1 1.13.0 (OD-881). The operator-realm twin of POST /admin/v1/supplier-data/purge, on PLATFORM credential sets only; it never touches the dataset store (L3). Needs supplier_data:purge (OPS_ENGINEER or OPS_ADMIN). reason is REQUIRED here (8 to 500 characters), and so is a fresh step-up proof (stepUpCode or stepUpAssertion, exactly one; DR-54), otherwise 403 PERMISSION_DENIED details.reason STEP_UP_FAILED; an AI-marked session is refused. Two-person rule (FEAT-023 §9, FEAT-024 §9): on a rung that needs a second approver (PROD, or an undeclared rung) nothing is purged — the request opens a PROD_PURGE ops approval (scope.key the supplierId) and answers 202 OpsApprovalRequested. A DIFFERENT operator holding ops:approvals:money (OPS_FINANCE) approves it at POST /ops/v1/approvals/{id}:approve (step-up again) and the purge runs then, stamped with the approval id; the requester deciding is 409 APPROVER_IS_REQUESTER. Below PROD the purge runs at once (200). Idempotency-Key is REQUIRED. Audited as supplier_data.purge (and ops.approval.request when parked) in the PLATFORM slice.\n","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsSupplierDataPurgeRequest"},"examples":{"oneKey":{"summary":"Purge one product's cache entries","value":{"scope":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","supplierKeys":["5010SYDNEY"]},"reason":"Stale image set reported by the supplier","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"Purged now (a rung that needs no second approver)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierDataPurgeResult"},"examples":{"purged":{"summary":"200 — entries removed per layer","value":{"purged":{"L1":1,"L2":1}}}}}}},"202":{"description":"Parked for a second operator (a PROD_PURGE approval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpsApprovalRequested"},"examples":{"parked":{"summary":"202 — PROD, approval opened","value":{"approvalId":"apr_3MZ8D2KQ5VNE7WHXB4TC9GJA"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enquiries":{"post":{"operationId":"createEnquiry","x-mcp":{"expose":"tool","toolset":"public","name":"create_enquiry","class":"commit-lite","autonomy":"L1"},"tags":["enquiries"],"summary":"Send a lead to a lead-only supplier: private dining and other enquiries (v1.7.0)","description":"Added in connect-v1 v1.7.0 (dining proposal §5.5, OD-874). An enquiry is a lead, not a booking: nothing is priced, held or paid, and no order is created. The platform validates the lead the way the supplier would, sends it ONCE and answers the enquiry with its state. SUBMITTED carries the supplier's lead id as supplierReference; UNCONFIRMED means the supplier did not answer, the lead may have reached the venue and is never re-sent (warning <SUPPLIER>_LEAD_UNCONFIRMED). Requires orders:write and the domain tab entitlement (search:dining for FNB). Idempotency-Key is required; a replay answers the stored 201. Personal data in contact travels to the supplier and is never echoed.","parameters":[{"$ref":"#/components/parameters/SandboxTest"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquiryCreateRequest"},"examples":{"privateDining":{"summary":"FNB — an Example Dining private-dining lead for 24 guests","value":{"context":{"domain":"FNB"},"message":{"intent":{"productId":"prov:dining:100123","contact":{"firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100"},"eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","flexibleDate":false,"clientReference":"acme-pd-0042"}}}}}}}},"responses":{"201":{"description":"The enquiry, SUBMITTED or UNCONFIRMED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquiryResponse"},"examples":{"submitted":{"summary":"201 SUBMITTED — the restaurant acknowledged the lead","value":{"context":{"domain":"FNB","action":"on_enquiry","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":830,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"enquiryRef":"EN-4KD-8QT-2ZP","state":"SUBMITTED","domain":"FNB","productId":"prov:dining:100123","supplier":"EXAMPLEDINING","supplierReference":"lead-7731","eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","clientReference":"acme-pd-0042","createdAt":"2026-10-05T17:04:11Z","updatedAt":"2026-10-05T17:04:12Z"}}},"unconfirmed":{"summary":"201 UNCONFIRMED — not answered; never re-sent","value":{"context":{"domain":"FNB","action":"on_enquiry","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":10020,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","warnings":[{"code":"EXAMPLEDINING_LEAD_UNCONFIRMED","message":"The lead was not answered; it may have reached the restaurant and is never re-sent.","supplier":"EXAMPLEDINING"}]},"message":{"enquiryRef":"EN-9TR-3MV-6LC","state":"UNCONFIRMED","domain":"FNB","productId":"prov:dining:100123","supplier":"EXAMPLEDINING","eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","clientReference":"acme-pd-0042","createdAt":"2026-10-05T17:04:11Z","updatedAt":"2026-10-05T17:04:12Z"}}}}}}},"400":{"description":"VALIDATION or UNKNOWN_DOMAIN (03 §9). A lead the supplier would refuse is refused before anything is sent: details.reason <SUPPLIER>_LEAD_INVALID with details.invalid naming the fields; a domain whose suppliers take no enquiries is details.reason ENQUIRY_NOT_SUPPORTED; a card number in any text field is details.reason INSTRUMENT_DATA_REFUSED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"leadInvalid":{"summary":"400 VALIDATION <SUPPLIER>_LEAD_INVALID — refused before it was sent","value":{"code":"VALIDATION","message":"Example Dining requires the guest names and phone, a future date, a time, a party of at least one and a documented event type.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent","reason":"EXAMPLEDINING_LEAD_INVALID","invalid":["eventDate","eventType"]}}},"enquiryNotSupported":{"summary":"400 VALIDATION ENQUIRY_NOT_SUPPORTED — no supplier of this domain takes enquiries","value":{"code":"VALIDATION","message":"No supplier of domain HTL takes enquiries.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"context.domain","reason":"ENQUIRY_NOT_SUPPORTED"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'DINNER'. Did you mean FNB?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["FNB","REST","DINING"]}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"get":{"operationId":"listEnquiries","x-mcp":{"expose":"tool","toolset":"public","name":"list_enquiries","class":"read","autonomy":"L0"},"tags":["enquiries"],"summary":"List the tenant's enquiries, newest first, cursor-paged (v1.7.0)","description":"Added in connect-v1 v1.7.0 (WEB lane). The tenant's enquiries, newest first. Filters: domain (canonical code or alias), status (the enquiry state: SUBMITTED, UNCONFIRMED, FAILED) and createdAfter. Pages by an opaque cursor: pass message.nextCursor back as ?cursor= with the same filters; absent on the last page. A cursor this route did not issue is 400 VALIDATION details.field cursor. Requires orders:read. The contact is never listed.","parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque; the message.nextCursor of the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Page size (1-100); absent = 25","schema":{"type":"integer","minimum":1,"maximum":100},"example":25},{"name":"domain","in":"query","required":false,"description":"Only this domain (code or alias)","schema":{"type":"string"},"example":"FNB"},{"name":"status","in":"query","required":false,"description":"Only enquiries in this state (SUBMITTED, UNCONFIRMED, FAILED)","schema":{"type":"string"},"example":"SUBMITTED"},{"name":"createdAfter","in":"query","required":false,"description":"Only enquiries created after this instant","schema":{"type":"string","format":"date-time"},"example":"2026-10-01T00:00:00Z"}],"responses":{"200":{"description":"One page of enquiries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquiryListResponse"},"examples":{"firstPage":{"summary":"200 — two enquiries and a next cursor","value":{"context":{"domain":"FNB","action":"on_enquiries","version":"2026-09-30","timestamp":"2026-10-05T17:10:00Z","message_id":"msg_d1n1n8","took_ms":35,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"enquiries":[{"enquiryRef":"EN-4KD-8QT-2ZP","state":"SUBMITTED","domain":"FNB","productId":"prov:dining:100123","supplier":"EXAMPLEDINING","supplierReference":"lead-7731","eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","createdAt":"2026-10-05T17:04:11Z","updatedAt":"2026-10-05T17:04:12Z"},{"enquiryRef":"EN-9TR-3MV-6LC","state":"UNCONFIRMED","domain":"FNB","productId":"prov:dining:100123","supplier":"EXAMPLEDINING","eventDate":"2026-12-12","eventTime":"19:00","partySize":18,"eventType":"Birthday","createdAt":"2026-10-04T12:00:00Z","updatedAt":"2026-10-04T12:00:10Z"}],"nextCursor":"ZW5xLXBhZ2UtMg"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/enquiries/{enquiryRef}":{"get":{"operationId":"getEnquiry","x-mcp":{"expose":"resource","toolset":"public","name":"get_enquiry","class":"read","autonomy":"L0"},"tags":["enquiries"],"summary":"Read one enquiry (v1.7.0)","description":"Added in connect-v1 v1.7.0. The enquiry as POST /v1/enquiries answered it, with its current state. Another tenant's ref and an unknown ref are the same 404 NOT_FOUND. Requires orders:read.","parameters":[{"$ref":"#/components/parameters/EnquiryRefPath"}],"responses":{"200":{"description":"The enquiry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquiryResponse"},"examples":{"submitted":{"summary":"200 — a submitted private-dining lead","value":{"context":{"domain":"FNB","action":"on_enquiry","version":"2026-09-30","timestamp":"2026-10-05T17:04:11Z","message_id":"msg_d1n1n6","took_ms":40,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"},"message":{"enquiryRef":"EN-4KD-8QT-2ZP","state":"SUBMITTED","domain":"FNB","productId":"prov:dining:100123","supplier":"EXAMPLEDINING","supplierReference":"lead-7731","eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","clientReference":"acme-pd-0042","createdAt":"2026-10-05T17:04:11Z","updatedAt":"2026-10-05T17:04:12Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/orders/{orderRef}/notes":{"post":{"operationId":"createDiningOrderNote","x-mcp":{"expose":"excluded","toolset":"public","name":"create_dining_order_note","class":"commit-lite","autonomy":"L1","reason":"Writes a partner note onto a confirmed restaurant reservation at the supplier (Example Dining Create Partner Reservation Note v2), sent once; a staff-facing annotation with no agent use case in the spec set yet. Promote to a tool by a later minor if one appears (v1.30.0)."},"tags":["orders"],"summary":"Dining: add a partner note to a confirmed reservation (v1.30.0)","description":"Adds a partner note to the restaurant reservation behind a dining (FNB) order, as the restaurant sees it on the reservation (Example Dining Create Partner Reservation Note v2: POST /v2/booking/{rid}/reservations/{rid}-{confirmation}/notes). A partner note is not a guest-profile note and cannot change notes written by the restaurant's staff. At most 5,000 characters, not blank. Sent once under the Idempotency-Key; a replay answers the first result. An order of another domain, or a dining order on a supplier that has no partner notes, is 400 VALIDATION details.reason DINING_ORDER_ACTION_UNSUPPORTED, before any supplier call. A supplier that has not granted the call to the platform answers 403 at the supplier, surfaced as 403 PERMISSION_DENIED details.reason SUPPLIER_FORBIDDEN (as the partner routes).","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["orders:write"]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOrderNoteRequest"},"examples":{"partnerReference":{"summary":"A partner reference for the restaurant","value":{"note":"Booked through Apiq; the partner reference is MO-ri1IdksWcCAWsPPncUh-mw."}}}}}},"responses":{"201":{"description":"The note as the supplier stored it, with its id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOrderNote"},"examples":{"created":{"summary":"201 — note created","value":{"orderRef":"MO-ri1IdksWcCAWsPPncUh-mw","noteId":"fdaa5f45-f048-4e40-8417-5caf6b743834","note":"Booked through Apiq; the partner reference is MO-ri1IdksWcCAWsPPncUh-mw."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/orders/{orderRef}/notes/{noteId}":{"patch":{"operationId":"updateDiningOrderNote","x-mcp":{"expose":"excluded","toolset":"public","name":"update_dining_order_note","class":"commit-lite","autonomy":"L1","reason":"Replaces or deletes (empty note) a partner note on a restaurant reservation at the supplier (Example Dining Update Partner Reservation Note v2), sent once; no agent use case in the spec set yet (v1.30.0)."},"tags":["orders"],"summary":"Dining: change or delete a partner note (v1.30.0)","description":"Replaces the text of a partner note this platform added to the reservation behind a dining (FNB) order (Example Dining Update Partner Reservation Note v2: PATCH /v2/booking/{rid}/reservations/{rid}-{confirmation}/notes). An empty note deletes it. The noteId is the one POST /v1/dining/orders/{orderRef}/notes answered; a note id that is not this partner's is refused by the supplier (404 NOT_FOUND). At most 5,000 characters. Sent once under the Idempotency-Key. Same 400 and 403 rules as the create.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["orders:write"]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"name":"noteId","in":"path","required":true,"description":"The note id POST /v1/dining/orders/{orderRef}/notes answered","schema":{"type":"string","minLength":1,"maxLength":64}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOrderNoteUpdate"},"examples":{"change":{"summary":"Change the note","value":{"note":"Window table, please."}},"delete":{"summary":"Delete the note (empty text)","value":{"note":""}}}}}},"responses":{"200":{"description":"The note as the supplier now stores it (empty note when deleted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOrderNote"},"examples":{"changed":{"summary":"200 — note changed","value":{"orderRef":"MO-ri1IdksWcCAWsPPncUh-mw","noteId":"fdaa5f45-f048-4e40-8417-5caf6b743834","note":"Window table, please."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/orders/{orderRef}/confirm":{"post":{"operationId":"confirmDiningOrder","x-mcp":{"expose":"excluded","toolset":"public","name":"confirm_dining_order","class":"commit-lite","autonomy":"L1","reason":"Confirms the restaurant reservation at the supplier on the diner's behalf (Example Dining Partner Reservation Confirmation V2), sent once; it states the diner's intent to attend, which an agent may not assert without the diner (v1.30.0)."},"tags":["orders"],"summary":"Dining: confirm the reservation with the restaurant (v1.30.0)","description":"Tells the restaurant that the diner confirms the reservation behind a dining (FNB) order (Example Dining Partner Reservation Confirmation V2: POST /v2/booking/{rid}/reservations/{rid}-{confirmation}/confirm, no body). The order's state does not change; the supplier records the confirmation on the reservation. Sent once under the Idempotency-Key. Same 400 and 403 rules as the note routes.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["orders:write"]}],"parameters":[{"$ref":"#/components/parameters/OrderRefPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The supplier recorded the confirmation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOrderConfirmation"},"examples":{"confirmed":{"summary":"200 — confirmed with the restaurant","value":{"orderRef":"MO-ri1IdksWcCAWsPPncUh-mw","confirmed":true,"restaurantId":"1038007","confirmationNumber":"8255"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests":{"post":{"operationId":"diningPartnerCreateGuest","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_create_guest","class":"commit-lite","autonomy":"L1","reason":"Creates or matches a guest in the restaurant's Example Dining CRM from personal contact data (name, e-mail, phone); this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: create (or match) a restaurant guest (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner. Answers the guest id Example Dining assigns, or the existing guest it matched by e-mail or phone.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestCreateRequest"},"examples":{"guest":{"summary":"A new guest from the partner CRM","value":{"firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","partnerGuestId":"crm-88231"}}}}}},"responses":{"201":{"description":"The guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuest"},"examples":{"created":{"summary":"201 — the guest and its Example Dining id","value":{"guestId":"gst_7Q2K9","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","emailOptIn":true,"tags":["tag_vip"],"updatedAt":"2026-10-04T21:10:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests/{guestId}/loyalty":{"put":{"operationId":"diningPartnerPutGuestLoyalty","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_guest_loyalty","class":"commit-lite","autonomy":"L1","reason":"Sets a guest's loyalty-programme standing in the restaurant's Example Dining CRM, which drives how the host treats the guest; this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: set a guest's loyalty programme standing (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningLoyaltyRequest"},"examples":{"gold":{"summary":"Gold tier, 1,240 points","value":{"programName":"Harbour Rewards","memberId":"HR-55120","tier":"Gold","points":1240,"pointsToNextTier":760,"expiresOn":"2027-06-30"}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/tags":{"get":{"operationId":"diningPartnerListTags","x-mcp":{"expose":"resource","toolset":"partner","name":"dining_partner_list_tags","class":"read","autonomy":"L0","example":"{\"restaurantId\":\"100123\"}"},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: list the restaurant's guest tags (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"}],"responses":{"200":{"description":"The tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningTagList"},"examples":{"tags":{"summary":"Two tags","value":{"items":[{"tagId":"tag_vip","name":"VIP","category":"Guest"},{"tagId":"tag_veg","name":"Vegetarian","category":"Dietary"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/tags/{tagId}":{"put":{"operationId":"diningPartnerPutTag","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_tag","class":"commit-lite","autonomy":"L1","reason":"Creates or renames a guest tag in the restaurant's Example Dining CRM tag vocabulary; this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: create or rename a guest tag (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningTagIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningTagUpsertRequest"},"examples":{"vip":{"summary":"A VIP tag","value":{"name":"VIP","category":"Guest","icon":"star"}}}}}},"responses":{"200":{"description":"The tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningTag"},"examples":{"tag":{"summary":"The tag as stored","value":{"tagId":"tag_vip","name":"VIP","category":"Guest","icon":"star"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"delete":{"operationId":"diningPartnerDeleteTag","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_delete_tag","class":"commit-lite","autonomy":"L1","reason":"Deletes a guest tag from the restaurant's Example Dining CRM, which strips it from every guest who carries it; this is a destructive partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: delete a guest tag (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningTagIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"DELETED — the tag is gone","value":{"status":"DELETED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests/{guestId}/tags":{"put":{"operationId":"diningPartnerPutGuestTags","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_guest_tags","class":"commit-lite","autonomy":"L1","reason":"Replaces a guest's full tag set in the Example Dining CRM (any tag left out is removed); this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: set a guest's tags (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner. The body is the full set; a tag not in it is removed.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestTagsRequest"},"examples":{"vip":{"summary":"Tag the guest VIP and vegetarian","value":{"tagIds":["tag_vip","tag_veg"]}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests/{guestId}/insights":{"put":{"operationId":"diningPartnerPutGuestInsights","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_guest_insights","class":"commit-lite","autonomy":"L1","reason":"Sets the free-text insights a restaurant host sees about a guest in the Example Dining CRM (personal data shown to staff); this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: set the insights the host sees for a guest (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestInsightsRequest"},"examples":{"insights":{"summary":"Two insights","value":{"insights":[{"label":"Lifetime visits","value":"14"},{"label":"Favourite wine","value":"Albarino"}]}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests/{guestId}/properties":{"put":{"operationId":"diningPartnerPutGuestProperties","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_guest_properties","class":"commit-lite","autonomy":"L1","reason":"Writes a hotel-restaurant guest's stay details (personal data) into the Example Dining CRM; this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: set a hotel restaurant guest's stay details (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestPropertiesRequest"},"examples":{"stay":{"summary":"In-house hotel guest","value":{"properties":[{"name":"Harbour Hotel","roomNumber":"1204","checkIn":"2026-11-11","checkOut":"2026-11-14","vip":true}]}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/guests/{guestId}/photo":{"put":{"operationId":"diningPartnerPutGuestPhoto","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_guest_photo","class":"commit-lite","autonomy":"L1","reason":"Uploads a guest's photo (biometric-grade personal data, from a POST /v1/uploads id) to the Example Dining CRM; this is a partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: upload a guest's photo (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner. The image is an uploadId from POST /v1/uploads; Example Dining processes it asynchronously (read the status with GET).","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestPhotoRequest"},"examples":{"photo":{"summary":"A scanned upload","value":{"uploadId":"up_01JA7F3K2Q"}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"ACCEPTED — processing at the supplier","value":{"status":"ACCEPTED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"delete":{"operationId":"diningPartnerDeleteGuestPhoto","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_delete_guest_photo","class":"commit-lite","autonomy":"L1","reason":"Deletes a guest's photo from the restaurant's Example Dining CRM; this is a destructive partner-system write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: delete a guest's photo (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"DELETED — the photo is gone","value":{"status":"DELETED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"get":{"operationId":"diningPartnerGetGuestPhotoStatus","x-mcp":{"expose":"resource","toolset":"partner","name":"dining_partner_get_guest_photo_status","class":"read","autonomy":"L0"},"tags":["dining-partner"],"x-audience":"partner","summary":"CRM: read the status of a guest's photo (v1.7.0)","description":"Example Dining CRM v3 (D2, OD-874). The platform calls Example Dining as the integration partner on behalf of the restaurant the caller is linked to; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/DiningGuestIdPath"}],"responses":{"200":{"description":"The photo status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningGuestPhotoStatus"},"examples":{"active":{"summary":"Processed and shown to the host","value":{"status":"ACTIVE","updatedAt":"2026-10-05T16:58:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/pos/tickets":{"post":{"operationId":"diningPartnerCreatePosTicket","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_create_pos_ticket","class":"commit-lite","autonomy":"L1","reason":"This is the partner POS system reporting a closed check (totals, tax, tip) to Example Dining as the system of record; no spec text lets an agent author POS checks, and a fabricated check would corrupt the restaurant's matched spend data."},"tags":["dining-partner"],"x-audience":"partner","summary":"POS: send a check (v1.7.0)","description":"Example Dining POS API (D2, OD-874): the partner POS reports checks, lists its restaurants and switches a location on or off. Requires dining:partner; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPosTicket"},"examples":{"closedCheck":{"summary":"A closed check for a party of two","value":{"ticketId":"chk-55012","reservationId":"100123-2141","tableNumber":"12","covers":2,"openedAt":"2026-11-12T03:31:00Z","closedAt":"2026-11-12T05:02:00Z","status":"CLOSED","subtotal":{"amount":11800,"currency":"USD"},"tax":{"amount":1020,"currency":"USD"},"tip":{"amount":2360,"currency":"USD"},"total":{"amount":15180,"currency":"USD"},"items":[{"name":"Market fish","quantity":2,"price":{"amount":3800,"currency":"USD"}}]}}}}}},"responses":{"202":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"ACCEPTED — Example Dining queued the check for matching","value":{"status":"ACCEPTED","supplierReference":"chk-55012"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"get":{"operationId":"diningPartnerListPosTickets","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_list_pos_tickets","class":"read","autonomy":"L0","example":"{\"restaurantId\":\"100123\",\"updatedFrom\":\"2026-11-12T00:00:00Z\",\"updatedTo\":\"2026-11-13T00:00:00Z\",\"limit\":100,\"offset\":0}"},"tags":["dining-partner"],"x-audience":"partner","summary":"POS: list checks in a window (bootstrap) (v1.7.0)","description":"Example Dining POS API (D2, OD-874): the partner POS reports checks, lists its restaurants and switches a location on or off. Requires dining:partner; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"name":"updatedFrom","in":"query","required":true,"description":"Inclusive lower bound (UTC)","schema":{"type":"string","format":"date-time"},"example":"2026-11-12T00:00:00Z"},{"name":"updatedTo","in":"query","required":true,"description":"Exclusive upper bound (UTC)","schema":{"type":"string","format":"date-time"},"example":"2026-11-13T00:00:00Z"},{"name":"limit","in":"query","required":false,"description":"Page size (1-1000); absent = 100","schema":{"type":"integer","minimum":1,"maximum":1000},"example":100},{"name":"offset","in":"query","required":false,"description":"Rows to skip; absent = 0","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"One page of checks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPosTicketList"},"examples":{"page":{"summary":"One check, last page","value":{"items":[{"ticketId":"chk-55012","reservationId":"100123-2141","tableNumber":"12","covers":2,"openedAt":"2026-11-12T03:31:00Z","closedAt":"2026-11-12T05:02:00Z","status":"CLOSED","subtotal":{"amount":11800,"currency":"USD"},"tax":{"amount":1020,"currency":"USD"},"tip":{"amount":2360,"currency":"USD"},"total":{"amount":15180,"currency":"USD"},"items":[{"name":"Market fish","quantity":2,"price":{"amount":3800,"currency":"USD"}}]}],"page":{"limit":100,"offset":0,"hasMore":false}}},"currencyUnstated":{"summary":"A check Example Dining stated no currency for: money members absent","value":{"items":[{"ticketId":"chk-55013","reservationId":"100123-2142","covers":4,"openedAt":"2026-11-12T04:00:00Z","status":"CLOSED","currencyStated":false,"items":[{"name":"Market fish","quantity":2}]}],"page":{"limit":100,"offset":0,"hasMore":false}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/pos/restaurants":{"get":{"operationId":"diningPartnerListPosRestaurants","x-mcp":{"expose":"resource","toolset":"partner","name":"dining_partner_list_pos_restaurants","class":"read","autonomy":"L0"},"tags":["dining-partner"],"x-audience":"partner","summary":"POS: list the restaurants linked to the partner POS (v1.7.0)","description":"Example Dining POS API (D2, OD-874): the partner POS reports checks, lists its restaurants and switches a location on or off. Requires dining:partner; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"responses":{"200":{"description":"The linked restaurants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPosRestaurantList"},"examples":{"two":{"summary":"Two linked restaurants","value":{"items":[{"rid":"100123","name":"Harbour Table","partnerLocationId":"LOC-001","status":"ACTIVE"},{"rid":"100456","name":"Harbour Table Uptown","partnerLocationId":"LOC-002","status":"PENDING"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/pos/status":{"patch":{"operationId":"diningPartnerSetPosLocationStatus","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_set_pos_location_status","class":"commit-money","autonomy":"L1","reason":"Switches a restaurant's POS integration on or off and completes delayed-email onboarding (ACTIVE connects the tile), which changes tenant integration state; the proposal flow carries only orders.create and orders.cancel (DR-44), so it cannot be proposal-bound."},"tags":["dining-partner"],"x-audience":"partner","summary":"POS: switch a location on or off (v1.7.0)","description":"Example Dining POS API (D2, OD-874): the partner POS reports checks, lists its restaurants and switches a location on or off. Requires dining:partner; a restaurant the caller has not onboarded is 403 PERMISSION_DENIED details.reason RESTAURANT_NOT_LINKED. Also completes a delayed-email POS onboarding: ACTIVE with the partner location id connects the tile.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPosStatusRequest"},"examples":{"activate":{"summary":"Activate the location","value":{"status":"ACTIVE","partnerLocationId":"LOC-001"}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/sync/reservations":{"get":{"operationId":"diningPartnerSyncReservations","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_sync_reservations","class":"read","autonomy":"L0","example":"{\"restaurantId\":\"100123\",\"scheduledFrom\":\"2026-11-12T00:00\",\"scheduledTo\":\"2026-11-13T00:00\",\"limit\":100,\"offset\":0}"},"tags":["dining-partner"],"x-audience":"partner","summary":"Sync: pull reservations (v1.7.0)","description":"Example Dining Sync API v2 (D2, OD-874): pull the restaurant's reservations, guests and POS data, paged by limit and offset; read until page.hasMore is false. Requires dining:partner; the restaurant must be linked to the caller.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"restaurantId","in":"query","required":true,"description":"The restaurant's supplier id (Example Dining rid)","schema":{"type":"string"},"example":"100123"},{"name":"scheduledFrom","in":"query","required":false,"description":"Restaurant-local lower bound (yyyy-MM-ddTHH:mm)","schema":{"type":"string"},"example":"2026-11-12T00:00"},{"name":"scheduledTo","in":"query","required":false,"description":"Restaurant-local upper bound","schema":{"type":"string"},"example":"2026-11-13T00:00"},{"name":"updatedAfter","in":"query","required":false,"description":"Only rows updated after this instant (UTC)","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"description":"Page size (1-1000); absent = 100","schema":{"type":"integer","minimum":1,"maximum":1000},"example":100},{"name":"offset","in":"query","required":false,"description":"Rows to skip; absent = 0","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"One page of reservations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningSyncReservationList"},"examples":{"page":{"summary":"First page","value":{"items":[{"reservationId":"100123-2141","rid":"100123","guestId":"gst_7Q2K9","scheduledTime":"2026-11-12T19:30","partySize":2,"state":"SEATED","updatedAt":"2026-11-12T03:35:00Z"}],"page":{"limit":100,"offset":0,"hasMore":true,"nextOffset":100}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/sync/guests":{"get":{"operationId":"diningPartnerSyncGuests","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_sync_guests","class":"read","autonomy":"L0","reason":"Bulk export of every guest record a restaurant holds (names, e-mail, phone, opt-ins), built so a partner system can stay in sync (OD-874); exposing it would page guest contact PII into model context with no per-guest purpose."},"tags":["dining-partner"],"x-audience":"partner","summary":"Sync: pull guests (v1.7.0)","description":"Example Dining Sync API v2 (D2, OD-874): pull the restaurant's reservations, guests and POS data, paged by limit and offset; read until page.hasMore is false. Requires dining:partner; the restaurant must be linked to the caller.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"restaurantId","in":"query","required":true,"description":"The restaurant's supplier id (Example Dining rid)","schema":{"type":"string"},"example":"100123"},{"name":"emailOptIn","in":"query","required":false,"description":"Only guests who opted in to e-mail","schema":{"type":"boolean"},"example":true},{"name":"updatedAfter","in":"query","required":false,"description":"Only rows updated after this instant (UTC)","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"description":"Page size (1-1000); absent = 100","schema":{"type":"integer","minimum":1,"maximum":1000},"example":100},{"name":"offset","in":"query","required":false,"description":"Rows to skip; absent = 0","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"One page of guests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningSyncGuestList"},"examples":{"page":{"summary":"Last page","value":{"items":[{"guestId":"gst_7Q2K9","firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100","emailOptIn":true,"tags":["tag_vip"],"updatedAt":"2026-10-04T21:10:00Z"}],"page":{"limit":100,"offset":0,"hasMore":false}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/sync/pos-data":{"get":{"operationId":"diningPartnerSyncPosData","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_sync_pos_data","class":"read","autonomy":"L0","example":"{\"restaurantId\":\"100123\",\"scheduledFrom\":\"2026-11-12T00:00\",\"scheduledTo\":\"2026-11-13T00:00\",\"limit\":100,\"offset\":0}"},"tags":["dining-partner"],"x-audience":"partner","summary":"Sync: pull POS data matched to reservations (v1.7.0)","description":"Example Dining Sync API v2 (D2, OD-874): pull the restaurant's reservations, guests and POS data, paged by limit and offset; read until page.hasMore is false. Requires dining:partner; the restaurant must be linked to the caller.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"restaurantId","in":"query","required":true,"description":"The restaurant's supplier id (Example Dining rid)","schema":{"type":"string"},"example":"100123"},{"name":"scheduledFrom","in":"query","required":true,"description":"Restaurant-local lower bound","schema":{"type":"string"},"example":"2026-11-12T00:00"},{"name":"scheduledTo","in":"query","required":true,"description":"Restaurant-local upper bound","schema":{"type":"string"},"example":"2026-11-13T00:00"},{"name":"limit","in":"query","required":false,"description":"Page size (1-1000); absent = 100","schema":{"type":"integer","minimum":1,"maximum":1000},"example":100},{"name":"offset","in":"query","required":false,"description":"Rows to skip; absent = 0","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"One page of POS data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningSyncPosDataList"},"examples":{"page":{"summary":"One matched check","value":{"items":[{"reservationId":"100123-2141","rid":"100123","ticketId":"chk-55012","total":{"amount":15180,"currency":"USD"},"closedAt":"2026-11-12T05:02:00Z"}],"page":{"limit":100,"offset":0,"hasMore":false}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/menus":{"post":{"operationId":"diningPartnerPublishMenus","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_publish_menus","class":"commit-lite","autonomy":"L1","reason":"Publishes the restaurant's full public menu set to Example Dining (anything not in the body is unpublished); this is public content with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Menus: publish the restaurant's menus (v1.7.0)","description":"Example Dining Menus API publish (D2, OD-874). The full set: what is not in the body is unpublished. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningMenusPublishRequest"},"examples":{"dinner":{"summary":"One structured menu","value":{"menus":[{"menuId":"mnu-dinner","name":"Dinner","currency":"USD","sections":[{"name":"Mains","items":[{"name":"Market fish","description":"Daily catch","price":{"amount":3800,"currency":"USD"}}]}]}]}}}}}},"responses":{"202":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"ACCEPTED — Example Dining queued the menus","value":{"status":"ACCEPTED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/profile":{"put":{"operationId":"diningPartnerUpdateProfileContent","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_update_profile_content","class":"commit-lite","autonomy":"L1","reason":"Replaces the restaurant's public Example Dining profile content; this is a public-content write with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Profile: update the restaurant's profile content (v1.7.0)","description":"Example Dining Profile Content API (D2, OD-874). Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningProfileContentRequest"},"examples":{"profile":{"summary":"Description and links","value":{"description":"Seasonal seafood on the waterfront.","cuisines":["Seafood","Californian"],"dressCode":"Smart casual","parking":"Valet","websiteUrl":"https://harbour-table.example.test","menuUrl":"https://harbour-table.example.test/menu"}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — The supplier applied the write","value":{"status":"APPLIED"}},"fieldNotPublished":{"summary":"Applied; parking and menuUrl not published (<SUPPLIER>_FIELD_NOT_PUBLISHED)","value":{"status":"APPLIED","warnings":[{"code":"EXAMPLEDINING_FIELD_NOT_PUBLISHED","message":"Example Dining's Profile Content has no parking field; it was not sent.","details":{"field":"parking"}},{"code":"EXAMPLEDINING_FIELD_NOT_PUBLISHED","message":"Example Dining's Profile Content has no menu URL field; publish menus instead.","details":{"field":"menuUrl"}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/reviews":{"get":{"operationId":"diningPartnerListReviews","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_list_reviews","class":"read","autonomy":"L0","example":"{\"restaurantId\":\"100123\",\"limit\":5}"},"tags":["dining-partner"],"x-audience":"partner","summary":"Reviews: list a restaurant's reviews (v1.7.0)","description":"Example Dining Reviews API (D2, OD-874). Requires dining:partner; the restaurant must be linked to the caller.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"restaurantId","in":"query","required":true,"description":"The restaurant's supplier id (Example Dining rid)","schema":{"type":"string"},"example":"100123"},{"name":"limit","in":"query","required":false,"description":"How many of the newest reviews (1-100); absent = 20","schema":{"type":"integer","minimum":1,"maximum":100},"example":5}],"responses":{"200":{"description":"The reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerReviewList"},"examples":{"recent":{"summary":"The newest review","value":{"items":[{"reviewId":"OT-100123-2141-1700000001","rid":"100123","overall":5,"ratings":{"food":5,"service":5,"ambience":4,"value":4},"title":"Lovely evening","text":"Great fish and a friendly team.","dinedOn":"2026-09-20","submittedAt":"2026-09-21T09:12:00Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/reviews/summary":{"get":{"operationId":"diningPartnerGetReviewSummary","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_get_review_summary","class":"read","autonomy":"L0","example":"{\"restaurantIds\":\"100123,100456\"}"},"tags":["dining-partner"],"x-audience":"partner","summary":"Reviews: rating summaries for one or more restaurants (v1.7.0)","description":"Example Dining Reviews API (D2, OD-874). Requires dining:partner; the restaurant must be linked to the caller.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"restaurantIds","in":"query","required":true,"description":"Comma-separated rids (at most 50)","schema":{"type":"string"},"example":"100123,100456"}],"responses":{"200":{"description":"The summaries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningReviewSummaryList"},"examples":{"two":{"summary":"Two restaurants","value":{"items":[{"rid":"100123","count":1284,"ratings":{"overall":"4.6","food":"4.7"}},{"rid":"100456","count":310,"ratings":{"overall":"4.3"}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/reviews/{reviewId}/replies":{"post":{"operationId":"diningPartnerReplyToReview","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_reply_to_review","class":"commit-lite","autonomy":"L1","reason":"Posts the restaurant's single public reply to a guest review on Example Dining, which is public and cannot be repeated (one reply per review); this is public content with no spec grant for agent callers (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Reviews: reply to a review (v1.7.0)","description":"Example Dining Reviews API (D2, OD-874). Requires dining:partner; the restaurant must be linked to the caller. One public reply per review.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningReviewIdPath"},{"name":"restaurantId","in":"query","required":true,"description":"The restaurant's supplier id (Example Dining rid)","schema":{"type":"string"},"example":"100123"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningReviewReplyRequest"},"examples":{"thanks":{"summary":"A public reply","value":{"text":"Thank you, we hope to see you again soon."}}}}}},"responses":{"201":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — the reply is published","value":{"status":"APPLIED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/directory":{"get":{"operationId":"diningPartnerDirectory","x-mcp":{"expose":"tool","toolset":"partner","name":"dining_partner_directory","class":"read","autonomy":"L0","example":"{\"privateDining\":true,\"limit\":100,\"offset\":0}"},"tags":["dining-partner"],"x-audience":"partner","summary":"Directory: the restaurants available to the partner (v1.7.0)","description":"Example Dining Directory API (D2, OD-874), paged by limit and offset. A truncated answer carries warning <SUPPLIER>_DIRECTORY_TRUNCATED. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size (1-1000); absent = 100","schema":{"type":"integer","minimum":1,"maximum":1000},"example":100},{"name":"offset","in":"query","required":false,"description":"Rows to skip; absent = 0","schema":{"type":"integer","minimum":0},"example":0},{"name":"privateDining","in":"query","required":false,"description":"Only restaurants that take private-dining leads","schema":{"type":"boolean"}}],"responses":{"200":{"description":"One page of the directory","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningDirectoryPage"},"examples":{"page":{"summary":"First page","value":{"items":[{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US","privateDining":true,"updatedAt":"2026-10-01T00:00:00Z"}],"page":{"limit":100,"offset":0,"hasMore":true,"nextOffset":100}}},"truncated":{"summary":"A truncated page carries <SUPPLIER>_DIRECTORY_TRUNCATED","value":{"items":[{"rid":"100123","restaurantId":"prov:dining:100123","name":"Harbour Table","city":"San Francisco","country":"US","privateDining":true}],"page":{"limit":100,"offset":0,"hasMore":true,"nextOffset":1},"warnings":[{"code":"EXAMPLEDINING_DIRECTORY_TRUNCATED","message":"Example Dining returned fewer rows than asked; continue from nextOffset.","supplier":"EXAMPLEDINING"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/restaurants/{restaurantId}/private-dining/leads":{"post":{"operationId":"diningPartnerCreatePrivateDiningLead","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_create_private_dining_lead","class":"commit-lite","autonomy":"L1","reason":"Sends an event planner's lead to a restaurant's private-dining team exactly once and never re-sends it (an unanswered call is ACCEPTED with <SUPPLIER>_LEAD_UNCONFIRMED); this is a partner-system write with no spec grant for agent callers, and the guest-side equivalent is POST /v1/enquiries (OD-874)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Private dining: send a lead to a restaurant's private-dining team (v1.7.0)","description":"Example Dining Private Dining Lead API (D2, OD-874), for a partner that collects leads itself (an event planner). The guest side of the same call is POST /v1/enquiries. Validated as Example Dining would and sent ONCE; an unanswered call is ACCEPTED with warning <SUPPLIER>_LEAD_UNCONFIRMED and is never re-sent. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerLeadRequest"},"examples":{"birthday":{"summary":"A birthday dinner for 18","value":{"contact":{"firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100"},"eventDate":"2026-12-12","eventTime":"18:30","partySize":18,"eventType":"Birthday","flexibleDate":true}}}}}},"responses":{"201":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"applied":{"summary":"APPLIED — the restaurant acknowledged the lead","value":{"status":"APPLIED","supplierReference":"lead-7732"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/oauth/authorize":{"get":{"operationId":"diningPartnerOauthStart","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_oauth_start","class":"read","autonomy":"L0","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: OAuth connect start, the authorization URL Example Dining redirects a restaurant to (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining sends the restaurant's browser here with redirect_uri and state (and client_id for a standard integration). Example Dining's state is EXAMPLEDINING'S: it generates it (an encoded OTP; rid, origin, partner id) and verifies it on its own callback, so the platform stores it with the onboarding and returns it to Example Dining's redirect_uri byte for byte, never altered or re-minted (collection: POS and Standard Onboarding, OAuth Flow). For its OWN leg (this redirect, the platform sign-in and location picker, then the callback below) the platform generates a separate state: opaque, unguessable, single-use, valid 10 minutes, bound to the onboarding session, Example Dining's state and redirect_uri. It travels as the session parameter of the callback and is answered as state in the JSON form. redirect_uri must be one of the Example Dining redirect URIs the platform registered; otherwise 400 VALIDATION details.field redirect_uri and nothing is redirected. A missing Example Dining state is 400 VALIDATION details.field state. PKCE is optional: when Example Dining sends code_challenge (S256) the token exchange then requires code_verifier. Anonymous by design: the state is the binding.","security":[],"parameters":[{"name":"redirect_uri","in":"query","required":true,"description":"Example Dining's callback URL","schema":{"type":"string","format":"uri"},"example":"https://ot.example.test/redirect"},{"name":"state","in":"query","required":true,"description":"Example Dining's opaque state (encoded OTP; rid, origin, partner id)","schema":{"type":"string","maxLength":2048},"example":"syn-ot-state-0001"},{"name":"client_id","in":"query","required":false,"description":"The client id the platform issued to Example Dining (standard integrations)","schema":{"type":"string"},"example":"syn-ot-client"},{"name":"code_challenge","in":"query","required":false,"description":"PKCE (optional): the S256 challenge; the token exchange then needs code_verifier","schema":{"type":"string","maxLength":128}},{"name":"code_challenge_method","in":"query","required":false,"description":"PKCE: S256 only","schema":{"type":"string","enum":["S256"]}}],"responses":{"200":{"description":"With Accept: application/json, the redirect target as JSON instead of a 302 (for a client that drives the browser itself)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingRedirect"},"examples":{"signIn":{"summary":"Where the browser goes next","value":{"location":"https://console.example/dining/onboarding?session=onb_01JB2C3D","state":"syn-platform-onboarding-state-0001"}}}}}},"302":{"description":"To the platform sign-in and location picker","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/dining/partner/onboarding/oauth/callback":{"get":{"operationId":"diningPartnerOauthCallback","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_oauth_callback","class":"read","autonomy":"L0","reason":"Anonymous (security: []) sign-in, signup, callback or redirect route; there is no principal for the autonomy ladder or the tenant."},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: OAuth connect callback, back to Example Dining with an authorization code (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Where the platform sign-in returns once the tenant user has chosen the location. session is the platform's server-generated state from authorize: a missing, unknown, expired, already used or mismatched one (not the session this browser started, or bound to a different redirect_uri) is 400 VALIDATION details.field session, details.reason ONBOARDING_STATE_INVALID, and nothing is redirected or minted. On success the platform spends the state, mints a single-use authorization code (60 s) bound to it, the chosen location and the redirect_uri, and answers 302 to Example Dining's redirect_uri with code and Example Dining's OWN state, unchanged. Example Dining then exchanges the code at POST /v1/dining/partner/onboarding/oauth/token (with code_verifier when it used PKCE).","security":[],"parameters":[{"name":"session","in":"query","required":true,"description":"The onboarding session the start bound","schema":{"type":"string"},"example":"onb_01JB2C3D"},{"name":"locationId","in":"query","required":true,"description":"The partner location the user chose","schema":{"type":"string"},"example":"LOC-001"}],"responses":{"200":{"description":"With Accept: application/json, the redirect target as JSON instead of a 302","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingRedirect"},"examples":{"toOpenTable":{"summary":"Back to Example Dining's redirect_uri","value":{"location":"https://ot.example.test/redirect?code=syn-ot-auth-code-0001&state=syn-ot-state-0001"}}}}}},"302":{"description":"To Example Dining's redirect_uri with code and state","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/dining/partner/onboarding/oauth/token":{"post":{"operationId":"diningPartnerOauthToken","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_oauth_token","class":"commit-money","autonomy":"L1","reason":"Anonymous RFC 6749 token endpoint where Example Dining exchanges a code or refresh token (with its client secret or PKCE verifier) for a restaurant-link access token; it mints credentials and has no principal for the autonomy ladder."},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: OAuth token exchange for Example Dining (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining exchanges the code from the callback (or a refresh token) for an access token it then uses on the link and validation calls, and learns the location it maps the restaurant to. RFC 6749: the client authenticates with client_id + client_secret (body or HTTP Basic) or PKCE code_verifier; a bad client, code or verifier is 400 VALIDATION details.reason INVALID_GRANT. Tokens are opaque, scoped to the one restaurant link, at most 2 h; the refresh token is single-use and rotates.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOAuthTokenRequest"},"examples":{"authorizationCode":{"summary":"Exchange the code","value":{"grant_type":"authorization_code","code":"syn-ot-auth-code-0001","redirect_uri":"https://ot.example.test/redirect","client_id":"syn-ot-client","client_secret":"syn-ot-client-secret-placeholder"}}}}}},"responses":{"200":{"description":"The access token and the linked location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOAuthTokenResponse"},"examples":{"issued":{"summary":"Token and location","value":{"access_token":"syn-access-token-placeholder","token_type":"Bearer","expires_in":7200,"refresh_token":"syn-refresh-token-placeholder","location":{"location_id":"LOC-001","location_name":"Harbour Table"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/locations":{"get":{"operationId":"diningPartnerOnboardingLocations","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_onboarding_locations","class":"read","autonomy":"L0","reason":"Example Dining's own Location API call, made with the access token from the onboarding OAuth token exchange (OD-874), so it is not an agent surface; agents read the same data through dining_partner_list_locations."},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: the partner locations Example Dining may link a restaurant to (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining's Location API call, made with the access token from the token exchange; answers the locations of the tenant that connected.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"responses":{"200":{"description":"The locations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLocationList"},"examples":{"two":{"summary":"Two locations","value":{"items":[{"partner_identifier":"LOC-001","name":"Harbour Table","state":"active","address":{"line1":"1 Pier Way","city":"San Francisco","region":"CA","postalCode":"94105","countryCode":"US"}},{"partner_identifier":"LOC-002","name":"Harbour Table Uptown","state":"active"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/identifiers/{partnerIdentifier}":{"get":{"operationId":"diningPartnerValidateIdentifier","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_validate_identifier","class":"read","autonomy":"L0","reason":"Called by an external system with its own credential (opentable-integration-key); not an agent surface."},"tags":["dining-partner"],"x-audience":"partner","x-aikonic-credential":"opentable-integration-key","summary":"Onboarding: validate a unique identifier (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining's Validation API call in the unique-identifier flow: is this partner identifier a live location of ours, and not already linked elsewhere? 404 NOT_FOUND when it is unknown; 409 CONFLICT details.reason IDENTIFIER_ALREADY_LINKED when another restaurant holds it.","security":[{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/DiningPartnerIdentifierPath"}],"responses":{"200":{"description":"The location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLocation"},"examples":{"found":{"summary":"Location found","value":{"partner_identifier":"LOC-001","name":"Harbour Table","state":"active","address":{"line1":"1 Pier Way","city":"San Francisco","region":"CA","postalCode":"94105","countryCode":"US"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"description":"CONFLICT details.reason IDENTIFIER_ALREADY_LINKED — another restaurant holds this identifier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"alreadyLinked":{"summary":"409 CONFLICT IDENTIFIER_ALREADY_LINKED","value":{"code":"CONFLICT","message":"This identifier is already active within Example Dining.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"IDENTIFIER_ALREADY_LINKED"}}}}}}},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/links":{"post":{"operationId":"diningPartnerLinkLocation","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_link_location","class":"commit-money","autonomy":"L1","reason":"Example Dining's Onboarding API call, made with its onboarding OAuth token or integration key (x-aikonic-credential opentable-integration-key), that starts serving a restaurant for a tenant; foreign credential and tenant state, so not an agent surface."},"tags":["dining-partner"],"x-audience":"partner","x-aikonic-credential":"opentable-integration-key","summary":"Onboarding: link a restaurant to a partner location (unique identifier or OAuth) (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining's Onboarding API call: from now on the restaurant (rid) is served for the tenant that owns the location. Called with the OAuth access token, or with the integration key in the unique-identifier flow. Idempotent per rid + identifier.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLinkRequest"},"examples":{"link":{"summary":"Link rid 100123 to LOC-001","value":{"rid":"100123","partner_identifier":"LOC-001"}}}}}},"responses":{"200":{"description":"Linked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLinkResult"},"examples":{"linked":{"summary":"Linked","value":{"success":true,"message":"Location linked successfully"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/links/{restaurantId}:unlink":{"post":{"operationId":"diningPartnerUnlinkLocation","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_unlink_location","class":"commit-money","autonomy":"L1","reason":"Example Dining's Offboarding API call, made with its own integration credential (x-aikonic-credential opentable-integration-key), that stops serving a restaurant for the tenant; foreign credential and tenant state, so not an agent surface."},"tags":["dining-partner"],"x-audience":"partner","x-aikonic-credential":"opentable-integration-key","summary":"Onboarding: unlink (offboard) a restaurant (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Example Dining's Offboarding API call: the restaurant disconnected the integration; the platform stops serving it for the tenant.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLinkRequest"},"examples":{"unlink":{"summary":"Unlink rid 100123","value":{"rid":"100123","partner_identifier":"LOC-001"}}}}}},"responses":{"200":{"description":"Unlinked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingLinkResult"},"examples":{"unlinked":{"summary":"Unlinked","value":{"success":true,"message":"Location unlinked successfully"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/onboarding/status/{restaurantId}":{"put":{"operationId":"diningPartnerSetOnboardingStatus","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_set_onboarding_status","class":"commit-money","autonomy":"L1","reason":"Reports PENDING/ACTIVE/INACTIVE to Example Dining's integration-status API, which connects or offboards a restaurant for the tenant (tenant integration state); the proposal flow carries only orders.create and orders.cancel (DR-44)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: report the delayed-email onboarding status of a restaurant (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). The delayed-email flow: Example Dining e-mailed the partner a restaurant's connect request; the partner reports PENDING (with the configured step) while it sets up, ACTIVE with its location id to connect the tile, or INACTIVE to offboard. The platform forwards it to Example Dining's integration-status API. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingStatusRequest"},"examples":{"pending":{"summary":"Setup in progress","value":{"status":"PENDING","step":"user_request_sent"}},"active":{"summary":"Setup done; connect the tile","value":{"status":"ACTIVE","locationId":"LOC-001"}}}}}},"responses":{"200":{"description":"The onboarding status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingStatus"},"examples":{"pending":{"summary":"Pending","value":{"rid":"100123","status":"PENDING","step":"user_request_sent","channel":"DELAYED_EMAIL","updatedAt":"2026-10-05T17:04:11Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}},"get":{"operationId":"diningPartnerGetOnboardingStatus","x-mcp":{"expose":"resource","toolset":"partner","name":"dining_partner_get_onboarding_status","class":"read","autonomy":"L0"},"tags":["dining-partner"],"x-audience":"partner","summary":"Onboarding: read the onboarding status of a restaurant (v1.7.0)","description":"Integrations Marketplace onboarding (D2, OD-874). Whatever channel linked it (OAuth, unique identifier, delayed e-mail). NONE when the restaurant never connected. Requires dining:partner.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"}],"responses":{"200":{"description":"The onboarding status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningOnboardingStatus"},"examples":{"active":{"summary":"Connected through OAuth","value":{"rid":"100123","status":"ACTIVE","locationId":"LOC-001","channel":"OAUTH","updatedAt":"2026-10-05T17:04:11Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/UpstreamUnavailable"},"503":{"$ref":"#/components/responses/ProviderDown"}}}},"/v1/dining/partner/locations":{"get":{"operationId":"diningPartnerListLocations","x-mcp":{"expose":"resource","toolset":"partner","name":"dining_partner_list_locations","class":"read","autonomy":"L0"},"tags":["dining-partner"],"x-audience":"partner","summary":"Locations: list the tenant's partner locations (v1.7.0)","description":"Partner location management (v1.7.0; PARTNER lane). The partner locations a tenant offers Example Dining to link restaurants to: what the Example Dining-facing GET /v1/dining/partner/onboarding/locations and the identifier validation answer. Until this release the backend read them from credential material; they are now tenant data. Requires dining:partner. Listed at /v1/dining/partner/locations because GET /v1/dining/partner/onboarding/locations is the call Example Dining makes with its OAuth token.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"responses":{"200":{"description":"The locations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerLocationList"},"examples":{"two":{"summary":"Two locations, one linked","value":{"items":[{"restaurantId":"100123","partner_identifier":"LOC-001","name":"Harbour Table","state":"active","address":{"line1":"1 Pier Way","city":"San Francisco","region":"CA","postalCode":"94105","countryCode":"US"},"updatedAt":"2026-10-06T09:00:00Z"},{"restaurantId":"100456","partner_identifier":"LOC-002","name":"Harbour Table Uptown","state":"inactive","updatedAt":"2026-10-06T09:05:00Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/dining/partner/onboarding/locations/{restaurantId}":{"put":{"operationId":"diningPartnerPutLocation","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_put_location","class":"commit-money","autonomy":"L1","reason":"Creates or replaces the partner location Example Dining may link a restaurant to; it moved out of credential material in v1.7.0 and decides which tenant a restaurant is served for, so it is tenant integration state the proposal flow cannot carry (DR-44)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Locations: create or replace the partner location for a restaurant (v1.7.0)","description":"Partner location management (v1.7.0; PARTNER lane). The partner locations a tenant offers Example Dining to link restaurants to: what the Example Dining-facing GET /v1/dining/partner/onboarding/locations and the identifier validation answer. Until this release the backend read them from credential material; they are now tenant data. Requires dining:partner. Keyed by the Example Dining restaurant id the location serves. partner_identifier must be unique within the tenant; one held by another restaurant of the tenant is 409 CONFLICT details.reason IDENTIFIER_ALREADY_LINKED. Idempotent per body.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerLocationUpsert"},"examples":{"downtown":{"summary":"The downtown location for rid 100123","value":{"partner_identifier":"LOC-001","name":"Harbour Table","state":"active","address":{"line1":"1 Pier Way","city":"San Francisco","region":"CA","postalCode":"94105","countryCode":"US"}}}}}}},"responses":{"200":{"description":"The location as stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerLocation"},"examples":{"stored":{"summary":"Stored","value":{"restaurantId":"100123","partner_identifier":"LOC-001","name":"Harbour Table","state":"active","address":{"line1":"1 Pier Way","city":"San Francisco","region":"CA","postalCode":"94105","countryCode":"US"},"updatedAt":"2026-10-06T09:00:00Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"414":{"$ref":"#/components/responses/UriTooLong"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"diningPartnerDeleteLocation","x-mcp":{"expose":"excluded","toolset":"partner","name":"dining_partner_delete_location","class":"commit-money","autonomy":"L1","reason":"Deletes a tenant's partner location, which is the integration identity Example Dining links restaurants to (formerly credential material); this is a tenant-state change the proposal flow cannot carry (DR-44)."},"tags":["dining-partner"],"x-audience":"partner","summary":"Locations: delete a restaurant's partner location (v1.7.0)","description":"Partner location management (v1.7.0; PARTNER lane). The partner locations a tenant offers Example Dining to link restaurants to: what the Example Dining-facing GET /v1/dining/partner/onboarding/locations and the identifier validation answer. Until this release the backend read them from credential material; they are now tenant data. Requires dining:partner. A location still linked at Example Dining is 409 CONFLICT details.reason LOCATION_LINKED: unlink (offboard) it first. Unknown is 404.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"oauthClientCredentials":["dining:partner"]}],"parameters":[{"$ref":"#/components/parameters/DiningRestaurantIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiningPartnerAck"},"examples":{"deleted":{"summary":"DELETED","value":{"status":"DELETED"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ingest/dining/opentable/media/{token}":{"get":{"operationId":"diningPartnerMediaFetch","x-mcp":{"expose":"excluded","toolset":"supplier","name":"dining_partner_media_fetch","class":"read","autonomy":"L0","reason":"Called by an external system with its own credential (signed-url); not an agent surface."},"tags":["dining-partner"],"x-audience":"supplier","x-aikonic-credential":"signed-url","summary":"Media edge: Example Dining fetches a guest photo or profile image by its signed URL (v1.7.0)","description":"The supplier-fetched media edge (D2, OD-874; PARTNER lane). Supplier-facing, not public: Example Dining fetches a guest photo (CRM v3) and profile images (Profile Content) by URL, and the URL must stay valid for 30 days. The client never calls this route: it uploads the image with POST /v1/uploads and sends the uploadId (DiningGuestPhotoRequest.uploadId, DiningProfileContentRequest.photos); the platform turns the uploadId into https://<public-base>/ingest/dining/opentable/media/{token}, where token is HMAC-SHA256 over tenant, uploadId and expiry (30 days) under a key derived from the platform master key, and gives Example Dining that URL. No other credential: the token is the credential and names nothing else. It serves only a SCANNED image/jpeg or image/png upload of the signing tenant, with Cache-Control no-store and X-Content-Type-Options nosniff. A bad or expired signature, an upload that is not scanned, not an image, or gone is a bare 404 with no body and no reason. Outside the /v1 envelope and not in any Postman collection.","security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"The signed token (base64url claims, a dot, base64url HMAC)","schema":{"type":"string","maxLength":1024},"example":"c3luLWNsYWltcw.c3luLXNpZ25hdHVyZQ"}],"responses":{"200":{"description":"The image bytes","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"no-store"}},"content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Bad or expired signature, or an upload that is not a scanned image of the signing tenant; no body"}}}},"/v1/agents:resolve":{"get":{"operationId":"resolveAgent","x-mcp":{"expose":"resource","toolset":"public","name":"resolve_agent","class":"read","autonomy":"L0","example":"{\"handle\":\"concierge@acme-travel.agents.aikonic.travel\"}"},"tags":["identity"],"summary":"The public profile of one agent by handle (FEAT-044 §5.5, §6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Answers the public AgentPublicProfile of a registered agent: its handle, name, operator name and domain, tier, current version, protocols, key thumbprints, A2A card URL, certificate state and a public log of registry events. It never carries the principal, the tenant id or any traffic (FEAT-044 §14). A private agent (private true) and an unknown handle are the same 404 NOT_FOUND, so a handle cannot be probed. Anonymous: no credential is needed (security is empty); rate-limited per IP and per key, and an over-limit caller is 429 RATE_LIMITED. Web Bot Auth (RFC 9421 HTTP Message Signatures): a caller may sign the request with the Signature, Signature-Input and Signature-Agent headers, and a registered agent declares its version in Agent-Version, which counts only when it is a covered component of the signature (FEAT-044 §5.2, SHIP-feat044 A3). A request with no signature is not an error: it is served as unverified. A signature that is present and wrong is 401 AGENT_SIGNATURE_INVALID, and a covered Agent-Version that names a REVOKED version is 401 AGENT_VERSION_REVOKED. A signature created more than 300 seconds ago, or more than 30 seconds in the future, is invalid (DR-173).","security":[],"parameters":[{"name":"handle","in":"query","required":true,"description":"The agent handle, name@domain (DR-165): a hosted handle is name@<tenantSlug>.agents.aikonic.travel, an own-domain handle is name@your-domain.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,31}@[a-z0-9]([a-z0-9.-]{0,251}[a-z0-9])?$","maxLength":300}},{"$ref":"#/components/parameters/SignatureHeader"},{"$ref":"#/components/parameters/SignatureInputHeader"},{"$ref":"#/components/parameters/SignatureAgentHeader"},{"$ref":"#/components/parameters/AgentVersionHeader"}],"responses":{"200":{"description":"The agent's public profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPublicProfile"},"examples":{"profile":{"summary":"200 — a T2 agent with an issued certificate","value":{"handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","operator":{"name":"Acme Travel","domain":"acme-travel.example"},"tier":"T2","currentVersion":"2026.10.1","protocols":["mcp","a2a"],"keyThumbprints":["NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"],"cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","certificate":{"ansName":"ans://v1.2026.10.1.concierge.acme-travel.agents.aikonic.travel","notAfter":"2027-01-27T09:15:00Z","state":"ISSUED"},"publicLog":[{"at":"2026-10-29T09:15:00Z","event":"version_registered","version":"2026.10.1"},{"at":"2026-10-29T09:10:00Z","event":"registered"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AgentSignatureFailed"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents:search":{"get":{"operationId":"searchAgentDirectory","x-mcp":{"expose":"tool","toolset":"public","name":"search_agent_directory","class":"read","autonomy":"L0","example":"{\"q\":\"concierge\",\"protocol\":\"mcp\",\"canHold\":true,\"limit\":20}"},"tags":["identity"],"summary":"Search the travel agent directory (FEAT-044 §5.9, DR-170)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.9). The travel-only directory (DR-170): public, non-private agents of T1 or above, paged by an opaque cursor. Each entry carries the handle, name, operator, tier, protocols, canHold, capabilities and outcomeScore, and never a principal, a tenant id or traffic. Served only while aikonic.agents.directory.enabled is on (Phase D, after Phase C); with it off the route answers 501 NOT_IMPLEMENTED. Anonymous (security is empty); rate-limited per IP and per key. Web Bot Auth (RFC 9421 HTTP Message Signatures): a caller may sign the request with the Signature, Signature-Input and Signature-Agent headers, and a registered agent declares its version in Agent-Version, which counts only when it is a covered component of the signature (FEAT-044 §5.2, SHIP-feat044 A3). A request with no signature is not an error: it is served as unverified. A signature that is present and wrong is 401 AGENT_SIGNATURE_INVALID, and a covered Agent-Version that names a REVOKED version is 401 AGENT_VERSION_REVOKED. A signature created more than 300 seconds ago, or more than 30 seconds in the future, is invalid (DR-173).","security":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Free text matched against the agent name, handle and capabilities.","schema":{"type":"string","maxLength":200}},{"name":"protocol","in":"query","required":false,"description":"Only agents that speak this protocol: mcp, a2a or http.","schema":{"type":"string","enum":["mcp","a2a","http"]}},{"name":"tier","in":"query","required":false,"description":"The lowest tier to list, T1 to T3; the default is T1 (a T0 agent is never listed, FEAT-044 §5.4).","schema":{"type":"string","enum":["T1","T2","T3"],"default":"T1"}},{"name":"canHold","in":"query","required":false,"description":"true lists only agents allowed to place holds (commit-lite tools); false lists only read-only agents; omitted lists both.","schema":{"type":"boolean"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page nextCursor; omit for the first page.","schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1 to 50; the default is 20.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"$ref":"#/components/parameters/SignatureHeader"},{"$ref":"#/components/parameters/SignatureInputHeader"},{"$ref":"#/components/parameters/SignatureAgentHeader"},{"$ref":"#/components/parameters/AgentVersionHeader"}],"responses":{"200":{"description":"One page of directory entries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDirectoryPage"},"examples":{"onePage":{"summary":"200 — one T2 agent","value":{"items":[{"handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","operator":{"name":"Acme Travel","domain":"acme-travel.example"},"tier":"T2","protocols":["mcp","a2a"],"canHold":true,"capabilities":["flights","hotels"],"outcomeScore":0.92}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AgentSignatureFailed"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"414":{"$ref":"#/components/responses/UriTooLong"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/admin/v1/agent-registry":{"get":{"operationId":"listRegisteredAgents","x-mcp":{"expose":"tool","toolset":"console","name":"list_registered_agents","class":"read","autonomy":"L0","example":"{\"tier\":\"T1\",\"limit\":25}"},"tags":["admin"],"x-audience":"console","summary":"The tenant's registered agents, newest first (FEAT-044 §5.5)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:read (Owner, Admin, Developer, Analyst; SHIP-feat044 A2). It is a different surface from GET /admin/v1/agents, which lists the platform AI agents of FEAT-019. A tenant sees only its own agents (FEAT-044 §9). Filter by tier and by suspended; paged by an opaque cursor. An MCP grant-derived token may read it as a console read tool inside its toolset; an API key is 403 PERMISSION_DENIED SESSION_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"tier","in":"query","required":false,"description":"Only agents at this tier, T0 to T3.","schema":{"type":"string","enum":["T0","T1","T2","T3"]}},{"name":"suspended","in":"query","required":false,"description":"true lists only suspended agents; false lists only active ones; omitted lists both.","schema":{"type":"boolean"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page nextCursor; omit for the first page.","schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1 to 100; the default is 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of registered agents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgentPage"},"examples":{"onePage":{"summary":"200 — one hosted agent","value":{"items":[{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"registerAgent","x-mcp":{"expose":"excluded","toolset":"console","name":"register_agent","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Register an agent: handle, first version and key (FEAT-044 §5.5)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage (Owner, Admin) and a human console session; an AI-kind API key or an MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED, because identity changes are human-session only (FEAT-044 §9). Without domain the handle is hosted, handleName@<tenantSlug>.agents.aikonic.travel (DR-165) and the domain proof is the platform's own; with domain the handle is handleName@domain and the 201 lists the DNS TXT record to add in dnsRecords, then POST ...:verify-domain. A handle already registered is 409 HANDLE_TAKEN; a reserved name is 422 HANDLE_RESERVED (both nextAction stop). The key is a public JWK or a jwksUrl; a JWK that carries a private member is refused (AC-044-16). Idempotency-Key is required. Emits agent.registered. The agent starts at T0 (own domain, unproven) or T1 (hosted); an unverified agent on PROD is read-only under the ceiling (DR-167, DR-168).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRegisterRequest"},"examples":{"hosted":{"summary":"A hosted handle: no domain, the platform provides name@<tenantSlug>.agents.aikonic.travel","value":{"name":"Acme Concierge","handleName":"concierge","private":false,"protocols":["mcp","a2a"],"firstVersion":{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d"},"key":{"publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"}}}},"ownDomain":{"summary":"An own-domain handle: the tenant proves the domain with a DNS TXT record","value":{"name":"Acme Concierge","handleName":"concierge","private":false,"protocols":["mcp","a2a"],"firstVersion":{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d"},"key":{"jwksUrl":"https://acme-travel.example/.well-known/http-message-signatures-directory"},"domain":"acme-travel.example","cimdUrl":"https://acme-travel.example/.well-known/oauth-client.json"}}}}}},"responses":{"201":{"description":"The registered agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"hosted":{"summary":"201 — hosted handle; the first version is ACTIVE","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"ownDomain":{"summary":"201 — own domain; domainProof.ok is false until the TXT record is added and verified","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.example","name":"Acme Concierge","domain":"acme-travel.example","hosted":false,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.example/.well-known/oauth-client.json","cardUrl":"https://acme-travel.example/.well-known/agent-card.json","tier":"T0","tierReason":"Domain proof pending: add the TXT record, then verify.","suspended":false,"domainProof":{"domain":"acme-travel.example","txtName":"_aikonic-verify.acme-travel.example","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":false,"dnssecValidated":false,"lastCheckedAt":null},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[{"name":"_aikonic-verify.acme-travel.example","type":"TXT","value":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D"}],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"409":{"description":"HANDLE_TAKEN — the handle is already registered (nextAction stop)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"handleTaken":{"summary":"409 HANDLE_TAKEN — another agent holds this handle","value":{"code":"CONFLICT","message":"The handle concierge@acme-travel.agents.aikonic.travel is already registered.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"HANDLE_TAKEN","handle":"concierge@acme-travel.agents.aikonic.travel"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"HANDLE_RESERVED — the name is reserved by the platform (nextAction stop)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"handleReserved":{"summary":"422 HANDLE_RESERVED — a platform-reserved handle name","value":{"code":"VALIDATION","message":"The handle name admin is reserved. Choose another name.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"HANDLE_RESERVED","field":"handleName","handleName":"admin"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}":{"get":{"operationId":"getRegisteredAgent","x-mcp":{"expose":"tool","toolset":"console","name":"get_registered_agent","class":"read","autonomy":"L0","example":"{\"agentId\":\"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D\"}"},"tags":["admin"],"x-audience":"console","summary":"One registered agent with versions, keys and domain proof (FEAT-044 §5.5, §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:read. Answers the RegisteredAgent: tier and tierReason, the domain proof state, every version with its ANS certificate state, every key with its state, and the DNS records still to add for an own domain. An agent of another tenant is 403 TENANT_MISMATCH; an unknown agentId is 404 NOT_FOUND.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"}],"responses":{"200":{"description":"The registered agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"agent":{"summary":"200 — a hosted agent at T1","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateRegisteredAgent","x-mcp":{"expose":"excluded","toolset":"console","name":"update_registered_agent","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Edit an agent's name, protocols, CIMD URL or privacy (FEAT-044 §5.5)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session; an AI-kind key is 403 PERMISSION_DENIED SESSION_REQUIRED (FEAT-044 §9). Only the members sent change; the handle, domain and tier never change here. Setting private true removes the agent from the directory and from resolve (DR-170). Idempotent by state: a repeat with the same body changes nothing.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUpdateRequest"},"examples":{"rename":{"summary":"Rename and make private","value":{"name":"Acme Concierge EU","private":true}}}}}},"responses":{"200":{"description":"The updated agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"updated":{"summary":"200 — renamed and private","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge EU","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":true,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}:verify-domain":{"post":{"operationId":"verifyAgentDomain","x-mcp":{"expose":"excluded","toolset":"console","name":"verify_agent_domain","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Check the domain TXT proof now (FEAT-044 §5.5)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). Looks up the TXT record named in domainProof.txtName through the DnsProofResolver and answers the agent with domainProof updated. A signed (DNSSEC) answer sets dnssecValidated and may raise the tier to T1; an unsigned answer proves the domain but never raises a tier (LAW-801, SHIP-feat044 A10). If the record is absent or differs the answer is 409 DOMAIN_NOT_VERIFIED with nextAction verify_agent, details.txtName and details.expected: add the record, then call again. Safe to repeat; no Idempotency-Key is needed. Emits agent.tier_changed when the tier moves.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"}],"responses":{"200":{"description":"The agent with its domain proof updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"verified":{"summary":"200 — the TXT record was found and its answer signed","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.example","name":"Acme Concierge","domain":"acme-travel.example","hosted":false,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.example/.well-known/oauth-client.json","cardUrl":"https://acme-travel.example/.well-known/agent-card.json","tier":"T1","tierReason":"Domain verified through a signed DNS answer.","suspended":false,"domainProof":{"domain":"acme-travel.example","txtName":"_aikonic-verify.acme-travel.example","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:20:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"DOMAIN_NOT_VERIFIED — the TXT record is absent or differs (nextAction verify_agent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"domainNotVerified":{"summary":"409 DOMAIN_NOT_VERIFIED — no matching TXT record at the name","value":{"code":"CONFLICT","message":"The TXT record _aikonic-verify.acme-travel.example was not found. Add it, then verify again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"verify_agent","details":{"reason":"DOMAIN_NOT_VERIFIED","domain":"acme-travel.example","txtName":"_aikonic-verify.acme-travel.example","expected":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}/versions":{"post":{"operationId":"registerAgentVersion","x-mcp":{"expose":"excluded","toolset":"console","name":"register_agent_version","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Add a version: model id and prompt hash (FEAT-044 §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). A version records the model id and the SHA-256 of the system prompt, never the prompt text. The new version is ACTIVE; the previous ACTIVE version stays ACTIVE until drained or revoked. A version string already registered for the agent is 409 CONFLICT details.reason VERSION_EXISTS. Idempotency-Key is required. Emits agent.version_registered. Its ANS certificate is requested separately (POST ...:request-certificate); until it is ISSUED the version gives no ANS proof.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVersionCreateRequest"},"examples":{"next":{"summary":"The next release of the agent","value":{"version":"2026.10.2","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d"}}}}}},"responses":{"201":{"description":"The agent with the new version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"added":{"summary":"201 — a second version, ACTIVE","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"},{"version":"2026.10.2","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT VERSION_EXISTS — the version string is already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionExists":{"summary":"409 CONFLICT VERSION_EXISTS","value":{"code":"CONFLICT","message":"Version 2026.10.1 is already registered for this agent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"VERSION_EXISTS","field":"version","version":"2026.10.1"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}/versions/{version}:drain":{"post":{"operationId":"drainAgentVersion","x-mcp":{"expose":"excluded","toolset":"console","name":"drain_agent_version","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Stop accepting new grants for a version (FEAT-044 §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). Moves an ACTIVE version to DRAINING: requests that declare it keep working and live grants run out, but no new grant is approved for it, so a release can be retired without breaking a person mid-trip. Draining a REVOKED version is 409 CONFLICT details.reason VERSION_REVOKED; draining a DRAINING version changes nothing. Safe to repeat; no Idempotency-Key is needed.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/AgentVersionPath"}],"responses":{"200":{"description":"The agent with the version updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"updated":{"summary":"200 — version 2026.10.1 is DRAINING","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"DRAINING","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"},{"version":"2026.10.2","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT VERSION_REVOKED — a revoked version cannot be drained","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionRevoked":{"summary":"409 CONFLICT VERSION_REVOKED","value":{"code":"CONFLICT","message":"Version 2026.10.1 is revoked and cannot be drained.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"VERSION_REVOKED","version":"2026.10.1"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}/versions/{version}:revoke":{"post":{"operationId":"revokeAgentVersion","x-mcp":{"expose":"excluded","toolset":"console","name":"revoke_agent_version","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Revoke a version within one token lifetime (FEAT-044 §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). Sets the version REVOKED with the reason, revokes its ANS certificate, ends every live MCP grant that was approved for it and leaves its pending proposals for a human to approve or reject (DR-38); the answer to a later request that declares it is 401 AGENT_VERSION_REVOKED within one access-token lifetime (AC-044-17). Emits agent.version_revoked with liveGrantsEnded and pendingProposals. A revoked version cannot be un-revoked: register a new one. Revoking a REVOKED version is 409 CONFLICT details.reason VERSION_ALREADY_REVOKED. Idempotency-Key is required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/AgentVersionPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentReasonRequest"},"examples":{"compromised":{"summary":"Revoke a version whose prompt leaked","value":{"reason":"System prompt leaked in a public paste."}}}}}},"responses":{"200":{"description":"The agent with the version updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"updated":{"summary":"200 — version 2026.10.1 is REVOKED","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"REVOKED","certificateState":"REVOKED","createdAt":"2026-10-29T09:15:00Z","revokedAt":"2026-10-29T10:00:00Z","revokeReason":"System prompt leaked in a public paste."}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT VERSION_ALREADY_REVOKED — the version is already revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"alreadyRevoked":{"summary":"409 CONFLICT VERSION_ALREADY_REVOKED","value":{"code":"CONFLICT","message":"Version 2026.10.1 is already revoked.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"VERSION_ALREADY_REVOKED","version":"2026.10.1"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}/versions/{version}:request-certificate":{"post":{"operationId":"requestAgentVersionCertificate","x-mcp":{"expose":"excluded","toolset":"console","name":"request_agent_version_certificate","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Request an ANS certificate for a version (FEAT-044 §5.6, DR-166)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044; behind aikonic.agents.ans.enabled). Requires agent-registry:manage and a human console session (FEAT-044 §9). Submits the version to the ANS registration authority (DR-166: per-version certificates through an existing registration authority); the version's certificateState becomes REQUESTED and ISSUED or FAILED follows asynchronously. The platform absorbs the fees of the first three agents per tenant (DR-171). An ISSUED certificate for the running version, with a valid Web Bot Auth signature, is the proof that makes T2. With the flag off the route answers 501 NOT_IMPLEMENTED. Safe to repeat while REQUESTED; a REVOKED version is 409 CONFLICT details.reason VERSION_REVOKED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/AgentVersionPath"}],"responses":{"200":{"description":"The agent with the version updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"updated":{"summary":"200 — certificate requested","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"REQUESTED","createdAt":"2026-10-29T09:15:00Z","ansName":"ans://v1.2026.10.1.concierge.acme-travel.agents.aikonic.travel"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT VERSION_REVOKED — a revoked version gets no certificate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"versionRevoked":{"summary":"409 CONFLICT VERSION_REVOKED","value":{"code":"CONFLICT","message":"Version 2026.10.1 is revoked; register a new version.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"VERSION_REVOKED","version":"2026.10.1"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}}}},"/admin/v1/agent-registry/{agentId}/keys":{"post":{"operationId":"addAgentKey","x-mcp":{"expose":"excluded","toolset":"console","name":"add_agent_key","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Add a signing key from its public JWK (FEAT-044 §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). The body carries a public JWK only: the keys of a hosted agent are generated in the browser (WebCrypto Ed25519, ECDSA P-256 where Ed25519 is unavailable; SHIP-feat044 A9) and the private half never reaches the platform. A JWK with a private member (d, p, q, dp, dq, qi, k) is 400 VALIDATION details.field publicJwk (AC-044-16). The keyId is the JWK thumbprint (RFC 7638). The new key is CURRENT; the previous CURRENT key becomes RETIRING and retires after the overlap window, so a signer can roll without a gap. Idempotency-Key is required. Emits agent.key_rotated.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentKeyCreateRequest"},"examples":{"ed25519":{"summary":"An Ed25519 public key","value":{"publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"}}}}}}},"responses":{"201":{"description":"The agent with the new key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"added":{"summary":"201 — the new key is CURRENT, the old one RETIRING","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"ZkR4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtE","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"Zk4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtEA","kid":"ZkR4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtE","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"},{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"RETIRING","createdAt":"2026-10-29T09:15:00Z","retiresAt":"2026-11-05T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT KEY_EXISTS — the key is already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"keyExists":{"summary":"409 CONFLICT KEY_EXISTS","value":{"code":"CONFLICT","message":"A key with this thumbprint is already registered for the agent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"KEY_EXISTS","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-registry/{agentId}/keys/{keyId}:retire":{"post":{"operationId":"retireAgentKey","x-mcp":{"expose":"excluded","toolset":"console","name":"retire_agent_key","class":"commit-lite","autonomy":"L1","reason":"Identity changes need a human session: an agent never creates or changes an agent record, version, key or tier (FEAT-044 §6, §9; FEAT-034: keys and identity belong to people)."},"tags":["admin"],"x-audience":"console","summary":"Retire a key (FEAT-044 §5.6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:manage and a human console session (FEAT-044 §9). Moves the key to RETIRED at once: a signature made with it is 401 AGENT_SIGNATURE_INVALID from then on. The only CURRENT key cannot be retired without another key to take its place: 409 CONFLICT details.reason LAST_CURRENT_KEY (add a key first). Retiring a RETIRED key changes nothing, so a repeat is harmless and no Idempotency-Key is needed. Emits agent.key_rotated.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/AgentKeyIdPath"}],"responses":{"200":{"description":"The agent with the key retired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"retired":{"summary":"200 — the old key is RETIRED, the new one CURRENT","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"ZkR4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtE","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"Zk4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtEA","kid":"ZkR4oY2nVq7wXc9bLs3TgH1uPd6AeJf0MiN5yK8rUtE","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"},{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"RETIRED","createdAt":"2026-10-29T09:15:00Z","retiresAt":"2026-10-29T10:30:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/AgentRegistryWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT LAST_CURRENT_KEY — the only CURRENT key cannot be retired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"lastCurrentKey":{"summary":"409 CONFLICT LAST_CURRENT_KEY","value":{"code":"CONFLICT","message":"This is the only current key. Add another key before retiring it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"LAST_CURRENT_KEY","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-traffic":{"get":{"operationId":"listAgentTraffic","x-mcp":{"expose":"tool","toolset":"console","name":"list_agent_traffic","class":"read","autonomy":"L0","example":"{\"tier\":\"T0\",\"proof\":\"web-bot-auth\",\"limit\":25}"},"tags":["admin"],"x-audience":"console","summary":"Agent traffic on the tenant's own keys and grants (FEAT-044 §5.8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:read. The \"know your agent\" view (FEAT-044 §5.8): the requests the tenant's keys and grants made with an agent identity, newest first, with a summary (counts by tier and by proof, signature failures, distinct principals, proposals filed) and a 24-hour trend per agent. A tenant sees only traffic on its own keys and grants (FEAT-044 §9). Filter by agent, tier, proof and window; paged by an opaque cursor.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"agentId","in":"query","required":false,"description":"Only requests stamped with this agent.","schema":{"type":"string","pattern":"^agt_[0-9A-Z]{26}$"}},{"name":"tier","in":"query","required":false,"description":"Only requests stamped with this tier, T0 to T3.","schema":{"type":"string","enum":["T0","T1","T2","T3"]}},{"name":"proof","in":"query","required":false,"description":"Only requests carrying this proof: web-bot-auth, cimd, ans or dns.","schema":{"type":"string","enum":["web-bot-auth","cimd","ans","dns"]}},{"name":"from","in":"query","required":false,"description":"Start of the window, an ISO 8601 instant; default 24 hours before to.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"End of the window, an ISO 8601 instant; default now.","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page nextCursor; omit for the first page.","schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1 to 100; the default is 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of agent traffic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTrafficPage"},"examples":{"onePage":{"summary":"200 — one allowed request","value":{"summary":{"byTier":{"T0":41,"T1":12,"T2":7,"T3":0},"byProof":{"web-bot-auth":7,"cimd":12,"ans":7,"dns":5},"signatureFailures":2,"principals":9,"proposalsFiled":3},"items":[{"requestId":"req_01JC4R2M9X7Q1V5T8N3K6P0ZBD","at":"2026-10-29T09:41:07Z","agent":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","version":"2026.10.1","operator":{"tenantId":"TEN-ACME","domain":"acme-travel.agents.aikonic.travel","verified":true},"proof":["web-bot-auth","ans"],"tier":"T2","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","directory":"https://skyhop.example/.well-known/http-message-signatures-directory"},"principal":"pr***@acme-travel.example","route":"POST /v1/holds","outcome":"ALLOWED"}],"trend":[{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","hourly":[0,0,0,0,0,0,1,2,4,6,5,3,2,1,0,0,0,0,0,0,0,0,1,2]}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/admin/v1/agent-traffic/{requestId}":{"get":{"operationId":"getAgentRequestTrace","x-mcp":{"expose":"tool","toolset":"console","name":"get_agent_request_trace","class":"read","autonomy":"L0","example":"{\"requestId\":\"req_01JC4R2M9X7Q1V5T8N3K6P0ZBD\"}"},"tags":["admin"],"x-audience":"console","summary":"The seven-check trace of one agent request (FEAT-044 §5.2, §5.8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). Requires agent-registry:read. Answers the AgentRequestTrace: the stamp and the seven checks in order (handle resolution, key directory, signature, version certificate, token binding, principal grant, tier ceiling), each PASS, FAIL, SKIPPED or UNVERIFIED with its time and a short detail. Signature and token values are redacted: the trace shows which components were covered and which claims were present, never a secret. A request of another tenant is 404 NOT_FOUND (existence hidden), as is an unknown requestId.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"human-session","parameters":[{"name":"requestId","in":"path","required":true,"description":"The request id of the traffic row, the X-Request-Id of the call (req_ followed by 26 characters).","schema":{"type":"string","pattern":"^req_[0-9A-Z]{26}$"}}],"responses":{"200":{"description":"The trace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRequestTrace"},"examples":{"allPass":{"summary":"200 — six checks passed, one skipped","value":{"requestId":"req_01JC4R2M9X7Q1V5T8N3K6P0ZBD","at":"2026-10-29T09:41:07Z","agent":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","version":"2026.10.1","operator":{"tenantId":"TEN-ACME","domain":"acme-travel.agents.aikonic.travel","verified":true},"proof":["web-bot-auth","ans"],"tier":"T2","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","directory":"https://skyhop.example/.well-known/http-message-signatures-directory"},"checks":[{"n":1,"name":"Handle resolution","standard":"DNS-AID, DNSSEC","outcome":"PASS","ms":4,"detail":"handle resolved through a signed DNS answer"},{"n":2,"name":"Key directory","standard":"Web Bot Auth","outcome":"PASS","ms":11,"detail":"key found by thumbprint, served 200 without redirects"},{"n":3,"name":"Signature","standard":"RFC 9421, Web Bot Auth","outcome":"PASS","ms":1,"detail":"created 12 s ago, covers @authority, @method, @path, signature-agent and agent-version"},{"n":4,"name":"Version certificate","standard":"ANS v2","outcome":"PASS","ms":2,"detail":"certificate for version 2026.10.1 valid and not revoked"},{"n":5,"name":"Token binding","standard":"DPoP, RFC 8707","outcome":"PASS","ms":1,"detail":"token bound and in audience"},{"n":6,"name":"Principal grant","standard":"RFC 8693, FEAT-031","outcome":"SKIPPED","ms":0,"detail":"API key call: no session toolset"},{"n":7,"name":"Tier ceiling","standard":"DR-167","outcome":"PASS","ms":1,"detail":"tier T2 allows tool class commit-lite"}],"signatureHeaders":{"Signature":"[redacted]","Signature-Input":"sig1=(\"@authority\" \"@method\" \"@path\" \"signature-agent\" \"agent-version\");created=1793353255;keyid=\"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs\";tag=\"web-bot-auth\"","Signature-Agent":"\"https://acme-travel.agents.aikonic.travel\"","Agent-Version":"2026.10.1"},"tokenClaims":{"sub":"[redacted]","aud":"https://api.apiq.example","iat":1793353250,"exp":1793354150,"cnf":"[redacted]"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/ConsoleForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/agent-registry":{"get":{"operationId":"opsListRegisteredAgents","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_registered_agents","class":"read","autonomy":"L0","example":"{\"tier\":\"T1\",\"suspended\":false,\"limit\":25}"},"tags":["operator"],"x-audience":"operator","summary":"The agent fleet across tenants (FEAT-044 §5.8, §6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). OPERATOR SESSION ONLY, any operator role. A cross-tenant read: it joins ops_list_tenants, ops_finance_summary, ops_list_supplier_cancellations, ops_list_approvals and ops_insights_environments in McpGrant.crossTenantReads, so an MCP grant-derived token reaches it only when the operator ticked it at consent, and every call is audited (ADR-C10 Phase 4). Every registered agent of every tenant, with the owning tenantId and counts, newest first. Filter by tier, suspended and domain; the tenant is not a selector on an exposed read (ADR-C09 §5), so one tenant is found by its domain. Paged by an opaque cursor. Reads only: set-tier, suspend and unsuspend are separate human operator writes.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"tier","in":"query","required":false,"description":"Only agents at this tier, T0 to T3.","schema":{"type":"string","enum":["T0","T1","T2","T3"]}},{"name":"suspended","in":"query","required":false,"description":"true lists only suspended agents; false lists only active ones; omitted lists both.","schema":{"type":"boolean"}},{"name":"domain","in":"query","required":false,"description":"Only agents registered under this domain, for example acme-travel.example.","schema":{"type":"string","maxLength":253}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page nextCursor; omit for the first page.","schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1 to 100; the default is 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of the fleet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentFleetPage"},"examples":{"onePage":{"summary":"200 — one agent, with counts","value":{"items":[{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","tenantId":"TEN-ACME","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","tier":"T1","suspended":false,"protocols":["mcp","a2a"],"versionCount":1,"activeVersionCount":1,"keyCount":1,"createdAt":"2026-10-29T09:15:00Z"}],"summary":{"total":1,"suspended":0,"byTier":{"T0":0,"T1":1,"T2":0,"T3":0}},"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/agent-traffic":{"get":{"operationId":"opsListAgentTraffic","x-mcp":{"expose":"tool","toolset":"operator","name":"ops_list_agent_traffic","class":"read","autonomy":"L0","example":"{\"tier\":\"T0\",\"proof\":\"dns\",\"limit\":25}"},"tags":["operator"],"x-audience":"operator","summary":"Agent traffic across tenants (FEAT-044 §5.8, §6)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). OPERATOR SESSION ONLY, any operator role. A cross-tenant read: with ops_list_registered_agents it takes McpGrant.crossTenantReads to seven names, so an MCP grant-derived token reaches it only when ticked at consent, and every call is audited (ADR-C10 Phase 4). The same AgentTrafficPage as GET /admin/v1/agent-traffic over every tenant. Filter by agent, domain, tier, proof and window; paged by an opaque cursor. The tenant is not a selector on an exposed read (ADR-C09 §5).","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"name":"agentId","in":"query","required":false,"description":"Only requests stamped with this agent.","schema":{"type":"string","pattern":"^agt_[0-9A-Z]{26}$"}},{"name":"domain","in":"query","required":false,"description":"Only requests whose agent is registered under this domain.","schema":{"type":"string","maxLength":253}},{"name":"tier","in":"query","required":false,"description":"Only requests stamped with this tier, T0 to T3.","schema":{"type":"string","enum":["T0","T1","T2","T3"]}},{"name":"proof","in":"query","required":false,"description":"Only requests carrying this proof: web-bot-auth, cimd, ans or dns.","schema":{"type":"string","enum":["web-bot-auth","cimd","ans","dns"]}},{"name":"from","in":"query","required":false,"description":"Start of the window, an ISO 8601 instant; default 24 hours before to.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"End of the window, an ISO 8601 instant; default now.","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page nextCursor; omit for the first page.","schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1 to 100; the default is 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of agent traffic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTrafficPage"},"examples":{"onePage":{"summary":"200 — one allowed request","value":{"summary":{"byTier":{"T0":41,"T1":12,"T2":7,"T3":0},"byProof":{"web-bot-auth":7,"cimd":12,"ans":7,"dns":5},"signatureFailures":2,"principals":9,"proposalsFiled":3},"items":[{"requestId":"req_01JC4R2M9X7Q1V5T8N3K6P0ZBD","at":"2026-10-29T09:41:07Z","agent":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","version":"2026.10.1","operator":{"tenantId":"TEN-ACME","domain":"acme-travel.agents.aikonic.travel","verified":true},"proof":["web-bot-auth","ans"],"tier":"T2","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","directory":"https://skyhop.example/.well-known/http-message-signatures-directory"},"principal":"pr***@acme-travel.example","route":"POST /v1/holds","outcome":"ALLOWED"}],"trend":[{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","hourly":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],"nextCursor":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/agent-registry/{agentId}:set-tier":{"post":{"operationId":"opsSetAgentTier","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_set_agent_tier","class":"commit-lite","autonomy":"L1","reason":"Operator writes stay human: the operator toolset is reads only (ADR-C10 Phase 4; FEAT-044 §6, §9)."},"tags":["operator"],"x-audience":"operator","summary":"Set an agent's tier (FEAT-044 §5.4, DR-167)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). OPERATOR SESSION ONLY. Step-up MFA is required (stepUpCode or stepUpAssertion, exactly one; a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED). Sets the tier T0 to T3 with a reason. T3 (partner toolset eligibility under its own key scope) needs an approved FEAT-024 AGENT_TIER_T3 approval: send its id as approvalId; a T3 request without one is 403 PERMISSION_DENIED details.reason APPROVAL_REQUIRED and no tier changes. A tier only relaxes friction or narrows an unverified caller through the CEL ceiling; it never adds a scope, a tool or a money right (DR-167, LAW-800). Emits agent.tier_changed. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED. Idempotency-Key is required.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTierSetRequest"},"examples":{"t2":{"summary":"Raise to T2 after a manual review","value":{"tier":"T2","reason":"ANS certificate checked against the registration authority record.","stepUpCode":"000000"}},"t3":{"summary":"Raise to T3 with the approved AGENT_TIER_T3 approval","value":{"tier":"T3","reason":"Signed partner agreement and security review passed.","approvalId":"APR-0142","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"The agent with the new tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"tierSet":{"summary":"200 — set to T2","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T2","tierReason":"Set by an operator: ANS certificate checked against the registration authority record.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/OpsAgentWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/agent-registry/{agentId}:suspend":{"post":{"operationId":"opsSuspendAgent","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_suspend_agent","class":"commit-lite","autonomy":"L1","reason":"Operator writes stay human: the operator toolset is reads only (ADR-C10 Phase 4; FEAT-044 §6, §9)."},"tags":["operator"],"x-audience":"operator","summary":"Suspend an agent (FEAT-044 §5.8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). OPERATOR SESSION ONLY. Step-up MFA is required (stepUpCode or stepUpAssertion, exactly one; a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED). Sets suspended true for every version of the agent, with the reason: its requests are refused (401 UNAUTHENTICATED details.reason AGENT_SUSPENDED) and its live grants end; its pending proposals stay for a human to approve or reject (DR-38). Emits agent.suspended (and the outbound CAEP signal behind aikonic.agents.caep.enabled). Suspending a suspended agent changes nothing, so a repeat is harmless. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSuspendRequest"},"examples":{"abuse":{"summary":"Suspend for abuse","value":{"reason":"Repeated signature failures from a rotated key.","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"The suspended agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"suspended":{"summary":"200 — suspended","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":true,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/OpsAgentWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ops/v1/agent-registry/{agentId}:unsuspend":{"post":{"operationId":"opsUnsuspendAgent","x-mcp":{"expose":"excluded","toolset":"operator","name":"ops_unsuspend_agent","class":"commit-lite","autonomy":"L1","reason":"Operator writes stay human: the operator toolset is reads only (ADR-C10 Phase 4; FEAT-044 §6, §9)."},"tags":["operator"],"x-audience":"operator","summary":"Lift a suspension (FEAT-044 §5.8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044). OPERATOR SESSION ONLY. Step-up MFA is required (stepUpCode or stepUpAssertion, exactly one; a missing, wrong or doubled proof is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED). A reason is required. Sets suspended false; the grants and proposals the suspension ended do not come back, the people approve again. Unsuspending an agent that is not suspended is 409 CONFLICT details.reason NOT_SUSPENDED. Emits agent.suspended with suspended false. An MCP grant-derived token is 403 PERMISSION_DENIED SESSION_REQUIRED.","security":[{"bearerAuth":[]}],"x-aikonic-credential":"operator-session","parameters":[{"$ref":"#/components/parameters/AgentIdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSuspendRequest"},"examples":{"cleared":{"summary":"Lift after the key rotation","value":{"reason":"The operator rotated the key and the signatures verify again.","stepUpCode":"000000"}}}}}},"responses":{"200":{"description":"The agent, no longer suspended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredAgent"},"examples":{"unsuspended":{"summary":"200 — suspension lifted","value":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","name":"Acme Concierge","domain":"acme-travel.agents.aikonic.travel","hosted":true,"private":false,"protocols":["mcp","a2a"],"cimdUrl":"https://acme-travel.agents.aikonic.travel/.well-known/oauth-client.json","cardUrl":"https://acme-travel.agents.aikonic.travel/.well-known/agent-card.json","tier":"T1","tierReason":"Hosted handle: the platform proves the domain; no ANS certificate for the running version yet.","suspended":false,"domainProof":{"domain":"acme-travel.agents.aikonic.travel","txtName":"_aikonic-verify.acme-travel.agents.aikonic.travel","txtValue":"av=agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","ok":true,"dnssecValidated":true,"lastCheckedAt":"2026-10-29T09:15:00Z"},"versions":[{"version":"2026.10.1","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d","state":"ACTIVE","certificateState":"NONE","createdAt":"2026-10-29T09:15:00Z"}],"keys":[{"keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"Ed25519","publicJwk":{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo","kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","alg":"EdDSA","use":"sig"},"state":"CURRENT","createdAt":"2026-10-29T09:15:00Z"}],"dnsRecords":[],"createdAt":"2026-10-29T09:15:00Z","createdBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/OpsAgentWriteForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"CONFLICT NOT_SUSPENDED — the agent is not suspended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notSuspended":{"summary":"409 CONFLICT NOT_SUSPENDED","value":{"code":"CONFLICT","message":"This agent is not suspended.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"fix_request","details":{"reason":"NOT_SUSPENDED"}}}}}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"webhooks":{"order.state_changed":{"post":{"operationId":"onOrderStateChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_state_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Order FSM transition (DOMAIN-CONTRACTS C1), incl. PENDING_RECONCILIATION resolution (06 §8)","description":"SIGNED DELIVERY (applies to every webhook here): each delivery carries X-Mondee-Signature (HMAC-SHA256 over timestamp + body with the subscription's signing secret) and X-Mondee-Timestamp; receivers must verify the signature and reject stale timestamps. Missed deliveries are recoverable via GET /v1/events cursor replay. AS BUILT every delivery also carries the combined API-0009 §5 header Mondee-Signature: t=<unix-ts>, v1=<hex HMAC> — both forms are sent today, and neither is renamed here (OD-298). During a secret-rotation overlap window (POST /v1/webhooks/{webhookId}/rotate-secret) a delivery is signed with both secrets: Mondee-Signature carries one v1= value per active secret [PROPOSED, API-0009 §5]; a receiver accepts the delivery when any v1= value verifies. The envelope keeps the contract names (data, tenant); C13's payload / tenantId naming is an open divergence (OD-298).\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"confirmed":{"summary":"BOOKED -> CONFIRMED (DOMAIN-CONTRACTS C1)","value":{"eventId":"ev_01J8Q2","type":"order.state_changed","occurredAt":"2026-09-14T17:06:04Z","tenant":"acme-travel","data":{"orderRef":"MO-8KD-2FQ-9XT","from":"BOOKED","to":"CONFIRMED"}}},"reconciliationResolved":{"summary":"PENDING_RECONCILIATION resolved by the reconciler (06 §8)","description":"The resolution of a 202 AMBIGUOUS_RESULT (03 §10.1 F3). data is a per-event payload and is deliberately untyped — the event catalogue lives in 13, not in this contract.","value":{"eventId":"ev_01J8Q7","type":"order.state_changed","occurredAt":"2026-09-14T17:09:44Z","tenant":"acme-travel","data":{"orderRef":"MO-8KD-2FQ-9XT","from":"PENDING_RECONCILIATION","to":"CONFIRMED","resolvedBy":"RECONCILER"}}}}}}},"responses":{"200":{"description":"Acknowledged (2xx = delivered; else retried with backoff)"}}}},"order.schedule_changed":{"post":{"operationId":"onOrderScheduleChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_schedule_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Supplier-initiated schedule change with accept/amend options (06 §7)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"scheduleChanged":{"summary":"Supplier moved a flight; accept or amend (03 §10.2 F4)","value":{"eventId":"ev_01J8R1","type":"order.schedule_changed","occurredAt":"2026-09-15T02:11:00Z","tenant":"acme-travel","data":{"orderRef":"MO-8KD-2FQ-9XT","itemRef":"OI-1","previousDeparture":"2026-11-03T08:10:00-03:00","departure":"2026-11-03T11:45:00-03:00","options":["ACCEPT","AMEND"]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"hold.expired":{"post":{"operationId":"onHoldExpired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_hold_expired","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Hold auto-released at holdExpiresAt (03 §4)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"holdExpired":{"summary":"Hold auto-released at holdExpiresAt (03 §4)","value":{"eventId":"ev_01J8Q3","type":"hold.expired","occurredAt":"2026-09-14T17:05:41Z","tenant":"acme-travel","data":{"holdRef":"HD-M-4419ab","domain":"MOV","releasedSeats":["A1","A2"]}}},"swept":{"summary":"Emitted by the hold-expiry sweep, with the supplier-side release outcome (BE-7 !72)","value":{"eventId":"ev_5f0c2a9b1d3e4f60a7b8c9d0e1f2a3b4","type":"hold.expired","occurredAt":"2026-09-14T17:05:41Z","tenant":"acme-travel","actor":{"kind":"system","id":"hold-expiry-job"},"data":{"holdRef":"HD-E-9b1c22","repriceRef":"RP-E-71aa30","domain":"EXP","holdExpiresAt":"2026-09-14T17:05:00Z","supplier":"EXAMPLEACTIVITIES2","supplierReleased":true,"releaseOutcome":"RELEASED"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"credential.expiring":{"post":{"operationId":"onCredentialExpiring","x-mcp":{"expose":"excluded","toolset":"public","name":"on_credential_expiring","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"BYOC credential set nearing expiry — T-30/7/1d alerts (08 §6)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"expiring":{"summary":"T-7d alert on a tenant credential set (08 §6)","value":{"eventId":"ev_01J8S9","type":"credential.expiring","occurredAt":"2026-09-14T06:00:00Z","tenant":"acme-travel","data":{"credentialSet":"tenant-acme-hb-01","supplier":"EXAMPLEHOTELS","expiresAt":"2026-09-21T00:00:00Z","daysRemaining":7}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"capabilities.changed":{"post":{"operationId":"onCapabilitiesChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_capabilities_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Capabilities doc version bumped (04 §2 rule 4)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"versionBumped":{"summary":"Capabilities version bumped — refetch GET /v1/capabilities (04 §2 rule 4)","value":{"eventId":"ev_01J8T2","type":"capabilities.changed","occurredAt":"2026-09-14T12:00:00Z","tenant":"acme-travel","data":{"previousVersion":"2026-09-14.2","version":"2026-09-14.3","changed":[{"domain":"HTL","verb":"amend","from":"PLANNED","to":"PARTIAL"}]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"reference.updated":{"post":{"operationId":"onReferenceUpdated","x-mcp":{"expose":"excluded","toolset":"public","name":"on_reference_updated","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Lookup/reference delta invalidation signal (05 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"referenceUpdated":{"summary":"Reference delta — invalidate the cached lookup type (05 §3)","value":{"eventId":"ev_01J8U5","type":"reference.updated","occurredAt":"2026-09-14T04:15:00Z","tenant":"acme-travel","data":{"type":"airports","since":"2026-09-13T00:00:00Z","count":14}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.item_failed":{"post":{"operationId":"onOrderItemFailed","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_item_failed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"One item of a composite order failed (03 §10, API-0009 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"itemFailed":{"summary":"The flight leg failed; the hotel stands (03 §10)","value":{"eventId":"ev_01J9A1","type":"order.item_failed","occurredAt":"2026-09-14T17:07:10Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88122,"actor":{"kind":"system","id":"orchestrator"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemRef":"OI-2","domain":"FLT","code":"SOLD_OUT"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.supplier_cancelled":{"post":{"operationId":"onOrderSupplierCancelled","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_supplier_cancelled","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"The supplier cancelled an item on its own initiative (03 §10, API-0009 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"supplierCancelled":{"summary":"Supplier-initiated cancellation of a hotel item","value":{"eventId":"ev_01J9A2","type":"order.supplier_cancelled","occurredAt":"2026-09-15T08:00:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88130,"actor":{"kind":"system","id":"supplier-sync"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemRef":"OI-1","supplier":"EXAMPLEHOTELS","reason":"PROPERTY_CLOSED"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.docs_required":{"post":{"operationId":"onOrderDocsRequired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_docs_required","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"The supplier needs traveler documents before it can confirm (03 §10, API-0009 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"docsRequired":{"summary":"Passport details required for an international leg","value":{"eventId":"ev_01J9A3","type":"order.docs_required","occurredAt":"2026-09-14T17:08:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88123,"actor":{"kind":"system","id":"orchestrator"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemRef":"OI-2","required":["traveler[0].passportNumber"]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.document_ready":{"post":{"operationId":"onOrderDocumentReady","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_document_ready","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A voucher, e-ticket or invoice is ready (Document §2.10, API-0009 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"voucherReady":{"summary":"Hotel voucher ready — fetch via GET /v1/orders/{orderRef}/documents","value":{"eventId":"ev_01J9A4","type":"order.document_ready","occurredAt":"2026-09-14T17:09:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88124,"actor":{"kind":"system","id":"documents"},"data":{"orderRef":"MO-8KD-2FQ-9XT","documentId":"DOC-7f21","kind":"VOUCHER"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.refund_issued":{"post":{"operationId":"onOrderRefundIssued","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_refund_issued","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Money went back to the customer — the order is the refund source of truth (03 §3 #19)","description":"data is OrderRefundIssuedData (orderRef, amount; refundRef and paymentRef when known, OD-520).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"refundIssued":{"summary":"Refund after a cancellation (03 §3 #19)","value":{"eventId":"ev_01J9A5","type":"order.refund_issued","occurredAt":"2026-09-16T10:00:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88140,"actor":{"kind":"system","id":"orders"},"data":{"orderRef":"MO-8KD-2FQ-9XT","amount":{"amount":69210,"currency":"USD"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"payment.captured":{"post":{"operationId":"onPaymentCaptured","x-mcp":{"expose":"excluded","toolset":"public","name":"on_payment_captured","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Money was captured — the confirmation signal of a capture (API-0010 §3/§6, API-0009 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"captured":{"summary":"Full capture of a card intent","value":{"eventId":"ev_01J9B1","type":"payment.captured","occurredAt":"2026-09-14T17:10:00Z","tenant":"acme-travel","seq":88125,"actor":{"kind":"system","id":"payments"},"data":{"paymentId":"pay_int_91c4","state":"CAPTURED","amount":{"amount":84210,"currency":"USD"},"capturedAmount":{"amount":84210,"currency":"USD"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"payment.failed":{"post":{"operationId":"onPaymentFailed","x-mcp":{"expose":"excluded","toolset":"public","name":"on_payment_failed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A payment failed at the provider, incl. a reconciled PENDING_RECONCILIATION that settled FAILED (API-0010 §3, OD-292)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"failed":{"summary":"Issuer decline reported asynchronously","value":{"eventId":"ev_01J9B2","type":"payment.failed","occurredAt":"2026-09-14T17:10:30Z","tenant":"acme-travel","seq":88126,"actor":{"kind":"system","id":"payments"},"data":{"paymentId":"pay_int_91c5","state":"FAILED","declineCode":"insufficient_funds"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"payment.refunded":{"post":{"operationId":"onPaymentRefunded","x-mcp":{"expose":"excluded","toolset":"public","name":"on_payment_refunded","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A provider-level refund executed (API-0010 §6); order.refund_issued stays the client-facing refund truth (API-0009 §10 Q2)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"refunded":{"summary":"Partial refund executed at the provider","value":{"eventId":"ev_01J9B3","type":"payment.refunded","occurredAt":"2026-09-16T10:00:00Z","tenant":"acme-travel","seq":88141,"actor":{"kind":"system","id":"payments"},"data":{"paymentId":"pay_int_91c4","amount":{"amount":15000,"currency":"USD"},"refundedAmount":{"amount":15000,"currency":"USD"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"payment.canceled":{"post":{"operationId":"onPaymentCanceled","x-mcp":{"expose":"excluded","toolset":"public","name":"on_payment_canceled","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An authorization was voided and its hold released (API-0010 §3, OD-497)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"canceled":{"summary":"Authorization voided at the provider","value":{"eventId":"ev_01J9B4","type":"payment.canceled","occurredAt":"2026-09-16T10:05:00Z","tenant":"acme-travel","seq":88142,"actor":{"kind":"system","id":"payments"},"data":{"paymentId":"pay_int_91c4","state":"CANCELED","amount":{"amount":84210,"currency":"USD"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.proposal.created":{"post":{"operationId":"onAgentProposalCreated","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_proposal_created","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A proposal was filed and is OPEN (FEAT-019 §6, DR-38)","description":"data is AgentProposalEventData. A subscriber never needs the proposal body to act; read GET /v1/agent-proposals/{proposalId} for it.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalEventData"}}}]},"examples":{"created":{"summary":"A proposal was filed and is OPEN","value":{"eventId":"ev_01J9C1","type":"agent.proposal.created","occurredAt":"2026-09-29T15:00:00Z","tenant":"acme-travel","seq":88150,"actor":{"kind":"ai","id":"agt_rebook_22"},"data":{"proposalId":"prop_01J9ZK2","state":"OPEN","approvalAction":"orders.create","resourceRef":"RP-F-7c3d20"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.proposal.approved":{"post":{"operationId":"onAgentProposalApproved","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_proposal_approved","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A human approved the proposal and it executed once (FEAT-019 §6, DR-38)","description":"data is AgentProposalEventData. A subscriber never needs the proposal body to act; read GET /v1/agent-proposals/{proposalId} for it.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalEventData"}}}]},"examples":{"approved":{"summary":"A human approved the proposal and it executed once","value":{"eventId":"ev_01J9C2","type":"agent.proposal.approved","occurredAt":"2026-09-29T15:01:00Z","tenant":"acme-travel","seq":88151,"actor":{"kind":"ai","id":"agt_rebook_22"},"data":{"proposalId":"prop_01J9ZK2","state":"APPROVED","approvalAction":"orders.create","resourceRef":"RP-F-7c3d20","orderRef":"MO-9XT-4KD-1AB"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.proposal.rejected":{"post":{"operationId":"onAgentProposalRejected","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_proposal_rejected","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A human rejected the proposal; nothing executed (FEAT-019 §6, DR-38)","description":"data is AgentProposalEventData. A subscriber never needs the proposal body to act; read GET /v1/agent-proposals/{proposalId} for it.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalEventData"}}}]},"examples":{"rejected":{"summary":"A human rejected the proposal; nothing executed","value":{"eventId":"ev_01J9C3","type":"agent.proposal.rejected","occurredAt":"2026-09-29T15:02:00Z","tenant":"acme-travel","seq":88152,"actor":{"kind":"ai","id":"agt_rebook_22"},"data":{"proposalId":"prop_01J9ZK2","state":"REJECTED","approvalAction":"orders.create","resourceRef":"RP-F-7c3d20"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.proposal.expired":{"post":{"operationId":"onAgentProposalExpired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_proposal_expired","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An OPEN proposal passed expiresAt; nothing executed (FEAT-019 §6, DR-38)","description":"data is AgentProposalEventData. A subscriber never needs the proposal body to act; read GET /v1/agent-proposals/{proposalId} for it.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalEventData"}}}]},"examples":{"expired":{"summary":"An OPEN proposal passed expiresAt; nothing executed","value":{"eventId":"ev_01J9C4","type":"agent.proposal.expired","occurredAt":"2026-09-29T15:03:00Z","tenant":"acme-travel","seq":88153,"actor":{"kind":"ai","id":"agt_rebook_22"},"data":{"proposalId":"prop_01J9ZK2","state":"EXPIRED","approvalAction":"orders.create","resourceRef":"RP-F-7c3d20"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.proposal.execution_unknown":{"post":{"operationId":"onAgentProposalExecutionUnknown","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_proposal_execution_unknown","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Approve ran the verb and its outcome is unknown; the desk resolves it (FEAT-019 §6, DR-38)","description":"data is AgentProposalEventData. A subscriber never needs the proposal body to act; read GET /v1/agent-proposals/{proposalId} for it.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalEventData"}}}]},"examples":{"execution_unknown":{"summary":"Approve ran the verb and its outcome is unknown; the desk resolves it","value":{"eventId":"ev_01J9C5","type":"agent.proposal.execution_unknown","occurredAt":"2026-09-29T15:04:00Z","tenant":"acme-travel","seq":88154,"actor":{"kind":"ai","id":"agt_rebook_22"},"data":{"proposalId":"prop_01J9ZK2","state":"EXECUTION_UNKNOWN","approvalAction":"orders.create","resourceRef":"RP-F-7c3d20"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"mcp.grant.approved":{"post":{"operationId":"onMcpGrantApproved","x-mcp":{"expose":"excluded","toolset":"public","name":"on_mcp_grant_approved","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A person approved an MCP grant request and the console grant is ACTIVE (FEAT-031 §8, DR-78)","description":"data is McpGrantEventData. TENANT-realm (console) grants only; supplier and operator grants are never delivered to a tenant (FEAT-031 §8, DR-78). Exactly one event per transition. No token, code, hash, session id or address is ever carried; read GET /admin/v1/mcp/grants/{grantId} for the grant.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/McpGrantEventData"}}}]},"examples":{"approved":{"summary":"A person approved a console grant","value":{"eventId":"evt_mcg_01JAQ9_approved","type":"mcp.grant.approved","occurredAt":"2026-10-20T10:00:00Z","tenant":"acme-travel","seq":88160,"actor":{"kind":"agent","id":"usr_01J8K2"},"data":{"grantId":"mcg_01JAQ9","toolset":"console","state":"ACTIVE","clientId":"https://client.example.com/mcp-client.json","clientVerified":true,"operations":["list_agent_proposals","get_queue"],"writeOperations":[],"expiresAt":"2026-10-20T18:00:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"mcp.grant.denied":{"post":{"operationId":"onMcpGrantDenied","x-mcp":{"expose":"excluded","toolset":"public","name":"on_mcp_grant_denied","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A person denied an MCP grant request; no grant exists (FEAT-031 §8, DR-78)","description":"data is McpGrantEventData with grantId set to the grant request's requestId (no grant was created). Delivered only when a signed-in person of the tenant denied the request; a request that nobody decided carries no tenant and emits nothing. No token, code, hash, session id or address is ever carried.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/McpGrantEventData"}}}]},"examples":{"denied":{"summary":"A person denied a console grant request","value":{"eventId":"evt_mgr_01JAQ8_denied","type":"mcp.grant.denied","occurredAt":"2026-10-20T10:01:00Z","tenant":"acme-travel","seq":88161,"actor":{"kind":"agent","id":"usr_01J8K2"},"data":{"grantId":"mgr_01JAQ8","toolset":"console","state":"DENIED","clientId":"https://client.example.com/mcp-client.json","clientVerified":false}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"mcp.grant.revoked":{"post":{"operationId":"onMcpGrantRevoked","x-mcp":{"expose":"excluded","toolset":"public","name":"on_mcp_grant_revoked","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A console MCP grant was revoked; reason says why (FEAT-031 §8, DR-78)","description":"data is McpGrantEventData. TENANT-realm (console) grants only; supplier and operator grants are never delivered to a tenant (FEAT-031 §8, DR-78). Exactly one event per transition. No token, code, hash, session id or address is ever carried; read GET /admin/v1/mcp/grants/{grantId} for the grant. reason is set. actor is the person (kind agent) for USER and ADMIN; every other reason, OPERATOR included, carries actor kind system, id platform, so no operator identity reaches a tenant.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/McpGrantEventData"}}}]},"examples":{"revoked":{"summary":"The person's session ended, which revoked the grant","value":{"eventId":"evt_mcg_01JAQ9_revoked","type":"mcp.grant.revoked","occurredAt":"2026-10-20T10:02:00Z","tenant":"acme-travel","seq":88162,"actor":{"kind":"system","id":"platform"},"data":{"grantId":"mcg_01JAQ9","toolset":"console","state":"REVOKED","reason":"SESSION_ENDED","clientId":"https://client.example.com/mcp-client.json","clientVerified":true,"operations":["list_agent_proposals","get_queue"],"writeOperations":[],"expiresAt":"2026-10-20T18:00:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"mcp.grant.expired":{"post":{"operationId":"onMcpGrantExpired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_mcp_grant_expired","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An ACTIVE console MCP grant passed expiresAt (FEAT-031 §8, DR-78)","description":"data is McpGrantEventData. TENANT-realm (console) grants only; supplier and operator grants are never delivered to a tenant (FEAT-031 §8, DR-78). Exactly one event per transition. No token, code, hash, session id or address is ever carried; read GET /admin/v1/mcp/grants/{grantId} for the grant. Emitted by the platform's expiry sweep, at most once per grant; a grant revoked before expiry emits mcp.grant.revoked only.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/McpGrantEventData"}}}]},"examples":{"expired":{"summary":"The grant reached expiresAt","value":{"eventId":"evt_mcg_01JAQ9_expired","type":"mcp.grant.expired","occurredAt":"2026-10-20T10:03:00Z","tenant":"acme-travel","seq":88163,"actor":{"kind":"system","id":"platform"},"data":{"grantId":"mcg_01JAQ9","toolset":"console","state":"EXPIRED","clientId":"https://client.example.com/mcp-client.json","clientVerified":true,"operations":["list_agent_proposals","get_queue"],"writeOperations":[],"expiresAt":"2026-10-20T18:00:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"mcp.settings.changed":{"post":{"operationId":"onMcpSettingsChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_mcp_settings_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A tenant MCP setting changed: the console toolset switch or one write opt-in (FEAT-031 §8, DR-78)","description":"data is McpSettingsEventData. One event per changed setting, with its own eventId per (settings version, tool); an update that changes nothing emits none. Read GET /admin/v1/mcp/settings for the whole state.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/McpSettingsEventData"}}}]},"examples":{"changed":{"summary":"An Owner turned the claim_queue_item write ON","value":{"eventId":"evt_mcs_acme-travel_7_claim_queue_item","type":"mcp.settings.changed","occurredAt":"2026-10-20T10:04:00Z","tenant":"acme-travel","seq":88164,"actor":{"kind":"agent","id":"usr_01J8K2"},"data":{"tool":"claim_queue_item","from":"OFF","to":"ON"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"reference.entity_merged":{"post":{"operationId":"onReferenceEntityMerged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_reference_entity_merged","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"Two reference records merged; re-key the loser to the successor (05 §4, API-0008 §3)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"merged":{"summary":"A provisional place promoted and merged into its canonical id (05 §4)","value":{"eventId":"ev_01J9C1","type":"reference.entity_merged","occurredAt":"2026-09-14T04:20:00Z","tenant":"acme-travel","seq":88100,"actor":{"kind":"system","id":"refdata"},"data":{"type":"places","id":"prov-place-8812","successorId":"mondee:place:CUN-01"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"config.rules_published":{"post":{"operationId":"onRulesPublished","x-mcp":{"expose":"excluded","toolset":"public","name":"on_rules_published","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A markup rule-set version was published (FEAT-013 §8, audit-critical)","description":"Emitted on the C13 envelope on every publish (and every rollback, which republishes). Not yet in the subscribable set of POST /v1/webhooks (FEAT-013 §8 names it an audit event; subscribability is [PROPOSED], OD-296); it is readable on GET /v1/events.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"published":{"summary":"Version 8 published by a revenue manager (FEAT-013 §8)","value":{"eventId":"ev_01J9D1","type":"config.rules_published","occurredAt":"2026-09-20T10:12:00Z","tenant":"acme-travel","seq":88090,"actor":{"kind":"agent","id":"usr_rm_0142"},"data":{"version":8}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"config.rules_retired":{"post":{"operationId":"onRulesRetired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_rules_retired","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A markup rule-set version was superseded and reads back as RETIRED (FEAT-013 §8)","description":"Emitted on the C13 envelope when a publish supersedes the served version. Not yet in the subscribable set of POST /v1/webhooks ([PROPOSED], OD-296); readable on GET /v1/events.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"retired":{"summary":"Version 7 retired by the publish of version 8 (FEAT-013 §8)","value":{"eventId":"ev_01J9D2","type":"config.rules_retired","occurredAt":"2026-09-20T10:12:00Z","tenant":"acme-travel","seq":88091,"actor":{"kind":"agent","id":"usr_rm_0142"},"data":{"version":7}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"rules.floor_pressure":{"post":{"operationId":"onRulesFloorPressure","x-mcp":{"expose":"excluded","toolset":"public","name":"on_rules_floor_pressure","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A supplier's floor-clamp rate crossed the threshold — a commercial review signal (FEAT-013 §8)","description":"Emitted when the clamp rate for a supplier exceeds the threshold ([PROPOSED] 5%, FEAT-013 §8). clampRateBps is clamped / evaluated in basis points. Not yet in the subscribable set of POST /v1/webhooks ([PROPOSED], OD-296); readable on GET /v1/events.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"floorPressure":{"summary":"7.5% of EXAMPLEHOTELS4 evaluations clamped to the floor","value":{"eventId":"ev_01J9D3","type":"rules.floor_pressure","occurredAt":"2026-09-21T00:00:00Z","tenant":"acme-travel","seq":88200,"actor":{"kind":"system","id":"pricing"},"data":{"supplier":"EXAMPLEHOTELS4","evaluated":2000,"clamped":150,"clampRateBps":750}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.dispatch_updated":{"post":{"operationId":"onOrderDispatchUpdated","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_dispatch_updated","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A ground line's dispatch moved — driver assigned, arrived, in progress, completed, no-show (CANONICAL-MODEL §3.3; IMP-P2-082)","description":"Operational, never an order-state change; the line's extension.ground.dispatch is the same object. NO_SHOW and NOT_COMPLETED are terminal dispatch states that may lead to a supplier-initiated price adjustment (order.price_adjusted).\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"driverAssigned":{"summary":"Driver assigned 40 minutes before pickup","value":{"eventId":"ev_01JA7S1","type":"order.dispatch_updated","occurredAt":"2026-11-11T07:30:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88301,"actor":{"kind":"system","id":"dispatch"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemId":"OI-2","previousState":"REQUESTED","dispatch":{"state":"DRIVER_ASSIGNED","driver":{"name":"Carlos","phone":"+5511988880000"},"vehicle":{"make":"Toyota","model":"Corolla","colour":"Black","plate":"ABC1D23"},"trackingUrl":"https://track.example.com/t/9f2a","updatedAt":"2026-11-11T07:30:00-03:00"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.flight_status_changed":{"post":{"operationId":"onOrderFlightStatusChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_flight_status_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A watched flight on a confirmed order changed materially — delay, gate, terminal, belt, departed, landed (EnrichmentProvider SPI §8; IMP-P2-082)","description":"Operational and never an order-state change. Derived by the platform from consecutive FlightStatus snapshots so every provider yields the same event. A disruption that leads the booking supplier to change the itinerary arrives separately as order.schedule_changed, linked by data.causedBy.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"delayed":{"summary":"45-minute departure delay","value":{"eventId":"ev_01JA7S2","type":"order.flight_status_changed","occurredAt":"2026-11-10T21:02:10Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88302,"actor":{"kind":"system","id":"flight-watch"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemId":"OI-1","segmentIndex":0,"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"status":"DELAYED","previousStatus":"SCHEDULED","departure":{"scheduled":"2026-11-10T23:50:00-03:00","estimated":"2026-11-11T00:35:00-03:00","gate":"318"},"arrival":{"scheduled":"2026-11-11T07:10:00-05:00","estimated":"2026-11-11T07:50:00-05:00"},"delayMinutes":{"departure":45,"arrival":40},"alerts":[{"alertId":"fsa_01JA7H","kind":"DELAY","severity":"WARN","detectedAt":"2026-11-10T21:02:10Z","via":"PUSH"}]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.flight_disrupted":{"post":{"operationId":"onOrderFlightDisrupted","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_flight_disrupted","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A CRITICAL or WARN flight alert on a confirmed order — cancellation, diversion, long delay — the ALERT product (SPI §8; IMP-P2-082)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"cancelled":{"summary":"The carrier cancelled the flight","value":{"eventId":"ev_01JA7S3","type":"order.flight_disrupted","occurredAt":"2026-11-10T14:00:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88303,"actor":{"kind":"system","id":"flight-watch"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemId":"OI-1","segmentIndex":0,"kind":"CANCELLATION","severity":"CRITICAL","flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"options":["REBOOK","REFUND"]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.ticket_revoked":{"post":{"operationId":"onOrderTicketRevoked","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_ticket_revoked","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A supplier revoked an issued ticket or voucher after issue (an activity supplier; IMP-P2-082)","description":"The line's redemption state becomes REVOKED; the order may move to CANCELLATION_PENDING or a refund may follow (order.refund_issued).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"revoked":{"summary":"Supplier voided the voucher","value":{"eventId":"ev_01JA7S4","type":"order.ticket_revoked","occurredAt":"2026-11-12T08:00:00Z","tenant":"acme-travel","orderRef":"MO-4QP-7TT-1LM","seq":88304,"actor":{"kind":"system","id":"supplier-feed"},"data":{"orderRef":"MO-4QP-7TT-1LM","itemId":"OI-1","documentId":"doc_01JA7T","reasonCode":"SUPPLIER_VOID","supplier":"GLOBALTIX"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.ticket_expired":{"post":{"operationId":"onOrderTicketExpired","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_ticket_expired","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An open-dated ticket's validity window passed unredeemed (an activity supplier; IMP-P2-082)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"expired":{"summary":"Validity ended, never redeemed","value":{"eventId":"ev_01JA7S5","type":"order.ticket_expired","occurredAt":"2027-05-11T00:00:00Z","tenant":"acme-travel","orderRef":"MO-4QP-7TT-1LM","seq":90120,"actor":{"kind":"system","id":"redemption-sweep"},"data":{"orderRef":"MO-4QP-7TT-1LM","itemId":"OI-1","validUntil":"2027-05-10","redemptionState":"EXPIRED"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.price_adjusted":{"post":{"operationId":"onOrderPriceAdjusted","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_price_adjusted","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A supplier-initiated price change after the trip or service (tolls, waiting time, no-show fees — a transfer supplier; IMP-P2-080)","description":"The adjustment rides the PostBookingAction shape with kind PRICE_ADJUSTMENT and initiator SUPPLIER; the order's priceAdjustments[] carries it and the tenant consents or disputes through the console. Never a silent charge.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"tolls":{"summary":"Tolls billed after the ride","value":{"eventId":"ev_01JA7S6","type":"order.price_adjusted","occurredAt":"2026-11-12T10:00:00Z","tenant":"acme-travel","orderRef":"MO-8KD-2FQ-9XT","seq":88310,"actor":{"kind":"system","id":"supplier-feed"},"data":{"orderRef":"MO-8KD-2FQ-9XT","itemId":"OI-2","adjustmentRef":"PA-9c1f22","kind":"PRICE_ADJUSTMENT","initiator":"SUPPLIER","delta":{"amount":1850,"currency":"USD"},"breakdown":[{"type":"TOLL","amount":1850}],"state":"PENDING_CONSENT"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.supplier_price_mismatch":{"post":{"operationId":"onOrderSupplierPriceMismatch","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_supplier_price_mismatch","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An asynchronous supplier charge did not match the consented price — the async form of SUPPLIER_PRICE_MISMATCH (IMP-P2-082; an insurance supplier)","description":"Raised when a PLATFORM_EMULATED price (local rating table, platform price list) is later charged by the supplier at a different figure. The line goes to the reconciliation queue; nothing is re-charged to the traveller without consent. The synchronous twin is ApiErrorResponse code SUPPLIER_PRICE_MISMATCH on the order write.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"premiumMismatch":{"summary":"Insurer charged a premium the rating table did not predict","value":{"eventId":"ev_01JA7S7","type":"order.supplier_price_mismatch","occurredAt":"2026-11-01T11:00:00Z","tenant":"acme-travel","orderRef":"MO-2AA-9ZZ-4KD","seq":88311,"actor":{"kind":"system","id":"reconciler"},"data":{"orderRef":"MO-2AA-9ZZ-4KD","itemId":"OI-1","code":"SUPPLIER_PRICE_MISMATCH","consentedPrice":{"amount":189000,"currency":"INR"},"supplierPrice":{"amount":196000,"currency":"INR"},"firmness":"PLATFORM_EMULATED","firmnessBasis":"LOCAL_TABLE","queue":"RECONCILIATION"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"order.supplier_event":{"post":{"operationId":"onOrderSupplierEvent","x-mcp":{"expose":"excluded","toolset":"public","name":"on_order_supplier_event","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A supplier-side fact about a booked item that changes no order state (v1.7.0; seated, done, no-show)","description":"Published since backend !166; declared in connect-v1 v1.7.0. A supplier status the platform does not apply to the order (the order stays as it is; a seated or no-show table was still booked) lands on the order timeline and is delivered here. data carries only canonical, tenant-safe facts, never the supplier payload: supplierEventKind (STATUS_CHANGED | OTHER), occurredAt, supplierState (the canonical order state the supplier reported, when any) and supplierLifecycle, an upper-case token from the adapter's catalogue. FNB tokens: BOOKED, DINER_CONFIRMED, LATE, ARRIVED, SEATED, DONE, NO_SHOW, CANCELLED, UNRECOGNISED. An UNRECOGNISED status also opens an OFFLINE_SUPPLIER_ACTION / VERIFY ops case. Open strings: a client ignores a token it does not know.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"seated":{"summary":"FNB — the party was seated","value":{"eventId":"supplier-event:3f2a9c0d1e4b5a6978c1d2e3f4a5b6c7","type":"order.supplier_event","occurredAt":"2026-11-13T03:31:00Z","tenant":"acme-travel","orderRef":"MO-4DN-7QK-2TB","seq":90211,"actor":{"kind":"system","id":"supplier-events"},"data":{"orderRef":"MO-4DN-7QK-2TB","supplier":"EXAMPLEDINING","supplierEventKind":"OTHER","occurredAt":"2026-11-13T03:31:00Z","supplierLifecycle":"SEATED"}}},"noShow":{"summary":"FNB — the party did not arrive","value":{"eventId":"supplier-event:9a8b7c6d5e4f30211203f4e5d6c7b8a9","type":"order.supplier_event","occurredAt":"2026-11-13T04:20:00Z","tenant":"acme-travel","orderRef":"MO-4DN-7QK-2TB","seq":90230,"actor":{"kind":"system","id":"supplier-events"},"data":{"orderRef":"MO-4DN-7QK-2TB","supplier":"EXAMPLEDINING","supplierEventKind":"OTHER","occurredAt":"2026-11-13T04:20:00Z","supplierLifecycle":"NO_SHOW"}}},"unrecognised":{"summary":"FNB — a status nobody catalogued; a VERIFY case is opened","value":{"eventId":"supplier-event:0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f","type":"order.supplier_event","occurredAt":"2026-11-13T03:40:00Z","tenant":"acme-travel","orderRef":"MO-4DN-7QK-2TB","seq":90215,"actor":{"kind":"system","id":"supplier-events"},"data":{"orderRef":"MO-4DN-7QK-2TB","supplier":"EXAMPLEDINING","supplierEventKind":"OTHER","occurredAt":"2026-11-13T03:40:00Z","supplierLifecycle":"UNRECOGNISED"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"enquiry.submitted":{"post":{"operationId":"onEnquirySubmitted","x-mcp":{"expose":"excluded","toolset":"public","name":"on_enquiry_submitted","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An enquiry became SUBMITTED (v1.7.0)","description":"Delivered for POST /v1/enquiries outcomes (v1.7.0). data is EnquiryEventData: the enquiry reference, domain, product, status, the supplier reference when there is one and createdAt. Never the contact or any other personal data; read GET /v1/enquiries/{enquiryRef} for the rest.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"submitted":{"summary":"The supplier acknowledged the lead","value":{"eventId":"ev_01JBSUBM","type":"enquiry.submitted","occurredAt":"2026-10-05T17:04:12Z","tenant":"acme-travel","seq":91001,"actor":{"kind":"system","id":"enquiries"},"data":{"enquiryRef":"EN-4KD-8QT-2ZP","domain":"FNB","productId":"prov:dining:100123","status":"SUBMITTED","createdAt":"2026-10-05T17:04:11Z","supplierReference":"lead-7731"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"enquiry.unconfirmed":{"post":{"operationId":"onEnquiryUnconfirmed","x-mcp":{"expose":"excluded","toolset":"public","name":"on_enquiry_unconfirmed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An enquiry became UNCONFIRMED (v1.7.0)","description":"Delivered for POST /v1/enquiries outcomes (v1.7.0). data is EnquiryEventData: the enquiry reference, domain, product, status, the supplier reference when there is one and createdAt. Never the contact or any other personal data; read GET /v1/enquiries/{enquiryRef} for the rest.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"unconfirmed":{"summary":"The supplier did not answer; the lead is never re-sent","value":{"eventId":"ev_01JBUNCO","type":"enquiry.unconfirmed","occurredAt":"2026-10-05T17:04:12Z","tenant":"acme-travel","seq":91001,"actor":{"kind":"system","id":"enquiries"},"data":{"enquiryRef":"EN-4KD-8QT-2ZP","domain":"FNB","productId":"prov:dining:100123","status":"UNCONFIRMED","createdAt":"2026-10-05T17:04:11Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"enquiry.failed":{"post":{"operationId":"onEnquiryFailed","x-mcp":{"expose":"excluded","toolset":"public","name":"on_enquiry_failed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An enquiry became FAILED (v1.7.0)","description":"Delivered for POST /v1/enquiries outcomes (v1.7.0). data is EnquiryEventData: the enquiry reference, domain, product, status, the supplier reference when there is one and createdAt. Never the contact or any other personal data; read GET /v1/enquiries/{enquiryRef} for the rest.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"failed":{"summary":"The supplier refused the lead","value":{"eventId":"ev_01JBFAIL","type":"enquiry.failed","occurredAt":"2026-10-05T17:04:12Z","tenant":"acme-travel","seq":91001,"actor":{"kind":"system","id":"enquiries"},"data":{"enquiryRef":"EN-4KD-8QT-2ZP","domain":"FNB","productId":"prov:dining:100123","status":"FAILED","createdAt":"2026-10-05T17:04:11Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.minted":{"post":{"operationId":"onKeyMinted","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_minted","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An API key was minted (FEAT-034 §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData: the key, its kind, principal, agent and parent key (mintedBy, null for a session mint). Never the secret. Read GET /v1/keys for the scope.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"minted":{"summary":"An agent key minted it a narrower key for the quick-start","value":{"eventId":"evt_key_01JBK91201","type":"key.minted","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91201,"actor":{"kind":"ai","id":"mo_test_SK7HQ2WN4R8T5M3Z"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.paused":{"post":{"operationId":"onKeyPaused","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_paused","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An API key was paused by a velocity or loop rule, or by an Owner (FEAT-034 §5.4, §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData with reason, evidence (ApiKeyPauseEvidence: the rule, the dimension, observed against the limit, the window and the request ids) and resumeBy owner. Every call with the key now answers 423 KEY_PAUSED until an Owner or Admin resumes it. Also a notification of kind key.paused. Emitted by the velocity rules only while aikonic.keys.velocity.enabled is on.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"loopDetected":{"summary":"21 identical holds in 60 seconds paused the key (LOOP_DETECTED)","value":{"eventId":"evt_key_01JBK91202","type":"key.paused","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91202,"actor":{"kind":"system","id":"velocity"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"PAUSED","reason":"LOOP_DETECTED","resumeBy":"owner","evidence":{"rule":"LOOP_IDENTICAL","dimension":"identicalRequestsPerMinute","observed":21,"limit":20,"windowSeconds":60,"summary":"21 identical POST /v1/hold in 60 s","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736","0af7651916cd43dd8448eb211c80319c"],"at":"2026-10-27T10:02:00Z"}}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.resumed":{"post":{"operationId":"onKeyResumed","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_resumed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A paused API key was resumed by an Owner or Admin (FEAT-034 §5.4, §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData with the resume reason and category; actor is the person who resumed it. Also a notification of kind key.resumed.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"resumed":{"summary":"The Owner fixed the script and resumed the key","value":{"eventId":"evt_key_01JBK91203","type":"key.resumed","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91203,"actor":{"kind":"user","id":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","reason":"The quick-start retry loop is fixed","category":"fixed"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.revoked":{"post":{"operationId":"onKeyRevoked","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_revoked","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An API key was revoked (FEAT-034 §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData; reason SYSTEM_STOP_IGNORED when the platform revoked a paused key that kept calling (100 calls in 10 minutes after stop, FEAT-034 §12), else absent.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"revoked":{"summary":"The Owner revoked an agent key","value":{"eventId":"evt_key_01JBK91204","type":"key.revoked","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91204,"actor":{"kind":"user","id":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"REVOKED"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.rotated":{"post":{"operationId":"onKeyRotated","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_rotated","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An API key was rotated; the successor carries its scope (FEAT-034 §5.3, §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData with successorKeyPrefix and overlapUntil (the old key works until then, then reads EXPIRED). Never either secret.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"rotated":{"summary":"The Owner rotated an agent key early (an emergency rotation, no 7-day rule); 24 h overlap","value":{"eventId":"evt_key_01JBK91205","type":"key.rotated","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91205,"actor":{"kind":"user","id":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","successorKeyPrefix":"mo_test_NX5QW8RT2KM4P7ZB","overlapUntil":"2026-10-28T10:02:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"key.expiring":{"post":{"operationId":"onKeyExpiring","x-mcp":{"expose":"excluded","toolset":"public","name":"on_key_expiring","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An API key expires within 7 days (FEAT-034 §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.keys.agent-mint.enabled is on, else never). data is KeyEventData with expiresAt. Rotate it (POST /v1/keys/{keyPrefix}/rotate) to keep its scope; emitted once per key [assumption].","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KeyEventData"}}}]},"examples":{"expiring":{"summary":"The key expires in 7 days","value":{"eventId":"evt_key_01JBK91206","type":"key.expiring","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91206,"actor":{"kind":"system","id":"platform"},"data":{"keyPrefix":"mo_test_AG3NT7KQ2M4P9R5X","kind":"agt","environment":"sandbox","principalUserId":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93","agent":"claude-code/2.4 (claude-fable-5-1)","mintedBy":"mo_test_SK7HQ2WN4R8T5M3Z","state":"ACTIVE","expiresAt":"2026-11-03T10:02:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"signup.device.approved":{"post":{"operationId":"onSignupDeviceApproved","x-mcp":{"expose":"excluded","toolset":"public","name":"on_signup_device_approved","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A person approved an agent device-code sign-up; the tenant exists (FEAT-034 §5.2, §8)","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8; emitted when aikonic.signup.device-flow.enabled is on, else never). data is DeviceSignupEventData: the user code, the agent, the tenant origin (no e-mail, FEAT-020 §8). Recorded on the new tenant when the first poll after approval creates it, so it is replayable from GET /v1/events once a subscription exists [assumption: the tenant has no subscription at approval]. Also a notification of kind signup.device.approved.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeviceSignupEventData"}}}]},"examples":{"approved":{"summary":"priya approved Claude Code's sign-up","value":{"eventId":"evt_key_01JBK91207","type":"signup.device.approved","occurredAt":"2026-10-27T10:02:00Z","tenant":"TEN-EVAL-S5KH8Q2W","seq":91207,"actor":{"kind":"user","id":"3c8e1f42-7a5b-4d9e-8c21-6f0b2a4d7e93"},"data":{"userCode":"WDJB-MJHT","agent":"claude-code/2.4 (claude-fable-5-1)","origin":"agent","approvedAt":"2026-10-27T09:12:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.registered":{"post":{"operationId":"onAgentRegistered","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_registered","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A tenant registered an agent (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentRegisteredEventData: agentId, handle and protocols. Delivered to the owning tenant when registerAgent succeeds; replayable from GET /v1/events once a subscription exists.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentRegisteredEventData"}}}]},"examples":{"event":{"summary":"Acme registered a hosted agent","value":{"eventId":"evt_agt_93001","type":"agent.registered","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93001,"actor":{"kind":"user","id":"1e9bb43d-5e35-42ba-90f1-152117834fb9"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","handle":"concierge@acme-travel.agents.aikonic.travel","protocols":["mcp","a2a"]}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.version_registered":{"post":{"operationId":"onAgentVersionRegistered","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_version_registered","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"A new agent version was added (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentVersionRegisteredEventData: agentId, version, modelId and promptSha256 (never the prompt). Delivered when registerAgentVersion succeeds.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentVersionRegisteredEventData"}}}]},"examples":{"event":{"summary":"Version 2026.10.2 registered","value":{"eventId":"evt_agt_93002","type":"agent.version_registered","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93002,"actor":{"kind":"user","id":"1e9bb43d-5e35-42ba-90f1-152117834fb9"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","version":"2026.10.2","modelId":"claude-sonnet-5-5","promptSha256":"9f2b7c1d4e6a8b0c3d5f7a9b1c2e4d6f8a0b3c5d7e9f1a2b4c6d8e0f2a4b6c8d"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.version_revoked":{"post":{"operationId":"onAgentVersionRevoked","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_version_revoked","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An agent version was revoked (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentVersionRevokedEventData: agentId, version, reason, liveGrantsEnded and pendingProposals. Delivered when revokeAgentVersion succeeds; outbound Shared Signals (CAEP) follow behind aikonic.agents.caep.enabled.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentVersionRevokedEventData"}}}]},"examples":{"event":{"summary":"Version 2026.10.1 revoked; two grants ended","value":{"eventId":"evt_agt_93003","type":"agent.version_revoked","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93003,"actor":{"kind":"user","id":"1e9bb43d-5e35-42ba-90f1-152117834fb9"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","version":"2026.10.1","reason":"System prompt leaked in a public paste.","liveGrantsEnded":2,"pendingProposals":1}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.key_rotated":{"post":{"operationId":"onAgentKeyRotated","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_key_rotated","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An agent key was added or retired (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentKeyRotatedEventData: agentId, keyId, state and retiresAt for a RETIRING key. Delivered when addAgentKey or retireAgentKey succeeds, once per key whose state changed.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentKeyRotatedEventData"}}}]},"examples":{"event":{"summary":"The previous key is RETIRING for seven days","value":{"eventId":"evt_agt_93004","type":"agent.key_rotated","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93004,"actor":{"kind":"user","id":"1e9bb43d-5e35-42ba-90f1-152117834fb9"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","keyId":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","state":"RETIRING","retiresAt":"2026-11-05T10:00:00Z"}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.tier_changed":{"post":{"operationId":"onAgentTierChanged","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_tier_changed","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An agent tier moved (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentTierChangedEventData: agentId, from, to and reason. Delivered when verifyAgentDomain raises a tier, when a domain proof is lost, and when opsSetAgentTier sets one (DR-167: a tier never widens authority).","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentTierChangedEventData"}}}]},"examples":{"event":{"summary":"The domain proof lapsed, T1 to T0","value":{"eventId":"evt_agt_93005","type":"agent.tier_changed","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93005,"actor":{"kind":"system","id":"platform"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","from":"T1","to":"T0","reason":"Domain proof lost: the TXT record was removed."}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}},"agent.suspended":{"post":{"operationId":"onAgentSuspended","x-mcp":{"expose":"excluded","toolset":"public","name":"on_agent_suspended","class":"read","autonomy":"L0","reason":"outbound webhook event — delivered to the tenant, not called by an agent"},"summary":"An operator suspended or unsuspended an agent (FEAT-044 §8)","description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §8). data is AgentSuspendedEventData: agentId, suspended and reason. Delivered to the owning tenant when opsSuspendAgent or opsUnsuspendAgent succeeds; outbound Shared Signals (CAEP) follow behind aikonic.agents.caep.enabled.","requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentSuspendedEventData"}}}]},"examples":{"event":{"summary":"An operator suspended the agent","value":{"eventId":"evt_agt_93006","type":"agent.suspended","occurredAt":"2026-10-29T10:00:00Z","tenant":"TEN-ACME","seq":93006,"actor":{"kind":"operator","id":"op_51d2e0"},"data":{"agentId":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D","suspended":true,"reason":"Repeated signature failures from a rotated key."}}}}}}},"responses":{"200":{"description":"Acknowledged"}}}}},"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Self-serve API keys from the console (mo_test_ sandbox, mo_live_ production), sent as X-Api-Key. A key holds a set of scopes (the permission strings listed under oauthClientCredentials) and calls only the operations those scopes allow.\n"},"oauthClientCredentials":{"type":"oauth2","description":"Machine-to-machine tokens: an API key exchanged at POST /v1/oauth/token (relative to each server) for a Bearer access token of at most 15 minutes. client_id is the key's prefix, client_secret the whole key. The scopes are the permission strings an API key can hold, and a token only ever narrows its key's.\n","flows":{"clientCredentials":{"tokenUrl":"/v1/oauth/token","scopes":{"search:flights":"Shop flights","search:ground":"Shop ground transport","search:cars":"Shop cars","search:hotels":"Shop hotels","search:cruises":"Shop cruises","search:experiences":"Shop experiences","search:events":"Shop events","search:ancillary-products":"Shop ancillary products","search:packages":"Shop packages","search:dining":"Shop dining; also gates enquiries to dining suppliers","dining:partner":"The restaurant-partner surface /v1/dining/partner","data:read":"Read reference data","data:subscribe":"Subscribe to data feeds","orders:write":"Reprice, hold and book","booking:write":"Permanent alias of orders:write","orders:read":"Read and search orders, documents and events","orders:cancel":"Cancel orders","orders:amend":"Amend orders","payments:write":"Payment intents and confirmation","cart:write":"Cart mutations","webhooks:manage":"Manage webhook subscriptions","content:read":"Read static content","agents:propose":"Submit and read agent proposals (an agent service principal)","tenant:read":"Read the account activation steps and certification status; in the Owner and Admin default bundles","lookups:personal":"Read one traveller's consented personal data in a lookup, such as a loyalty balance; in the Owner and Admin bundles only","finance:read":"Read wallets and their movements","finance:write":"Change wallet settings; in no key's default grant"}}}},"oauthClientBasic":{"type":"http","scheme":"basic","description":"Token endpoint only (POST /v1/oauth/token): client_id = the API key's prefix, client_secret = the whole API key, each form-urlencoded, joined by a colon, base64 (RFC 6749 §2.3.1). connect-v1 1.26.0: the AI plane's exchange client authenticates the token-exchange grant the same way, with its own configured client id and secret (never an API key).\n"},"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"An operation marked `x-aikonic-credential: human-session` accepts ONLY a human session token from POST /v1/auth/login; a service token or an API key is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED whatever its scopes (OD-403, FEAT-019 §9). OpenAPI cannot tell the two bearer kinds apart, so the extension is the machine-readable contract; it is an extension rather than a second security scheme because a second bearer scheme makes the Kotlin generator emit a conflicting setBearerToken overload (OD-461). Console tokens (login {username, password, siteId}) and, since FEAT-020 phase C, service tokens issued by POST /v1/oauth/token (they carry client_id and no site). Tenant derived from the token, never the body. A service token is accepted exactly as its API key would be, and is refused at once when that key is revoked. connect-v1 1.26.0 (OD-884 Phase 2, DR-67): a third bearer kind, the downstream token the AI plane obtains by token exchange for an MCP grant a human approved. It is never a human session: every human-session operation answers it 403 PERMISSION_DENIED details.reason SESSION_REQUIRED (details.credential mcp-grant), and the platform serves it only the read operations of the grant's toolset that the human ticked, under the human's live permissions. The MCP client's own access token (audience the MCP resource) is refused by every platform route.\n"}},"headers":{"RateLimitLimit":{"description":"The burst size of the tightest rate-limit bucket this request was counted against (draft-ietf-httpapi-ratelimit-headers). Sent while the limiter is on.\n","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in that bucket right now. Sent while the limiter is on.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until that bucket is full again. Sent while the limiter is on.","schema":{"type":"integer"}},"RateLimitPolicy":{"description":"The bucket's policy as \"<burst>;w=<window seconds>\", for example \"20;w=2\" for the supplier budget of the standard plan. Sent while the limiter is on.\n","schema":{"type":"string"}},"X-Correlation-Id":{"description":"The request's X-Correlation-Id echoed (or the trace id when none was sent)","schema":{"type":"string","maxLength":128}},"X-Principal":{"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §6; sent when aikonic.keys.agent-mint.enabled is on, else absent). The principal the request was attributed to — the key's principal, or the Member named by X-On-Behalf-Of — as the lowercase hex SHA-256 of the principal's user id, never the id or the e-mail itself [assumption: the hash; FEAT-034 §6 says \"hashed\"]. Echoed so an agent can tell which person a call was attributed to without learning who.\n","schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}}},"parameters":{"AgentIdPath":{"name":"agentId","in":"path","required":true,"description":"A registered agent id, agt_ followed by a 26-character ULID (FEAT-044 §7).","schema":{"type":"string","pattern":"^agt_[0-9A-Z]{26}$"}},"AgentVersionPath":{"name":"version","in":"path","required":true,"description":"The version string the agent registered, for example 2026.10.1 (1 to 64 characters).","schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$"}},"AgentKeyIdPath":{"name":"keyId","in":"path","required":true,"description":"The key id: the RFC 7638 JWK thumbprint of the key, base64url without padding.","schema":{"type":"string","minLength":43,"maxLength":64,"pattern":"^[A-Za-z0-9_-]{43,64}$"}},"SignatureHeader":{"name":"Signature","in":"header","required":false,"description":"RFC 9421 Web Bot Auth: the signature value, labelled to match Signature-Input. Optional: an unsigned request is served as unverified (FEAT-044 §5.2). Present and wrong is 401 AGENT_SIGNATURE_INVALID.","schema":{"type":"string","maxLength":2048}},"SignatureInputHeader":{"name":"Signature-Input","in":"header","required":false,"description":"RFC 9421 Web Bot Auth: the covered components and parameters (created, keyid, tag=\"web-bot-auth\"). The signature must cover @authority or @target-uri, @method, @path and signature-agent, and content-digest on bodies; created may be at most 300 seconds old and 30 seconds in the future (DR-173).","schema":{"type":"string","maxLength":2048}},"SignatureAgentHeader":{"name":"Signature-Agent","in":"header","required":false,"description":"Web Bot Auth: the HTTPS URL of the signer, whose key directory is /.well-known/http-message-signatures-directory (200 only, no redirects, cached for at most one hour).","schema":{"type":"string","maxLength":2048}},"AgentVersionHeader":{"name":"Agent-Version","in":"header","required":false,"description":"The version a registered agent runs. It counts only when it is a covered component of the signature (SHIP-feat044 A3); a covered REVOKED version is 401 AGENT_VERSION_REVOKED, and an absent or unknown version gives no ANS proof (tier at most T1).","schema":{"type":"string","minLength":1,"maxLength":64}},"PlatformSupplierCode":{"name":"supplierCode","in":"path","required":true,"schema":{"type":"string","pattern":"^SUP-[A-Z0-9]+(-[A-Z0-9]+)+$"},"description":"A supplier registry code, e.g. SUP-BUS-EXAMPLE (connect-v1 1.17.0)"},"SupplierCallId":{"name":"callId","in":"path","required":true,"schema":{"type":"string","maxLength":64},"description":"One supplier call attempt's id, as GET /ops/v1/supplier-calls lists it (connect-v1 1.25.0)"},"XAgent":{"name":"X-Agent","in":"header","required":false,"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §6). The calling agent, as <name>/<version> (<model>), at most 120 characters, e.g. claude-code/2.4 (claude-fable-5-1). Optional on every key-authenticated route in phase 1 (OD-AGT-5 default; required on agt keys in phase 2). Logged and stamped as the agent of the request's records (Attribution.agent) and of a key minted on it (ApiKeyMetadata.agent); a product string, never personal data. POST /v1/signup/device reads it when the body names no agent.\n","schema":{"type":"string","maxLength":120},"example":"claude-code/2.4 (claude-fable-5-1)"},"XOnBehalfOf":{"name":"X-On-Behalf-Of","in":"header","required":false,"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §6, §9; honoured when aikonic.keys.agent-mint.enabled is on, else ignored). The Member of the tenant this call is made for, by e-mail; the request's records carry that Member as principalUserId instead of the key's principal. Accepted on any key-authenticated route: on a key minted by another key (kind agt) it must equal the key's stamped principal; on a key minted by a session (kind own) it is honoured only when the key's principal is an Owner or Admin [assumption]. Anything else — another Member on an agt key, any Member on an own key whose principal is not an Owner or Admin, an address that is not an active Member of the credential's tenant, another tenant's Member — is 403 PERMISSION_DENIED details.reason PRINCIPAL_UNKNOWN (nextAction widen_scope_via_owner).\n","schema":{"type":"string","format":"email","maxLength":254},"example":"priya@skyhop.example"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","format":"uuid"},"description":"Mandatory on mutations (03 §7): **48h TTL** (fixed by DR-16, closing OD-01); replay with same body-hash returns the original response with Idempotency-Replayed true; same key + different body is 409 IDEMPOTENCY_CONFLICT; in-flight duplicate is 409 IDEMPOTENCY_IN_PROGRESS.\n"},"PaymentMethodIdPath":{"name":"methodId","in":"path","required":true,"schema":{"type":"string"},"description":"A saved payment method's id (SavedPaymentMethod.id)"},"InvoiceIdPath":{"name":"invoiceId","in":"path","required":true,"schema":{"type":"string"},"description":"An invoice's id (Invoice.id)"},"SearchRefPath":{"name":"searchRef","in":"path","required":true,"schema":{"type":"string"},"description":"Async search reference (03 §4; TTL 15 min after last poll [assumption])"},"OrderRefPath":{"name":"orderRef","in":"path","required":true,"schema":{"type":"string"},"description":"MO-XXX-XXX-XXX (D-48); BK- retained as read alias"},"PaymentIdPath":{"name":"paymentId","in":"path","required":true,"schema":{"type":"string"},"description":"The paymentId returned by POST /v1/payments/intent (API-0010 §3)"},"WebhookIdPath":{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"},"description":"The webhookId returned by POST /v1/webhooks (API-0009 §3)"},"DeviceIdPath":{"name":"deviceId","in":"path","required":true,"schema":{"type":"string"},"description":"The deviceId returned by POST /v1/devices (FEAT-021 §18a)"},"ActorKind":{"name":"X-Actor-Kind","in":"header","required":false,"schema":{"type":"string"},"description":"The channel that initiated a money verb: ai marks an AI-initiated call, which must then carry X-Approval-Token (D-50, 12 §B2.4, API-0010 §5). NOT a trust signal (OD-305): the platform binds the actor kind to the credential — an AI principal's call is AI whatever this header says, and omitting it never exempts a call from the D-50 gate. The header is accepted for compatibility and can only mark a call ai, never un-mark one. New routes (POST /v1/approvals, /v1/agent-proposals) do not take it.\n"},"FareSourceDomainPath":{"name":"domain","in":"path","required":true,"description":"Canonical domain code or a registered alias (02 §1), e.g. EXP","schema":{"type":"string"}},"ApprovalToken":{"name":"X-Approval-Token","in":"header","required":false,"schema":{"type":"string"},"description":"The D-50 approval grant from POST /v1/approvals (approvalToken; opaque) presented on the retry after a 403 whose details.reason is APPROVAL_REQUIRED. Redeemed by the payment verbs and by POST /v1/orders (orders.create), cancel (orders.cancel) and amend COMMIT (amend.commit) (DR-39). Human sessions and system keys (DR-40) are exempt. Single use, ~2 min TTL, bound to tenant + action + resource + amount; any drift fails closed and burns the token (API-0010 §5).\n"},"PageQuery":{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, 1-based (console lists; HANDOVER-w25 §4)"},"PageSizeQuery":{"name":"pageSize","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Rows per page, 1..200 (console lists)"},"UserIdPath":{"name":"userId","in":"path","required":true,"schema":{"type":"string"},"description":"A tenant user's id (AccessContext.userId)"},"QueuePath":{"name":"queue","in":"path","required":true,"schema":{"type":"string","enum":["ON_REQUEST_AGING","RECONCILIATION","DOC_GEN_FAILED","SCHEDULE_CHANGES","OFFLINE_SUPPLIER_ACTION","REQUEST_TO_BOOK","VOUCHER_REISSUE"]},"description":"One of the fulfilment queues (09 §4; seven since connect-v1 v1.5.0). The path parameter keeps a closed enum (a request-side addition is additive); the response-side ConsoleQueueName is the matching x-extensible-enum.\n"},"QueueItemIdPath":{"name":"itemId","in":"path","required":true,"schema":{"type":"string"},"description":"A queue row id (ConsoleQueueItem.itemId)"},"RuleSetVersionPath":{"name":"version","in":"path","required":true,"schema":{"type":"integer","minimum":1},"description":"An immutable rule-set version number (FEAT-013 §5)"},"CredentialSetIdPath":{"name":"credentialSetId","in":"path","required":true,"schema":{"type":"string"},"description":"One of the tenant's own credential-set ids (08 §3)"},"ProposalIdPath":{"name":"proposalId","in":"path","required":true,"schema":{"type":"string"},"description":"An agent proposal id (FEAT-019 §7)"},"McpGrantRequestIdPath":{"name":"requestId","in":"path","required":true,"schema":{"type":"string","maxLength":64},"description":"A pending MCP grant request id, from the consent redirect (?request=) or a user-code lookup. Opaque and unguessable: at least 128 bits from a cryptographic random source, never a sequential or monotonic id, because it is the only handle in the consent URL.\n"},"McpGrantIdPath":{"name":"grantId","in":"path","required":true,"schema":{"type":"string","maxLength":64},"description":"An MCP grant id (FEAT-031 §7)"},"CaseIdPath":{"name":"caseId","in":"path","required":true,"description":"case_ + 16 hex characters","schema":{"type":"string"}},"FlagEnvironmentQuery":{"name":"environment","in":"query","required":false,"description":"SANDBOX | PRODUCTION (case-insensitive). Default PRODUCTION on the PROD rung, SANDBOX elsewhere.","schema":{"type":"string"}},"CopilotSessionIdPath":{"name":"sessionId","in":"path","required":true,"description":"CPS-<uuid>; must match [A-Za-z0-9_-]{1,64} (400 VALIDATION ID_MALFORMED otherwise)","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"SandboxTest":{"name":"Test","in":"header","required":false,"description":"Sandbox scenario simulation (BE-7, !72): price_changed | sold_out | ambiguous | hold_expired | payment_declined | supplier_timeout | partial_search | duplicate_order (case-insensitive). Accepted only from a mo_test_ API key on a rung where aikonic.sandbox.simulation is on (DEV and SANDBOX by default; UAT and PROD refuse to boot with it on); anything else is 400 VALIDATION details.reason TEST_HEADER_NOT_ALLOWED, an unknown value 400 UNKNOWN_TEST_SCENARIO (details.accepted). Read on every travel verb before the permission wall and idempotency; a scenario sent on a verb it does not apply to has no effect. Every simulated error carries details.simulated true and details.scenario. price_changed (reprice, hold, orders) → 409 PRICE_CHANGED; sold_out → 422 SOLD_OUT; hold_expired (orders) → 409 HOLD_EXPIRED; duplicate_order (orders) → 409 DUPLICATE_ORDER existingOrderRef MO-SIM-DUPLICATE; payment_declined (orders, POST /v1/payments/{paymentId}/confirm) → 402 PAYMENT_FAILED; supplier_timeout → 502 UPSTREAM_UNAVAILABLE; partial_search (search) → two SUPPLIER_DROPPED warnings; ambiguous (orders) → 202 PENDING_RECONCILIATION on an MO-SIM- ref. wallet_insufficient (v1.18.0, served since 1.18.1, FEAT-030 WP-3; orders with payment.mode WALLET, matched exact-case) → 402 PAYMENT_FAILED details.reason WALLET_INSUFFICIENT.\n","schema":{"type":"string"}},"Traceparent":{"name":"traceparent","in":"header","required":false,"description":"W3C Trace Context (version-traceid-parentid-flags). When sent, the platform continues the trace and its 32-hex trace id is the ApiErrorResponse.traceId and the response context.traceId; when absent a new trace starts.\n","schema":{"type":"string","pattern":"^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$"}},"CorrelationId":{"name":"X-Correlation-Id","in":"header","required":false,"description":"The caller's own correlation handle (≤ 128 printable ASCII). Echoed on the response and stamped on the API log and the audit row; it never replaces traceId as the support handle.\n","schema":{"type":"string","maxLength":128}},"WalletCurrencyPath":{"name":"currency","in":"path","required":true,"description":"The wallet's settlement currency, ISO-4217 (one wallet per tenant per currency, FEAT-030 §5.5)","schema":{"type":"string","pattern":"^[A-Z]{3}$"},"example":"USD"},"WalletEnvironmentQuery":{"name":"environment","in":"query","required":false,"description":"Which side of the sandbox/production tenant pair (07 §5) to read or act on. An API key is bound to its own side (mo_test_ = SANDBOX, mo_live_ = PRODUCTION): absent means that side, and naming the other is 400 VALIDATION details.reason ENVIRONMENT_MISMATCH. A human session may name either; absent means SANDBOX while the tenant is TRIAL and PRODUCTION once it is LIVE [assumption].\n","schema":{"$ref":"#/components/schemas/WalletEnvironment"}},"EnquiryRefPath":{"name":"enquiryRef","in":"path","required":true,"description":"EN-XXX-XXX-XXX from POST /v1/enquiries","schema":{"type":"string"},"example":"EN-4KD-8QT-2ZP"},"DiningRestaurantIdPath":{"name":"restaurantId","in":"path","required":true,"description":"The restaurant's supplier id (Example Dining rid), as text","schema":{"type":"string","maxLength":32},"example":"100123"},"DiningGuestIdPath":{"name":"guestId","in":"path","required":true,"description":"The guest id Example Dining assigned","schema":{"type":"string","maxLength":64},"example":"gst_7Q2K9"},"DiningTagIdPath":{"name":"tagId","in":"path","required":true,"description":"The tag id (the partner chooses it at create)","schema":{"type":"string","maxLength":64},"example":"tag_vip"},"DiningReviewIdPath":{"name":"reviewId","in":"path","required":true,"description":"Example Dining's review id","schema":{"type":"string","maxLength":128},"example":"OT-100123-2141-1700000001"},"DiningPartnerIdentifierPath":{"name":"partnerIdentifier","in":"path","required":true,"description":"The partner's unique location identifier","schema":{"type":"string","maxLength":128},"example":"LOC-001"}},"responses":{"AgentSignatureFailed":{"description":"The two 401s of the agent identity chain on a route a caller reaches without a credential (FEAT-044 §5.2): AGENT_SIGNATURE_INVALID (a Web Bot Auth signature is present and wrong: bad signature, an uncovered required component, created more than 300 seconds ago or more than 30 seconds ahead (DR-173), an unknown or retired key; nextAction reauth) and AGENT_VERSION_REVOKED (the covered Agent-Version names a REVOKED version; nextAction stop). A request without a signature is never refused here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"signatureInvalid":{"summary":"401 AGENT_SIGNATURE_INVALID — a present signature does not verify","value":{"code":"UNAUTHENTICATED","message":"The Web Bot Auth signature did not verify: created is older than 300 seconds.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"reauth","details":{"reason":"AGENT_SIGNATURE_INVALID","check":3,"signatureReason":"STALE"}}},"versionRevoked":{"summary":"401 AGENT_VERSION_REVOKED — the declared version was revoked","value":{"code":"UNAUTHENTICATED","message":"Version 2026.10.1 of this agent was revoked. Run a current version.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","details":{"reason":"AGENT_VERSION_REVOKED","check":4,"version":"2026.10.1"}}}}}}},"AgentRegistryWriteForbidden":{"description":"The 403s of the agent registry writes (FEAT-044 §9): PERMISSION_DENIED MISSING_PERMISSION when the role lacks agent-registry:manage (Owner and Admin hold it); PERMISSION_DENIED SESSION_REQUIRED when the credential is an AI-kind API key or an MCP grant-derived token, because identity changes are human-session only; TENANT_MISMATCH when the agent belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the role lacks agent-registry:manage","value":{"code":"PERMISSION_DENIED","message":"Changing an agent needs the agent-registry:manage permission (Owner or Admin).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"agent-registry:manage"}}},"aiKeyRefused":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — an AI-kind API key; identity changes are human-session only (FEAT-044 §9)","value":{"code":"PERMISSION_DENIED","message":"Agent identity changes need a console session. An AI-kind API key can resolve an agent but never create or change one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth","actorKind":"ai"}}},"grantTokenRefused":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — an MCP grant-derived token; identity changes are human-session only (FEAT-044 §9)","value":{"code":"PERMISSION_DENIED","message":"Agent identity changes need a console session. An agent connected through an MCP grant can only read.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","credential":"mcp-grant"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — the agent belongs to another tenant","value":{"code":"TENANT_MISMATCH","message":"agentId agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"agt_01JC4Q8T2R6Y0M3K7W9H5N1B4D"}}}}}}},"OpsAgentWriteForbidden":{"description":"The 403s of the operator agent writes (FEAT-044 §5.4, §5.8): PERMISSION_DENIED MISSING_PERMISSION without the operator role, STEP_UP_FAILED for a missing, wrong or doubled step-up proof, APPROVAL_REQUIRED for T3 without an approved FEAT-024 AGENT_TIER_T3 approval, and SESSION_REQUIRED for an MCP grant-derived token (operator writes stay human, ADR-C10 Phase 4).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"missingPermission":{"summary":"403 PERMISSION_DENIED — the operator role lacks the verb","value":{"code":"PERMISSION_DENIED","message":"Changing an agent tier needs the ops:agents:manage permission.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"ops:agents:manage"}}},"stepUpFailed":{"summary":"403 PERMISSION_DENIED STEP_UP_FAILED — the step-up proof is missing or wrong","value":{"code":"PERMISSION_DENIED","message":"The step-up code did not verify.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_FAILED","field":"stepUpCode"}}},"approvalRequired":{"summary":"403 PERMISSION_DENIED APPROVAL_REQUIRED — T3 needs an approved AGENT_TIER_T3 approval","value":{"code":"PERMISSION_DENIED","message":"Tier T3 needs an approved AGENT_TIER_T3 approval; open one in the approval queue and send its id as approvalId.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","kind":"AGENT_TIER_T3"}}},"grantTokenRefused":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — an MCP grant-derived token","value":{"code":"PERMISSION_DENIED","message":"This route needs an operator session. An agent connected through an MCP grant can only read.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","credential":"mcp-grant"}}}}}}},"SearchWarming":{"description":"SEARCH_WARMING: success-in-progress (03 §5, ADR-C04); poll GET /v1/search/{searchRef}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"},"examples":{"warming":{"summary":"202 WARMING — nothing ready yet; poll the searchRef","description":"03 §5 / §10.2: an async-by-default product returns the 202 form rather than timing out. suppliers reports the fan-out width before any supplier has answered.","value":{"context":{"domain":"FLT","action":"on_search","version":"2026-09-14","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":140,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","searchRef":"SR-F-7b31d0a9","ranking":"DEFAULT_V3"},"message":{"status":"WARMING","retryAfterMs":1200,"suppliers":{"done":0,"pending":5,"failed":0},"offers":[]}}}}}}},"AmbiguousResult":{"description":"AMBIGUOUS_RESULT: book timed out upstream; order persisted PENDING_RECONCILIATION; poll the order or await order.state_changed (06 §8, ADR-C07). Never re-book on a different key.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"examples":{"ambiguousResult":{"summary":"202 AMBIGUOUS_RESULT — supplier timed out at commit; reconciliation running","description":"03 §10.1 F3: poll GET /v1/orders/{ref} or await order.state_changed. The client never re-books on a different Idempotency-Key while the order is PENDING_RECONCILIATION.","value":{"context":{"domain":"HTL","action":"on_orders","version":"2026-09-14","timestamp":"2026-09-14T17:04:11Z","message_id":"msg_9f2a1c","took_ms":812,"from_cache":false,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","ranking":"DEFAULT_V3"},"message":{"orderRef":"MO-8KD-2FQ-9XT","state":"PENDING_RECONCILIATION","items":[{"itemRef":"OI-1","domain":"HTL","state":"PENDING_RECONCILIATION","fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}],"fareSource":{"supplier":"EXAMPLEHOTELS","credentialSet":"MONDEE","moneyModel":"MERCHANT"}}}}}}}},"BadRequest":{"description":"VALIDATION or UNKNOWN_DOMAIN (03 §9)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — intent schema violation (03 §2.4, §9)","value":{"code":"VALIDATION","message":"message.intent.checkOut must be after message.intent.checkIn","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.checkOut","constraint":"after:checkIn"}}},"unissuedCursor":{"summary":"400 VALIDATION — a cursor GET /v1/orders/{orderRef}/events never issued (OD-P9-01)","value":{"code":"VALIDATION","message":"cursor is not one this route issued","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"cursor"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry; accepted list in details (03 §9)","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'HOTELS'. Did you mean HTL?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["HTL","VR","DAYUSE","FLT","CRU"]}}},"testHeaderNotAllowed":{"summary":"400 VALIDATION TEST_HEADER_NOT_ALLOWED — the Test header from a key or rung that may not simulate (BE-7)","value":{"code":"VALIDATION","message":"The Test header is accepted only from a sandbox (mo_test_) API key where simulation is on.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"Test","reason":"TEST_HEADER_NOT_ALLOWED"}}},"unknownTestScenario":{"summary":"400 VALIDATION UNKNOWN_TEST_SCENARIO — a Test value outside the list (BE-7)","value":{"code":"VALIDATION","message":"Unknown Test scenario.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"Test","reason":"UNKNOWN_TEST_SCENARIO","accepted":["price_changed","sold_out","ambiguous","hold_expired","payment_declined","supplier_timeout","partial_search","duplicate_order"]}}},"checkoutSignatureUnsupported":{"summary":"400 VALIDATION CHECKOUT_SIGNATURE_UNSUPPORTED — the provider has no checkout signature (BE-7)","value":{"code":"VALIDATION","message":"checkoutSignature is not supported by this payment's provider.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"checkoutSignature","reason":"CHECKOUT_SIGNATURE_UNSUPPORTED"}}},"paymentMethodRequired":{"summary":"400 VALIDATION PAYMENT_METHOD_REQUIRED — a card-secured dining slot booked or amended without a payment-method token (v1.7.0)","value":{"code":"VALIDATION","message":"The new slot is card-secured: send message.intent.payment.paymentMethod (a Spreedly token).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.payment.paymentMethod","reason":"PAYMENT_METHOD_REQUIRED","supplierCode":"EXAMPLEDINING_CARD_REQUIRED"}}},"onboardingStateInvalid":{"summary":"400 VALIDATION ONBOARDING_STATE_INVALID — the onboarding callback without the state the platform issued (v1.7.0)","value":{"code":"VALIDATION","message":"The onboarding state is missing, expired, already used or does not match this onboarding.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"session","reason":"ONBOARDING_STATE_INVALID"}}}}}}},"PaymentIntentBadRequest":{"description":"VALIDATION or UNKNOWN_DOMAIN (03 §9). details.reason CAPTURE_MODEL_NOT_SUPPORTED (the provider cannot run the named captureModel), INVALID_SESSION (a bad session.* field, details.field names it) or INSTRUMENT_DATA_REFUSED (a card number in a text field). v1.49.0 (paymentSource): AMOUNT_FROM_REPRICE_ONLY (an amount without a source), REPRICE_REF_WITH_PAYMENT_SOURCE, AMOUNT_REQUIRED, AMOUNT_NOT_POSITIVE, IDEMPOTENCY_KEY_REQUIRED, PAYMENT_SOURCE_NOT_OFFERED.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — intent schema violation (03 §2.4, §9)","value":{"code":"VALIDATION","message":"message.intent.checkOut must be after message.intent.checkIn","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"message.intent.checkOut","constraint":"after:checkIn"}}},"unknownDomain":{"summary":"400 UNKNOWN_DOMAIN — not in the registry; accepted list in details (03 §9)","value":{"code":"UNKNOWN_DOMAIN","message":"Unknown domain 'HOTELS'. Did you mean HTL?","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"accepted":["HTL","VR","DAYUSE","FLT","CRU"]}}},"captureModelNotSupported":{"summary":"400 VALIDATION CAPTURE_MODEL_NOT_SUPPORTED — the provider cannot run this captureModel (OD-655)","value":{"code":"VALIDATION","message":"This payment provider cannot run captureModel BNPL_AUTH.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"captureModel","reason":"CAPTURE_MODEL_NOT_SUPPORTED"}}},"invalidSession":{"summary":"400 VALIDATION INVALID_SESSION — a session field is malformed (OD-655)","value":{"code":"VALIDATION","message":"session.country must be an ISO 3166-1 alpha-2 code.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"session.country","reason":"INVALID_SESSION"}}},"instrumentDataRefused":{"summary":"400 VALIDATION INSTRUMENT_DATA_REFUSED — a card number in a text field (18 §6)","value":{"code":"VALIDATION","message":"A card number was found in a text field and refused; the digits are not echoed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"session.orderLines[0].name","reason":"INSTRUMENT_DATA_REFUSED"}}},"repriceRefWithPaymentSource":{"summary":"400 VALIDATION REPRICE_REF_WITH_PAYMENT_SOURCE — a payment on the tenant's own account pays no Apiq quote (v1.49.0)","value":{"code":"VALIDATION","message":"A payment on the tenant's own account takes its amount from the caller, never from a repriceRef.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"repriceRef","reason":"REPRICE_REF_WITH_PAYMENT_SOURCE"}}},"idempotencyKeyRequired":{"summary":"400 VALIDATION IDEMPOTENCY_KEY_REQUIRED — a payment on the tenant's own account needs an Idempotency-Key (v1.49.0)","value":{"code":"VALIDATION","message":"An Idempotency-Key header is required to create a payment on the tenant's own account.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"Idempotency-Key","reason":"IDEMPOTENCY_KEY_REQUIRED"}}}}}}},"NotFound":{"description":"NOT_FOUND — unknown or expired ref (unguessable refs, 15)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notFound":{"summary":"404 NOT_FOUND — unknown or expired ref (03 §9, 15 §5)","value":{"code":"NOT_FOUND","message":"searchRef SR-F-7b31d0a9 is unknown or has expired; re-search","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"SR-F-7b31d0a9","ttl":"15 min after last poll"}}}}}}},"DeviceForbidden":{"description":"The 403 of the /v1/devices routes (FEAT-021 §18a): PERMISSION_DENIED with details.reason SESSION_REQUIRED when the credential is not a human session — an API key (X-Api-Key) or a service token from POST /v1/oauth/token, whatever its scopes. A device belongs to a person, and only a person's session can register, list or delete one.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"apiKey":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an API key presented to /v1/devices","value":{"code":"PERMISSION_DENIED","message":"Devices are registered by a signed-in person only. Sign in with POST /v1/auth/login; API keys and service tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth"}}},"serviceToken":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — a service token from POST /v1/oauth/token","value":{"code":"PERMISSION_DENIED","message":"Devices are registered by a signed-in person only. Sign in with POST /v1/auth/login; API keys and service tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"bearerAuth"}}}}}}},"DeviceNotFound":{"description":"NOT_FOUND — the deviceId is unknown, already deleted, moved to another user, or another user's: all four answer alike (FEAT-021 §18a).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notFound":{"summary":"404 NOT_FOUND — no such device for this user","value":{"code":"NOT_FOUND","message":"Device dev_7Qm2c9Lx4Rt8 was not found.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"deviceId":"dev_7Qm2c9Lx4Rt8"}}}}}}},"Conflict":{"description":"State conflict — see code: CONFLICT | OFFER_EXPIRED | HOLD_EXPIRED | IDEMPOTENCY_CONFLICT (03 §9). CONFLICT is 03 §9's GENERIC state conflict and is the one a client falls back to when it does not recognise the specific code; the specific codes are always preferred.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"conflict":{"summary":"409 CONFLICT — generic state conflict (03 §9)","description":"03 §9's catch-all 409. The specific codes above are always preferred; this exists so a state conflict the registry has no narrower code for is still a declared, mockable branch rather than an undeclared status (OD-CONTRACT2-01).\n","value":{"code":"CONFLICT","message":"The order is in a state that does not allow this transition","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"state":"CANCELLED","attempted":"amend"}}},"offerExpired":{"summary":"409 OFFER_EXPIRED — re-shop required (03 §4, §9)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-H-8c1f4a2e expired at 2026-09-14T17:34:11Z; re-reprice","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","expiredAt":"2026-09-14T17:34:11Z"}}},"holdExpired":{"summary":"409 HOLD_EXPIRED — hold auto-released (03 §10.3 F1)","value":{"code":"HOLD_EXPIRED","message":"holdRef HD-M-4419ab expired at 2026-09-14T17:05:41Z; the seats were released","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"holdRef":"HD-M-4419ab","expiredAt":"2026-09-14T17:05:41Z"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"OfferExpired":{"description":"OFFER_EXPIRED (03 §9) — the offer a read-only drill-down names has expired; re-shop","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"offerExpired":{"summary":"409 OFFER_EXPIRED — re-shop required (03 §4, §9)","value":{"code":"OFFER_EXPIRED","message":"offerRef OF-H-8c1f4a2e expired at 2026-09-14T17:34:11Z; search again","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-H-8c1f4a2e","expiredAt":"2026-09-14T17:34:11Z"}}}}}}},"Unprocessable":{"description":"SOLD_OUT or TRAVELER_MISMATCH (03 §9)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"soldOut":{"summary":"422 SOLD_OUT — gone at commit (03 §9, §10.2 F2)","value":{"code":"SOLD_OUT","message":"The selected fare class is no longer available at the supplier","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"offerRef":"OF-F-31c9d0","supplier":"EXAMPLEGDS"}}},"travelerMismatch":{"summary":"422 TRAVELER_MISMATCH — supplier rejected traveler data (03 §9)","value":{"code":"TRAVELER_MISMATCH","message":"Supplier rejected traveler[0]: passport expiry precedes the return date","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"traveler":0,"field":"passportExpiry"}}},"offerUnsellable":{"summary":"422 SOLD_OUT — the tenant's rules cannot sell this offer (details.reason OFFER_UNSELLABLE, OD-295)","description":"The offer cannot be priced within the tenant's pricing rules (a floor above the capped maximum, FEAT-013 §12), so it is refused rather than answered at net. Choose another offer.\n","value":{"code":"SOLD_OUT","message":"This offer cannot be sold at a price within this tenant's pricing rules. Choose another offer.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OFFER_UNSELLABLE","repriceRef":"RP-H-2ab91c"}}}}}}},"OfferUnsellable":{"description":"SOLD_OUT with details.reason OFFER_UNSELLABLE — a drill-down (detail, rules) of an offer the tenant's pricing rules cannot sell. A drill-down shows one offer, so it cannot drop and count it as search does; it is refused, never answered at the supplier's net (FEAT-013 §12, OD-323, OD-295).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"offerUnsellable":{"summary":"422 SOLD_OUT — OFFER_UNSELLABLE on a drill-down (OD-295)","value":{"code":"SOLD_OUT","message":"This offer cannot be sold at a price within this tenant's pricing rules. Choose another offer.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OFFER_UNSELLABLE","offerRef":"OF-H-8c1f4a2e"}}}}}}},"PaymentFailed":{"description":"PAYMENT_FAILED — PSP decline reason in details. On POST /v1/orders with payment.mode WALLET (v1.18.0, served since 1.18.1, FEAT-030 §12) also details.reason WALLET_INSUFFICIENT with details.required, the order total, and details.available, the wallet's available amount; nothing was sent to a supplier. With payment.mode SAVED_METHOD (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b) a declined saved method is details.reason CARD_DECLINED with details.declineCode and details.methodId; nothing was sent to a supplier.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"paymentFailed":{"summary":"402 PAYMENT_FAILED — PSP decline reason in details (03 §8, §9)","value":{"code":"PAYMENT_FAILED","message":"Card declined by the issuer","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"paymentId":"pay_int_91c4","declineCode":"insufficient_funds","psp":"EXAMPLEPAY"}}},"walletInsufficient":{"summary":"402 PAYMENT_FAILED WALLET_INSUFFICIENT — the wallet cannot cover the order (FEAT-030 §12, v1.18.0)","value":{"code":"PAYMENT_FAILED","message":"The wallet's available balance does not cover this order; top up and retry.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WALLET_INSUFFICIENT","required":{"amount":45000,"currency":"USD"},"available":{"amount":12000,"currency":"USD"}}}},"savedMethodDeclined":{"summary":"402 PAYMENT_FAILED CARD_DECLINED — the saved method was declined (v1.51.0 PROPOSAL)","value":{"code":"PAYMENT_FAILED","message":"The saved card was declined. Choose another payment method.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CARD_DECLINED","declineCode":"card_declined","methodId":"PM-01JB2C4V"}}}}}}},"PaymentIntentCreateFailed":{"description":"PAYMENT_FAILED — the provider refused to create the intent (OD-655). details.reason PROVIDER_REFUSED_CREATE with declineCode, paymentId and psp.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"providerRefusedCreate":{"summary":"402 PAYMENT_FAILED PROVIDER_REFUSED_CREATE — the provider refused to create the intent","value":{"code":"PAYMENT_FAILED","message":"The payment provider refused to create the payment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PROVIDER_REFUSED_CREATE","declineCode":"invalid_account","paymentId":"pay_int_91c4","psp":"EXAMPLEPAY"}}}}}}},"RateLimited":{"description":"RATE_LIMITED — the API key or its tenant is over a rate limit; wait for Retry-After, then retry. Every routed request is counted against two token buckets, the key's and the tenant's (all its keys together): 50 requests a second with a burst of 100 on the standard plan. Every travel verb except polling a search also spends from a supplier budget, per key and per tenant: 10 requests a second with a burst of 20, and a plan may add a daily quota of supplier calls. Answers carry the IETF RateLimit header fields for the tightest bucket consulted (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy); a refusal adds Retry-After and details.scope (KEY or TENANT), details.budget (PLATFORM or SUPPLIER), details.reason (RATE or DAILY_QUOTA), details.tier and details.retryAfterMs. A sandbox may run with the limiter off, in which case none of these headers is sent. A search that is still warming is a different answer with a different meaning (keep polling). connect-v1 1.57.0 (PROPOSAL): with the key-velocity check on, also details.reason VELOCITY_LIMIT, a soft breach of the API key's own scope (components/responses/VelocityLimited), nextAction slow_down.\n","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the bucket admits the call; mirrored in details.retryAfterMs"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"rateLimited":{"summary":"429 RATE_LIMITED — the key's supplier budget is spent; distinct from SEARCH_WARMING","value":{"code":"RATE_LIMITED","message":"Too many requests for this key on the supplier budget. Wait for the Retry-After interval before trying again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"scope":"KEY","budget":"SUPPLIER","reason":"RATE","tier":"STANDARD","retryAfterMs":1000}}},"velocityLimit":{"summary":"429 RATE_LIMITED VELOCITY_LIMIT — the key's own scope, not the tenant quota (v1.57.0 PROPOSAL)","value":{"code":"RATE_LIMITED","message":"This API key exceeded its rate: 10 requests a second. Retry after 1.2 s.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"nextAction":"slow_down","docs":"https://apiq.example/developers/errors#velocity-limit","details":{"reason":"VELOCITY_LIMIT","field":"reqPerSec","limit":10,"windowSeconds":1,"retryAfterMs":1200}}}}}}},"NotImplemented":{"description":"NOT_IMPLEMENTED — honest fallback; every production 501 is a capabilities-doc bug (04 §2 rule 2, metric unexpected_501_total). Also the order read routes (getOrder, listOrderDocuments, listOrderEvents, searchOrders) on a deployment with no order reader assembled (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3); not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notImplemented":{"summary":"501 NOT_IMPLEMENTED — and a capabilities-doc bug (04 §2 rule 2)","value":{"code":"NOT_IMPLEMENTED","message":"seatmap is NOT_APPLICABLE for HTL; GET /v1/capabilities says so","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"HTL","verb":"seatmap","capabilitiesStatus":"NOT_APPLICABLE"}}},"noOrderReader":{"summary":"501 NOT_IMPLEMENTED — no order reader assembled on this deployment (OD-P9-01)","value":{"code":"NOT_IMPLEMENTED","message":"This deployment has no order reader; orders cannot be read here","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false}}}}}},"Unauthenticated":{"description":"UNAUTHENTICATED — missing or expired token (03 §9). Declared on every operation because `security` is declared globally at the document root: every route is authenticated, so every route can answer 401. Transcribed from 03 §9's registry, which is the complete code table and maps this code to 401 — see OD-CONTRACT2-01 for the per-operation scoping rule.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"unauthenticated":{"summary":"401 UNAUTHENTICATED — missing or expired token (03 §9)","description":"Not retryable with the same credential: mint a new token. A client branches on `code`, never on the HTTP status alone (03 §1 #3, §9).\n","value":{"code":"UNAUTHENTICATED","message":"The bearer token is expired. Re-authenticate and retry.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"scheme":"bearerAuth","expiredAt":"2026-09-14T16:59:02Z"}}}}}}},"Forbidden":{"description":"PERMISSION_DENIED | TAB_DISABLED | OUT_OF_SCOPE | TENANT_MISMATCH — all 403 in 03 §9's registry, and deliberately four distinct codes rather than one: 03 §9 marks TAB_DISABLED \"distinct on purpose\" because \"your tenant does not sell this product\" and \"your role lacks this verb\" are different answers to the caller. Transcribed from 03 §9; the scope-ladder and tenant-wall violations are 07 §1/§3. connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.4, §5.2 check 7): AGENT_TIER_INSUFFICIENT, also 403, is a request whose agent tier does not allow the tool class (DR-167, DR-168); it carries nextAction verify_agent and details.tier, details.required, details.toolClass and details.operationId. A ceiling only narrows an unverified caller and never widens a scope.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — role lacks the verb (03 §9)","description":"The scope is named in details so an integrator can fix the grant: 03 §9 names search:<tabKey> and booking:write as the shapes.","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: booking:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"booking:write"}}},"supplierNotAllowed":{"summary":"403 PERMISSION_DENIED — the API key's supplier allowlist does not include this supplier","description":"A key minted with MintApiKeyRequest.suppliers used an offer, reprice, hold or order of a supplier outside its list. Not retryable with this key; mint one that lists the supplier.\n","value":{"code":"PERMISSION_DENIED","message":"This API key may not reach supplier SUP-HTL-EXAMPLE3","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_ALLOWED_FOR_KEY","supplier":"SUP-HTL-EXAMPLE3"}}},"tabDisabled":{"summary":"403 TAB_DISABLED — the tenant does not sell this product (03 §9)","description":"Distinct from PERMISSION_DENIED on purpose (03 §9). It is the entitlement twin of 04 §2 rule 2's \"a product the tenant isn't entitled to is ABSENT from capabilities\": the product is missing from GET /v1/capabilities and calling it answers this.\n","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"outOfScope":{"summary":"403 OUT_OF_SCOPE — scope-ladder violation (03 §9, 07 §1)","value":{"code":"OUT_OF_SCOPE","message":"The token's scope ladder does not reach this customer subtree","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requestedCustomerId":"cus_91ac3"}}},"agentTierInsufficient":{"summary":"403 AGENT_TIER_INSUFFICIENT — an unverified agent asked for a commit-lite tool on PROD (DR-167, DR-168)","description":"The ceiling for an unverified caller on PROD is read-only; a hold needs T1. Verify the agent domain (POST /admin/v1/agent-registry/{agentId}:verify-domain) and try again. Not a scope: the key's own scopes are unchanged.\n","value":{"code":"PERMISSION_DENIED","message":"This agent is unverified (T0). Holds need T1 on production; verify the agent's domain and try again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"verify_agent","details":{"reason":"AGENT_TIER_INSUFFICIENT","tier":"T0","requiredTier":"T1","toolClass":"commit-lite","operationId":"createHold"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — tenant-wall violation (03 §9, 07 §3)","description":"The ref belongs to another tenant. Refs are unguessable (15 §5) so this is the cross-tenant probe answer, and it is deliberately NOT 404 — the platform records the probe rather than pretending the ref does not exist.\n","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}}}}}},"OrderForbiddenOrSuspended":{"description":"The 403 of POST /v1/orders: everything components/responses/ForbiddenOrSuspended answers (PERMISSION_DENIED, TAB_DISABLED, OUT_OF_SCOPE, TENANT_MISMATCH, TENANT_SUSPENDED), plus the D-50 refusals as PERMISSION_DENIED details.reason (OD-327 dialect), and (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b) PERMISSION_DENIED details.reason TIER_CAP_REACHED for an order of a LIVE_CAPPED tenant (not a sandbox twin, any rung) past its daily cap.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — role lacks the verb (03 §9)","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: booking:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"booking:write"}}},"supplierNotAllowed":{"summary":"403 PERMISSION_DENIED — the API key's supplier allowlist does not include this supplier","description":"A key minted with MintApiKeyRequest.suppliers used an offer, reprice, hold or order of a supplier outside its list. Not retryable with this key; mint one that lists the supplier.\n","value":{"code":"PERMISSION_DENIED","message":"This API key may not reach supplier SUP-HTL-EXAMPLE3","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_ALLOWED_FOR_KEY","supplier":"SUP-HTL-EXAMPLE3"}}},"tabDisabled":{"summary":"403 TAB_DISABLED — the tenant does not sell this product (03 §9)","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"outOfScope":{"summary":"403 OUT_OF_SCOPE — scope-ladder violation (03 §9, 07 §1)","value":{"code":"OUT_OF_SCOPE","message":"The token's scope ladder does not reach this customer subtree","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requestedCustomerId":"cus_91ac3"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — tenant-wall violation (03 §9, 07 §3)","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}},"tenantSuspended":{"summary":"403 TENANT_SUSPENDED — new business refused; existing orders stay serviceable (07 §5)","description":"details.verb names the refused verb. Not retryable: wait for reinstatement. A cancel-quote, cancel or amend on an existing order is still served.\n","value":{"code":"TENANT_SUSPENDED","message":"This tenant is suspended: new searches, quotes, holds, orders and payments are refused until it is reinstated. Existing orders can still be read, cancelled and amended (07 §5).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_SUSPENDED","verb":"ORDERS"}}},"tenantNotLive":{"summary":"403 PERMISSION_DENIED, TENANT_NOT_LIVE — production is off until activation (07 §5, D-60)","description":"Production deployment only. details.verb (or details.route, PAYMENTS, on a payments route) names what was refused. Not retryable: the answer changes when platform ops approves the tenant's activation.\n","value":{"code":"PERMISSION_DENIED","message":"This tenant's production activation has not been approved (D-60), so this deployment starts no new searches, quotes, holds, orders or payments for it. Use the sandbox deployment until platform ops activates the tenant (07 §5).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_NOT_LIVE","verb":"ORDERS"}}},"approvalRequired":{"summary":"403 PERMISSION_DENIED — APPROVAL_REQUIRED; details carry the confirmation artifact (D-50)","value":{"code":"PERMISSION_DENIED","message":"This money-committing call came from an AI channel without an approval token. A human must confirm it first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","action":"orders.create","resourceRef":"RP-H-2ab91c","amount":{"amount":84210,"currency":"USD"}}}},"approvalTokenExpired":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_EXPIRED (past the ~2 min TTL)","value":{"code":"PERMISSION_DENIED","message":"The approval token expired. Obtain a new human confirmation.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_EXPIRED","action":"orders.create","resourceRef":"RP-H-2ab91c"}}},"approvalTokenUsed":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_USED (single use)","value":{"code":"PERMISSION_DENIED","message":"The approval token was already spent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_USED","action":"orders.create","resourceRef":"RP-H-2ab91c"}}},"approvalTokenMismatch":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_MISMATCH (another action, resource or amount; token burnt)","value":{"code":"PERMISSION_DENIED","message":"The approval token was minted for a different action, resource or amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_MISMATCH","action":"orders.create","resourceRef":"RP-H-2ab91c"}}},"approvalTokenInvalid":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_INVALID (unknown and foreign look alike)","value":{"code":"PERMISSION_DENIED","message":"The approval token is not one this platform minted for this tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_INVALID","action":"orders.create","resourceRef":"RP-H-2ab91c"}}},"tierCapReached":{"summary":"403 PERMISSION_DENIED TIER_CAP_REACHED — the order would pass today's limit (v1.51.0 PROPOSAL)","description":"A LIVE_CAPPED tenant's live order whose total would take today's live spend past the daily cap. Nothing was charged, held or sent. Not retryable until resetAt, or ask for a higher limit (POST /v1/activation/limit-requests).\n","value":{"code":"PERMISSION_DENIED","message":"This booking would pass today's limit of INR 4,00,000.00. Ask for a higher limit on the Go live page.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TIER_CAP_REACHED","cap":{"amount":40000000,"currency":"INR"},"usedToday":{"amount":38000000,"currency":"INR"},"resetAt":"2026-10-27T00:00:00Z"}}}}}}},"PostBookingForbidden":{"description":"The 403 of cancel and amend: everything components/responses/Forbidden answers (PERMISSION_DENIED, TAB_DISABLED, OUT_OF_SCOPE, TENANT_MISMATCH — never the suspension refusal, because post-booking servicing stays up while a tenant is suspended), plus the D-50 refusals as PERMISSION_DENIED details.reason (OD-327 dialect).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — role lacks the verb (03 §9)","description":"The scope is named in details so an integrator can fix the grant: 03 §9 names search:<tabKey> and booking:write as the shapes.","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: booking:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"booking:write"}}},"supplierNotAllowed":{"summary":"403 PERMISSION_DENIED — the API key's supplier allowlist does not include this supplier","description":"A key minted with MintApiKeyRequest.suppliers used an offer, reprice, hold or order of a supplier outside its list. Not retryable with this key; mint one that lists the supplier.\n","value":{"code":"PERMISSION_DENIED","message":"This API key may not reach supplier SUP-HTL-EXAMPLE3","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_ALLOWED_FOR_KEY","supplier":"SUP-HTL-EXAMPLE3"}}},"tabDisabled":{"summary":"403 TAB_DISABLED — the tenant does not sell this product (03 §9)","description":"Distinct from PERMISSION_DENIED on purpose (03 §9). It is the entitlement twin of 04 §2 rule 2's \"a product the tenant isn't entitled to is ABSENT from capabilities\": the product is missing from GET /v1/capabilities and calling it answers this.\n","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"outOfScope":{"summary":"403 OUT_OF_SCOPE — scope-ladder violation (03 §9, 07 §1)","value":{"code":"OUT_OF_SCOPE","message":"The token's scope ladder does not reach this customer subtree","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requestedCustomerId":"cus_91ac3"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — tenant-wall violation (03 §9, 07 §3)","description":"The ref belongs to another tenant. Refs are unguessable (15 §5) so this is the cross-tenant probe answer, and it is deliberately NOT 404 — the platform records the probe rather than pretending the ref does not exist.\n","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}},"approvalRequired":{"summary":"403 PERMISSION_DENIED APPROVAL_REQUIRED — an AI-channel call without a D-50 grant (12 §B2.4)","value":{"code":"PERMISSION_DENIED","message":"This action needs a person's approval. Mint a grant with POST /v1/approvals and retry with the same Idempotency-Key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","action":"orders.cancel","resourceRef":"MO-8KD-2FQ-9XT","amount":{"amount":84210,"currency":"USD"}}}},"approvalTokenMismatch":{"summary":"403 PERMISSION_DENIED APPROVAL_TOKEN_MISMATCH — the grant binds another action, resource or amount; it is burnt","value":{"code":"PERMISSION_DENIED","message":"The approval grant does not match this action, resource or amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_MISMATCH","action":"orders.cancel","resourceRef":"MO-8KD-2FQ-9XT"}}},"approvalTokenExpired":{"summary":"403 PERMISSION_DENIED APPROVAL_TOKEN_EXPIRED — mint a new grant","value":{"code":"PERMISSION_DENIED","message":"The approval grant has expired. Approve again to mint a new one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_EXPIRED","action":"orders.cancel","resourceRef":"MO-8KD-2FQ-9XT"}}},"approvalRequiredAmend":{"summary":"403 PERMISSION_DENIED APPROVAL_REQUIRED — an AI-channel amend COMMIT without a grant for amend.commit","value":{"code":"PERMISSION_DENIED","message":"This amendment needs a person's approval. Mint a grant with POST /v1/approvals and retry with the same Idempotency-Key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","action":"amend.commit","resourceRef":"MO-8KD-2FQ-9XT","amount":{"amount":4500,"currency":"USD"}}}}}}}},"ForbiddenOrSuspended":{"description":"PERMISSION_DENIED | TAB_DISABLED | OUT_OF_SCOPE | TENANT_MISMATCH, exactly as Forbidden, plus TENANT_SUSPENDED (FEAT-020 phase C; 07 §5, FEAT-020 §12) — declared on the verbs that start new business: search and poll, detail, schedules, seatmap, ancillaries, rules, reprice, hold and order create, and on the payments routes (intent, confirm, 3ds-callback, checkout-session). While the caller's tenant is SUSPENDED every such request is refused TENANT_SUSPENDED, except an idempotent replay of an answer completed before the suspension (see hold / createOrder), before the entitlement and permission checks, and it is not retryable: the answer changes only when platform ops reinstates the tenant. The post-booking verbs (cancel-quote, cancel, amend), the order reads, and capture and refund on an existing payment intent (whose 403 is PaymentForbidden) stay up, so no traveller is stranded. On the production deployment the same requests are refused PERMISSION_DENIED with details.reason TENANT_NOT_LIVE while the tenant's production activation has not been approved (a TRIAL tenant or a sandbox twin — 07 §5, D-60), whichever credential is presented, console session or API key; use the sandbox deployment until activation. Not retryable; the post-booking verbs stay up. Where this response is declared on a money verb that honours the D-50 approval gate (confirm, from an AI channel — X-Actor-Kind ai), it also carries the approval refusals, exactly as PaymentForbidden: PERMISSION_DENIED with details.reason APPROVAL_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_EXPIRED, APPROVAL_TOKEN_USED or APPROVAL_TOKEN_MISMATCH, plus the confirmation artifact (action, resourceRef, amount).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — role lacks the verb (03 §9)","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: booking:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"booking:write"}}},"supplierNotAllowed":{"summary":"403 PERMISSION_DENIED — the API key's supplier allowlist does not include this supplier","description":"A key minted with MintApiKeyRequest.suppliers used an offer, reprice, hold or order of a supplier outside its list. Not retryable with this key; mint one that lists the supplier.\n","value":{"code":"PERMISSION_DENIED","message":"This API key may not reach supplier SUP-HTL-EXAMPLE3","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SUPPLIER_NOT_ALLOWED_FOR_KEY","supplier":"SUP-HTL-EXAMPLE3"}}},"tabDisabled":{"summary":"403 TAB_DISABLED — the tenant does not sell this product (03 §9)","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"outOfScope":{"summary":"403 OUT_OF_SCOPE — scope-ladder violation (03 §9, 07 §1)","value":{"code":"OUT_OF_SCOPE","message":"The token's scope ladder does not reach this customer subtree","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requestedCustomerId":"cus_91ac3"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — tenant-wall violation (03 §9, 07 §3)","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}},"tenantSuspended":{"summary":"403 TENANT_SUSPENDED — new business refused; existing orders stay serviceable (07 §5)","description":"details.verb names the refused verb. Not retryable: wait for reinstatement. A cancel-quote, cancel or amend on an existing order is still served.\n","value":{"code":"TENANT_SUSPENDED","message":"This tenant is suspended: new searches, quotes, holds, orders and payments are refused until it is reinstated. Existing orders can still be read, cancelled and amended (07 §5).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_SUSPENDED","verb":"SEARCH"}}},"tenantNotLive":{"summary":"403 PERMISSION_DENIED, TENANT_NOT_LIVE — production is off until activation (07 §5, D-60)","description":"Production deployment only. details.verb (or details.route, PAYMENTS, on a payments route) names what was refused. Not retryable: the answer changes when platform ops approves the tenant's activation.\n","value":{"code":"PERMISSION_DENIED","message":"This tenant's production activation has not been approved (D-60), so this deployment starts no new searches, quotes, holds, orders or payments for it. Use the sandbox deployment until platform ops activates the tenant (07 §5).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_NOT_LIVE","verb":"SEARCH"}}},"approvalRequired":{"summary":"403 PERMISSION_DENIED — APPROVAL_REQUIRED; details carry the confirmation artifact (D-50)","value":{"code":"PERMISSION_DENIED","message":"This money-committing call came from an AI channel without an approval token. A human must confirm it first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","action":"payments.confirm","resourceRef":"pay_int_91c4","amount":{"amount":84210,"currency":"USD"}}}},"approvalTokenExpired":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_EXPIRED (past the ~2 min TTL)","value":{"code":"PERMISSION_DENIED","message":"The approval token expired. Obtain a new human confirmation.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_EXPIRED","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenUsed":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_USED (single use)","value":{"code":"PERMISSION_DENIED","message":"The approval token was already spent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_USED","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenMismatch":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_MISMATCH (another action, resource or amount; token burnt)","value":{"code":"PERMISSION_DENIED","message":"The approval token was minted for a different action, resource or amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_MISMATCH","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenInvalid":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_INVALID (unknown and foreign look alike)","value":{"code":"PERMISSION_DENIED","message":"The approval token is not one this platform minted for this tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_INVALID","action":"payments.confirm","resourceRef":"pay_int_91c4"}}}}}}},"CredentialForbidden":{"description":"The 403s of the credential-issuing routes (POST /v1/keys, POST /v1/keys/{keyPrefix}/rotate, POST /v1/invites). PERMISSION_DENIED when the caller lacks keys:manage / users:manage, and on the key routes also when a live key is asked for a tenant that is not LIVE or is a sandbox twin (details.reason TENANT_NOT_LIVE — production keys follow activation approval, D-60). On POST /v1/keys also PERMISSION_DENIED with details.reason SYSTEM_KEY_REQUIRES_ADMIN when actorKind is system and the caller is not a tenant admin (DR-40). TENANT_SUSPENDED while the tenant is SUSPENDED: its credential set is frozen (07 §5) — nothing is minted, rotated or invited, while revoking a key still works. connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3) on POST /v1/keys only: PERMISSION_DENIED with details.reason SCOPE_WIDENING (details.dimensions) when an API key asks for a key wider than itself on any dimension, PRINCIPAL_UNKNOWN when onBehalfOf names someone other than the minting key's principal or a non-Member, and ACCESS_TOKEN_REQUIRED when an API key mints while aikonic.keys.agent-mint.enabled is off (as built).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the caller lacks the managing permission","value":{"code":"PERMISSION_DENIED","message":"Inviting colleagues needs the users:manage permission.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"users:manage"}}},"systemKeyRequiresAdmin":{"summary":"403 PERMISSION_DENIED, SYSTEM_KEY_REQUIRES_ADMIN — only a tenant admin mints a system key (keys only, DR-40)","value":{"code":"PERMISSION_DENIED","message":"Minting a system key needs the tenant admin role.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SYSTEM_KEY_REQUIRES_ADMIN","actorKind":"system"}}},"tenantNotLive":{"summary":"403 PERMISSION_DENIED, TENANT_NOT_LIVE — no live key before activation (keys only)","value":{"code":"PERMISSION_DENIED","message":"Production keys are minted only for a tenant whose activation has been approved (D-60); this tenant is TRIAL.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_NOT_LIVE"}}},"tenantSuspended":{"summary":"403 TENANT_SUSPENDED — the credential set is frozen (07 §5)","value":{"code":"TENANT_SUSPENDED","message":"This tenant is suspended; no key can be minted or rotated until it is reinstated. Existing keys can still be revoked.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TENANT_SUSPENDED"}}},"scopeWidening":{"summary":"403 PERMISSION_DENIED SCOPE_WIDENING — on POST /v1/keys, a key asked for more than it has (v1.57.0 PROPOSAL)","value":{"code":"PERMISSION_DENIED","message":"A key may only mint a key equal to or narrower than itself. Wider on: writes, spendPerDay.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","docs":"https://apiq.example/developers/errors#scope-widening","details":{"reason":"SCOPE_WIDENING","dimensions":["writes","spendPerDay"]}}},"principalUnknown":{"summary":"403 PERMISSION_DENIED PRINCIPAL_UNKNOWN — on POST /v1/keys, onBehalfOf is not the key's principal or not a Member (v1.57.0 PROPOSAL)","value":{"code":"PERMISSION_DENIED","message":"The principal named is not a Member this key may act for.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","docs":"https://apiq.example/developers/errors#principal-unknown","details":{"reason":"PRINCIPAL_UNKNOWN"}}},"agentMintOff":{"summary":"403 PERMISSION_DENIED ACCESS_TOKEN_REQUIRED — on POST /v1/keys, an API key mints while agent-mint is off (as built)","value":{"code":"PERMISSION_DENIED","message":"Keys are minted from a signed-in session on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"widen_scope_via_owner","details":{"reason":"ACCESS_TOKEN_REQUIRED"}}}}}}},"EnrollmentForbidden":{"description":"The 403s of the FEAT-030 activation, certification and wallet routes (v1.18.0, served since 1.18.1): always PERMISSION_DENIED, with details.reason MISSING_PERMISSION (details.permission names it: tenant:read, users:manage, orders:write, finance:read or finance:write), SESSION_REQUIRED (a session-only route called with an API key or a service token), OWNER_ONLY (an Owner-only rail step: KYB, money model, terms and, since connect-v1 1.47.0, the identity check; role OWNER or CUSTOMER_ADMIN as OD-870), or on the terms acceptance STEP_UP_FAILED, STEP_UP_LOCKED and STEP_UP_UNAVAILABLE. connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): also the 403s of the proposed GST registration, signatory-check, limits and limit-request routes and of /v1/payments/methods, with the same reasons (OWNER_ONLY on the signatory check, the limit request, replacing a confirmed GSTIN, and adding, removing or defaulting a payment method; MISSING_PERMISSION naming payments:write on the payment-method list). connect-v1 1.52.0 (PROPOSAL, FEAT-030 §18b): also the 403s of POST /v1/activation/terms:send-code (SESSION_REQUIRED, OWNER_ONLY); on the terms acceptance a wrong, expired or voided emailCode is STEP_UP_FAILED and the code's 5th wrong attempt STEP_UP_LOCKED; on the CAPPED flow STEP_UP_UNAVAILABLE points the caller at terms:send-code and emailCode. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): also the 403s of the registration look-up by number, the business-people routes (OWNER_ONLY on PUT), the work-e-mail routes (SESSION_REQUIRED, OWNER_ONLY; on the confirm a wrong, expired or voided code is STEP_UP_FAILED and the 5th wrong attempt STEP_UP_LOCKED) and POST /v1/activation:go-live (SESSION_REQUIRED, OWNER_ONLY). connect-v1 1.63.0 round 3 (PROPOSAL, FEAT-030 §18c; amended 2026-10-29, same version): also the 403s of POST /v1/payments/methods:setup (SESSION_REQUIRED, OWNER_ONLY), as adding a payment method.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"missingPermission":{"summary":"403 PERMISSION_DENIED MISSING_PERMISSION — the caller lacks the route's permission","value":{"code":"PERMISSION_DENIED","message":"Reading wallets needs the finance:read permission.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"finance:read"}}},"sessionRequired":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — a session-only route called with a key","value":{"code":"PERMISSION_DENIED","message":"This route is served to a signed-in person only, not to an API key.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED"}}},"ownerOnly":{"summary":"403 PERMISSION_DENIED OWNER_ONLY — only an Owner takes this rail step","value":{"code":"PERMISSION_DENIED","message":"Only an Owner of the tenant can do this.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OWNER_ONLY"}}},"stepUpFailed":{"summary":"403 PERMISSION_DENIED STEP_UP_FAILED — missing, wrong or doubled step-up proof","value":{"code":"PERMISSION_DENIED","message":"The step-up code was not accepted.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_FAILED"}}},"stepUpUnavailable":{"summary":"403 PERMISSION_DENIED STEP_UP_UNAVAILABLE — no MFA factor to step up with [assumption]","value":{"code":"PERMISSION_DENIED","message":"Add an authenticator or passkey before accepting the terms.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_UNAVAILABLE"}}},"stepUpUnavailableCapped":{"summary":"403 PERMISSION_DENIED STEP_UP_UNAVAILABLE — CAPPED flow, no factor; use an e-mailed code (v1.52.0)","value":{"code":"PERMISSION_DENIED","message":"You have no authenticator yet. Ask for a code by e-mail (POST /v1/activation/terms:send-code) and accept with emailCode.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_UNAVAILABLE"}}},"stepUpLocked":{"summary":"403 PERMISSION_DENIED STEP_UP_LOCKED — too many wrong codes; send a new one (v1.52.0)","value":{"code":"PERMISSION_DENIED","message":"Too many wrong codes. Ask for a new code and try again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_LOCKED"}}}}}}},"GstinValidation":{"description":"VALIDATION on the GST registration routes (v1.51.0 PROPOSAL): details.reason INDIA_ONLY_FIELD when the tenant's country is not IN (or not yet set), or details.field gstin when the GSTIN does not match the 15-character pattern.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"indiaOnlyField":{"summary":"400 VALIDATION INDIA_ONLY_FIELD — the GST register is for Indian businesses only","value":{"code":"VALIDATION","message":"A GSTIN can be used only for a business in India.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INDIA_ONLY_FIELD","field":"gstin"}}},"invalidGstin":{"summary":"400 VALIDATION — the GSTIN does not match the 15-character pattern","value":{"code":"VALIDATION","message":"gstin must be 15 characters, two digits then 13 letters or digits.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"gstin"}}}}}}},"GstinNotFound":{"description":"NOT_FOUND GSTIN_NOT_FOUND — the GST register has no registration for this GSTIN (v1.51.0 PROPOSAL)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"gstinNotFound":{"summary":"404 NOT_FOUND GSTIN_NOT_FOUND","value":{"code":"NOT_FOUND","message":"The GST register has no registration for 29AABCS9999F1Z5.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GSTIN_NOT_FOUND","field":"gstin"}}}}}}},"GstRegisterUnreachable":{"description":"UPSTREAM_UNAVAILABLE — the GST register provider could not be reached (v1.51.0 PROPOSAL; 502, retryable). Nothing was stored; the rail shows step business_verification blocked until it answers.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"gstRegisterUnreachable":{"summary":"502 UPSTREAM_UNAVAILABLE — the GST register did not answer; retry","value":{"code":"UPSTREAM_UNAVAILABLE","message":"The GST register is not answering right now. Try again in a minute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"provider":"harness","retryAfterMs":30000}}}}}}},"CappedFlowUnavailable":{"description":"NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE — the proposed capped activation flow (FEAT-030 §18b, v1.51.0 PROPOSAL) is off on this deployment (aikonic.activation.capped-flow.enabled, off by default, on at DEV and SANDBOX). Not retryable; the nine-step rail of GET /v1/activation is unchanged.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}}}}}},"GstRegisterUnavailable":{"description":"NOT_IMPLEMENTED — the GST registration routes are not served here (v1.51.0 PROPOSAL): details.reason CAPPED_FLOW_UNAVAILABLE when the capped flow is off (aikonic.activation.capped-flow.enabled), or GST_REGISTER_UNAVAILABLE when no GST register provider is configured (aikonic.gst-register.provider unset; harness on DEV and SANDBOX, the real provider waits on OD-ENR-24). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}},"gstRegisterUnavailable":{"summary":"501 NOT_IMPLEMENTED GST_REGISTER_UNAVAILABLE — no GST register provider is configured","value":{"code":"NOT_IMPLEMENTED","message":"GST register look-ups are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GST_REGISTER_UNAVAILABLE"}}}}}}},"BusinessRegistrationValidation":{"description":"VALIDATION on the business registration routes (v1.63.0 PROPOSAL): as GstinValidation (INDIA_ONLY_FIELD, the GSTIN pattern), plus details.field country (a country not served, or not the tenant's), number (not the scheme's format), legalName (missing on a US EIN look-up) and gstin (the confirm with neither or both of gstin and scheme with number).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"indiaOnlyField":{"summary":"400 VALIDATION INDIA_ONLY_FIELD — the GST register is for Indian businesses only","value":{"code":"VALIDATION","message":"A GSTIN can be used only for a business in India.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INDIA_ONLY_FIELD","field":"gstin"}}},"invalidGstin":{"summary":"400 VALIDATION — the GSTIN does not match the 15-character pattern","value":{"code":"VALIDATION","message":"gstin must be 15 characters, two digits then 13 letters or digits.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"gstin"}}},"countryNotServed":{"summary":"400 VALIDATION — a country whose register is not served, or not the tenant's","value":{"code":"VALIDATION","message":"Business registrations can be looked up for IN, GB, AE, US and CA, in the account's own country.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"country"}}},"invalidNumber":{"summary":"400 VALIDATION — the number does not match the scheme's format","value":{"code":"VALIDATION","message":"A UK company number is 8 characters.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"number"}}},"legalNameRequired":{"summary":"400 VALIDATION — a US EIN look-up without the legal name","value":{"code":"VALIDATION","message":"legalName is required to match a US EIN.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"legalName"}}},"gstinOrNumber":{"summary":"400 VALIDATION — the confirm needs exactly one of gstin, or scheme with number","value":{"code":"VALIDATION","message":"Send either gstin, or scheme with number.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"gstin"}}}}}}},"BusinessRegistrationNotFound":{"description":"NOT_FOUND — the register has no registration for the number (v1.63.0 PROPOSAL): details.reason REGISTRATION_NOT_FOUND, or GSTIN_NOT_FOUND for an India GSTIN (as GstinNotFound).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"gstinNotFound":{"summary":"404 NOT_FOUND GSTIN_NOT_FOUND","value":{"code":"NOT_FOUND","message":"The GST register has no registration for 29AABCS9999F1Z5.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GSTIN_NOT_FOUND","field":"gstin"}}},"registrationNotFound":{"summary":"404 NOT_FOUND REGISTRATION_NOT_FOUND — the register does not know this number","value":{"code":"NOT_FOUND","message":"The companies register has no company 09999999.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTRATION_NOT_FOUND","field":"number"}}}}}}},"BusinessRegisterUnavailable":{"description":"NOT_IMPLEMENTED — the business registration routes are not served here (v1.63.0 PROPOSAL): details.reason CAPPED_FLOW_UNAVAILABLE when the capped flow is off, GST_REGISTER_UNAVAILABLE when no GST register provider is configured (India), or REGISTER_UNAVAILABLE when no register provider is configured for the country (GB, AE, US, CA; the real providers wait on OD-ENR-31). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"cappedFlowUnavailable":{"summary":"501 NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The capped activation flow is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CAPPED_FLOW_UNAVAILABLE"}}},"gstRegisterUnavailable":{"summary":"501 NOT_IMPLEMENTED GST_REGISTER_UNAVAILABLE — no GST register provider is configured","value":{"code":"NOT_IMPLEMENTED","message":"GST register look-ups are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GST_REGISTER_UNAVAILABLE"}}},"registerUnavailable":{"summary":"501 NOT_IMPLEMENTED REGISTER_UNAVAILABLE — no register provider for this country","value":{"code":"NOT_IMPLEMENTED","message":"Business register look-ups for GB are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"REGISTER_UNAVAILABLE","field":"country"}}}}}}},"BusinessRegisterUnreachable":{"description":"UPSTREAM_UNAVAILABLE — the register provider could not be reached (v1.63.0 PROPOSAL; 502, retryable), as GstRegisterUnreachable for every country. Nothing was stored.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"gstRegisterUnreachable":{"summary":"502 UPSTREAM_UNAVAILABLE — the GST register did not answer; retry","value":{"code":"UPSTREAM_UNAVAILABLE","message":"The GST register is not answering right now. Try again in a minute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"provider":"harness","retryAfterMs":30000}}},"registerUnreachable":{"summary":"502 UPSTREAM_UNAVAILABLE — the business register did not answer; retry","value":{"code":"UPSTREAM_UNAVAILABLE","message":"The business register is not answering right now. Try again in a minute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"provider":"harness","retryAfterMs":30000}}}}}}},"PaymentMethodsUnavailable":{"description":"NOT_IMPLEMENTED PAYMENT_METHODS_UNAVAILABLE — saved payment methods (FEAT-030 §18b, v1.51.0 PROPOSAL) are off on this deployment (aikonic.payment-methods.enabled, off by default, on at DEV and SANDBOX). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"paymentMethodsUnavailable":{"summary":"501 NOT_IMPLEMENTED PAYMENT_METHODS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"Saved payment methods are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PAYMENT_METHODS_UNAVAILABLE"}}}}}}},"PaymentMethodProviderUnreachable":{"description":"UPSTREAM_UNAVAILABLE — connect-v1 1.63.0 round 3 (FEAT-030 §18c PROPOSAL, amended 2026-10-29): the card provider or the UPI provider behind the saved payment methods cannot be reached; nothing is saved or set up. Retryable; details.psp names the provider.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"pspUnreachable":{"summary":"502 UPSTREAM_UNAVAILABLE — the payment provider is not answering","value":{"code":"UPSTREAM_UNAVAILABLE","message":"The payment provider is not answering right now. Try again in a minute.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"psp":"EXAMPLEPAY","retryAfterMs":30000}}}}}}},"InvoicesUnavailable":{"description":"NOT_IMPLEMENTED INVOICES_UNAVAILABLE — GST invoices (FEAT-030 §18b, v1.51.0 PROPOSAL) are off on this deployment (aikonic.invoices.gst.enabled, off by default, on at DEV and SANDBOX). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"invoicesUnavailable":{"summary":"501 NOT_IMPLEMENTED INVOICES_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"GST invoices are not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INVOICES_UNAVAILABLE"}}}}}}},"NotificationsUnavailable":{"description":"NOT_IMPLEMENTED NOTIFICATIONS_UNAVAILABLE — the notifications feed (FEAT-030 §18b, v1.51.0 PROPOSAL) is off on this deployment (aikonic.notifications.enabled, off by default, on at DEV and SANDBOX). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notificationsUnavailable":{"summary":"501 NOT_IMPLEMENTED NOTIFICATIONS_UNAVAILABLE","value":{"code":"NOT_IMPLEMENTED","message":"The notifications feed is not available on this deployment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"NOTIFICATIONS_UNAVAILABLE"}}}}}}},"IdentityRateLimited":{"description":"RATE_LIMITED on an unauthenticated identity route — the FEAT-020 §12 abuse limit, not a tenant quota (no tenant is known yet). Counted in fixed windows (default 15 min). The signup, login, password and invite routes count every attempt per client address AND per subject (the email, the username + site, or the invite token). POST /v1/oauth/token is counted three ways, none of them on client_id alone, so knowing a client_id (which is not secret) never lets anyone spend its holder's budget: oauth-token counts every attempt per client address only; oauth-token-failure counts FAILED client authentications per client address and per (client address, client_id) pair; oauth-token-issued counts tokens issued per client_id only, and only a request whose secret, grant_type and scope all passed can spend it. details.reason says which limit tripped (CLIENT_RATE_LIMITED | SUBJECT_RATE_LIMITED) and details.action which count (signup | signup-verify | login | password-forgot | password-reset | invite-accept | oauth-token | oauth-token-failure | oauth-token-issued). Retry-After is whole seconds to the end of the window, and details.retryAfterMs mirrors it. The answer is the same whether or not the account or key exists, so the limit cannot be used to enumerate accounts, and nothing the caller sent is echoed. connect-v1 1.26.0 adds three counts per client address for the MCP grant routes: oauth-authorize (GET /v1/oauth/authorize, which may fetch a Client ID Metadata Document), oauth-register (POST /v1/oauth/register) and oauth-device (POST /v1/oauth/device_authorization), and one count per MCP grant, oauth-exchange-grant (the token exchange of POST /v1/oauth/token, counted per grant after the exchange client and the subject token verified, default 120 a minute), whose details.reason is EXCHANGE_RATE_LIMITED.\n","headers":{"Retry-After":{"description":"Whole seconds until the limit window ends","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"subjectRateLimited":{"summary":"429 RATE_LIMITED — too many attempts for one account","value":{"code":"RATE_LIMITED","message":"Too many requests of this kind for this address or account. Wait for the Retry-After interval before trying again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"SUBJECT_RATE_LIMITED","action":"login","retryAfterMs":55000}}},"clientRateLimited":{"summary":"429 RATE_LIMITED — too many attempts from one network address","value":{"code":"RATE_LIMITED","message":"Too many requests of this kind from this network address. Wait for the Retry-After interval before trying again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"CLIENT_RATE_LIMITED","action":"signup","retryAfterMs":412000}}},"oauthTokenFailures":{"summary":"429 RATE_LIMITED — too many failed client authentications for one client_id from this address","description":"POST /v1/oauth/token only. The (client address, client_id) failure count tripped, so the same client_id from any other address is unaffected, and the key's holder is never locked out by a stranger elsewhere. oauth-token-issued (SUBJECT_RATE_LIMITED) is the holder's own ceiling on tokens issued per client_id.\n","value":{"code":"RATE_LIMITED","message":"Too many requests of this kind for this address or account. Wait for the Retry-After interval before trying again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"SUBJECT_RATE_LIMITED","action":"oauth-token-failure","retryAfterMs":318000}}},"exchangeRateLimited":{"summary":"429 RATE_LIMITED, EXCHANGE_RATE_LIMITED — one MCP grant exchanged too often (connect-v1 1.26.0)","description":"POST /v1/oauth/token, the token exchange only (the AI plane). Counted per grant after the exchange client and the subject token verified (default 120 a minute); another grant exchanged from the same plane address is unaffected. The plane answers its caller 429 RATE_LIMITED, retryable, and does not cache the refusal.\n","value":{"code":"RATE_LIMITED","message":"Too many token exchanges for this agent connection. Wait for the Retry-After interval before trying again.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"EXCHANGE_RATE_LIMITED","action":"oauth-exchange-grant","retryAfterMs":21000}}}}}}},"UpstreamUnavailable":{"description":"UPSTREAM_UNAVAILABLE — circuit open (03 §9, 502, retryable). 06 §2 is the normative case: \"a search fails outright only when ZERO suppliers respond (UPSTREAM_UNAVAILABLE, retryable)\" — one supplier failing is a PARTIAL with a warning, never this. Declared on the supplier-backed verbs of 04 §1 only (OD-CONTRACT2-01). Also the order read routes (getOrder, listOrderDocuments, listOrderEvents, searchOrders) when the order store times out (OD-P9-01; API-0005 §4, API-0006 §6, 03 §2.3): details.store names the store, retryable true.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"upstreamUnavailable":{"summary":"502 UPSTREAM_UNAVAILABLE — every supplier circuit is open (03 §9, 06 §2)","value":{"code":"UPSTREAM_UNAVAILABLE","message":"Every supplier for HTL is circuit-open; no fan-out was attempted","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"domain":"HTL","retryAfterMs":5000}}},"orderStoreTimeout":{"summary":"502 UPSTREAM_UNAVAILABLE — the order store timed out on a read (OD-P9-01, API-0005 §4)","description":"A read writes nothing, so a retry with backoff is safe.","value":{"code":"UPSTREAM_UNAVAILABLE","message":"The order store did not answer in time. Retry the read.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"store":"orders"}}}}}}},"HomeRegionRedirect":{"description":"HOME_REGION_REDIRECT (FEAT-016 §16.1, AC-016-07): this tenant's writer region is not the region that received the request. Nothing was executed at this region. There is no body. Clients MUST NOT auto-follow this redirect: a cross-origin redirect makes most HTTP stacks drop the Authorization header, and some drop Location altogether. The client MUST re-send the same request (method and body unchanged) to Location WITH its credentials and the SAME Idempotency-Key. Follow-up sends are deduplicated on the Idempotency-Key in the home region.\n","headers":{"Location":{"required":true,"description":"Absolute URL of the same route in the tenant's home (writer) region","schema":{"type":"string","format":"uri"}}}},"ProviderDown":{"description":"PROVIDER_DOWN — the supplier is dead (03 §9, 503, retryable). 08 §4 E4 is the normative case on a write: a revoked key mid-flight fails the order PROVIDER_DOWN, because \"in-flight orders never fall back\" to another credential set. Declared on the supplier-backed verbs of 04 §1 only (OD-CONTRACT2-01).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"providerDown":{"summary":"503 PROVIDER_DOWN — supplier dead; no fallback on a write (03 §9, 08 §4 E4)","value":{"code":"PROVIDER_DOWN","message":"EXAMPLEHOTELS is unavailable; the order was not attempted and nothing was captured","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"supplier":"EXAMPLEHOTELS","retryAfterMs":15000}}}}}}},"PayloadTooLarge":{"description":"VALIDATION — the request body is larger than the surface's limit (v1.6.7, OD-846 (b); the backend answers it today in BodyLimitFilter, default 256 KiB per surface, OD-845). Not retryable as sent: shrink the body. Declared on every operation that takes a request body. details carries exactly one key, maxBodyBytes (integer): the limit that applies to this surface, in bytes (v1.6.8 corrects the v1.6.7 example, which showed a details.reason the backend never sent).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"payloadTooLarge":{"summary":"413 VALIDATION — the body is over the size limit (OD-846)","value":{"code":"VALIDATION","message":"The request body is larger than this API accepts (262144 bytes).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"maxBodyBytes":262144}}}}}}},"MethodNotAllowed":{"description":"VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge). Not retryable as sent.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"methodNotAllowed":{"summary":"405 VALIDATION — a method the edge does not serve","value":{"code":"VALIDATION","message":"This method is not served.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"METHOD_NOT_ALLOWED","source":"edge"}}}}}}},"UriTooLong":{"description":"VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge). Not retryable as sent: send long input in the body of a POST.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"uriTooLong":{"summary":"414 VALIDATION — the request URI is over 8 KiB","value":{"code":"VALIDATION","message":"The request URI is longer than 8 KiB.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"URI_TOO_LONG","source":"edge"}}}}}}},"UnsupportedMediaType":{"description":"VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes). The public edge refuses it before the platform sees it (details.reason UNSUPPORTED_MEDIA_TYPE, details.source edge). Not retryable as sent: send Content-Type application/json. Declared on every operation that takes a JSON request body.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"unsupportedMediaType":{"summary":"415 VALIDATION — the body is not JSON","value":{"code":"VALIDATION","message":"Send application/json.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"UNSUPPORTED_MEDIA_TYPE","source":"edge"}}}}}}},"InternalError":{"description":"INTERNAL — a bug, and traceId is the support handle (03 §9, 500, retryable). Declared on every operation because 03 §9 scopes it to no route: any route can have a bug. 13 §3 makes traceId the X-Correlation-Id-derived OTel trace, which is what makes this branch actionable rather than decorative.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"internal":{"summary":"500 INTERNAL — traceId is the support handle (03 §9, 13 §3)","value":{"code":"INTERNAL","message":"An unexpected error occurred. Quote the traceId to support.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{}}},"fxSnapshotUnavailable":{"summary":"500 INTERNAL — FX snapshot or rate unavailable; retry, never priced at a stale snapshot (OD-295)","description":"Pricing needed an amount converted at the request's FX snapshot and that snapshot or rate is unavailable (FEAT-013 §12). Retryable: the price is never computed at a stale snapshot and never answered at net. Raised by reprice and by the drill-downs.\n","value":{"code":"INTERNAL","message":"The price could not be computed at the reprice's FX snapshot, which is unavailable. Retry the reprice; it is never priced at a stale snapshot.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"FX_SNAPSHOT_UNAVAILABLE","fxSnapshotId":"fx-2026-09-14T17:00Z-01"}}}}}}},"IdempotencyConflict":{"description":"IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7) — the only 409s of a mutation with no domain conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body; never executed (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","retryAfterMs":500}}}}}}},"ApprovalMintForbidden":{"description":"PERMISSION_DENIED (03 §9) on POST /v1/approvals: details.reason SESSION_REQUIRED when the credential is not a human session (OD-403, 15 §1.4), or details.requiredScope when the signed-in person lacks the permission the action needs.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"sessionRequired":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — a machine credential cannot mint an approval","value":{"code":"PERMISSION_DENIED","message":"Approvals are minted by a signed-in person only. API keys and service tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth"}}},"missingPermission":{"summary":"403 PERMISSION_DENIED — the person may not do what they would approve","value":{"code":"PERMISSION_DENIED","message":"Approving payments.confirm needs payments:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"payments:write"}}}}}}},"AgentApprovalForbidden":{"description":"PERMISSION_DENIED (03 §9) on approve / reject: details.reason SESSION_REQUIRED for a machine credential (FEAT-019 §9 — an agent never approves), details.requiredScope agents:act for a person without it, or on approve the D-50 grant refusals APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_EXPIRED, APPROVAL_TOKEN_USED, APPROVAL_TOKEN_MISMATCH (OD-327 dialect).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"sessionRequired":{"summary":"403 PERMISSION_DENIED SESSION_REQUIRED — an agent credential tried to approve","value":{"code":"PERMISSION_DENIED","message":"Proposals are approved by a signed-in person only.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"oauthClientCredentials"}}},"approvalTokenExpired":{"summary":"403 PERMISSION_DENIED APPROVAL_TOKEN_EXPIRED — mint a new grant and approve again","value":{"code":"PERMISSION_DENIED","message":"The approval grant has expired. Approve again to mint a new one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_EXPIRED"}}},"approvalTokenMismatch":{"summary":"403 PERMISSION_DENIED APPROVAL_TOKEN_MISMATCH — the grant binds another amount or resource; it is burnt","value":{"code":"PERMISSION_DENIED","message":"The approval grant does not match this proposal's action, resource or amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_MISMATCH","action":"amend.commit","resourceRef":"MO-8KD-2FQ-9XT","amount":{"amount":4500,"currency":"USD"}}}}}}}},"AgentProposalConflict":{"description":"CONFLICT details.reason AGENT_PAUSED (D-54 kill switch, FEAT-019 §14) | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"agentPaused":{"summary":"409 CONFLICT AGENT_PAUSED — the agent is paused; nothing stored","value":{"code":"CONFLICT","message":"Agent agt_rebook_22 is paused and cannot submit proposals.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_PAUSED","agentId":"agt_rebook_22"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"AgentProposalExecutionUnknown":{"description":"INTERNAL with details.reason EXECUTION_UNKNOWN: approve claimed the proposal (EXECUTING) and the executed verb's outcome is not proven undispatched. Any failure after the claim answers this, never a retryable INTERNAL: the proposal is now EXECUTION_UNKNOWN (terminal for the API), the D-50 grant stays spent, the platform never retries the verb and emits agent.proposal.execution_unknown. Not retryable: read the proposal and let the desk resolve it. A failure proven NOT dispatched is not this answer; it is the verb's own error and the proposal returns to OPEN.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"executionUnknown":{"summary":"500 INTERNAL EXECUTION_UNKNOWN — the executed verb's outcome is ambiguous; the proposal is terminal, never retried","value":{"code":"INTERNAL","message":"The outcome of proposal prop_01J9ZK2 is unknown; it was not retried. The desk will resolve it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXECUTION_UNKNOWN","proposalId":"prop_01J9ZK2"}}},"internal":{"summary":"500 INTERNAL — after the claim every failure is EXECUTION_UNKNOWN and never retryable (traceId is the support handle)","value":{"code":"INTERNAL","message":"The outcome of proposal prop_01J9ZK2 is unknown; it was not retried. Quote the traceId to support.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXECUTION_UNKNOWN","proposalId":"prop_01J9ZK2"}}},"fxSnapshotUnavailable":{"summary":"500 INTERNAL EXECUTION_UNKNOWN — a pricing or FX failure after the claim is not retried (kept key, OD-295)","value":{"code":"INTERNAL","message":"The outcome of proposal prop_01J9ZK2 is unknown; it was not retried. The desk will resolve it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXECUTION_UNKNOWN","proposalId":"prop_01J9ZK2"}}}}}}},"AgentProposalDecisionConflict":{"description":"CONFLICT details.reason WRONG_STATE (the proposal is not OPEN, including EXECUTING — details.state) or AGENT_PAUSED | EXECUTION_MISMATCH (the stored execution no longer matches the approved resource or amount; nothing executed) | PRICE_CHANGED (preconditions re-validated at approval moved; the proposal stays OPEN, FEAT-019 §12) | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §7)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"notOpen":{"summary":"409 CONFLICT WRONG_STATE — already decided or expired","value":{"code":"CONFLICT","message":"Proposal prop_01J9ZK2 is EXPIRED and can no longer be decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WRONG_STATE","state":"EXPIRED"}}},"executionMismatch":{"summary":"409 CONFLICT EXECUTION_MISMATCH — the stored execution no longer matches what was approved; nothing executed","value":{"code":"CONFLICT","message":"The stored execution no longer matches proposal prop_01J9ZK2; nothing was executed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXECUTION_MISMATCH","proposalId":"prop_01J9ZK2"}}},"executing":{"summary":"409 CONFLICT WRONG_STATE — the proposal is already being executed","value":{"code":"CONFLICT","message":"Proposal prop_01J9ZK2 is EXECUTING and can no longer be decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WRONG_STATE","state":"EXECUTING"}}},"agentPaused":{"summary":"409 CONFLICT AGENT_PAUSED — a paused agent's proposal is not executed","value":{"code":"CONFLICT","message":"Agent agt_rebook_22 is paused; resume it before approving its proposals.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AGENT_PAUSED","agentId":"agt_rebook_22"}}},"priceChanged":{"summary":"409 PRICE_CHANGED — the price moved since the proposal; it stays OPEN for the agent to re-propose","value":{"code":"PRICE_CHANGED","message":"The price moved since this proposal was made. It was not executed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"repriceRequired":true,"pricesWithheld":true}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — duplicate in flight; retryable (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still executing","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}}}}}},"PaymentAmountInvalid":{"description":"VALIDATION on a capture or refund amount (03 §9, API-0010 §4). details.field names the member and details.reason is AMOUNT_NOT_INTEGER_MINOR_UNITS (a fraction, an exponent or a trailing .0 — money is integer minor units, never a float; OD-340), AMOUNT_NOT_POSITIVE, or CURRENCY_MISMATCH (details.currency is the payment's). Nothing was sent to the PSP.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"amountNotIntegerMinorUnits":{"summary":"400 VALIDATION AMOUNT_NOT_INTEGER_MINOR_UNITS — 100.0 is not an integer amount (OD-340)","value":{"code":"VALIDATION","message":"amount.amount must be an integer number of minor units","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"amount.amount","reason":"AMOUNT_NOT_INTEGER_MINOR_UNITS"}}},"currencyMismatch":{"summary":"400 VALIDATION CURRENCY_MISMATCH — the amount is not in the payment's currency","value":{"code":"VALIDATION","message":"amount.currency must be USD, the payment's currency","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"field":"amount.currency","reason":"CURRENCY_MISMATCH","currency":"USD"}}}}}}},"PaymentsNotImplemented":{"description":"NOT_IMPLEMENTED — this deployment serves no payments: no payment provider is configured (provider=none), so every payment route answers 501 (AS BUILT, OD-295). Not a capabilities-doc bug: payments are deployment configuration (D-26).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"noProvider":{"summary":"501 NOT_IMPLEMENTED — no payment provider configured (provider=none)","value":{"code":"NOT_IMPLEMENTED","message":"This deployment serves no payments: no payment provider is configured.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"route":"PAYMENTS"}}},"hostedCheckoutUnsupported":{"summary":"501 NOT_IMPLEMENTED HOSTED_CHECKOUT_UNSUPPORTED — this provider has no hosted checkout, or the harness provider above DEV (OD-447)","value":{"code":"NOT_IMPLEMENTED","message":"This payment provider has no hosted checkout.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"HOSTED_CHECKOUT_UNSUPPORTED"}}}}}}},"PaymentNotFound":{"description":"NOT_FOUND — no such payment intent for this tenant (unknown and foreign look alike, 15 §5)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"noSuchPayment":{"summary":"404 NOT_FOUND — unknown or cross-tenant paymentId (existence hidden)","value":{"code":"NOT_FOUND","message":"No such payment intent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"paymentId":"pay_int_0000"}}}}}}},"PaymentForbidden":{"description":"PERMISSION_DENIED (03 §9). Either the credential lacks payments:write, or a money verb from an AI channel (X-Actor-Kind ai) failed the D-50 approval gate. AS BUILT the approval refusals are PERMISSION_DENIED with the API-0010 §4 [PROPOSED] name in details.reason — APPROVAL_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_EXPIRED, APPROVAL_TOKEN_USED, APPROVAL_TOKEN_MISMATCH — plus the confirmation artifact (action, resourceRef, amount). The dedicated codes are not in the 03 §9 registry (OD-299); the wire moves to them the day the registry takes them.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the credential lacks payments:write","value":{"code":"PERMISSION_DENIED","message":"Role lacks the required scope: payments:write","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"requiredScope":"payments:write"}}},"approvalRequired":{"summary":"403 PERMISSION_DENIED — APPROVAL_REQUIRED; details carry the confirmation artifact (D-50)","value":{"code":"PERMISSION_DENIED","message":"This money-committing call came from an AI channel without an approval token. A human must confirm it first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_REQUIRED","action":"payments.confirm","resourceRef":"pay_int_91c4","amount":{"amount":84210,"currency":"USD"}}}},"approvalTokenExpired":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_EXPIRED (past the ~2 min TTL)","value":{"code":"PERMISSION_DENIED","message":"The approval token expired. Obtain a new human confirmation.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_EXPIRED","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenUsed":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_USED (single use)","value":{"code":"PERMISSION_DENIED","message":"The approval token was already spent.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_USED","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenMismatch":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_MISMATCH (another action, resource or amount; token burnt)","value":{"code":"PERMISSION_DENIED","message":"The approval token was minted for a different action, resource or amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_MISMATCH","action":"payments.confirm","resourceRef":"pay_int_91c4"}}},"approvalTokenInvalid":{"summary":"403 PERMISSION_DENIED — APPROVAL_TOKEN_INVALID (unknown and foreign look alike)","value":{"code":"PERMISSION_DENIED","message":"The approval token is not one this platform minted for this tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"APPROVAL_TOKEN_INVALID","action":"payments.confirm","resourceRef":"pay_int_91c4"}}}}}}},"PaymentMoneyConflict":{"description":"CONFLICT | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS (03 §9) on capture, refund and void (a void of a payment that has captured money is INVALID_STATE with details.state CAPTURED: refund it instead). CONFLICT carries details.reason: EXCEEDS_AUTHORIZED_REMAINDER (capture beyond what is authorized and not yet captured), EXCEEDS_REFUNDABLE (refund beyond what is captured and not yet refunded; when the order has been cancelled, also a refund beyond the order's committed cancellation refund, captured minus penalty, in which case details.refundableMinor carries the remaining refundable amount and details.basis is CANCELLATION, OD-707), INVALID_STATE (details.state names the payment's state) or CONCURRENT_UPDATE (API-0010 §4/§5). Never an over-capture or an over-refund. A payment whose cumulative refunds equal its captured amount moves to state REFUNDED (OD-708); one whose refunds are below its captured amount and that has nothing left to capture is PARTIALLY_REFUNDED (v1.6.2). Void (BE-7 !72) adds BOUND_TO_ORDER (the payment is bound to an order — cancel the order) and PROVIDER_REFUSED (details.providerCode; Example Pay has no void endpoint, so an Example Pay void is always PROVIDER_REFUSED providerCode UNSUPPORTED and nothing moves, OD-749 (a)).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"exceedsAuthorized":{"summary":"409 CONFLICT — capture beyond the authorized remainder (API-0010 §4)","value":{"code":"CONFLICT","message":"The capture exceeds the authorized amount not yet captured.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"EXCEEDS_AUTHORIZED_REMAINDER","paymentId":"pay_int_91c4","remaining":{"amount":34210,"currency":"USD"}}}},"refundInstead":{"summary":"409 CONFLICT — void of a payment that has captured money; refund it instead (OD-497)","value":{"code":"CONFLICT","message":"This payment has captured money and cannot be voided; refund it instead.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INVALID_STATE","paymentId":"pay_int_91c4","state":"CAPTURED"}}},"invalidState":{"summary":"409 CONFLICT — the payment is not in a state that allows this verb","value":{"code":"CONFLICT","message":"This payment is not in a state that allows the operation.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"INVALID_STATE","paymentId":"pay_int_91c4","state":"PENDING_RECONCILIATION"}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}},"idempotencyInProgress":{"summary":"409 IDEMPOTENCY_IN_PROGRESS — the first request with this key is still running (03 §7)","value":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still in flight. Retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55"}}},"boundToOrder":{"summary":"409 CONFLICT BOUND_TO_ORDER — void refused, the payment belongs to an order (BE-7)","value":{"code":"CONFLICT","message":"This payment is bound to an order; cancel the order instead.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"BOUND_TO_ORDER","paymentRef":"PAY-8KD-2FQ"}}},"providerRefused":{"summary":"409 CONFLICT PROVIDER_REFUSED UNSUPPORTED — Example Pay has no void (BE-7, OD-749)","value":{"code":"CONFLICT","message":"The payment provider refused the void; nothing moved.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"PROVIDER_REFUSED","paymentId":"pay_int_91c4","providerCode":"UNSUPPORTED"}}}}}}},"ConsoleForbidden":{"description":"The 403s of the /admin/v1 console routes that carry no product/domain facet (OD-400): PERMISSION_DENIED when the caller's role lacks the verb (details.reason MISSING_PERMISSION, details.permission), or when the credential is not a console session at all (details.reason SESSION_REQUIRED — an API key or OAuth client-credentials token can never reach /admin/v1, whatever its scopes); TENANT_MISMATCH when a ref belongs to another tenant (07 §3 — deliberately not 404, as on /v1). These routes administer the tenant itself (team, audit, finance, rules, webhooks, usage, branding, credentials, agents), not one product, so an entitlement-gap code has no bearing here (03 §9's distinct code is on ConsoleForbiddenTabDisabled, §3.2/§3.3's queues and analytics). connect-v1 1.26.0 (OD-884 Phase 2, DR-67): a grant-derived token, the downstream token the AI plane holds for an MCP grant a human approved, is 403 PERMISSION_DENIED details.reason SESSION_REQUIRED, details.credential mcp-grant, on every operation outside its toolset's read allow-list (the x-mcp tools and resources of that toolset whose class is read, as ticked at consent). It is never read as a console session: it cannot approve, reject or mint, manage keys, team, credentials or settings, or call a commit-lite tool.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the role lacks the verb (09 §12)","value":{"code":"PERMISSION_DENIED","message":"Changing roles needs the users:manage permission (Owner or Admin).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"users:manage"}}},"sessionRequired":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an API key presented to /admin/v1 (OD-400)","value":{"code":"PERMISSION_DENIED","message":"/admin/v1 is served to console sessions only. Sign in with POST /v1/auth/login; API keys and OAuth tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth"}}},"grantTokenRefused":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an MCP grant-derived token outside its toolset's reads (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"This route needs a console session. An agent connected through an MCP grant can only read what its grant lists.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","credential":"mcp-grant"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — the ref belongs to another tenant (07 §3)","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}}}}}},"McpConsentForbidden":{"description":"PERMISSION_DENIED details.reason SESSION_REQUIRED on the MCP consent routes (/v1/mcp/grant-requests/*, /supplier/v1/mcp/grant-requests/*, /ops/v1/mcp/grant-requests/*): only a signed-in human of the realm reads or decides a grant request. On the operator routes also OPERATOR_MFA_NOT_ENFORCED (aikonic.ops.mfa.required is false on this rung) and OPERATOR_GRANTS_DISABLED (connect-v1 1.26.0). An API key, a service token and an MCP grant-derived token (details.credential mcp-grant) are all refused, so an agent never reads or approves its own consent.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"grantTokenRefused":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — a grant-derived token at a consent route","value":{"code":"PERMISSION_DENIED","message":"Approving agent connections needs a signed-in person. An agent cannot read or approve its own connection.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","credential":"mcp-grant"}}}}}}},"McpGrantApprovalForbidden":{"description":"The 403s of approveMcpGrantRequest, supplierApproveMcpGrantRequest and opsApproveMcpGrantRequest: PERMISSION_DENIED details.reason SESSION_REQUIRED (not a human session of the realm; a grant-derived token included), TOOLSET_DISABLED (the tenant's console switch is OFF, or the platform's supplier switch is off), GRANTS_DISABLED (aikonic.mcp.grants.enabled is false on this rung), STEP_UP_REQUIRED, STEP_UP_FAILED or STEP_UP_LOCKED (connect-v1 1.26.0: an approval with a write ticked, or any operator approval, without a valid step-up proof), OPERATOR_GRANTS_DISABLED or OPERATOR_MFA_NOT_ENFORCED (operator only); TENANT_SUSPENDED (the tenant is SUSPENDED and takes no new credentials, 07 §5).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"grantTokenRefused":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an agent tries to approve a grant","value":{"code":"PERMISSION_DENIED","message":"Approving agent connections needs a signed-in person. An agent cannot read or approve its own connection.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","credential":"mcp-grant"}}},"toolsetDisabled":{"summary":"403 PERMISSION_DENIED, TOOLSET_DISABLED — the tenant switched the console toolset off","value":{"code":"PERMISSION_DENIED","message":"Agent access to the console is switched off for this organisation.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TOOLSET_DISABLED","toolset":"console"}}},"grantsDisabled":{"summary":"403 PERMISSION_DENIED, GRANTS_DISABLED — MCP grants are not served on this rung","value":{"code":"PERMISSION_DENIED","message":"Connecting agents is not available in this environment.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"GRANTS_DISABLED"}}},"tenantSuspended":{"summary":"403 TENANT_SUSPENDED — a suspended tenant takes no new credentials (07 §5)","value":{"code":"TENANT_SUSPENDED","message":"This organisation is suspended; new agent connections are refused.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false}},"stepUpRequired":{"summary":"403 PERMISSION_DENIED, STEP_UP_REQUIRED — a write was ticked, or the approval is an operator's, without a step-up proof (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"Confirm with your authenticator before letting an agent act as you.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_REQUIRED"}}},"stepUpFailed":{"summary":"403 PERMISSION_DENIED, STEP_UP_FAILED — the step-up proof is wrong (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"The verification code is not valid.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"STEP_UP_FAILED","field":"stepUpCode"}}},"operatorMfaNotEnforced":{"summary":"403 PERMISSION_DENIED, OPERATOR_MFA_NOT_ENFORCED — operator grants need operator MFA on this rung (connect-v1 1.26.0)","value":{"code":"PERMISSION_DENIED","message":"Operator agent access needs the operator second factor, which this environment does not require.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OPERATOR_MFA_NOT_ENFORCED"}}}}}}},"McpGrantApprovalInvalid":{"description":"VALIDATION on approveMcpGrantRequest, supplierApproveMcpGrantRequest and opsApproveMcpGrantRequest: toolset missing or not the request's (details.reason TOOLSET_MISMATCH), an operation outside the request's list or an empty list (OPERATION_NOT_ALLOWED, details.field operations), a write tool or a cross-tenant read the request did not offer (OPERATION_NOT_ALLOWED, details.field writeOperations or crossTenantReads; connect-v1 1.26.0), or a device flow without confirmOwnDevice true (OWN_DEVICE_CONFIRMATION_REQUIRED).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"toolsetMismatch":{"summary":"400 VALIDATION, TOOLSET_MISMATCH","value":{"code":"VALIDATION","message":"Select the toolset this agent asked for before approving.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"TOOLSET_MISMATCH","field":"toolset"}}},"operationNotAllowed":{"summary":"400 VALIDATION, OPERATION_NOT_ALLOWED","value":{"code":"VALIDATION","message":"Only the operations listed on this request can be granted.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OPERATION_NOT_ALLOWED","field":"operations"}}},"confirmRequired":{"summary":"400 VALIDATION, OWN_DEVICE_CONFIRMATION_REQUIRED","value":{"code":"VALIDATION","message":"Confirm that you started this connection on your own device.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OWN_DEVICE_CONFIRMATION_REQUIRED","field":"confirmOwnDevice"}}},"writeNotOffered":{"summary":"400 VALIDATION, OPERATION_NOT_ALLOWED — a write tool the request did not offer (connect-v1 1.26.0)","value":{"code":"VALIDATION","message":"Only the changes listed on this request can be allowed.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"OPERATION_NOT_ALLOWED","field":"writeOperations"}}}}}}},"McpGrantRequestConflict":{"description":"CONFLICT details.reason WRONG_STATE — the grant request is no longer PENDING (already approved or denied, expired, or, for an operator request, AWAITING_SECOND_APPROVAL); details.state says which.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"wrongState":{"summary":"409 CONFLICT, WRONG_STATE — decided already","value":{"code":"CONFLICT","message":"This agent connection request has already been decided.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WRONG_STATE","state":"APPROVED"}}}}}}},"McpUserCodeUnknown":{"description":"NOT_FOUND details.reason USER_CODE_UNKNOWN — no pending device-flow request of this realm matches the code (unknown, expired, already used, or another realm's). One answer for all four, so codes cannot be probed.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"userCodeUnknown":{"summary":"404 NOT_FOUND, USER_CODE_UNKNOWN","value":{"code":"NOT_FOUND","message":"No pending agent connection matches that code. Check the code on the agent's screen or start again there.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"USER_CODE_UNKNOWN"}}}}}}},"ConsoleAiAssistBadRequest":{"description":"VALIDATION on the POST /admin/v1/ai/* assists: the body does not match the route's request schema (the AI plane validates it and the gateway passes its 400 through), or, on error-explain, `code` is not shaped like a platform error code. Not retryable unchanged.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"validation":{"summary":"400 VALIDATION — the body does not match the route's shape","value":{"code":"VALIDATION","message":"request body or query does not match the route's shape","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false}},"notAnErrorCode":{"summary":"400 VALIDATION — error-explain's code is not a platform error code","value":{"code":"VALIDATION","message":"code is not a platform error code","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false}}}}}},"ConsoleAiAssistForbidden":{"description":"The 403s of the POST /admin/v1/ai/* assists: PERMISSION_DENIED when the credential is not a console session at all (details.reason SESSION_REQUIRED — an API key or OAuth token never reaches /admin/v1, whatever its scopes, OD-400), or when the session lacks ai:assist (details.reason MISSING_PERMISSION, details.permission ai:assist). ai:assist is its own grant: agents:read or agents:act does not imply it, because reading the agent desk and spending model calls are different grants (OD-848). Not retryable.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"missingAiAssist":{"summary":"403 PERMISSION_DENIED — the session lacks ai:assist (OD-848)","value":{"code":"PERMISSION_DENIED","message":"This credential does not hold the permission ai:assist.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"ai:assist","missing":["ai:assist"],"route":"AI_ASSIST"}}},"sessionRequired":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an API key presented to /admin/v1 (OD-400)","value":{"code":"PERMISSION_DENIED","message":"/admin/v1 is served to console sessions only. Sign in with POST /v1/auth/login; API keys and OAuth tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth"}}}}}}},"ConsoleAiAssistRateLimited":{"description":"RATE_LIMITED on the POST /admin/v1/ai/* assists, retryable. Two limits answer it: the gateway's per-tenant AI-assist budget (details.reason AI_ASSIST_RATE_LIMITED, counted in fixed one-minute windows per replica, checked before the plane is called), and the AI plane's own per-tenant limit passed through (details.reason AI_PLANE_RATE_LIMITED). Retry-After is whole seconds and details.retryAfterSeconds mirrors it; on the plane's limit both are present whenever the plane sent a numeric Retry-After. Wait that long, then retry the same body.\n","headers":{"Retry-After":{"description":"Whole seconds until the limit admits the call; mirrored in details.retryAfterSeconds","schema":{"type":"integer","minimum":0},"example":42}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"tenantBudget":{"summary":"429 RATE_LIMITED — the tenant's AI-assist budget for this minute is spent (OD-848)","value":{"code":"RATE_LIMITED","message":"This tenant has used its AI-assist budget for the current minute. Wait for the Retry-After interval before trying again (OD-848).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"AI_ASSIST_RATE_LIMITED","retryAfterSeconds":42}}},"planeRateLimited":{"summary":"429 RATE_LIMITED — the AI plane's own per-tenant limit, passed through","value":{"code":"RATE_LIMITED","message":"the P0 rate limit for this tenant is exhausted; retry shortly","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"retryAfterSeconds":12,"reason":"AI_PLANE_RATE_LIMITED"}}}}}}},"ConsoleAiAssistUpstreamUnavailable":{"description":"UPSTREAM_UNAVAILABLE on the POST /admin/v1/ai/* assists (502, retryable): the AI plane answered, but the model gateway behind it did not (the plane's 503 UPSTREAM_UNAVAILABLE, re-derived at this platform's status for the code), or refused the call (the plane's 502 UPSTREAM_REJECTED, which is not a platform code: details.upstreamCode carries it). Nothing was written; retry with backoff.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"modelGatewayUnavailable":{"summary":"502 UPSTREAM_UNAVAILABLE — the model gateway behind the AI plane is unavailable","value":{"code":"UPSTREAM_UNAVAILABLE","message":"the model gateway is unavailable","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true}},"modelGatewayRejected":{"summary":"502 UPSTREAM_UNAVAILABLE — the model gateway refused the call (plane code UPSTREAM_REJECTED)","value":{"code":"UPSTREAM_UNAVAILABLE","message":"the model gateway refused this request","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"upstreamCode":"UPSTREAM_REJECTED"}}}}}}},"ConsoleAiAssistUnavailable":{"description":"PROVIDER_DOWN on the POST /admin/v1/ai/* assists (503). retryable false when a retry meets the same state: the AI feature is switched off or not wired on the plane (details.reason AI_FEATURE_DISABLED — the kill switch, no model provider, or for search-intent no golden index loaded), or this deployment has no AI plane configured (details.reason AI_PLANE_UNAVAILABLE). retryable true when the plane did not answer within budget, refused the connection, or its circuit breaker is open. The console hides or disables the assist on this answer; the rest of the console is unaffected.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"aiFeatureDisabled":{"summary":"503 PROVIDER_DOWN, AI_FEATURE_DISABLED — the assist is switched off or not wired; not retryable","value":{"code":"PROVIDER_DOWN","message":"AI feature 'p0' is disabled (no model provider is wired on this plane)","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AI_FEATURE_DISABLED"}}},"aiPlaneNotConfigured":{"summary":"503 PROVIDER_DOWN, AI_PLANE_UNAVAILABLE — no AI plane on this deployment; not retryable","value":{"code":"PROVIDER_DOWN","message":"The AI plane is not configured on this deployment (aikonic.ai.base-url / AIKONIC_AI_BASE_URL is unset), so the agents, agent-proposals and copilot admin routes are unavailable here. Retrying will not help until an operator configures it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AI_PLANE_UNAVAILABLE","property":"aikonic.ai.base-url"}}},"aiPlaneDown":{"summary":"503 PROVIDER_DOWN — the AI plane did not answer or its circuit is open; retryable","value":{"code":"PROVIDER_DOWN","message":"The AI plane did not answer within budget or refused the connection.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true}}}}}},"ConsoleForbiddenTabDisabled":{"description":"ConsoleForbidden's codes, plus the fourth one 03 §9 registers as distinct on purpose (API-0012 §2, §3.2, §3.3) for the console's own product/domain-scoped reads and actions — queues (09 §3/§4), the reconcile actions on an order in that domain, analytics reports scoped by `domain`, and the combined `/admin/v1/home` read. Ordinary navigation never reaches it: the projection filters on capabilities.products first (OD-401), so the console renders disabled-with-reason (DR-18) before a request is ever made; this is the forced-URL answer.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"permissionDenied":{"summary":"403 PERMISSION_DENIED — the role lacks the verb (09 §12)","value":{"code":"PERMISSION_DENIED","message":"Changing roles needs the users:manage permission (Owner or Admin).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"MISSING_PERMISSION","permission":"users:manage"}}},"sessionRequired":{"summary":"403 PERMISSION_DENIED, SESSION_REQUIRED — an API key presented to /admin/v1 (OD-400)","value":{"code":"PERMISSION_DENIED","message":"/admin/v1 is served to console sessions only. Sign in with POST /v1/auth/login; API keys and OAuth tokens are refused here whatever their scopes.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SESSION_REQUIRED","scheme":"apiKeyAuth"}}},"tabDisabled":{"summary":"403 the tab-disabled code — the tenant does not sell this product (03 §9, OD-401)","description":"Reached only by a forced URL: the queue/analytics/home projection already filters on capabilities.products, so the console UI never issues this request itself (09 §4).\n","value":{"code":"TAB_DISABLED","message":"Tenant acme-travel is not entitled to sell CRU","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"domain":"CRU","tenant":"acme-travel"}}},"tenantMismatch":{"summary":"403 TENANT_MISMATCH — the ref belongs to another tenant (07 §3)","value":{"code":"TENANT_MISMATCH","message":"orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"ref":"MO-8KD-2FQ-9XT"}}}}}}},"ConsoleApprovalForbidden":{"description":"The 403s of a route that presents a D-50 approval token (12 §B2.4, OD-403): APPROVAL_TOKEN_EXPIRED when presented after expiresAt (mint a new one — the human confirms again); APPROVAL_TOKEN_USED when presented a second time OR for a resource, action or amount it was not bound to (fail closed, no hint which binding failed); and PERMISSION_DENIED as on every console route, including details.reason SELF_APPROVAL where a second approver is required (reconcile:fail, ADR-003). None is retryable with the same token.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleApprovalErrorResponse"},"examples":{"approvalTokenExpired":{"summary":"403 APPROVAL_TOKEN_EXPIRED — ~2 min TTL passed; confirm again (12 §B2.4)","value":{"code":"APPROVAL_TOKEN_EXPIRED","message":"Approval token apt_01J8QM3 expired at 2026-09-27T09:07:00Z. Confirm the action again to mint a new one.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"approvalTokenId":"apt_01J8QM3","expiredAt":"2026-09-27T09:07:00Z"}}},"approvalTokenUsed":{"summary":"403 APPROVAL_TOKEN_USED — single-use; already spent, or bound to something else","value":{"code":"APPROVAL_TOKEN_USED","message":"Approval token apt_01J8QM3 has already been used, or is not bound to this action, resource and amount.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"approvalTokenId":"apt_01J8QM3","usedAt":"2026-09-27T09:05:30Z"}}},"permissionDenied":{"summary":"403 PERMISSION_DENIED — the role lacks the verb, or the approver is the caller (SELF_APPROVAL)","value":{"code":"PERMISSION_DENIED","message":"Marking an order failed & compensated needs a token minted by a second approver, not by the caller (ADR-003).","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"SELF_APPROVAL","permission":"orders:cancel"}}}}}}},"ConsoleConflict":{"description":"State conflict on a console action — see code: CONFLICT | IDEMPOTENCY_CONFLICT (03 §9). CONFLICT carries details.reason for the console to explain the refusal: a queue row held by someone else (details.assignee), an order not PENDING_RECONCILIATION (details.state), a proposal no longer OPEN (details.state), LAST_OWNER / SELF on a member change, SUPPLIER_UNSETTLED on a reconcile adopt (the supplier holds the booking but has not settled it), ALREADY_CURRENT on a rollback, a brand `version` that moved (details.currentVersion), IN_FLIGHT_ORDERS on a credential-set delete, ENDPOINT_DISABLED on a replay, AGENT_PAUSED on a new proposal.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"claimedByAnother":{"summary":"409 CONFLICT — the queue row is held by someone else (09 §4)","value":{"code":"CONFLICT","message":"Queue item qi_rec_01J8Q2 is claimed by Dana Ops; ask them to release it","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CLAIMED_BY_OTHER","assignee":{"userId":"7c1d0e2f-3a4b-4c5d-8e6f-0a1b2c3d4e5f","displayName":"Dana Ops"}}}},"wrongState":{"summary":"409 CONFLICT — the order or proposal is not in the state the action needs","value":{"code":"CONFLICT","message":"Order MO-8KD-2FQ-9XT is CONFIRMED; reconciliation actions apply to PENDING_RECONCILIATION only","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"WRONG_STATE","state":"CONFIRMED","attempted":"reconcile.retry"}}},"versionMoved":{"summary":"409 CONFLICT — optimistic concurrency; re-read and resend (branding PUT)","value":{"code":"CONFLICT","message":"Branding is at version 4; the request was authored against version 3","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"VERSION_MOVED","currentVersion":4}}},"idempotencyConflict":{"summary":"409 IDEMPOTENCY_CONFLICT — same key, different body (03 §7)","value":{"code":"IDEMPOTENCY_CONFLICT","message":"Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"idempotencyKey":"7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55","keyTtlHours":48}}}}}}},"ConsoleCaseConflict":{"description":"CONFLICT on a case verb — details.reason CASE_CLOSED (already DONE or FAILED) or STALE_VERSION (a racing write won; re-read)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"caseClosed":{"summary":"409 CONFLICT CASE_CLOSED — a second closer, too late","value":{"code":"CONFLICT","message":"Case case_9f2a1c33b0d4e871 is already DONE.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"CASE_CLOSED","status":"DONE"}}},"staleVersion":{"summary":"409 CONFLICT STALE_VERSION — re-read the case","value":{"code":"CONFLICT","message":"Case case_9f2a1c33b0d4e871 moved under this action; re-read it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"STALE_VERSION"}}}}}}},"AiPlaneUnavailable":{"description":"PROVIDER_DOWN on an /admin/v1 agents, agent-proposals or copilot operation (BE-6, OD-404). details.reason AI_PLANE_UNAVAILABLE (retryable false) when the AI plane is not configured on this deployment (details.property names the setting); AI_FEATURE_DISABLED (retryable false; details.disabledBy) when the feature is off; RESPONSE_TOO_LARGE (over 4 MiB); no reason (retryable true) when the circuit is open or the transport failed. No Retry-After header. Session, permission and validation refusals (401, 403, 400) win over it.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"aiPlaneUnavailable":{"summary":"503 PROVIDER_DOWN AI_PLANE_UNAVAILABLE — the AI plane is not configured here","value":{"code":"PROVIDER_DOWN","message":"The AI plane is not configured on this deployment, so the agents, agent-proposals and copilot admin routes are unavailable here. Retrying will not help until an operator configures it.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AI_PLANE_UNAVAILABLE","property":"aikonic.ai.base-url"}}},"aiFeatureDisabled":{"summary":"503 PROVIDER_DOWN AI_FEATURE_DISABLED — the feature is off for this tenant","value":{"code":"PROVIDER_DOWN","message":"The AI features are disabled for this tenant.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"details":{"reason":"AI_FEATURE_DISABLED","disabledBy":"tenant-config"}}},"circuitOpen":{"summary":"503 PROVIDER_DOWN — the AI plane did not answer; retry","value":{"code":"PROVIDER_DOWN","message":"The AI plane is unavailable; retry shortly.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true}}}}}},"HoldRateLimited":{"description":"RATE_LIMITED on POST /v1/hold — the tenant quota (Retry-After set), or details.reason HOLD_CAP_REACHED when the tenant already has its maximum of live holds (BE-7, !72; aikonic.holds.max-live-per-tenant, default 50; details.limit and details.live). Refused before the supplier is called; book, or let a hold expire, then retry. connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.4): with aikonic.keys.velocity.enabled on, also details.reason VELOCITY_LIMIT, a soft breach of the API key's own scope (holdsPerHour, reqPerSec; components/responses/VelocityLimited), nextAction slow_down.\n","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota admits the call (tenant quota only)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"rateLimited":{"summary":"429 RATE_LIMITED — tenant quota (03 §9)","value":{"code":"RATE_LIMITED","message":"Tenant quota exceeded. Retry after 2s","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"retryAfterMs":2000}}},"holdCapReached":{"summary":"429 RATE_LIMITED HOLD_CAP_REACHED — the tenant's live-hold cap","value":{"code":"RATE_LIMITED","message":"This tenant already holds 50 live holds; book or let one expire first.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"details":{"reason":"HOLD_CAP_REACHED","limit":50,"live":50}}},"velocityLimit":{"summary":"429 RATE_LIMITED VELOCITY_LIMIT — the key's own scope, not the tenant quota (v1.57.0 PROPOSAL)","value":{"code":"RATE_LIMITED","message":"This API key exceeded its rate: 10 requests a second. Retry after 1.2 s.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"nextAction":"slow_down","docs":"https://apiq.example/developers/errors#velocity-limit","details":{"reason":"VELOCITY_LIMIT","field":"reqPerSec","limit":10,"windowSeconds":1,"retryAfterMs":1200}}}}}}},"KeyPaused":{"description":"423 — connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.4; answered only while aikonic.keys.velocity.enabled is on or a key was paused by hand). PERMISSION_DENIED with details.reason KEY_PAUSED: the API key is PAUSED and every call with it is refused until an Owner or Admin resumes it (POST /v1/keys/{keyPrefix}:resume, a person only); or details.reason LOOP_DETECTED on the call whose loop paused the key (20 identical requests in 60 s, or 50 holds without a booking or release in 10 min; replays of one Idempotency-Key never count). Not retryable; nextAction stop — the agent stops using the key and tells the person; details carry resumeBy (owner), consoleLink and evidence (ApiKeyPauseEvidence). Nothing was charged, held or sent; holds and orders already made are untouched. A key whose ancestor (the key that minted it, or that key's minter) is PAUSED answers the same 423 KEY_PAUSED, and resuming the ancestor resumes it, so a child never outlasts a pause of its parent [assumption; OD-AGT-6]. The MCP plane answers the identical body as an isError result. Any key-authenticated route may answer it; it is declared on the order and hold writes, search, reprice and the key routes.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"keyPaused":{"summary":"423 PERMISSION_DENIED KEY_PAUSED — nextAction stop until a person resumes the key","value":{"code":"PERMISSION_DENIED","message":"This API key is paused: 212 holds in 9 min. An Owner or Admin must resume it in the console.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":false,"nextAction":"stop","docs":"https://apiq.example/developers/errors#key-paused","details":{"reason":"KEY_PAUSED","resumeBy":"owner","consoleLink":"https://console.example/console/developers/keys/mo_test_AG3NT7KQ2M4P9R5X","evidence":{"rule":"LOOP_HOLDS","dimension":"holdsWithoutOutcomePer10Minutes","observed":212,"limit":50,"windowSeconds":540,"summary":"212 holds in 9 min","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736"],"at":"2026-10-27T10:02:00Z"}}}},"loopDetected":{"summary":"423 PERMISSION_DENIED LOOP_DETECTED — the call that paused the key","value":{"code":"PERMISSION_DENIED","message":"Loop detected: 20 identical requests in 60 s. This API key is now paused.","traceId":"0af7651916cd43dd8448eb211c80319c","retryable":false,"nextAction":"stop","docs":"https://apiq.example/developers/errors#loop-detected","details":{"reason":"LOOP_DETECTED","resumeBy":"owner","consoleLink":"https://console.example/console/developers/keys/mo_test_AG3NT7KQ2M4P9R5X","evidence":{"rule":"LOOP_IDENTICAL","dimension":"identicalRequestsPerMinute","observed":20,"limit":20,"windowSeconds":60,"summary":"20 identical POST /v1/hold in 60 s","requestIds":["0af7651916cd43dd8448eb211c80319c"],"at":"2026-10-27T10:02:00Z"}}}}}}}},"VelocityLimited":{"description":"429 — connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.4; answered only while aikonic.keys.velocity.enabled is on). RATE_LIMITED with details.reason VELOCITY_LIMIT: a soft breach of the API key's own scope (reqPerSec, holdsPerHour, cancelsPerDay, lookToBook, spendPerDay) — the first two in 24 hours; the third, or a burst above 5× the limit, pauses the key instead (423 KeyPaused). Retryable after retryAfterMs (mirrored by Retry-After); nextAction slow_down; details carry limit, windowSeconds and the dimension in details.field. The tenant quota of RateLimited and the key's velocity are separate limits. Every call of a minted key also counts against each ancestor key's velocity and spend windows, so minting N children never multiplies the blast radius (FEAT-034 G4) [assumption; OD-AGT-6]; spendPerDay is reserved atomically before the order is placed and released if it fails.\n","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the key's window admits the call; mirrored in details.retryAfterMs"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"},"examples":{"velocityLimit":{"summary":"429 RATE_LIMITED VELOCITY_LIMIT — the key's own rate; nextAction slow_down","value":{"code":"RATE_LIMITED","message":"This API key exceeded its rate: 10 requests a second. Retry after 1.2 s.","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","retryable":true,"nextAction":"slow_down","docs":"https://apiq.example/developers/errors#velocity-limit","details":{"reason":"VELOCITY_LIMIT","field":"reqPerSec","limit":10,"windowSeconds":1,"retryAfterMs":1200}}}}}}}},"schemas":{"AgentPublicJwk":{"type":"object","description":"A public JSON Web Key (RFC 7517) and nothing else. additionalProperties is false and only the public members kty, crv, x, y, kid, alg and use are allowed; the private members d, p, q, dp, dq, qi and k are refused (AC-044-16, SHIP-feat044 A9): a body that carries one is 400 VALIDATION details.field publicJwk, and the private half of a key never reaches the platform. OKP keys (Ed25519) carry x; EC keys (P-256, ES256) carry x and y.","required":["kty","crv","x"],"additionalProperties":false,"properties":{"kty":{"type":"string","description":"Key type: OKP (Ed25519) or EC (P-256).","enum":["OKP","EC"]},"crv":{"type":"string","description":"Curve: Ed25519 for OKP, P-256 for EC.","enum":["Ed25519","P-256"]},"x":{"type":"string","description":"The public x coordinate (OKP: the public key), base64url without padding.","minLength":43,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"},"y":{"type":"string","description":"The public y coordinate, EC keys only, base64url without padding.","minLength":43,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"},"kid":{"type":"string","description":"Optional key id; the platform keys the key by the RFC 7638 thumbprint it computes.","maxLength":128},"alg":{"type":"string","description":"Signature algorithm: EdDSA or ES256.","enum":["EdDSA","ES256"]},"use":{"type":"string","description":"Key use: sig.","enum":["sig"]}},"not":{"anyOf":[{"required":["d"]},{"required":["p"]},{"required":["q"]},{"required":["dp"]},{"required":["dq"]},{"required":["qi"]},{"required":["k"]}]}},"AgentStamp":{"type":"object","description":"The identity of an agent request as the platform resolved it (FEAT-044 §5.3, §5.4): operator, agent with version, the proofs that count and the tier. Carried on the AccessContext, audit rows, traces and proposals. Identity never widens authority: the stamp only relaxes friction or narrows an unverified caller (DR-167). agentId, handle, version and keyId are absent for an unregistered caller.","required":["operator","proof","tier"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The registered agent id (agt_ and a ULID); absent for an unregistered caller.","pattern":"^agt_[0-9A-Z]{26}$"},"handle":{"type":"string","description":"The agent handle, name@domain (DR-165).","maxLength":300},"version":{"type":"string","description":"The version the agent declared in a covered Agent-Version header; absent when not declared or unknown (SHIP-feat044 A3).","maxLength":64},"operator":{"type":"object","description":"Who runs the agent.","required":["domain","verified"],"additionalProperties":false,"properties":{"tenantId":{"type":"string","description":"The registering tenant, shown only to that tenant and to operators; absent on public surfaces (FEAT-044 §14) and on another tenant's records, where an agent of tenant A acting on tenant B's key shows B only A's public profile (FEAT-044 §9)."},"domain":{"type":"string","description":"The operator domain the handle is under."},"verified":{"type":"boolean","description":"true when the domain proof holds (DNS TXT or a resolving CIMD document)."}}},"proof":{"type":"array","description":"The proofs that count for this request: web-bot-auth (a valid RFC 9421 signature), cimd (a resolving client id document on a verified domain), ans (a valid certificate for the running version) and dns (a signed DNS answer).","uniqueItems":true,"items":{"type":"string","enum":["web-bot-auth","cimd","ans","dns"]}},"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The trust tier T0 to T3 (FEAT-044 §5.4); only ever relaxes friction or narrows an unverified caller."},"keyId":{"type":"string","description":"The JWK thumbprint of the key that signed the request."},"directory":{"type":"string","format":"uri","description":"The signer's key directory URL (`/.well-known/http-message-signatures-directory`), present when a Web Bot Auth signature verified (FEAT-044 §5.3)."}}},"AgentVersion":{"type":"object","description":"One version of an agent (FEAT-044 §5.6): the model and the hash of the system prompt, with its ANS certificate state. The prompt text is never stored here.","required":["version","modelId","promptSha256","state","certificateState","createdAt"],"additionalProperties":false,"properties":{"version":{"type":"string","description":"The version string the tenant registered.","minLength":1,"maxLength":64},"modelId":{"type":"string","description":"The model id the version runs, for example claude-sonnet-5-5.","minLength":1,"maxLength":128},"promptSha256":{"type":"string","description":"Lower-case hex SHA-256 of the system prompt.","pattern":"^[a-f0-9]{64}$"},"state":{"type":"string","description":"ACTIVE accepts requests and new grants; DRAINING keeps working but takes no new grant; REVOKED is refused with 401 AGENT_VERSION_REVOKED.","enum":["ACTIVE","DRAINING","REVOKED"]},"ansName":{"type":"string","description":"The ANS name of the version, when a certificate was requested."},"ansCertSerial":{"type":"string","description":"The serial of the issued ANS certificate."},"ansNotAfter":{"type":"string","description":"When the ANS certificate expires.","format":"date-time"},"certificateState":{"type":"string","description":"The ANS certificate state: NONE, REQUESTED, ISSUED, REVOKED or FAILED (DR-166).","enum":["NONE","REQUESTED","ISSUED","REVOKED","FAILED"]},"revokedAt":{"type":"string","description":"When the version was revoked.","format":"date-time"},"revokeReason":{"type":"string","description":"The reason given at revoke.","maxLength":500},"createdAt":{"type":"string","description":"When the version was registered.","format":"date-time"}}},"AgentKey":{"type":"object","description":"A public signing key of an agent (FEAT-044 §5.6). Only public members are stored (AC-044-16).","required":["keyId","alg","publicJwk","state","createdAt"],"additionalProperties":false,"properties":{"keyId":{"type":"string","description":"The RFC 7638 JWK thumbprint, base64url without padding."},"alg":{"type":"string","description":"The key algorithm: Ed25519 or ES256.","enum":["Ed25519","ES256"]},"publicJwk":{"$ref":"#/components/schemas/AgentPublicJwk"},"state":{"type":"string","description":"CURRENT signs new requests; RETIRING still verifies until retiresAt; RETIRED no longer verifies.","enum":["CURRENT","RETIRING","RETIRED"]},"retiresAt":{"type":"string","description":"When a RETIRING key stops verifying, or when a RETIRED key stopped.","format":"date-time"},"createdAt":{"type":"string","description":"When the key was added.","format":"date-time"}}},"RegisteredAgent":{"type":"object","description":"An agent a tenant registered (FEAT-044 §5.5, §7): handle, versions, keys, domain proof and tier. Distinct from the platform AI agents of FEAT-019 (GET /admin/v1/agents).","required":["agentId","handle","name","domain","hosted","private","protocols","tier","tierReason","suspended","domainProof","versions","keys","dnsRecords","createdAt","createdBy"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id, agt_ and a ULID.","pattern":"^agt_[0-9A-Z]{26}$"},"handle":{"type":"string","description":"The handle, name@domain (DR-165).","maxLength":300},"name":{"type":"string","description":"The display name.","minLength":1,"maxLength":120},"domain":{"type":"string","description":"The domain of the handle: <tenantSlug>.agents.aikonic.travel when hosted, the tenant's own domain otherwise."},"hosted":{"type":"boolean","description":"true when the platform hosts the handle and proves the domain."},"private":{"type":"boolean","description":"true removes the agent from resolve and the directory (DR-170)."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks: mcp, a2a, http."},"cimdUrl":{"type":"string","description":"The client id metadata document URL (FEAT-031 CIMD).","format":"uri"},"cardUrl":{"type":"string","description":"The A2A agent card URL.","format":"uri"},"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The current trust tier."},"tierReason":{"type":"string","description":"Why the agent is at this tier, including a lost domain proof."},"suspended":{"type":"boolean","description":"true while an operator has suspended the agent."},"domainProof":{"type":"object","description":"The state of the domain proof.","additionalProperties":false,"required":["domain","txtName","txtValue","ok","dnssecValidated","lastCheckedAt"],"properties":{"domain":{"type":"string","description":"The domain being proved."},"txtName":{"type":"string","description":"The DNS name of the TXT record."},"txtValue":{"type":"string","description":"The TXT value to publish."},"ok":{"type":"boolean","description":"true when the record was found and matched."},"dnssecValidated":{"type":"boolean","description":"true when the answer was DNSSEC-validated; only a signed answer can raise a tier (LAW-801)."},"lastCheckedAt":{"type":["string","null"],"format":"date-time","description":"When the record was last checked; null before the first check."}}},"versions":{"type":"array","description":"Every version, newest first.","items":{"$ref":"#/components/schemas/AgentVersion"}},"keys":{"type":"array","description":"Every key, with its state.","items":{"$ref":"#/components/schemas/AgentKey"}},"dnsRecords":{"type":"array","description":"The DNS records still to add for an own domain; empty for a hosted handle or once verified.","items":{"type":"object","required":["name","type","value"],"additionalProperties":false,"description":"One DNS record to add.","properties":{"name":{"type":"string","description":"The record name."},"type":{"type":"string","description":"The record type, TXT."},"value":{"type":"string","description":"The record value."}}}},"createdAt":{"type":"string","description":"When the agent was registered.","format":"date-time"},"createdBy":{"type":"string","description":"The user id of the person who registered it."}}},"RegisteredAgentPage":{"type":"object","description":"One page of registered agents.","required":["items","nextCursor"],"additionalProperties":false,"properties":{"items":{"type":"array","description":"The agents on this page.","items":{"$ref":"#/components/schemas/RegisteredAgent"}},"nextCursor":{"type":["string","null"],"description":"Opaque cursor of the next page; null on the last page."}}},"AgentRegisterRequest":{"type":"object","description":"Register an agent (FEAT-044 §5.5). Without domain the handle is hosted (DR-165).","required":["name","handleName","private","protocols","firstVersion","key"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The display name.","minLength":1,"maxLength":120},"handleName":{"type":"string","description":"The local part of the handle (DR-165, FEAT-044 §5.5): 2 to 32 characters of lower-case letters, digits and hyphens, starting with a letter or digit. Reserved names are 422 VALIDATION HANDLE_RESERVED.","pattern":"^[a-z0-9][a-z0-9-]{1,31}$"},"domain":{"type":"string","description":"The tenant's own domain; absent registers a hosted handle.","maxLength":253},"private":{"type":"boolean","description":"true keeps the agent out of resolve and the directory."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks: mcp, a2a, http."},"cimdUrl":{"type":"string","description":"The client id metadata document URL, for an agent that uses CIMD.","format":"uri"},"firstVersion":{"$ref":"#/components/schemas/AgentVersionCreateRequest"},"key":{"type":"object","description":"The first key: a public JWK or the URL of a key directory, exactly one of the two; both or neither is 422 VALIDATION details.reason KEY_SOURCE_AMBIGUOUS.","additionalProperties":false,"properties":{"publicJwk":{"$ref":"#/components/schemas/AgentPublicJwk"},"jwksUrl":{"type":"string","description":"The HTTPS URL of the signer's key directory (/.well-known/http-message-signatures-directory).","format":"uri"}}}}},"AgentUpdateRequest":{"type":"object","description":"Edit an agent (FEAT-044 §5.5). Only the members sent change.","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","description":"The new display name.","minLength":1,"maxLength":120},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The new protocol list."},"cimdUrl":{"type":"string","description":"The new client id metadata document URL.","format":"uri"},"private":{"type":"boolean","description":"true removes the agent from resolve and the directory."}}},"AgentVersionCreateRequest":{"type":"object","description":"Register a version (FEAT-044 §5.6).","required":["version","modelId","promptSha256"],"additionalProperties":false,"properties":{"version":{"type":"string","description":"The version string, unique per agent.","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$"},"modelId":{"type":"string","description":"The model id the version runs.","minLength":1,"maxLength":128},"promptSha256":{"type":"string","description":"Lower-case hex SHA-256 of the system prompt; never the prompt itself.","pattern":"^[a-f0-9]{64}$"}}},"AgentKeyCreateRequest":{"type":"object","description":"Add a key (FEAT-044 §5.6): a public JWK only (AC-044-16).","required":["publicJwk"],"additionalProperties":false,"properties":{"publicJwk":{"$ref":"#/components/schemas/AgentPublicJwk"}}},"AgentReasonRequest":{"type":"object","description":"A reason for a registry change (FEAT-044 §5.6).","required":["reason"],"additionalProperties":false,"properties":{"reason":{"type":"string","description":"Why, 1 to 500 characters; the public log shows the event, not the reason.","minLength":1,"maxLength":500}}},"AgentTierSetRequest":{"type":"object","description":"Set a tier (FEAT-044 §5.4). Exactly one of stepUpCode and stepUpAssertion is required; neither or both is 403 PERMISSION_DENIED STEP_UP_FAILED (both are optional in the schema and the backend enforces the rule, as for OpsApproveRequest).","required":["tier","reason"],"additionalProperties":false,"properties":{"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The tier to set."},"reason":{"type":"string","description":"Why, 1 to 500 characters.","minLength":1,"maxLength":500},"approvalId":{"type":"string","description":"The approved FEAT-024 AGENT_TIER_T3 approval; required for T3."},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code; send this or stepUpAssertion, not both."},"stepUpAssertion":{"description":"The WebAuthn assertion; send this or stepUpCode, not both.","allOf":[{"$ref":"#/components/schemas/WebAuthnAssertion"}]}}},"AgentSuspendRequest":{"type":"object","description":"Suspend or unsuspend an agent (FEAT-044 §5.8). A reason is required on both; step-up as for AgentTierSetRequest.","required":["reason"],"additionalProperties":false,"properties":{"reason":{"type":"string","description":"Why, 1 to 500 characters.","minLength":1,"maxLength":500},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code; send this or stepUpAssertion, not both."},"stepUpAssertion":{"description":"The WebAuthn assertion; send this or stepUpCode, not both.","allOf":[{"$ref":"#/components/schemas/WebAuthnAssertion"}]}}},"AgentPublicProfile":{"type":"object","description":"The public profile of an agent (FEAT-044 §5.5, §14). Never the principal, the tenant id or any traffic.","required":["handle","name","operator","tier","currentVersion","protocols","keyThumbprints","certificate","publicLog"],"additionalProperties":false,"properties":{"handle":{"type":"string","description":"The handle, name@domain."},"name":{"type":"string","description":"The display name."},"operator":{"type":"object","description":"Who runs the agent.","required":["name","domain"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The operator name."},"domain":{"type":"string","description":"The operator domain."}}},"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The current trust tier."},"currentVersion":{"type":["string","null"],"description":"The newest ACTIVE version; null when none is ACTIVE."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks."},"keyThumbprints":{"type":"array","description":"The JWK thumbprints of the CURRENT and RETIRING keys.","items":{"type":"string"}},"cardUrl":{"type":"string","description":"The A2A agent card URL.","format":"uri"},"certificate":{"type":"object","description":"The ANS certificate of the current version.","required":["state"],"additionalProperties":false,"properties":{"ansName":{"type":"string","description":"The ANS name."},"notAfter":{"type":"string","description":"When the certificate expires.","format":"date-time"},"state":{"type":"string","description":"NONE, REQUESTED, ISSUED, REVOKED or FAILED.","enum":["NONE","REQUESTED","ISSUED","REVOKED","FAILED"]}}},"publicLog":{"type":"array","description":"Public registry events, newest first: registered, version_registered, version_revoked, key_rotated, tier_changed.","items":{"type":"object","required":["at","event"],"additionalProperties":false,"description":"One public log entry.","properties":{"at":{"type":"string","description":"When it happened.","format":"date-time"},"event":{"type":"string","description":"The event name without the agent. prefix."},"version":{"type":"string","description":"The version it concerns, where one."}}}}}},"AgentDirectoryEntry":{"type":"object","description":"One agent in the travel directory (FEAT-044 §5.9, DR-170).","required":["handle","name","operator","tier","protocols","canHold","capabilities","outcomeScore"],"additionalProperties":false,"properties":{"handle":{"type":"string","description":"The handle."},"name":{"type":"string","description":"The display name."},"operator":{"type":"object","description":"Who runs the agent.","required":["name","domain"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The operator name."},"domain":{"type":"string","description":"The operator domain."}}},"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The trust tier, T1 or above."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks."},"canHold":{"type":"boolean","description":"true when the agent may use commit-lite tools such as hold (T1 or above under the ceiling)."},"capabilities":{"type":"array","description":"The travel products the agent says it serves, for example flights and hotels.","items":{"type":"string"}},"outcomeScore":{"type":["number","null"],"minimum":0,"maximum":1,"description":"Share of the agent's completed holds that ended well, 0 to 1; null before enough outcomes exist."}}},"AgentDirectoryPage":{"type":"object","description":"One page of the travel agent directory.","required":["items","nextCursor"],"additionalProperties":false,"properties":{"items":{"type":"array","description":"The entries on this page.","items":{"$ref":"#/components/schemas/AgentDirectoryEntry"}},"nextCursor":{"type":["string","null"],"description":"Opaque cursor of the next page; null on the last page."}}},"AgentTrafficRow":{"type":"object","description":"One request that carried an agent identity (FEAT-044 §5.8).","required":["requestId","at","agent","route","outcome"],"additionalProperties":false,"properties":{"requestId":{"type":"string","description":"The request id."},"at":{"type":"string","description":"When the request arrived.","format":"date-time"},"agent":{"$ref":"#/components/schemas/AgentStamp"},"principal":{"type":"string","description":"The masked principal e-mail, where a person stands behind the request."},"route":{"type":"string","description":"Method and path template, for example POST /v1/holds."},"outcome":{"type":"string","description":"ALLOWED, REFUSED (a check or the ceiling refused it) or UNVERIFIED_ALLOWED (served as unverified).","enum":["ALLOWED","REFUSED","UNVERIFIED_ALLOWED"]},"failedCheck":{"type":"integer","description":"The number 1 to 7 of the check that stopped the request, when one did.","minimum":1,"maximum":7}}},"AgentTrafficPage":{"type":"object","description":"Agent traffic with a summary and a 24-hour trend (FEAT-044 §5.8).","required":["summary","items","trend","nextCursor"],"additionalProperties":false,"properties":{"summary":{"type":"object","description":"Counts over the window.","required":["byTier","byProof","signatureFailures","principals","proposalsFiled"],"additionalProperties":false,"properties":{"byTier":{"type":"object","description":"Requests per tier T0 to T3.","additionalProperties":{"type":"integer","minimum":0}},"byProof":{"type":"object","description":"Requests per proof: web-bot-auth, cimd, ans, dns.","additionalProperties":{"type":"integer","minimum":0}},"signatureFailures":{"type":"integer","description":"Requests refused 401 AGENT_SIGNATURE_INVALID.","minimum":0},"principals":{"type":"integer","description":"Distinct principals behind the requests.","minimum":0},"proposalsFiled":{"type":"integer","description":"Proposals agents filed in the window.","minimum":0}}},"items":{"type":"array","description":"The rows on this page, newest first.","items":{"$ref":"#/components/schemas/AgentTrafficRow"}},"trend":{"type":"array","description":"Requests per hour of the last 24 hours, per agent.","items":{"type":"object","required":["agentId","hourly"],"additionalProperties":false,"description":"One agent's trend.","properties":{"agentId":{"type":"string","description":"The agent."},"hourly":{"type":"array","description":"Exactly 24 counts, oldest hour first.","minItems":24,"maxItems":24,"items":{"type":"integer","minimum":0}}}}},"nextCursor":{"type":["string","null"],"description":"Opaque cursor of the next page; null on the last page."}}},"AgentRequestTrace":{"type":"object","description":"The seven-check trace of one agent request (FEAT-044 §5.2, §5.8). Signature and token values are redacted.","required":["requestId","at","agent","checks","signatureHeaders","tokenClaims"],"additionalProperties":false,"properties":{"requestId":{"type":"string","description":"The request id."},"at":{"type":"string","description":"When the request arrived.","format":"date-time"},"agent":{"$ref":"#/components/schemas/AgentStamp"},"checks":{"type":"array","description":"The seven checks in order.","minItems":7,"maxItems":7,"items":{"type":"object","required":["n","name","standard","outcome","ms","detail"],"additionalProperties":false,"description":"One check.","properties":{"n":{"type":"integer","description":"The check number 1 to 7.","minimum":1,"maximum":7},"name":{"type":"string","description":"The check name."},"standard":{"type":"string","description":"The standard it applies."},"outcome":{"type":"string","description":"PASS, FAIL, SKIPPED or UNVERIFIED.","enum":["PASS","FAIL","SKIPPED","UNVERIFIED"]},"ms":{"type":"integer","description":"Time the check took, in milliseconds.","minimum":0},"detail":{"type":"string","description":"A short, secret-free explanation."}}}},"signatureHeaders":{"type":"object","description":"The signature headers as received; the Signature value is redacted.","additionalProperties":{"type":"string"}},"tokenClaims":{"type":"object","description":"The token claims that were present; subject and key binding are redacted.","additionalProperties":true}}},"AgentFleetRow":{"type":"object","description":"One agent in the operator fleet view, with its tenant and counts (FEAT-044 §5.8).","required":["agentId","tenantId","handle","name","domain","tier","suspended","protocols","versionCount","activeVersionCount","keyCount","createdAt"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id."},"tenantId":{"type":"string","description":"The owning tenant."},"handle":{"type":"string","description":"The handle."},"name":{"type":"string","description":"The display name."},"domain":{"type":"string","description":"The handle domain."},"tier":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The current tier."},"suspended":{"type":"boolean","description":"true while suspended."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks."},"versionCount":{"type":"integer","description":"All versions.","minimum":0},"activeVersionCount":{"type":"integer","description":"ACTIVE versions.","minimum":0},"keyCount":{"type":"integer","description":"CURRENT and RETIRING keys.","minimum":0},"createdAt":{"type":"string","description":"When registered.","format":"date-time"}}},"AgentFleetPage":{"type":"object","description":"One page of the operator fleet view (FEAT-044 §6).","required":["items","summary","nextCursor"],"additionalProperties":false,"properties":{"items":{"type":"array","description":"The agents on this page.","items":{"$ref":"#/components/schemas/AgentFleetRow"}},"summary":{"type":"object","description":"Counts over the filter.","required":["total","suspended","byTier"],"additionalProperties":false,"properties":{"total":{"type":"integer","description":"Agents matching.","minimum":0},"suspended":{"type":"integer","description":"Of them suspended.","minimum":0},"byTier":{"type":"object","description":"Agents per tier T0 to T3.","additionalProperties":{"type":"integer","minimum":0}}}},"nextCursor":{"type":["string","null"],"description":"Opaque cursor of the next page; null on the last page."}}},"AgentConsent":{"type":"object","description":"What the consent screen shows about the agent asking for a grant (FEAT-044 §5.7): whether the identity is verified, the warnings to show a person, the ceilings that narrowed the offer and the longest grant offered (DR-168: one hour for an unverified caller on PROD).","required":["verified","warnings","ceilingApplied","maxGrantSeconds"],"additionalProperties":false,"properties":{"verified":{"type":"boolean","description":"true when the agent is at T2 or above."},"warnings":{"type":"array","description":"Plain-language warnings, for example that the agent is unverified.","items":{"type":"string"}},"ceilingApplied":{"type":"array","description":"The tool names the tier ceiling removed from the offer (DR-167).","items":{"type":"string"}},"maxGrantSeconds":{"type":"integer","description":"The longest lifetime offered, in seconds.","minimum":60}}},"AgentRegisteredEventData":{"type":"object","description":"data of agent.registered (FEAT-044 §8).","required":["agentId","handle","protocols"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"handle":{"type":"string","description":"The handle."},"protocols":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["mcp","a2a","http"]},"description":"The protocols the agent speaks."}}},"AgentVersionRegisteredEventData":{"type":"object","description":"data of agent.version_registered (FEAT-044 §8).","required":["agentId","version","modelId","promptSha256"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"version":{"type":"string","description":"The version."},"modelId":{"type":"string","description":"The model id."},"promptSha256":{"type":"string","description":"SHA-256 of the system prompt.","pattern":"^[a-f0-9]{64}$"}}},"AgentVersionRevokedEventData":{"type":"object","description":"data of agent.version_revoked (FEAT-044 §8).","required":["agentId","version","reason","liveGrantsEnded","pendingProposals"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"version":{"type":"string","description":"The revoked version."},"reason":{"type":"string","description":"The reason given."},"liveGrantsEnded":{"type":"integer","description":"MCP grants ended by the revoke.","minimum":0},"pendingProposals":{"type":"integer","description":"Pending proposals the revoke left for a human to approve or reject (DR-38); none is withdrawn.","minimum":0}}},"AgentKeyRotatedEventData":{"type":"object","description":"data of agent.key_rotated (FEAT-044 §8).","required":["agentId","keyId","state"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"keyId":{"type":"string","description":"The key thumbprint."},"state":{"type":"string","description":"The key state after the change.","enum":["CURRENT","RETIRING","RETIRED"]},"retiresAt":{"type":"string","description":"When a RETIRING key stops verifying.","format":"date-time"}}},"AgentTierChangedEventData":{"type":"object","description":"data of agent.tier_changed (FEAT-044 §8).","required":["agentId","from","to","reason"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"from":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The previous tier."},"to":{"type":"string","enum":["T0","T1","T2","T3"],"description":"The new tier."},"reason":{"type":"string","description":"Why the tier moved, including a lost domain proof."}}},"AgentSuspendedEventData":{"type":"object","description":"data of agent.suspended (FEAT-044 §8).","required":["agentId","suspended","reason"],"additionalProperties":false,"properties":{"agentId":{"type":"string","description":"The agent id.","pattern":"^agt_[0-9A-Z]{26}$"},"suspended":{"type":"boolean","description":"true when suspended, false when unsuspended."},"reason":{"type":"string","description":"The operator's reason."}}},"MondeeContext":{"type":"object","description":"Request context envelope (03 §2.1). domain selects the product; URL carries the verb.","required":["domain"],"properties":{"domain":{"type":"string","description":"Canonical code or alias per the domain registry (02 §1); unknown = 400 UNKNOWN_DOMAIN. APTSVC (airport services: meet and assist, porter, buggy, wheelchair, concierge bundles) is registered by this release beside FASTTRK (CANONICAL-MODEL §3.8, applied awaiting ratification; IMP-P2-088). FNB (dining: restaurant reservations; aliases REST, DINING, RESTAURANT, RESTAURANTS; category DO, money model FREE, artifact CONF_NUMBER, served under the dining service key with base path /v1/dining) is registered since v1.7.0 (OD-873). GIFT (gift cards; category SUPPORT, money model MERCHANT, artifact VOUCHER, served under the gift-cards service key with base path /v1/gift-cards) is described from v1.31.0 (OD-892): until the backend seam change merges, domain GIFT answers 400 UNKNOWN_DOMAIN. ESIM (eSIM data plans; aliases <SUPPLIER>_CHIP, <SUPPLIER>_CHIPS, CHIP, CHIPS; category SUPPORT, money model MERCHANT, artifacts ESIM_ACTIVATION and QR, served under the esim service key with base path /v1/esim) has its typed intent from v1.33.0; its verb book is served since OD-893 (an eSIM supplier, SUP-ESIM-EXAMPLE2). AICONT (the Example Content experience-scraping service's operations; category DO, money model MERCHANT, artifact NONE, served under the aicont service key with base path /v1/aicont; a content supplier) has its typed intent from v1.39.0; it answers only where its supplier set is landed and ACTIVE. PARK (airport and cruise-port parking; aliases PRK and PARKING; category SUPPORT, money model MERCHANT, artifact CONF_NUMBER — the supplier's gate barcode is the order's BARCODE document — served under the parking service key with base path /v1/parking) has its typed intent and its verb book from v1.55.0 (PLAT-PARK; a parking supplier).\n"},"action":{"type":"string","description":"Mirrors the verb; optional (server sets from route)"},"enrich":{"type":"array","description":"Opt-in enrichment on shopping answers (IMP-P2-091; SPI §4.1): EMISSIONS | RATINGS | AMENITIES | ROUTE. Absent = the tenant's configured default. Decoration reads cache or the pre-warmed store only and never delays the verb.\n","items":{"type":"string"}},"session_id":{"description":"Your own id for the user session the call belongs to.","type":"string"},"transaction_id":{"type":"string","description":"Constant across one shop-to-book journey (13 §3)"},"currency":{"type":"string","description":"ISO-4217; absent = tenant default; blank/invalid = 400 (tri-state fix, 03 §2.3)"},"locale":{"description":"The language and region for text in the answer, as a BCP 47 tag (for example en-US).","type":"string"},"pos":{"type":"string","description":"Point-of-sale market, orthogonal to tenant"},"clientReference":{"type":"string","description":"Caller's own id; searchable; the reconciliation primitive (06 §8)"},"fareSourcePolicy":{"type":"string","enum":["DEFAULT","OWN_ONLY","MONDEE_ONLY","BLEND"],"description":"BYOC tenants only; narrows, never widens, the configured strategy (08 §4)"},"includeUpstream":{"type":"boolean","default":false,"description":"Ask for the supplier's own answer, verbatim, beside what the platform read from it: Offer.upstream on every offer of a search, poll, detail or flight-returns answer, AirReturnOption.upstream on each listed return, RepriceResponse.message.upstream and OrderItem.upstream (UpstreamPassThrough; OD-901). Served only where the deployment enables upstream pass-through; elsewhere a true value is 400 VALIDATION details.reason UPSTREAM_PASS_THROUGH_DISABLED, details.field context.includeUpstream, before any supplier call. Absent or false, nothing changes. The block is informational: offerRef, repriceRef and price stay the only handles and the only price of record.\n"},"selection":{"$ref":"#/components/schemas/SelectionPreferences"},"onBehalfOf":{"$ref":"#/components/schemas/OnBehalfOf"}}},"OnBehalfOf":{"type":"object","description":"Who a desk or agent books FOR (OD-513, spec/09 §5): stamped into the audit actor and the clientReference lineage of the order. Send customerId (a customer or traveller id the tenant knows) or traveler (a free-text identity), or both. Attribution only: it grants nothing, and the credential's own actor is still what authorises the call. Audit mapping: the audit actor.onBehalfOf (a string) is customerId when present, otherwise \"traveler:<redacted-hash>\" (a stable hash of traveler, never the text).\n","properties":{"customerId":{"description":"A customer or traveller id your own system knows.","type":"string","maxLength":128},"traveler":{"type":"string","maxLength":200,"description":"Free-text traveller identity when there is no customer id. Personal data: it is logged redacted, never in clear. A card number in it is 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED.\n"}}},"UpstreamPassThrough":{"type":"object","description":"The supplier's answer(s) a canonical object was read from, exactly as answered — the supplier's own keys in the supplier's order, decimals as written, nothing renamed and nothing priced (OD-901). Present ONLY when the request sent context.includeUpstream true on a deployment that serves it. It carries what the canonical model otherwise keeps off the wire — the supplier's ids (an Example Air ItineraryId, FareId, superGuid) and its figures as the supplier states them (the agency's fares, major-unit decimals) — by that decision, for the caller that asked. Never book or price from it: the canonical price is the price of record, and offerRef / repriceRef / orderRef are the only handles the next call takes. The ids inside are the platform account's and cannot be used against the supplier without it.\n","required":["supplier","calls"],"properties":{"supplier":{"type":"string","description":"The supplier code the answers came from, for example SUP-AIR-EXAMPLE"},"calls":{"type":"array","description":"One entry per upstream call the object was read from, in the order they were made — for a flight journey one per sector (the search for sector 1, the sector poll for each later one); for a reprice the one reprice call; for an order the book (after createTrip when the deployment opens a trip first).\n","items":{"$ref":"#/components/schemas/UpstreamCall"}}}},"UpstreamCall":{"type":"object","description":"One upstream answer, verbatim, named by the adapter's declared operation (never a host or a URL).\n","required":["operation","response"],"properties":{"operation":{"type":"string","description":"The adapter's operation name, e.g. flights/search, flights/itinerariesBySector, itinerary/reprice, trip/createTrip, itinerary/book (a flight supplier since v1.36.0: Tabhi booking-service /resources/itinerary/{reprice,book}; were flight/legacy/itineraryreprice and flights/bookItinerary)\n"},"sector":{"type":"integer","minimum":1,"description":"FLT only — the journey sector this answer is for (1 = the outbound), when the operation is per sector\n"},"context":{"type":"object","additionalProperties":true,"description":"The answer's own envelope when it carried one (Example Air's TPContext — messageId, SearchRequestId, sectorId, currency)\n"},"response":{"type":"object","additionalProperties":true,"description":"The supplier's JSON exactly as answered — for a flight search one itinerary object; for a reprice or a book the whole body\n"}}},"MondeeResponseContext":{"type":"object","description":"Response context envelope (03 §2.2). Echoes canonical domain + domainAlias when an alias was used.","required":["domain","timestamp"],"properties":{"domain":{"type":"string"},"domainAlias":{"type":"string"},"action":{"type":"string"},"version":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"message_id":{"type":"string"},"took_ms":{"type":"integer"},"from_cache":{"type":"boolean"},"traceId":{"type":"string"},"searchRef":{"type":"string"},"ranking":{"type":"string","description":"Applied ranking policy stamp (06 §3)"},"selection":{"$ref":"#/components/schemas/SelectionStamp"},"warnings":{"type":"array","description":"Non-fatal — suppliers dropped, CURRENCY_UNAVAILABLE counts, CREDENTIAL_DEGRADED, deprecations","items":{"$ref":"#/components/schemas/Warning"}},"page":{"$ref":"#/components/schemas/Page"}}},"ApiErrorResponse":{"type":"object","description":"The one error shape, on every non-2xx answer. Branch on code and honour retryable, never on the HTTP status alone; quote traceId to support.\n","required":["code","message","traceId","retryable"],"properties":{"code":{"type":"string","description":"The complete, closed list of error codes; a finer cause rides in details.reason, never as a new code. Two codes are delivered as 2xx answers rather than error bodies: AMBIGUOUS_RESULT is the 202 OrderResponse (the booking's outcome is not yet known; read the order), and SEARCH_WARMING is primarily the 202 SearchResponse (keep polling), with a 429 form only on legacy flight searches. TENANT_SUSPENDED (403, not retryable) refuses a suspended tenant's new business and new credentials. A supplier charge that differs from the price you consented to is a 409 CONFLICT with details.reason SUPPLIER_PRICE_MISMATCH on the order write (details carry consentedPrice, supplierPrice and repriceRequired), and later the order.supplier_price_mismatch event. connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.2, §6): six agent identity refusals ride as details.reason under stable codes; this enum does not grow. 401 UNAUTHENTICATED details.reason AGENT_SIGNATURE_INVALID (nextAction reauth; details.signatureReason BAD_SIGNATURE, STALE, FUTURE, MISSING_COMPONENT or DIGEST_MISMATCH) is a Web Bot Auth signature that is present and wrong; a request with no signature is never refused for it. 401 UNAUTHENTICATED AGENT_VERSION_REVOKED (stop; AGENT_SUSPENDED for a suspended agent) is a covered Agent-Version header that names a REVOKED version. 403 PERMISSION_DENIED AGENT_TIER_INSUFFICIENT (verify_agent) is a tool class the agent's tier ceiling does not allow (DR-167, DR-168): details carry tier, requiredTier, toolClass and operationId, and the ceiling only ever narrows an unverified caller, it never widens a scope. 409 CONFLICT HANDLE_TAKEN and 422 VALIDATION HANDLE_RESERVED (both stop) refuse a handle at registration. 409 CONFLICT DOMAIN_NOT_VERIFIED (verify_agent) is a domain proof that does not hold yet (details.txtName, details.expected).\n","enum":["VALIDATION","UNKNOWN_DOMAIN","UNAUTHENTICATED","PERMISSION_DENIED","TAB_DISABLED","OUT_OF_SCOPE","TENANT_MISMATCH","TENANT_SUSPENDED","NOT_FOUND","CONFLICT","OFFER_EXPIRED","QUOTE_EXPIRED","HOLD_EXPIRED","PRICE_CHANGED","PENALTY_CHANGED","IDEMPOTENCY_CONFLICT","IDEMPOTENCY_IN_PROGRESS","DUPLICATE_ORDER","TRAVELER_MISMATCH","SOLD_OUT","PAYMENT_FAILED","SEARCH_WARMING","RATE_LIMITED","UPSTREAM_UNAVAILABLE","PROVIDER_DOWN","AMBIGUOUS_RESULT","NOT_IMPLEMENTED","INTERNAL"]},"message":{"type":"string"},"traceId":{"type":"string","description":"The support handle: the 32-hex W3C trace id. When the request carried a valid `traceparent` the platform continues that trace and this is its trace id; otherwise a new trace starts. `X-Correlation-Id` is your own handle, echoed on the response; it never replaces traceId. Quote traceId to support.\n"},"retryable":{"type":"boolean"},"nextAction":{"type":"string","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.6; present when aikonic.errors.next-action.enabled is on — off by default, on at DEV and SANDBOX — and then on every error; absent while it is off and from an older server). What a caller, an agent above all, does next: retry (the same request, honouring retryAfterMs) | slow_down (wait retryAfterMs, then lower the rate) | reprice (409 PRICE_CHANGED: reprice and confirm the new price) | reauth (401: a fresh credential) | fix_request (400 VALIDATION, 404 NOT_FOUND of a ref the caller sent, 409 CONFLICT of the request's own state: change the request — details.field and details.reason say what — and send it again; never terminal) | widen_scope_via_owner (403 PERMISSION_DENIED and its scope reasons: only a person can grant more) | fund_wallet (402 PAYMENT_FAILED WALLET_INSUFFICIENT) | request_invite (409 TENANT_EXISTS) | stop (423 KEY_PAUSED or LOOP_DETECTED: terminal, the same key keeps receiving 423 until a person acts, and the body says who), or complete_step:<step> naming an activation step (ActivationStep.key) or limits — the FEAT-030 §18b limits card (GET /v1/activation/limits, POST /v1/activation/limit-requests), which is not an ActivationStep.key: 403 TIER_CAP_REACHED is complete_step:limits. connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.2): verify_agent (403 AGENT_TIER_INSUFFICIENT and 409 DOMAIN_NOT_VERIFIED: the agent's operator proves its domain or its certificate, in the console or through the agent registry, then tries again). An x-extensible-enum plus the complete_step: pattern; a client that does not know a value falls back to code and retryable. Never free text from a supplier (FEAT-034 §14). REST and MCP (the isError result) carry the identical body. While aikonic.keys.velocity.enabled is on, ANY key-authenticated route may answer 423 (components/responses/KeyPaused) and 429 VELOCITY_LIMIT (components/responses/VelocityLimited); they are declared on the order and hold writes, search, reprice and the key routes [assumption: declaring them on every operation is not required].\n","x-extensible-enum":["retry","slow_down","reprice","reauth","fix_request","widen_scope_via_owner","fund_wallet","request_invite","stop","verify_agent"],"pattern":"^([a-z_]+|complete_step:[a-z_]+)$"},"docs":{"type":"string","format":"uri","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.6; present when aikonic.errors.next-action.enabled is on, as nextAction): the public page that explains this error and what to do, under the developer portal's errors reference, https://apiq.example/developers/errors#<anchor>. The anchor is the kebab-case details.reason where the body carries one (SCOPE_WIDENING is #scope-widening), else the kebab-case code (#rate-limited).\n"},"details":{"type":"object","description":"Code-specific payload, and open by design: every code carries what it needs, so a client reads the keys it knows about and ignores the rest. The keys declared below are the ones a client needs to handle the booking lifecycle (the accepted domains of UNKNOWN_DOMAIN, the old and new price of PRICE_CHANGED, the existing orderRef of DUPLICATE_ORDER, retryAfterMs); everything else is code-specific. Key by key: `previousPrice` / `price` — PRICE_CHANGED. 03 §7: \"any price movement at supplier commit => 409 PRICE_CHANGED with old/new price in details and a fresh reprice required — never a silent swap\"; 03 §10.1 F1 names the pair. Typed as Money so the mandatory interstitial receives the money stamps with the figures (CANONICAL-MODEL §2.1) instead of rebuilding {amount, currency} and dropping fxApplied / fxSnapshotId, which is the lossy narrowing OD-CONSOLE2-03 found on screen. `previousPenalty` / `penalty` — PENALTY_CHANGED, 03 §9 \"cancel penalty moved vs quote\"; the cancel-side twin, typed for the same reason. `snapshotPenalty` / `supplierPenalty` — PENALTY_CHANGED with reason CONTRADICTS_BOOKED_TERMS (OD-369): what the cancellation terms the order was booked on charge now, and what the supplier quoted, both in the order's currency at its booked FX snapshot (fxApplied / fxSnapshotId when converted), beside `orderRef` and `at` (the instant the terms were read at). Raised on cancel-quote and cancel before any consent is read or anything is committed; no consent overrides it. `state` / `attempted` / `links` — CONFLICT on cancel-quote or cancel of an order that is over (CANCELLED, REJECTED, FAILED_COMPENSATED; OD-368, API-0006 §4): the order's current state, the verb refused (cancel-quote | cancel), and the routes the order still offers (self, documents, events — no servicing verb), beside `orderRef`. Nothing was sent to the supplier and nothing was written. `accepted` — UNKNOWN_DOMAIN's accepted domain list (03 §9, 02 §1). `orderRef` — DUPLICATE_ORDER's existing order (03 §9), the backstop for a client that re-books while an order is PENDING_RECONCILIATION (03 §10.1 F3). `retryAfterMs` — SEARCH_WARMING and the retryable codes (03 §5, §9); on a 429 it mirrors the Retry-After header. `reason` — a machine-readable sub-reason where one code covers several causes, left undeclared as a key because its values are code-specific: on an identity-route 429 RATE_LIMITED it is CLIENT_RATE_LIMITED | SUBJECT_RATE_LIMITED, beside `action` (signup | signup-verify | login | password-forgot | password-reset | invite-accept | oauth-token | oauth-token-failure | oauth-token-issued); on a 409 CONFLICT from DELETE /v1/keys/{keyPrefix} it may be KEY_CHANGED_CONCURRENTLY; on a 403 TENANT_SUSPENDED it is TENANT_SUSPENDED, beside `verb` (the refused verb) where a verb was refused; on a key-mint 403 PERMISSION_DENIED it may be TENANT_NOT_LIVE, and on the production deployment so may a verb or payments route's 403 PERMISSION_DENIED, beside `verb` or `route` (FEAT-020 phase C). FEAT-030 (v1.18.0, served since 1.18.1) adds sub-reasons, never codes: PAYMENT_FAILED WALLET_INSUFFICIENT (beside `available` and `required`, Money) on a wallet-paid order; PERMISSION_DENIED TIER_CAP_REACHED (beside `cap` and `resetAt`) when a capped tier's limit is reached (reserved: capped LIVE is not built, OD-ENR-1; un-reserved by connect-v1 1.51.0, below); PERMISSION_DENIED OWNER_ONLY, SESSION_REQUIRED, STEP_UP_FAILED, STEP_UP_LOCKED and STEP_UP_UNAVAILABLE on the activation routes; VALIDATION SANDBOX_ONLY, INDIA_ONLY_FIELD and CURRENCY_MISMATCH; CONFLICT ALREADY_VERIFIED, MONEY_MODEL_LOCKED and TERMS_VERSION_STALE; NOT_IMPLEMENTED KYB_UNAVAILABLE and WALLET_TOPUP_UNAVAILABLE. connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b) adds sub-reasons, never codes: PERMISSION_DENIED TIER_CAP_REACHED is un-reserved on POST /v1/orders, beside `cap` and `usedToday` (Money, in the cap currency: INR when the activation country is IN, else USD; usedToday since 00:00 UTC) and `resetAt` (the next 00:00 UTC); PAYMENT_FAILED CARD_DECLINED (beside `declineCode`) when a card is saved or a SAVED_METHOD order is charged; CONFLICT GSTIN_NOT_ACTIVE, GSTIN_ALREADY_CLAIMED, REGISTRATION_LOCKED, REGISTRATION_NOT_CONFIRMED, LIMIT_REQUEST_OPEN, TENANT_NOT_CAPPED and LAST_METHOD_IN_USE; VALIDATION SIGNATORY_NOT_SIGNED_IN_USER and, on POST /v1/orders with SAVED_METHOD, PAYMENT_MODE_CONFLICT; RATE_LIMITED SIGNATORY_ATTEMPTS_EXCEEDED; NOT_FOUND GSTIN_NOT_FOUND; NOT_IMPLEMENTED CAPPED_FLOW_UNAVAILABLE, GST_REGISTER_UNAVAILABLE, PAYMENT_METHODS_UNAVAILABLE, INVOICES_UNAVAILABLE and NOTIFICATIONS_UNAVAILABLE. connect-v1 1.52.0 (PROPOSAL, FEAT-030 §18b) adds sub-reasons, never codes: CONFLICT FLOW_NOT_CAPPED (POST /v1/activation/terms:send-code on the nine-step flow) and VALIDATION FLOW_NOT_CAPPED (emailCode on the terms acceptance on the nine-step flow); RATE_LIMITED SUBJECT_RATE_LIMITED with `action` terms-code-send; NOT_IMPLEMENTED NO_VERIFICATION_SENDER on terms:send-code. connect-v1 1.57.0 (PROPOSAL, FEAT-034; OD-AGT-17) adds sub-reasons, never codes — the seven FEAT-034 §6 names ride existing codes with the FEAT-034 statuses: PERMISSION_DENIED SCOPE_WIDENING (403, beside `dimensions`, the request members wider than the minting key), PERMISSION_DENIED PRINCIPAL_UNKNOWN (403; X-On-Behalf-Of or onBehalfOf is not a Member the key may act for), PERMISSION_DENIED KEY_PAUSED and LOOP_DETECTED (423, beside `resumeBy`, `consoleLink` and `evidence`), RATE_LIMITED VELOCITY_LIMIT (429, beside `limit`, `windowSeconds`, `retryAfterMs` and the dimension in `field`), CONFLICT TENANT_EXISTS (409 on POST /v1/signup/device:approve and :token, once the e-mail is proven) and RATE_LIMITED SIGNUP_RATE_LIMITED (429). Also PERMISSION_DENIED OWNER_OR_ADMIN_ONLY (key pause and resume); CONFLICT KEY_NOT_ACTIVE, KEY_NOT_PAUSED, ROTATION_TOO_EARLY (beside `expiresAt`) and DEVICE_SIGNUP_DECIDED; VALIDATION EMAIL_MISMATCH and CODE_INVALID (device sign-up approval); NOT_IMPLEMENTED DEVICE_SIGNUP_UNAVAILABLE, AGENT_KEYS_UNAVAILABLE and ONBOARD_UNAVAILABLE; and on an identity-route 429 the `action` values signup-device, signup-device-lookup and signup-device-approve. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2) adds sub-reasons, never codes: VALIDATION FREE_MAIL_DOMAIN and DOMAIN_NOT_COMPANY (the work e-mail); NOT_FOUND REGISTRATION_NOT_FOUND and NOT_IMPLEMENTED REGISTER_UNAVAILABLE (the registration look-up by number); CONFLICT COUNTRY_NOT_LIVE (beside `country`, the tenant's ISO 3166-1 alpha-2 country), PREREQUISITES_OPEN (beside `open`, the open step keys), WORK_EMAIL_REQUIRED and ALREADY_LIVE (POST /v1/activation:go-live); the existing FLOW_NOT_CAPPED, REGISTRATION_LOCKED, GSTIN_NOT_ACTIVE, GSTIN_ALREADY_CLAIMED, STEP_UP_FAILED, STEP_UP_LOCKED and SUBJECT_RATE_LIMITED (`action` work-email-code-send) on the new routes. connect-v1 1.63.0 round 3 (PROPOSAL, FEAT-030 §18c; amended 2026-10-29, same version) adds sub-reasons, never codes: CONFLICT SIGNATORY_NOT_MATCHED, VERIFICATION_IN_REVIEW and VERIFICATION_REJECTED (PUT /v1/activation/business-people), with the existing REGISTRATION_NOT_CONFIRMED and ALREADY_VERIFIED there and NOT_IMPLEMENTED KYB_UNAVAILABLE (beside `country`); VALIDATION VPA_INVALID (POST /v1/payments/methods, the UPI provider refused the VPA). A business that is not verified yet keeps CONFLICT PREREQUISITES_OPEN on POST /v1/activation:go-live. connect-v1 1.63.0 go-live review (PROPOSAL, FEAT-030 §18d; amended 2026-10-29, same version) adds sub-reasons, never codes: CONFLICT GO_LIVE_IN_REVIEW (POST /v1/activation:go-live while a go-live review is open) and VALIDATION PRODUCT_NOT_AVAILABLE (beside `field` products; a requested product is not in the tenant's sandbox capabilities); on POST /ops/v1/approvals/{id}:approve of a GO_LIVE_REVIEW, CONFLICT GO_LIVE_TEST_CALLS_MISSING (beside `missingTestCalls`) and CONFLICT PREREQUISITES_OPEN (beside `open`). On the money and quote verbs `reason` also carries the as-built sub-reason (OD-295): QUOTE_EXPIRED NO_SELL_PRICE_STAMP / SELL_PRICE_STAMP_MISMATCH, SOLD_OUT OFFER_UNSELLABLE, INTERNAL FX_SNAPSHOT_UNAVAILABLE, CONFLICT PENDING_RECONCILIATION, PERMISSION_DENIED APPROVAL_*; on the cancel verbs PENALTY_CHANGED CONTRADICTS_BOOKED_TERMS (OD-369) and INTERNAL FX_SNAPSHOT_UNAVAILABLE (OD-367, there beside `orderRef`, `currency`, `fxSnapshotId` and `supplierCurrency`, and not retryable). An open string: clients branch on code first and read reason when they know it. `pricesWithheld` — PRICE_CHANGED with every amount removed because not all of them could be stated as sell prices (OD-291); previousPrice / price are then absent and a reprice shows the new price. `repriceRequired` — a fresh reprice is required (03 §7). `field` — the request member a refusal is about (VALIDATION, and CONFLICT on the order payment check, where it is message.intent.payment.paymentRef). `detail` — a second-level qualifier under `reason` where one reason covers several cases: AMOUNT | CURRENCY under AMOUNT_MISMATCH, and the payment intent's state under WRONG_STATE (OD-361 (1)).\n","additionalProperties":true,"properties":{"reason":{"type":"string"},"field":{"type":"string"},"detail":{"type":"string"},"pricesWithheld":{"type":"boolean"},"repriceRequired":{"type":"boolean"},"previousPrice":{"$ref":"#/components/schemas/Money"},"price":{"$ref":"#/components/schemas/Money"},"previousPenalty":{"$ref":"#/components/schemas/Money"},"penalty":{"$ref":"#/components/schemas/Money"},"snapshotPenalty":{"$ref":"#/components/schemas/Money"},"supplierPenalty":{"$ref":"#/components/schemas/Money"},"state":{"type":"string"},"links":{"type":"object","additionalProperties":{"type":"string"}},"accepted":{"type":"array","items":{"type":"string"}},"orderRef":{"type":"string"},"retryAfterMs":{"type":"integer"},"cap":{"$ref":"#/components/schemas/Money"},"usedToday":{"$ref":"#/components/schemas/Money"},"resetAt":{"type":"string","format":"date-time"},"dimensions":{"type":"array","description":"SCOPE_WIDENING — the request members wider than the minting key (or the key's current scope)","items":{"type":"string"}},"resumeBy":{"type":"string","description":"KEY_PAUSED, LOOP_DETECTED — who can resume the key; owner (an Owner or Admin)","x-extensible-enum":["owner"]},"consoleLink":{"type":"string","format":"uri","description":"KEY_PAUSED, LOOP_DETECTED — the console page of the key"},"evidence":{"$ref":"#/components/schemas/ApiKeyPauseEvidence"},"windowSeconds":{"type":"integer","description":"VELOCITY_LIMIT, SIGNUP_RATE_LIMITED — the window the limit is counted over"},"open":{"type":"array","items":{"type":"string"},"description":"connect-v1 1.63.0 (PROPOSAL): on 409 CONFLICT PREREQUISITES_OPEN, the keys of the required rail steps that are not done, in rail order\n"},"country":{"type":"string","description":"connect-v1 1.63.0 (PROPOSAL): on 409 CONFLICT COUNTRY_NOT_LIVE, the tenant's country (ISO 3166-1 alpha-2) for which live bookings are not open yet\n"},"missingTestCalls":{"type":"array","maxItems":50,"description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d; amended 2026-10-29, same version): on 409 CONFLICT GO_LIVE_TEST_CALLS_MISSING (POST /ops/v1/approvals/{id}:approve of a GO_LIVE_REVIEW), each requested product whose test_calls check is not passed and the verbs (search, reprice, orders, cancel) not yet seen in the tenant's sandbox\n","items":{"$ref":"#/components/schemas/GoLiveMissingTestCalls"}}}}}},"Money":{"type":"object","description":"Integer minor units + ISO-4217, never floats (platform law)","required":["amount","currency"],"properties":{"amount":{"type":"integer","description":"Minor units"},"currency":{"type":"string","description":"ISO-4217"},"fxApplied":{"description":"True when the amount was converted from the supplier's currency.","type":"boolean"},"fxSnapshotId":{"type":"string","description":"The exchange-rate snapshot the amount was converted with, fixed when the price was quoted."},"breakdown":{"type":"array","description":"Sell-side component lines summing to amount (CANONICAL-MODEL §2.1 breakdown[]); the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers (OD-296, OD-157). Never carries a supplier net.\n","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}}}},"Conditions":{"type":"object","description":"Rate-level capability variance on every offer/reprice (03 §6); authoritative over capabilities at offer level","properties":{"refundable":{"type":"boolean"},"cancellation":{"$ref":"#/components/schemas/CancellationPolicy"},"holdable":{"type":"boolean"},"holdTtlSeconds":{"type":"integer"},"amendable":{"$ref":"#/components/schemas/Amendable"},"instantConfirm":{"type":"boolean"},"confirmSlaSeconds":{"type":"integer"}}},"CancellationPolicy":{"type":"object","description":"The cancellation ladder on an offer's conditions block. Shape transcribed from 03-api-design.md §6, which writes it literally as {\"freeUntil\": \"...\", \"penalties\": [{\"from\": \"2026-09-02\", \"amount\": {...}}]}. Typed here so a client can render the ladder; it was an untyped object, which meant the penalty a traveller is about to accept had no field to read it from. This block is AUTHORITATIVE over the capabilities document at offer level (04 §2 rule 5).\n","properties":{"freeUntil":{"type":"string","format":"date-time","description":"Free-cancellation deadline; absent means no free window (03 §6)"},"penalties":{"type":"array","description":"Ascending ladder; the applicable rung is the last whose `from` has passed","items":{"$ref":"#/components/schemas/CancellationPenalty"}},"quoteRequired":{"type":"boolean","description":"True when the supplier's terms make the penalty knowable only from a live cancel-quote (the ladder is then indicative); CancelPolicyCheck answers QUOTE_REQUIRED (OD-369)\n"},"policyText":{"type":"string","description":"The supplier's cancellation terms as written, for display (v1.25.0). Informational only: the typed ladder (freeUntil, penalties) is AUTHORITATIVE and is what servicing acts on; when the text and the ladder disagree, the ladder wins.\n"}}},"CancellationPenalty":{"type":"object","description":"One rung of the cancellation ladder (03 §6)","required":["from","amount"],"properties":{"from":{"type":"string","format":"date","description":"Date this penalty starts applying (03 §6)"},"amount":{"$ref":"#/components/schemas/Money","description":"The penalty as money, always — a percentage rung is restated as Money at the line's price, never left for the client to compute (OD-341 (k))\n"},"percentBasisPoints":{"type":"integer","minimum":0,"maximum":10000,"description":"Informative: the supplier's percentage when it stated the rung as one (CANONICAL-MODEL §2.3 allows Money or percent); `amount` is still the figure that applies (OD-341 (k))\n"},"fromDateTime":{"type":"string","format":"date-time","description":"Hour-level start of the rung with explicit offset (IMP-P2-083; an activity supplier, a transfer supplier: transfer and activity deadlines are hours, not dates). When present it is authoritative and `from` is its date part.\n"},"hoursBefore":{"type":"integer","minimum":0,"description":"The same rung stated relative to the service start (\"48 hours before pickup\"), for products whose start is the anchor; informative beside fromDateTime\n"}}},"Amendable":{"type":"object","description":"Which amendments this rate permits (03 §6, written there as {\"dates\": true, \"names\": \"CORRECTION_ONLY\", \"partialCancel\": false}). The per-verb capabilities entry (04 §2) is the PLANNING document; this block wins at offer level (04 §2 rule 5).\n","properties":{"dates":{"type":"boolean","description":"Date shift permitted (03 §3 #18)"},"names":{"type":"string","description":"Name-change policy. OPEN string, not a closed enum — 04 §2 carries the same axis as the supplier flag `supportsNameChange` and it is generated from the supplier registry, so a new value must stay additive-and-ignorable (11 §8). Known values today: NONE, CORRECTION_ONLY, FULL.\n"},"partialCancel":{"type":"boolean","description":"Per-line cancellation permitted (03 §10.5 atomicity)"}}},"RequiredInfo":{"type":"array","description":"Example Activities booking-questions mechanism generalized (03 §6): the offer declares what POST /v1/orders will demand, so clients render forms from data. Typed by IMP-P2-077 (G-1, G-9, G-11 of the supplier batches): type, group, requirement, allowed answers, length, data class and FILE answers. Field names follow the backend's api-common RequiredInfo record (`requirement` is CANONICAL-MODEL §2.4's `required`).\n","items":{"type":"object","required":["field"],"properties":{"field":{"type":"string","description":"The answer key — in requiredInfoAnswers (PER_BOOKING) or Traveler.answers (PER_TRAVELER)"},"when":{"type":"string","description":"The condition under which the row applies (CONDITIONAL rows), as text"},"label":{"type":"string","description":"Human question text, in the request locale"},"hint":{"type":"string"},"type":{"type":"string","description":"STRING | NUMBER | DATE | DATETIME | BOOLEAN | ENUM | MULTI_ENUM | PHONE | EMAIL | ADDRESS | DECLARATION | FILE; open string. FILE is answered with an uploadId from POST /v1/uploads (the backend's DOCUMENT_UPLOAD is its as-built alias). DECLARATION is a yes/no whose wrong answer is an eligibility gate (insurance).\n"},"group":{"type":"string","description":"PER_TRAVELER | PER_BOOKING | PER_UNIT; open string"},"requirement":{"type":"string","description":"MANDATORY | OPTIONAL | CONDITIONAL; open string"},"allowedAnswers":{"type":"array","description":"The permitted values of an ENUM / MULTI_ENUM row, as built (plain strings)","items":{"type":"string"}},"answerOptions":{"type":"array","description":"The same choices with labels and the follow-up rows each unlocks","items":{"$ref":"#/components/schemas/RequiredInfoAnswerOption"}},"minLength":{"type":"integer"},"maxLength":{"type":"integer"},"pattern":{"type":"string","description":"A regular expression the answer must match (ECMA-262)"},"dataClass":{"type":"string","description":"DATA-GOVERNANCE §2 class of the answer — C1 | C2 | C3 | C4 | HEALTH; drives consent and retention"},"purpose":{"type":"string","description":"Why the supplier needs it, in the request locale (shown beside a C2 or HEALTH question)"},"consentRequired":{"type":"string","description":"The Consent.consentId that must be present in intent.consents[] before this answer is accepted"},"file":{"$ref":"#/components/schemas/FileAnswerSpec"},"schema":{"type":"object","additionalProperties":true,"description":"A JSON Schema fragment for answers richer than the fields above (Example Activities ticket options — G-1)"}}}},"FareSource":{"type":"object","description":"The money stamp carried on every offer and every order line (08 §1); BYOC visibility rules in 08 §5. CANONICAL-MODEL §2.8 and 17-data-model.md §4 both write it literally as the **`fareSource` stamp `{moneyModel, credentialSet, fxSnapshotId}`**, and §1.5 makes those three mandatory on every money-bearing line. Two of the three are published here; `fxSnapshotId` is published one level down on the `Money` object it stamps (CANONICAL-MODEL §2.1) and is deliberately NOT duplicated here — see OD-CONTRACT2-02.\n","required":["supplier","credentialSet"],"properties":{"supplier":{"type":"string"},"moneyModel":{"type":"string","description":"Which money model this line settles under: `MERCHANT` (the platform is merchant of record) | `AGENCY` | `PASS_THROUGH` (includes BYOC) | `FEE_ONLY` | `FREE`. The value set is 08-suppliers-and-credentials.md §7's money-flow table, read across its four columns; the registry default per product is `domain_registry.moneyModelDefault` (17-data-model.md §4). This is the FIRST of CANONICAL-MODEL §2.1/§1.5's three mandatory money stamps, and it was the one stamp the contract published nowhere — it appeared in this file only inside `Money.fxSnapshotId`'s own description, so a console rendering the stamp row beside an Approve control had two server facts and one invented literal (OD-CONSOLE2-04 / OD-103). Deliberately an OPEN string, not a closed enum, for the same reason `credentialSet` below is: it is serialized from the registry, and 11 §8 exempts only open enums from a version bump — closing it would make a registry addition a breaking wire change (the same trap `ApiErrorResponse.code` is already in — OD-16).\n"},"credentialSet":{"type":"string","description":"`MONDEE` | a tenant credential-set id — the isolation axis (CANONICAL-MODEL §2.2). Deliberately an OPEN string, not an enum: DR-14 removed [MONDEE, TENANT_OWN, SANDBOX] because a closed enum collapses every tenant's own credentials into one value, and F-07's AC requires two tenants on the same supplier with different credentials to get isolated cache entries and rate buckets. Every cache key embeds this value (FOUNDATION-SPEC §2.3, F-07); a cross-credential leak is a commercial incident.\n"}}},"DeviceRegistration":{"type":"object","description":"POST /v1/devices — one phone's push token for the signed-in user (FEAT-021 §18a)","required":["platform","token"],"properties":{"platform":{"type":"string","enum":["APNS","FCM"],"description":"APNS for an iOS device, FCM for an Android device"},"token":{"type":"string","minLength":1,"maxLength":4096,"description":"The APNs device token (hex) or the FCM registration token, exactly as the OS handed it to the app. Treated like a credential: stored for sending, never logged, never returned (a response shows tokenSuffix only).\n"},"appVersion":{"type":["string","null"],"maxLength":64,"description":"The app's version string, for support only; refreshed on every registration"}}},"Device":{"type":"object","description":"One registered device of the caller (FEAT-021 §18a). The token itself is never returned.","required":["deviceId","platform","createdAt"],"properties":{"deviceId":{"type":"string","description":"Opaque id, the handle for DELETE /v1/devices/{deviceId}"},"platform":{"type":"string","enum":["APNS","FCM"]},"tokenSuffix":{"type":"string","minLength":6,"maxLength":6,"description":"The last six characters of the token, so a person can tell two of their devices apart"},"appVersion":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time","description":"When this user first registered the token"},"updatedAt":{"type":"string","format":"date-time","description":"When the token was last registered by this user (every app launch re-registers)"}}},"DeviceList":{"type":"object","required":["devices"],"properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/Device"},"description":"The caller's own devices, newest first"}}},"PushNotificationPayload":{"type":"object","description":"NOT a route and not a webhook: the data the platform's push relay puts into an APNs / FCM notification sent to a registered device (FEAT-021 §18a, DR-35). It carries no traveller data — no name, email, document, itinerary or price: only the order reference, the event type and the new state. On APNs these keys sit at the top level of the notification JSON beside `aps`, whose alert names only the orderRef and the new state; on FCM they are the message's `data` map. The app treats a push as a hint: on a tap it reads GET /v1/orders/{orderRef} (and its events) with the signed-in session, so a push for an order that session cannot read is a 404 there and nothing is shown. The relay sends each event at most once to each device. Example: {\"type\": \"order.state_changed\", \"orderRef\": \"MO-8KD-2FQ-9XT\", \"state\": \"CONFIRMED\"}.\n","required":["type","orderRef","state"],"properties":{"type":{"type":"string","enum":["order.state_changed","payment.captured","payment.failed","payment.refunded"],"description":"The platform event that caused the push (the webhook event of the same name). The payment events are optional for the relay: they are sent only for a payment tied to an order.\n"},"orderRef":{"type":"string","description":"MO-XXX-XXX-XXX (D-48)"},"state":{"type":"string","description":"For order.state_changed the order's new state (the event's `to`); for a payment event the payment's new status — CAPTURED, FAILED or REFUNDED.\n"}}},"Page":{"type":"object","description":"Cursor pagination block (03 §2.2)","properties":{"cursor":{"type":"string"},"nextCursor":{"type":"string"},"total":{"type":"integer"}}},"Warning":{"type":"object","description":"One non-fatal event carried on a response context (03 §2.2: \"suppliers dropped, currency conversions applied, deprecations\"). 03 §2.3 writes the shape literally as {code: \"CURRENCY_UNAVAILABLE\", supplier, dropped: 12} — transcribed here. `code` is deliberately an OPEN string and never a closed enum: 11-developer-portal.md §8 classifies \"a new warnings[] code\" as additive-and-ignorable (no version bump), which a closed enum would turn into a breaking change. Codes in use today: CURRENCY_UNAVAILABLE (03 §2.3), SUPPLIER_DROPPED (03 §5 — the same suppliers the `failed` count counts), CREDENTIAL_DEGRADED (08 §6), DEPRECATION (11 §8, alongside Deprecation/Sunset headers). Added by this release: ENRICHMENT_UNAVAILABLE and ENRICHMENT_STALE (IMP-P2-091 — a decoration missing or served stale; details.kind names the enrichment kind), DATA_STALE (IMP-P2-154 — an offer built from synced data older than its maximum staleness; details.supplier, details.dataset, details.dataAsOf), INDICATIVE_PRICE (an offer whose pricing.indicative is true was returned; reprice firms it). SEARCH_FILTER_UNSUPPORTED (v1.37.0): an FLT search filter (FlightSearchIntent carriers, alliance, refundableOnly, changeableOnly, excludeBasicEconomy, slices[].cabin, fareTypes) that this supplier could not apply, so its offers were searched without it; supplier is set, details.filter names the field and details.reason says why. JOURNEY_MODE_UNSUPPORTED (v1.27.0): an FLT search asked for journeyMode OUTBOUND_FIRST and this supplier answered COMPLETE offers instead; supplier is set. NO_RETURN_OPTIONS (v1.27.0): POST /v1/flights/returns, the supplier said there are no options for the next slice of this outbound; choose another outbound. FREE_OFFER_PRICED (v1.7.0): an offer of a FREE money-model product (FNB) arrived with a non-zero price and was dropped at search, never sold with a price the platform does not collect; supplier and dropped are set. Added in v1.7.0, attributed to supplier EXAMPLEDINING (dining): <SUPPLIER>_RESTAURANT_SKIPPED, <SUPPLIER>_DESTINATION_UNRESOLVED, <SUPPLIER>_DIRECTORY_TRUNCATED, <SUPPLIER>_NO_AVAILABILITY, <SUPPLIER>_CARD_SLOTS_NOT_OFFERED, <SUPPLIER>_PARTY_OUT_OF_RANGE, <SUPPLIER>_LOCK_EXPIRY_CAPPED, <SUPPLIER>_LOCK_LAPSES_AT_SUPPLIER, <SUPPLIER>_POST_BOOKING_ACTION, <SUPPLIER>_BOOKED_OTHER_SLOT, <SUPPLIER>_ALREADY_CANCELLED, <SUPPLIER>_CANCEL_UNCONFIRMED, <SUPPLIER>_RECONCILE_NEEDS_LOCATOR, <SUPPLIER>_EXPERIENCES_UNAVAILABLE, <SUPPLIER>_EXPERIENCE_PRICE_TYPE_ASSUMED, <SUPPLIER>_EXPERIENCE_PAYABLE_AT_RESTAURANT, <SUPPLIER>_EXPERIENCE_TOTAL_UNAVAILABLE, <SUPPLIER>_MENUS_UNAVAILABLE, <SUPPLIER>_REVIEWS_UNAVAILABLE, <SUPPLIER>_PAYMENT_PROFILE_UNAVAILABLE, <SUPPLIER>_AMEND_UNCONFIRMED, <SUPPLIER>_AMEND_STATUS_NOT_READ, <SUPPLIER>_INHOUSE_STATUS_NOT_READ, <SUPPLIER>_INHOUSE_CARD_POLICY_UNSTATED, <SUPPLIER>_LEAD_UNCONFIRMED, <SUPPLIER>_CARD_TOKEN_UNUSED (an amend QUOTE's card token was checked and not kept; send it again on the COMMIT), <SUPPLIER>_FIELD_NOT_PUBLISHED (a partner profile field Example Dining has no field for, e.g. parking or menuUrl; accepted, not sent; details.field names it). The Example Dining refusal codes travel as ApiErrorResponse details.supplierCode (or details.reason where this file says so), never as warnings: <SUPPLIER>_CARD_REQUIRED, <SUPPLIER>_SLOT_GONE, <SUPPLIER>_GUEST_INCOMPLETE, <SUPPLIER>_HOLD_UNKNOWN, <SUPPLIER>_NOT_CANCELLABLE, <SUPPLIER>_QUOTE_UNDETERMINABLE, <SUPPLIER>_AMEND_UNSUPPORTED, <SUPPLIER>_AMEND_SLOT_UNAVAILABLE, <SUPPLIER>_EXPERIENCE_NOT_SERVICEABLE, <SUPPLIER>_LEAD_INVALID, <SUPPLIER>_MODE_UNSUPPORTED, <SUPPLIER>_NOT_PROVISIONED, <SUPPLIER>_CANCEL_CUTOFF_PASSED (409 on cancel-quote and cancel, with details.forfeit), <SUPPLIER>_SCA_REQUIRED-class refusals as details.reason SCA_REQUIRED (and the retired <SUPPLIER>_AMEND_NOT_BUILT, kept so an old journal entry still maps).\n","required":["code"],"properties":{"code":{"type":"string","description":"Open warning code — see the schema description for the codes in use today"},"message":{"type":"string","description":"Human-readable detail; never the thing a client branches on"},"details":{"type":"object","additionalProperties":true,"description":"Structured context for the code — e.g. {\"currency\": \"USD\"} on CURRENCY_DEFAULTED — as the platform sends it on every route today. An open object, the move ApiErrorResponse.details already makes in this file for the same reason (OD-157 D-1, contract half). The attributions below (supplier, fellBackTo, dropped) stay top-level fields; OD-157 D-1's platform half hoists them out of details, which the platform does not do yet — until it does, a client should read details as a fallback.\n"},"supplier":{"type":"string","description":"Supplier registry code when the warning is attributable to one (08 §2). This is what makes a warning a \"per-supplier failure class\" in 06 §2's sense, and it is what a console reads to name the supplier it can attribute a failure to.\n"},"fellBackTo":{"type":"string","description":"The credential set the platform fell back TO, on a CREDENTIAL_DEGRADED warning. 08 §4 rule 3 writes the shape literally as `{code:\"CREDENTIAL_DEGRADED\", supplier, fellBackTo:\"MONDEE\"}` — transcribed here; the contract published the first two and not the third, so a client could see that a fallback happened and not which contract its prices now came from (08 §4 E2: offers are \"tagged by actual source — never silently blended prices from two contracts under one tag\").\n"},"dropped":{"type":"integer","description":"Count of offers dropped by this warning — e.g. CURRENCY_UNAVAILABLE drops an offer that can be neither converted nor natively priced and counts it here rather than relabelling it, which is a CI-testable contract violation (03 §2.3)\n"}}},"SupplierProgress":{"type":"object","description":"Fan-out completeness for a partial or warming search. 03-api-design.md §5 specifies this block literally — \"PARTIAL carries suppliers: {done: 5, pending: 2, failed: 1}\" — and it is transcribed here unchanged. Until now it was an untyped object, so a conforming server could legally report nothing about completeness and a console had no field behind \"2 of 4 suppliers\" (OD-77 / OD-CONSOLE-01). The three counts count SUPPLIERS in this fan-out, not offers; done + pending + failed is the fan-out width. WHAT THIS BLOCK DOES NOT GUARANTEE (OD-CONSOLE2-02 / OD-101). 06 §2 obliges a SERVER to emit \"per-supplier failure classes in warnings\" beside these counts, and the examples on this operation show that pairing. It is a server obligation and NOT a schema invariant: nothing here can require a `failed` count of n to be matched by n entries in context.warnings[], because JSON Schema cannot constrain an integer against the length of a sibling array, and the `if`/`then` and discriminated-`oneOf` forms that could express related conditions do not survive the five generators this contract ships. A conforming response may therefore report `failed: 2` and name one supplier, or none. So a CLIENT MUST NOT pair the two lists. Render the count as a count and the warnings as warnings; a supplier counted in `failed` that no warning names cannot be named, and the honest surface says \"2 failed\" and lists the ones it can attribute. POST /v1/search's 200 branch carries `partialUnattributed` as the fixture for exactly that case, so a consumer can build against it instead of discovering it in production. Until this description said so, it promised the pairing outright and the schema did not enforce it. HOME: 03 §5 places this block in `context`; the published contract has always carried it under `message` and moving it would be a breaking change. It is therefore typed where it already lives — see OD-CONTRACT-02 in 70-decisions/DECISION-REGISTER.md §2.\n","required":["done","pending","failed"],"properties":{"done":{"type":"integer","minimum":0,"description":"Suppliers that have returned their result set (03 §5)"},"pending":{"type":"integer","minimum":0,"description":"Suppliers still in flight. Non-zero implies status PARTIAL or WARMING; poll GET /v1/search/{searchRef} until status is COMPLETE (03 §5)\n"},"failed":{"type":"integer","minimum":0,"description":"Suppliers that errored, timed out, or were dropped (circuit open, credential degraded, CURRENCY_UNAVAILABLE). 06 §2 obliges the server to describe each in context.warnings[] (03 §2.2); the schema cannot enforce it — see this block's own description before pairing the two lists (OD-CONSOLE2-02). Note the boundary: one supplier failing is a PARTIAL with a warning; only when ZERO suppliers respond does the search fail outright as 502 UPSTREAM_UNAVAILABLE (06 §2).\n"},"detail":{"type":"array","description":"Per-supplier status of this fan-out (IMP-P4-018): one entry per supplier counted above, so a warming screen can show \"Example Hotels Rapid: failed (circuit open), retry\". Optional and additive; when present its length equals done + pending + failed.\n","items":{"$ref":"#/components/schemas/SupplierProgressDetail"}}}},"OrderItem":{"type":"object","description":"One product line of an order — \"per-item states for composite orders\" (03 §7); a CART line is exactly this (03 §10.5: \"XFR line FAILED, price re-totaled\"). Fields transcribed from the `order_item` record in 17-data-model.md §4 / CANONICAL-MODEL §2.8, restricted to what the wire is allowed to carry. DELIBERATELY ABSENT: `supplierLocator`. CANONICAL-MODEL §2.2 says the SupplierRef envelope is persisted on order_item and \"never returned to clients\", while 03 §7 says the order response carries \"supplier confirmation numbers as they land\". Those two cannot both be satisfied by one field, so no locator field is published here — see OD-CONTRACT-03.\n","properties":{"itemId":{"type":"string","description":"Per-line handle (`order_item._id`, 17 §4) under its canonical name — the one the platform sends, API-0005 §3.1 and CANONICAL-MODEL §2.9 `itemIds` use (OD-341 (b)). Servicing verbs take it in intent.itemIds.\n"},"upstream":{"$ref":"#/components/schemas/UpstreamPassThrough","description":"The supplier's book answer(s) this line was confirmed by, verbatim (OD-901: for SUP-AIR-EXAMPLE the book, after createTrip when a trip is opened first; a card book's block never carries card data, v1.36.0), recorded with the line and served on POST /v1/orders and GET /v1/orders/{orderRef}. Present only when the booking request sent context.includeUpstream true on a deployment that serves it — except for a supplier that returns its booking answer always (v1.35.0; a cruise supplier, a package supplier): there the line carries the supplier's booking answer on every order answer and read, with the paths the supplier's spec withholds (commission, net figures, supplier session or account data, keyed document links) removed, and a later supplier read-back replaces it.\n"},"itemRef":{"type":"string","deprecated":true,"x-sunset":"2027-09-29","description":"Deprecated alias of itemId (OD-341 (b)). As built today only itemId is sent; the platform is to send both (same value) through the window (OD-341 checklist for P3), removed no earlier than 12 months after 2026-09-29 (A-4, 18 §5). Per-line handle (`order_item._id`, 17 §4); the same identity the supplier console addresses as {orderItemRef} (10 §5) and the one `order.item_failed` names (03 §10.5)\n"},"domain":{"type":"string","description":"Canonical product code for this line (02 §1) — a CART order has several"},"state":{"type":"string","description":"Per-line FSM state. OPEN string, not a closed enum, for the same reason the order-level `state` is one: the C1 FSM is owned by DOMAIN-CONTRACTS and a closed enum here would make an FSM addition a breaking wire change (11 §8). AS BUILT (IMP-P2-041, backend OrderView): PENDING, CONFIRMED, CANCELLED, REFUNDED, FAILED — derived from the order state while an order has one line. C1 per-line vocabulary: BOOKED, CONFIRMED, PENDING_SUPPLIER, PENDING_RECONCILIATION, CANCELLED, FAILED, FAILED_COMPENSATED, REJECTED (VISA, 03 §10.4). Added by IMP-P2-080: PENDING_SUPPLIER_CONFIRMATION (booked, awaiting the supplier's answer — request-to-book, on-request services), CANCELLATION_PENDING (a cancel was accepted and the supplier has not yet confirmed it), PARTIALLY_ISSUED (some of the line's documents or policies issued, see `issuance`), REDEEMED (ticket or voucher used), COMPLETED (service delivered — ground, experiences), NO_SHOW.\n"},"subState":{"type":"string","description":"A qualifier under `state` (IMP-P2-080): PENDING_SUPPLIER_CONFIRMATION | CANCELLATION_PENDING | PARTIALLY_ISSUED | PRICE_ADJUSTMENT_PENDING; absent when none applies. Open string.\n"},"offerRef":{"type":"string","description":"The OF- this line was booked from (IMP-P2-080). Several lines may share one offerRef — one offer that must become N items (one insurance policy per insured, an insurance supplier).\n"},"supplierBookingGroup":{"type":"string","description":"Opaque, platform-minted key shared by every line one supplier booking backs (IMP-P2-080; an airport-services supplier: lounge + porter on one booking). A grouping id, never a supplier locator (OD-CONTRACT-03 stands).\n"},"issuance":{"type":"object","description":"Per-unit issuance progress where one line issues several documents or policies (partial issuance, IMP-P2-080)","properties":{"expected":{"type":"integer"},"issued":{"type":"integer"},"failed":{"type":"integer"},"state":{"type":"string","description":"NONE | PARTIAL | COMPLETE | FAILED"}}},"extension":{"$ref":"#/components/schemas/ItemExtension","description":"The line's typed per-vertical block on the wire (IMP-P2-076): dispatch, ticketing, policy, voucher and application state reach clients here, the same block Offer.extension carries, plus the order-side fields each block names.\n"},"dataAsOf":{"type":"string","format":"date-time","description":"Freshness of the synced data the line's content was built from (FEAT-023); absent for live-only suppliers"},"price":{"$ref":"#/components/schemas/Money"},"fareSource":{"$ref":"#/components/schemas/FareSource"},"supplier":{"type":"string","deprecated":true,"description":"Deprecated; read fareSource.supplier. The order read still sends the supplier id on the line beside fareSource through the OD-341 sunset window (OD-P9-02), then drops it. OPTIONAL.\n"},"fareStamp":{"deprecated":true,"description":"Deprecated; read fareSource (moneyModel, credentialSet) and price.fxSnapshotId. The order read still sends the three F-08 stamps on the line beside fareSource through the OD-341 sunset window (OD-P9-02), then drops it. OPTIONAL. tenantId and travelers are not sent.\n","$ref":"#/components/schemas/FareStamp"},"conditions":{"$ref":"#/components/schemas/Conditions"},"appliedRules":{"type":"array","description":"The markup stamp copied verbatim from the RP- snapshot at commit (CANONICAL-MODEL §2.8, 17 §4 order_item.appliedRules[]). Orders stamp what was applied and never re-evaluate — a rule version retired mid-flow does not change it (FEAT-013 §12).\n","items":{"$ref":"#/components/schemas/AppliedRule"}},"documents":{"type":"array","description":"This line's fulfilment documents (17 §4 order_item.documents[]); empty until generated","items":{"$ref":"#/components/schemas/OrderDocument"}}}},"FareStamp":{"type":"object","description":"The three money stamps of an order line as the backend's canonical FareStamp record carries them (CANONICAL-MODEL §2.8, FOUNDATION-SPEC F-08): {moneyModel, credentialSet, fxSnapshotId}, all three always present when the object is. Only reachable through the DEPRECATED OrderItem.fareStamp, which stays beside fareSource through the OD-341 sunset window (OD-P9-02); new code reads fareSource and price.fxSnapshotId.\n","required":["moneyModel","credentialSet","fxSnapshotId"],"properties":{"moneyModel":{"type":"string","description":"Which money model this line settles under, the same OPEN string as FareSource.moneyModel (08 §7's money-flow table; 11 §8): read FareSource for the values.\n"},"credentialSet":{"type":"string","description":"MONDEE or a tenant credential-set id (OPEN string, as on FareSource.credentialSet)."},"fxSnapshotId":{"type":"string","description":"The FX snapshot minted at quote time, present even when no conversion happened (17 §5).\n"}}},"OrderDocument":{"type":"object","description":"One fulfilment document for an order — \"vouchers, e-tickets, invoices, QR\" with \"async generation -> 202 + document-ready event\" (03 §3 #15); the operation's own 200 already promised \"document descriptors + fetch URLs\" without a field to put either in. `documents[]` refs hang off `order_item` (17 §4 / CANONICAL-MODEL §2.8).\n","required":["type","status"],"properties":{"documentId":{"type":"string"},"type":{"type":"string","description":"Document kind. OPEN string (11 §8): VOUCHER, ETICKET, INVOICE, QR are the kinds 03 §3 #15 names; a new product brings new kinds without a version bump. AS BUILT (backend Document.Type): PNR, E_TICKET, VOUCHER, QR, BARCODE_PDF, POLICY_DOC, ESIM_ACTIVATION, PASS, INVOICE, CERTIFICATE — E_TICKET is the as-built spelling of ETICKET; clients accept both. Added by IMP-P2-084: E_VISA, APPROVAL_LETTER, APPLICATION_FORM (VISA — SUP-VIS-EXAMPLE2), PKPASS, BARCODE (raw code), MOBILE_TRANSFER (a transfer receipt, the ticket lives in the venue app), WILL_CALL (pickup instructions), SHIPMENT (tracking slip), VOUCHER_REHOSTED (a supplier-served voucher re-hosted in-region).\n"},"scope":{"type":"string","description":"PER_UNIT | PER_BOOKING — one document per ticket or one for the booking (IMP-P2-084; G-6)"},"unitIndex":{"type":"integer","description":"Which unit (traveller, ticket, policy) a PER_UNIT document belongs to"},"format":{"type":"string","description":"PDF | PKPASS | PNG | CODE128 | QR | ZIP | HTML; open string"},"delivery":{"$ref":"#/components/schemas/DocumentDelivery"},"status":{"type":"string","description":"PENDING while generating, READY when fetchable, FAILED when generation gave up (03 §3 #15, 03 §7 \"documents: PENDING\"). Open string, same reason as `type`\n"},"url":{"type":"string","format":"uri","description":"Fetch URL; present only once status is READY"},"expiresAt":{"type":"string","format":"date-time","description":"Expiry of the fetch URL (15 §5 — unguessable, time-boxed)"},"itemRef":{"type":"string","description":"The order line this document belongs to (17 §4 order_item.documents[])"},"supplierDeliveryEta":{"type":"string","format":"date-time","description":"When the supplier said the document will be ready, for suppliers that deliver late (API-0006 §7, the Example Activities pattern); absent when the supplier gave none\n"}}},"Offer":{"type":"object","description":"Normalized offer core (06 §3); typed per-domain payload deliberately not unified (03 §1)","required":["offerRef","expiresAt","price"],"properties":{"offerRef":{"type":"string","description":"OF-{v}- prefix; upstream tokens vaulted, never exposed"},"expiresAt":{"type":"string","format":"date-time"},"price":{"$ref":"#/components/schemas/Money"},"conditions":{"$ref":"#/components/schemas/Conditions"},"requiredInfo":{"$ref":"#/components/schemas/RequiredInfo"},"fareSource":{"$ref":"#/components/schemas/FareSource"},"extension":{"$ref":"#/components/schemas/ItemExtension","description":"The one typed per-vertical block (API-0001 §3.1, §8; CANONICAL-MODEL §2.5, §3), keyed by vertical — for example hotel {property, rate} or experiences {timeslot, variants}. It is deliberately NOT unified across verticals (03 §1): ItemExtension stays open and names the typed block each key carries (IMP-P2-076).\n"},"pricing":{"$ref":"#/components/schemas/OfferPricing"},"addOns":{"type":"array","description":"Priced extras available on this offer (IMP-P2-078); selected at order time through intent.addOns[]","items":{"$ref":"#/components/schemas/PricedAddOn"}},"dataAsOf":{"type":"string","format":"date-time","description":"When the offer was built from synced supplier data (FEAT-023 §6, IMP-P2-154), the freshness of that data; absent when the offer came from a live supplier read. An offer older than the dataset's maximum staleness carries context.warnings[] DATA_STALE.\n"},"canonicalId":{"type":"string","description":"Opaque canonical id of the underlying property or product (FEAT-014). Never a raw supplier code, and clients must not parse it. `prov-...` marks a provisional id minted inline at normalization for an unmapped property; it is kept as an alias after promotion, so it keeps resolving.\n"},"provisional":{"type":"boolean","readOnly":true,"description":"True while `canonicalId` is a provisional id, not yet reviewed or promoted (FEAT-014). A provisional offer is sold normally; mapping never blocks sale.\n"},"upstream":{"$ref":"#/components/schemas/UpstreamPassThrough","description":"The supplier's answer(s) this offer was read from, verbatim (OD-901). Present only when the request sent context.includeUpstream true on a deployment that serves it; absent otherwise. On a poll it is what the search run recorded. Never priced from, never booked from.\n"},"selection":{"$ref":"#/components/schemas/OfferSelection"},"alternatives":{"type":"array","description":"On a WINNER in the SELECTED view only: every other offer of the same item, from this or another supplier, best first. Each is a full Offer with its own offerRef, fareSource, conditions and selection.role ALTERNATIVE, and is repriced and booked on its own terms; none is dropped and none is capped. Absent everywhere else.\n","items":{"$ref":"#/components/schemas/Offer"}}}},"SelectionPreferences":{"type":"object","description":"context.selection (PLAT-SUPAGG §19.9, §19.10): the caller's offer-selection preferences for this search. Each field set here replaces the tenant's and the platform's for this search, within the caller's entitlements and the platform's ceilings, unless the tenant has locked request overrides. It never widens reach: a preferred supplier the caller may not reach is ignored with context.warnings[] SELECTION_PREFERENCE_IGNORED. Ignored where selection is off.\n","properties":{"view":{"type":"string","enum":["SELECTED","ALL"],"description":"SELECTED (one entry per item, alternatives under the winner) or ALL (the flat list, tagged)"},"objective":{"type":"string","enum":["CHEAPEST","REFUNDABLE_FIRST","PREFERRED_SUPPLIERS","PLATFORM_DEFAULT"],"description":"How the winner of an item is picked. CHEAPEST is the lowest comparison total; REFUNDABLE_FIRST the lowest total among offers free to cancel now (else CHEAPEST); PREFERRED_SUPPLIERS the first of preferSuppliers with an offer inside the tenant's preference band (else CHEAPEST); PLATFORM_DEFAULT defers to the tenant and platform policies. Any other value is 400 VALIDATION details.field context.selection.objective.\n"},"preferSuppliers":{"type":"array","description":"Supplier codes as in fareSource.supplier, most preferred first (PREFERRED_SUPPLIERS)","items":{"type":"string"}},"excludeSuppliers":{"type":"array","description":"Supplier codes whose offers this search does not show; narrows only","items":{"type":"string"}}}},"SelectionStamp":{"type":"object","description":"context.selection on a response (PLAT-SUPAGG §19.10): the view served and the selection policies that decided it. Absent when selection did not run (off, or a domain whose offers are never grouped). The ranking stamp stays alongside it.\n","properties":{"view":{"type":"string","enum":["SELECTED","ALL"]},"policyRefs":{"type":"array","description":"Every policy rule that set a field, most specific first, as <layer>:<policyId>@v<n>","items":{"type":"string"}},"final":{"type":"boolean","description":"False while an asynchronous run may still land an offer that changes a winner"}}},"OfferSelection":{"type":"object","description":"Offer.selection (PLAT-SUPAGG §19.3, §19.10): the item an offer belongs to and its role in it. An item is one thing a traveller buys whoever sells it; offers of different suppliers are one item only when proven so (matchTier). Present only where selection ran. Carries nothing the platform earns.\n","required":["itemRef","matchTier","role"],"properties":{"itemRef":{"type":"string","description":"Opaque IT-…, scoped to the searchRef; equal on a winner and its alternatives"},"matchTier":{"type":"string","enum":["IDENTICAL","EQUIVALENT","NONE"],"description":"IDENTICAL the same physical item; EQUIVALENT interchangeable by specification (opt-in per product, for example a private sedan transfer on one journey); NONE an offer with no item key\n"},"role":{"type":"string","enum":["WINNER","ALTERNATIVE","STANDALONE"],"description":"STANDALONE is an offer with no key or no partner, an item of one"},"reason":{"type":"string","enum":["ONLY_OFFER","CHEAPEST_TOTAL","IN_BAND_PLATFORM_EARNINGS","IN_BAND_TENANT_MARGIN","REFUNDABLE_FIRST","TENANT_PREFERRED","REQUEST_PREFERRED"],"description":"Why the item's winner won; on a WINNER or STANDALONE. IN_BAND_PLATFORM_EARNINGS and IN_BAND_TENANT_MARGIN mean a dearer offer won inside a small disclosed band above the lowest comparison total.\n"},"comparisonTotal":{"$ref":"#/components/schemas/Money"},"alternativesCount":{"type":"integer","minimum":0,"description":"On a WINNER, how many alternatives the item has"},"final":{"type":"boolean","description":"False while an asynchronous run may still land an offer that changes the winner"}}},"SearchRequest":{"type":"object","description":"Envelope in; intent schema is per-domain (03 §1). resultShape OFFERS | CALENDAR absorbs /v1/calendar.","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","required":["intent"],"properties":{"intent":{"type":"object","additionalProperties":true,"description":"Per-domain intent (deliberately not unified). The typed form per vertical is FlightSearchIntent, StaySearchIntent, GroundSearchIntent, ExperienceSearchIntent, InsuranceSearchIntent, AirportServiceSearchIntent, LoungeSearchIntent, VisaSearchIntent (IMP-P2-075), DiningSearchIntent (FNB, v1.7.0), BusSearchIntent (BUS, v1.15.0), CheckinSearchIntent (CHK, v1.21.0; code CHK since v1.22.0), GiftSearchIntent (GIFT, v1.31.0), EsimSearchIntent (ESIM, v1.33.0), CruiseSearchIntent (CRU, v1.35.0), PackageSearchIntent (PKG, v1.35.0), AicontSearchIntent (AICONT, v1.39.0) and ParkingSearchIntent (PARK, v1.55.0); the platform validates against the one context.domain selects and answers 400 VALIDATION details.field on a violation.\n","properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS","description":"The one cross-domain intent field (API-0001 §3.1); CALENDAR absorbs /v1/calendar"}}}}}}},"SearchResponse":{"type":"object","description":"status WARMING (202) | PARTIAL | COMPLETE with suppliers {done, pending, failed} (03 §5)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"status":{"type":"string","enum":["WARMING","PARTIAL","COMPLETE"]},"retryAfterMs":{"type":"integer"},"suppliers":{"$ref":"#/components/schemas/SupplierProgress"},"offers":{"type":"array","items":{"$ref":"#/components/schemas/Offer"}},"nextCursor":{"type":"string","description":"As served (BE-2 !64): present only on a paged GET /v1/search/{searchRef} answer with more offers left; pass it back as ?cursor= with the same filter and sort. Absent on POST /v1/search and on an unpaged poll. The context.page.nextCursor of earlier examples is not served by the poll.\n"},"searchRef":{"type":"string","description":"As served, the same value as context.searchRef"},"warnings":{"type":"array","description":"As served, the same list as context.warnings","items":{"$ref":"#/components/schemas/Warning"}}}}}},"DetailRequest":{"type":"object","description":"offerRef + optional selection refinement (03 §3 #4)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","additionalProperties":true,"properties":{"offerRef":{"type":"string"},"selection":{"type":"object","additionalProperties":true,"description":"Supplier-interpreted refinement, called repeatedly to narrow; the offerRef does not change. HTL: roomRate. FLT: brand (v1.42.0, one AirExtension.brandOptions key). CRU: fares (optionally with party), category, cabin, page. PKG: room, outbound, inbound, transfer (a key or \"none\"), page.\n"}}}}}}},"DetailResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object"}}},"SchedulesRequest":{"description":"The departures, sailings or showtimes of a product, in the request envelope.","type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` names the product and dates to list.","type":"object"}}},"SchedulesResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object"}}},"SeatmapRequest":{"description":"The seat, berth, cabin or table map of a selection, in the request envelope.","type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` names the selection whose map is read.","type":"object"}}},"SeatmapResponse":{"type":"object","description":"Canonical grid: rows/columns/entities with type SEAT|BERTH|CABIN|TABLE + per-domain decoration (06 §3)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object"}}},"AncillariesRequest":{"type":"object","description":"The extras (bags, seats, early check-in and the like) a selection can take before booking: send a repriceRef or an offerRef.","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` names the selection, a repriceRef or an offerRef.","type":"object"}}},"AncillariesResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object"}}},"RulesRequest":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object"}}},"RulesResponse":{"type":"object","description":"The fare / rate rules of one offer. As built the answer is the client-facing Offer, with the cancellation ladder at message.conditions.cancellation (OD-341 (d)). A flat list of rule text sections is [PROPOSED]: no supplier source feeds it yet.\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"$ref":"#/components/schemas/Offer"}}},"RepriceRequest":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","additionalProperties":true,"description":"Per-domain verb intent (03 §3). Typed in v1.7.0 only for the FNB key below; every other key is read as before.\n","properties":{"experienceSelection":{"$ref":"#/components/schemas/DiningExperienceSelection"}}}}}}},"RepriceResponse":{"type":"object","description":"repriceRef (RP-) + firm price; priceChanged true mandates a client interstitial (03 §10.1). Every reprice mints a FRESH repriceRef, even for an unchanged price (DR-07): an RP- is never reused, so an approval or a payment bound to an RP- binds to that one quote (OD-341 (i)).\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"repriceRef":{"type":"string"},"offerRef":{"type":"string","description":"The OF- ref this reprice was made from"},"validUntil":{"type":"string","format":"date-time","description":"The repriceRef can be booked until then (CANONICAL-MODEL §2.6)"},"firmness":{"type":"string","description":"How firm the price is; open string. SUPPLIER_FIRMED (the supplier confirmed the price) | OFFER_CARRIED (the offer's price carried forward, the verb being unconditional — DR-07) | PLATFORM_EMULATED (IMP-P2-079; ancillary proposal G-1: the platform computed the price from a local rating table, a platform price list or a fresh supplier re-read, and no supplier firmed it — firmnessBasis says which) | FRESH_READ_NO_GUARANTEE (IMP-P2-079; an activity supplier: the supplier was read just now and answered a price it does not guarantee until book). The verb never skips; a later supplier charge that differs is 409 CONFLICT details.reason SUPPLIER_PRICE_MISMATCH on the order write or the order.supplier_price_mismatch event.\n"},"firmnessBasis":{"type":"string","description":"What a PLATFORM_EMULATED or FRESH_READ_NO_GUARANTEE price rests on (IMP-P2-079): LOCAL_TABLE (a rating table the platform holds — Example Insurance) | PLATFORM_PRICE_LIST (a platform-owned price list — Example Lounges) | SUPPLIER_REREAD (a fresh unfirmed supplier read — Example Airport Services, Example Visas, TripXOXO) | SUPPLIER_QUOTE (a supplier quote with a stated validity); open string. Absent on SUPPLIER_FIRMED and OFFER_CARRIED.\n"},"basisVersion":{"type":"string","description":"The rating-table or price-list version the price was computed from (evidence for a later mismatch)"},"upstream":{"$ref":"#/components/schemas/UpstreamPassThrough","description":"The supplier's reprice answer, verbatim (OD-901: for SUP-AIR-EXAMPLE the legacy reprice body with every brand fare it restates). Present only when the request sent context.includeUpstream true on a deployment that serves it. The platform's price above is still the price of record.\n"},"pricing":{"$ref":"#/components/schemas/OfferPricing"},"extension":{"$ref":"#/components/schemas/ItemExtension","description":"The block as the supplier restated it at this reprice (v1.42.0), so a change between the card and the reprice is visible beside the price. FLT: the air block — segments, slices, fare (brand, fare basis, booking class), fare rules and baggage of what /v1/orders will book; never a brand menu (brandOptions is priced only on search and detail). CRZ: the cruise block — the chosen category, cabin and fare as repriced. Absent when the supplier restated no block, and for every other domain.\n"},"addOns":{"type":"array","description":"The add-ons priced into `price` at this reprice, with the chosen quantities (IMP-P2-078)","items":{"$ref":"#/components/schemas/PricedAddOn"}},"paymentRelevant":{"type":"object","additionalProperties":true,"description":"Facts a payment step needs (deposit, pay-at-property, holdable); per product, open (CANONICAL-MODEL §2.6). The fields below are the named ones (IMP-P2-081, 078).\n","properties":{"fork":{"type":"string","description":"PLATFORM_COLLECTS (the platform's PSP takes the money) | SUPPLIER_HOSTED (the supplier's own checkout collects; the order carries the supplier's payment reference) — the payment fork the supplier declared (a transfer supplier)\n"},"depositDue":{"$ref":"#/components/schemas/Money"},"balanceDue":{"$ref":"#/components/schemas/Money"},"balanceDueAt":{"type":"string","format":"date"},"payAtProperty":{"$ref":"#/components/schemas/Money"},"securityDeposit":{"$ref":"#/components/schemas/SecurityDeposit"},"settlementCurrency":{"type":"string"}}},"fxSnapshotId":{"type":"string","description":"The FX snapshot the price was computed at (also on price.fxSnapshotId)"},"price":{"$ref":"#/components/schemas/Money"},"priceChanged":{"type":"boolean"},"previousPrice":{"$ref":"#/components/schemas/Money"},"conditions":{"$ref":"#/components/schemas/Conditions"},"requiredInfo":{"$ref":"#/components/schemas/RequiredInfo"},"appliedRules":{"type":"array","description":"The C3-mandatory markup stamp (CANONICAL-MODEL §2.6, FEAT-013 §1): every rule the engine applied to reach `price`, evaluated exactly once at this reprice and copied onto the order line at commit (17-data-model.md §3/§4). Additive; absent only on a pre-FEAT-013 server.\n","items":{"$ref":"#/components/schemas/AppliedRule"}}}}}},"HoldRequest":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","additionalProperties":true,"description":"Per-domain verb intent (03 §3). Typed in v1.7.0 only for the FNB key below; every other key is read as before.\n","properties":{"experienceSelection":{"$ref":"#/components/schemas/DiningExperienceSelection"}}}}}}},"HoldResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"holdRef":{"type":"string","description":"HD-{v}- prefix"},"holdExpiresAt":{"type":"string","format":"date-time"},"repriceRef":{"type":"string","description":"The RP- the hold was placed on (CANONICAL-MODEL §2.7)"},"state":{"type":"string","description":"Open string — ACTIVE, CONSUMED, EXPIRED, RELEASED (API-0004 §3)"},"price":{"$ref":"#/components/schemas/Money"},"attribution":{"$ref":"#/components/schemas/Attribution","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7, AC-034-11): the principal, agent and key the hold was placed with; absent from an older server and for holds placed while aikonic.keys.agent-mint.enabled was off.\n"}}}}},"OrderCreateRequest":{"type":"object","description":"Intent: repriceRef or holdRef + travelers + requiredInfoAnswers + ancillaries + payment (03 §7)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","required":["intent"],"properties":{"intent":{"type":"object","description":"The booking intent (03 §7). Required as built: travelers[] and one of repriceRef or holdRef (backend IntentSchemaRegistry). payment.mode: PSP_TOKEN | CREDIT_LINE | PAY_LATER | SUPPLIER_HOSTED | NONE | PAYMENT_METHOD_TOKEN | OPENTABLE_SMS_LINK | WALLET | SAVED_METHOD (v1.51.0 PROPOSAL) (as built paymentRef and, since v1.7.0, paymentMethod are read). WALLET (v1.18.0, served since 1.18.1, FEAT-030 §5.5) pays from the tenant's prepaid wallet in the order's currency: no paymentRef; the platform holds the total at book (WALLET_HOLD), settles it at ticketing or confirmation and releases it when the booking fails. Too little available is 402 PAYMENT_FAILED details.reason WALLET_INSUFFICIENT with details.available, and nothing is sent to a supplier. A card-secured dining slot (DiningExtension.creditCardRequired) needs payment.paymentMethod, a Spreedly payment-method token: without it the order is 400 VALIDATION details.reason PAYMENT_METHOD_REQUIRED and nothing is sent (D3, OD-875). A supplier that demands 3-D Secure refuses the order with 400 VALIDATION details.reason SCA_REQUIRED (nothing booked); the client retries with paymentMethod.threeDs. An In-House slot (cardCapture OPENTABLE_SMS_LINK) takes no token: Example Dining texts the diner, so requiredInfoAnswers.smsOptIn must be true. ENCRYPTED_CARD (v1.36.0, PLAT-CARDENV; SANDBOX/UAT only, OD-904 — PROD answers 501 NOT_IMPLEMENTED details.reason CARD_PAYMENT_NOT_ENABLED) pays a supplier that charges the card itself (a flight supplier: PaymentType CC, ticketed at book) with a card the caller sealed to GET /v1/payments/card-key: payment.encryptedCard {kid, jwe}, and the lead traveller's billingAddress as the card's billing address. It excludes paymentRef, paymentMethod and checkoutSignature (400 VALIDATION details.reason PAYMENT_MODE_CONFLICT). Refused before the approval grant is redeemed and before anything is written or sent: an envelope that does not open (CARD_ENVELOPE_INVALID), an unusable card (CARD_VALIDATION_FAILED, details.field names the field, never the value), a card not on the rung's test-card list (CARD_NOT_A_TEST_CARD), a missing billing address (BILLING_ADDRESS_REQUIRED), an intent naming the supplier account (Customer, UserInfo, customerId, dkNumber, consolidatorId: CUSTOMER_NOT_CALLER_SUPPLIED), and a supplier that takes no card (501 NOT_IMPLEMENTED CARD_PAYMENT_NOT_SUPPORTED). The card is opened in memory for the one booking call and stored, logged or echoed nowhere. A retry must resend the identical bytes: a re-sealed card under the same Idempotency-Key is 409 IDEMPOTENCY_CONFLICT. SAVED_METHOD (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b; served when aikonic.payment-methods.enabled is on) charges a method the tenant saved at POST /v1/payments/methods: payment.methodId names it (absent, the default method [assumption]); the platform charges its stored PSP token through the payments seam inside the write-ahead flow, before any supplier byte, and no card data travels in the order. An unknown or another tenant's methodId is 400 VALIDATION details.field message.intent.payment.methodId; a decline is 402 PAYMENT_FAILED details.reason CARD_DECLINED and nothing is sent to a supplier. SAVED_METHOD excludes paymentRef, paymentMethod and encryptedCard: any of them with it is 400 VALIDATION details.reason PAYMENT_MODE_CONFLICT (the ENCRYPTED_CARD rule), refused before anything is written or sent. WALLET and PSP_TOKEN are unchanged.\n","properties":{"repriceRef":{"type":"string"},"holdRef":{"type":"string"},"clientReference":{"type":"string","description":"The tenant's own reference, echoed on the order (OD-157 item 5)"},"travelers":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/Traveler"}},"requiredInfoAnswers":{"type":"object","additionalProperties":true,"description":"PER_BOOKING answers keyed by RequiredInfo.field; a FILE answer is an uploadId (IMP-P2-077). FNB diner answers live HERE (v1.7.0), never on the traveller: phoneCountry (ISO 3166 alpha-2 of the diner's phone), phone (the diner's phone number as the restaurant should call it, national digits with phoneCountry, or E.164; absent = travelers[lead].phoneNumber), specialRequest (at most 75 characters; longer is 400 VALIDATION details.field message.intent.requiredInfoAnswers.specialRequest), restaurantEmailOptIn, opentableEmailOptIn and smsOptIn (booleans, absent = false: an opt-in is never defaulted to true; smsOptIn must be true for an OPENTABLE_SMS_LINK card capture).\n"},"consents":{"type":"array","description":"The consents this order was placed under (IMP-P2-077); a RequiredInfo row with consentRequired is refused 400 VALIDATION details.reason CONSENT_REQUIRED without the matching entry","items":{"$ref":"#/components/schemas/Consent"}},"addOns":{"type":"array","description":"Selected add-ons from Offer.addOns[] (IMP-P2-078); must match what the RP- priced","items":{"$ref":"#/components/schemas/AddOnSelection"}},"options":{"type":"array","description":"Experience option choices (ExperienceOption) when not already fixed at reprice","items":{"type":"object","required":["optionId","value"],"properties":{"optionId":{"type":"string"},"value":{"type":"string"}}}},"taxQuoteSignature":{"type":"string","description":"The tax-quote signature from the reprice, echoed so the order binds to it (an event-ticket supplier)"},"experienceSelection":{"$ref":"#/components/schemas/DiningExperienceSelection"},"payment":{"type":"object","properties":{"mode":{"type":"string"},"paymentRef":{"type":"string","description":"The paymentId of POST /v1/payments/intent, checked before any supplier byte (OD-301 (b))"},"checkoutSignature":{"$ref":"#/components/schemas/CheckoutSignature"},"paymentMethod":{"$ref":"#/components/schemas/PaymentMethodToken"},"encryptedCard":{"$ref":"#/components/schemas/EncryptedCardEnvelope"},"methodId":{"type":"string","description":"connect-v1 1.51.0 (PROPOSAL): with mode SAVED_METHOD, the saved payment method to charge (SavedPaymentMethod.id); absent, the tenant's default method [assumption].\n"}}},"ancillaries":{"type":"array","items":{"type":"object","additionalProperties":true}}},"example":{"repriceRef":"RP-H-2ab91c","clientReference":"acme-91827","travelers":[{"type":"ADT","firstName":"Ana","lastName":"Silva","email":"ana.silva@example.com","phoneNumber":"+5511999990000","lead":true}],"requiredInfoAnswers":{"arrivalTime":"15:00"},"consents":[{"consentId":"SUPPLIER_TERMS","version":"2026-09","acceptedAt":"2026-11-01T10:12:00Z"}],"payment":{"mode":"PSP_TOKEN","paymentRef":"pay_01J8Q1"}}}}}}},"OrderResponse":{"type":"object","description":"Order with FSM state (DOMAIN-CONTRACTS C1), per-item states, payments[] and refunds[] (OD-301). No links{} is published yet (OD-451 option (a)): clients gate servicing verbs on state and items[].conditions, and the verb's own 409 CONFLICT is authoritative.\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","description":"The order as POST /v1/orders (201/202) and GET /v1/orders/{orderRef} both answer it — one mapping on the platform (F-03 OrderView.message), so the write and the read cannot drift (OD-291). `totals` and `clientReference` are declared as built (OD-157 item 5): `totals` is the order's total as a client may see it — the consented sell price (the RP- stamp), never the supplier's net (FEAT-013 §1, §11) — in the order's currency, with fxApplied / fxSnapshotId when it was converted (API-0005 §3.1). `clientReference` is the tenant's own reference, echoed as sent in message.intent.clientReference at POST /v1/orders (trimmed; blank counts as none) and absent when none was sent; it is the key POST /v1/orders/search filters on for crash recovery (06 §8 step 6). `policyCheck` and `amountsWithheld` ride ONLY on the answer of POST /v1/orders/{orderRef}/cancel (OD-369, OD-367; `cancellation`, OD-488, likewise) and never on an order read or write; see CancelPolicyCheck.\n","properties":{"orderRef":{"type":"string","description":"MO-XXX-XXX-XXX (D-48)"},"state":{"type":"string","description":"Open string (the C1 FSM is owned by DOMAIN-CONTRACTS): BOOKED, CONFIRMED, PENDING_SUPPLIER, PENDING_RECONCILIATION, PARTIALLY_CONFIRMED (CART BEST_EFFORT), CANCELLED, REFUNDED, FAILED_COMPENSATED, REJECTED (VISA). AS BUILT the backend Order.State also holds DRAFT, QUOTED, IN_PAYMENT and GUARANTEED (IMP-P2-041); a read may show them on an order mid-saga and a client treats them as \"not yet BOOKED\".\n"},"subState":{"type":"string","description":"A qualifier under `state` (IMP-P2-080): PENDING_SUPPLIER_CONFIRMATION (booked, awaiting a supplier answer — request-to-book VR, on-request airport services), CANCELLATION_PENDING (cancel accepted, supplier confirmation outstanding), PARTIALLY_ISSUED (some lines or units issued), PRICE_ADJUSTMENT_PENDING (a supplier-initiated post-trip charge awaits consent). Absent when none applies.\n"},"supplierConfirmation":{"type":"object","description":"Present while subState is PENDING_SUPPLIER_CONFIRMATION","properties":{"dueAt":{"type":"string","format":"date-time","description":"The on-request SLA deadline (conditions.confirmSlaSeconds from book time)"},"channel":{"type":"string","description":"SUPPLIER_API | SUPPLIER_PORTAL | EMAIL | OPS_DESK — how the answer arrives"}}},"priceAdjustments":{"type":"array","description":"Supplier-initiated post-trip price changes (IMP-P2-080; order.price_adjusted), newest last","items":{"type":"object","properties":{"adjustmentRef":{"type":"string"},"itemId":{"type":"string"},"initiator":{"type":"string"},"delta":{"$ref":"#/components/schemas/Money"},"breakdown":{"type":"array","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}},"state":{"type":"string","description":"PENDING_CONSENT | ACCEPTED | DISPUTED | APPLIED | WITHDRAWN"},"createdAt":{"type":"string","format":"date-time"}}}},"consents":{"type":"array","description":"The consents the order was placed under, as evidence (IMP-P2-077)","items":{"$ref":"#/components/schemas/Consent"}},"stateVersion":{"type":"integer","description":"The order row's version, incremented on every transition — the optimistic-concurrency token servicing verbs are checked against (API-0006 §5). As built (OD-341).\n"},"clientReference":{"type":"string","description":"The tenant's own reference for this order, echoed as given in message.intent.clientReference at POST /v1/orders (trimmed; blank counts as none); absent when none was given. As built the book reads it from the intent, not from context.clientReference. The crash-recovery key (06 §8 step 6) and the DUPLICATE_ORDER heuristic's input (03 §9). OD-157 item 5.\n"},"totals":{"$ref":"#/components/schemas/Money"},"items":{"type":"array","description":"Per-item states for composite orders (03 §7); one line per product","items":{"$ref":"#/components/schemas/OrderItem"}},"fareSource":{"$ref":"#/components/schemas/FareSource"},"payments":{"type":"array","description":"The payments that pay for this order (OD-301): today one entry, the paymentRef the book was sent with (message.intent.payment.paymentRef), kept across every transition. Absent when the order names no payment. Read the payment itself with GET /v1/payments/{paymentId}.\n","items":{"$ref":"#/components/schemas/OrderPayment"}},"refunds":{"type":"array","description":"Money returned on this order (OD-301): one entry per refund on a payment that pays for it, newest last. Absent when nothing was refunded.\n","items":{"$ref":"#/components/schemas/OrderRefund"}},"serviceCases":{"type":"array","description":"GET /v1/orders/{orderRef} only (BE-4 !63, IMP-P3-014): the tenant-visible status of the ops-desk cases on this order, newest first; absent when there are none. Never on POST /v1/orders or POST /v1/orders/search.\n","items":{"$ref":"#/components/schemas/OrderServiceCase"}},"policyCheck":{"$ref":"#/components/schemas/CancelPolicyCheck"},"cancellation":{"type":"object","description":"Cancel answer only (OD-488): what the supplier actually applied, which can differ from the cancel-quote (a penalty that shrank between quote and cancel). Optional; absent on every order read and write, and when amountsWithheld is present. Both figures are in the order's currency, as in the cancel-quote.\n","properties":{"appliedPenalty":{"$ref":"#/components/schemas/Money"},"appliedRefund":{"$ref":"#/components/schemas/Money"}}},"amountsWithheld":{"type":"string","description":"Cancel answer only (OD-367): present when the supplier has acted (or may have) and its figures could not be restated in the order's currency at the order's booked FX snapshot, so every amount is withheld rather than answered in another currency. The value is the reason; today always FX_SNAPSHOT_UNAVAILABLE. Absent whenever the amounts are shown.\n"},"attribution":{"$ref":"#/components/schemas/Attribution","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7, AC-034-11): the principal, agent and key the order was created with; absent from an older server and for orders created while aikonic.keys.agent-mint.enabled was off.\n"}}}}},"OrderPayment":{"type":"object","description":"One payment an order is paid with (OD-301, API-0010). paymentRef is the paymentId of POST /v1/payments/intent. state and amount are the payment's as last seen by the order read; the payment read is authoritative.\n","required":["paymentRef"],"properties":{"paymentRef":{"type":"string"},"state":{"type":"string","description":"Open string — the PaymentIntent state (AUTHORIZED, CAPTURED, PARTIALLY_CAPTURED, PARTIALLY_REFUNDED, REFUNDED, PENDING_RECONCILIATION), passed through as the payment read reports it. Clients must treat an unknown value as non-terminal"},"amount":{"$ref":"#/components/schemas/Money"},"capturedAmount":{"$ref":"#/components/schemas/Money"},"refundedAmount":{"$ref":"#/components/schemas/Money","description":"Cumulative refunded against this payment; both figures are as last seen by the order read (OD-520)"}}},"OrderRefundIssuedData":{"type":"object","description":"The typed data of the order.refund_issued event (OD-520)","required":["orderRef","amount"],"properties":{"orderRef":{"type":"string"},"refundRef":{"type":"string"},"paymentRef":{"type":"string","description":"The payment the money went back to"},"amount":{"$ref":"#/components/schemas/Money"}}},"OrderRefund":{"type":"object","description":"One refund of money paid for this order (OD-301, API-0010 refund)","required":["paymentRef","amount"],"properties":{"refundRef":{"type":"string"},"paymentRef":{"type":"string","description":"The payment the money went back to"},"amount":{"$ref":"#/components/schemas/Money"},"state":{"type":"string","description":"Open string — PENDING, SUCCEEDED, FAILED, PENDING_RECONCILIATION"},"createdAt":{"type":"string","format":"date-time"}}},"OrderSearchRequest":{"type":"object","description":"Filter object (clientReference, traveler, date range, domain, state) — 03 §3 #14; message.page pages the result (OD-515)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"page":{"$ref":"#/components/schemas/OrderSearchPage"}}}}},"OrderSearchPage":{"type":"object","description":"Request paging for POST /v1/orders/search (OD-515). cursor is the context.page.nextCursor of the previous answer, unchanged; absent = the first page. limit caps the rows returned; the server applies its own default and maximum.\n","properties":{"cursor":{"type":"string"},"limit":{"type":"integer","minimum":1}}},"OrderSearchResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object"}}}}}},"DocumentsResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"documents":{"type":"array","description":"Vouchers, e-tickets, invoices, QR; status PENDING while generating","items":{"$ref":"#/components/schemas/OrderDocument"}}}}}},"OrderVisaAssistanceRequest":{"type":"object","description":"The trip behind an order to hand to the visa provider, one entry per traveller (v1.34.0)","additionalProperties":false,"required":["travellers","from","to"],"properties":{"travellers":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/OrderVisaAssistanceTraveller"}},"from":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country the trip starts from"},"to":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","pattern":"^[A-Z]{2}$"},"description":"ISO 3166-1 alpha-2 destination countries"},"startDate":{"type":"string","format":"date-time","description":"Departure; the provider's e-mail calls it the departure date"},"endDate":{"type":"string","format":"date-time","description":"Return or end of the trip"},"cc":{"type":"array","maxItems":10,"items":{"type":"string","format":"email","maxLength":254},"description":"Extra addresses for the provider's e-mail (whether the provider writes to them is not yet confirmed)"}}},"OrderVisaAssistanceTraveller":{"type":"object","description":"One traveller to send (v1.34.0). Personal data: sent to the provider once, never stored in full by the platform (the record keeps the first name, last initial and masked e-mail).\n","additionalProperties":false,"required":["firstName","lastName","email"],"properties":{"firstName":{"type":"string","minLength":1,"maxLength":64},"lastName":{"type":"string","minLength":1,"maxLength":64},"email":{"type":"string","format":"email","maxLength":254,"description":"The provider e-mails the traveller here"},"passportCountry":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country that issued the passport"}}},"OrderVisaAssistanceResponse":{"type":"object","description":"The sends of one request (POST), or every send of the order newest first (GET) (v1.34.0)","required":["orderRef","provider","supplier","sends"],"properties":{"orderRef":{"type":"string"},"provider":{"type":"string","description":"The visa provider's name, as shown to the traveller (Example Visas)"},"supplier":{"type":"string","description":"The supplier registry id (a visa supplier)"},"sends":{"type":"array","items":{"$ref":"#/components/schemas/OrderVisaAssistanceSend"}}}},"OrderVisaAssistanceSend":{"type":"object","description":"One traveller's trip as sent to the visa provider, with the provider's prepared application link (v1.34.0)","required":["sendId","traveller","from","to","applyUrl","emailedBy","sentAt"],"properties":{"sendId":{"type":"string","maxLength":64},"traveller":{"type":"string","maxLength":160,"description":"First name, last initial and masked e-mail, e.g. \"Test T. · t***@example.com\""},"from":{"type":"string","pattern":"^[A-Z]{2}$"},"to":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$"}},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"applyUrl":{"type":"string","format":"uri","description":"The provider's prepared application for this traveller and trip; opens on the provider's site"},"emailedBy":{"type":"string","description":"Who e-mailed the traveller (the provider)"},"sentAt":{"type":"string","format":"date-time"}}},"OrderTicketsResponse":{"type":"object","description":"connect-v1 1.16.0. GET /v1/orders/{orderRef}/tickets — read live, never stored.","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","required":["orderRef","tickets"],"properties":{"orderRef":{"type":"string"},"tickets":{"type":"array","items":{"$ref":"#/components/schemas/OrderTicket"}}}}}},"OrderTicket":{"type":"object","description":"connect-v1 1.16.0. One ticket as the supplier states it now. passenger is personal data: never cached by the platform and never to be logged by a client.\n","required":["ticketId","passenger","status"],"properties":{"ticketId":{"type":"string","maxLength":64},"reference":{"type":"string","maxLength":128,"description":"The operator's booking reference"},"passenger":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"category":{"type":"string","description":"The supplier's fare category (adult, youth, senior …)"}}},"seat":{"type":"string"},"status":{"type":"string","enum":["ISSUED","CANCELLED"]},"price":{"$ref":"#/components/schemas/Money"},"pdf":{"type":"string","format":"uri","description":"The ticket's own document, supplier-hosted"}}},"OrderFareRulesResponse":{"type":"object","description":"connect-v1 1.43.0. GET /v1/orders/{orderRef}/fare-rules — read live, never stored.","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","required":["orderRef","fareRules"],"properties":{"orderRef":{"type":"string"},"fareRules":{"type":"array","description":"One entry per flown segment, in itinerary order. Empty when the supplier states no segment.","items":{"$ref":"#/components/schemas/OrderFareRule"}}}}}},"OrderFareRule":{"type":"object","description":"connect-v1 1.43.0. The fare-rule text of one segment of a booked fare, as the supplier states it now. Display text: verbatim, never parsed by the platform into conditions (the structured terms are AirFareRules on the offer and the order line).\n","required":["origin","destination"],"properties":{"origin":{"type":"string","description":"IATA airport code the segment departs from","maxLength":3},"destination":{"type":"string","description":"IATA airport code the segment arrives at","maxLength":3},"carrier":{"type":"string","description":"The marketing carrier's IATA code","maxLength":3},"departureDate":{"type":"string","format":"date","description":"The segment's local departure date"},"fareBasis":{"type":"string","maxLength":16},"bookingClass":{"type":"string","maxLength":2},"text":{"type":["string","null"],"description":"The rule text, verbatim (newlines kept); null when the supplier stated none for this segment"}}},"CancelQuoteRequest":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object"}}},"CancelQuoteResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","description":"The penalty preview (03 §3 #16, API-0006 §3). Every figure is in the ORDER's terms (OD-367): the supplier's penalty and refund are restated in the order's currency at the order's own booked FX snapshot — the consented quote's, else the line's F-08 stamp, never the snapshot current now — and at the order's sell level; a converted figure carries fxApplied true and that snapshot's fxSnapshotId. A figure the booked snapshot cannot restate is never answered in the supplier's currency: the quote is refused 500 INTERNAL, details.reason FX_SNAPSHOT_UNAVAILABLE (drop-and-count). `policyCheck` says whether the supplier's penalty was checked against the cancellation terms the order was booked on (OD-369); `amountsWithheld` as on OrderResponse.\n","properties":{"cancelQuoteRef":{"type":"string"},"kind":{"type":"string","description":"CANCEL (CANONICAL-MODEL §2.9 PostBookingAction.kind); open string"},"initiator":{"type":"string","description":"Who asked — CUSTOMER, AGENT, SUPPLIER, SYSTEM (CANONICAL-MODEL §2.9); open string"},"orderRef":{"type":"string"},"itemIds":{"type":"array","items":{"type":"string"}},"outcome":{"type":"string","description":"QUOTED on a cancel-quote; open string (QUOTED, APPLIED, PENDING, AMBIGUOUS)"},"handledBy":{"type":"string","description":"Who executes the action (IMP-P2-083; ancillary proposal G-8): SUPPLIER_API (the supplier's API runs it) | OPS_DESK (no supplier API — an operations ticket is opened and outcome stays PENDING until the desk closes it; the commit never claims a supplier change happened) | PLATFORM (platform-only, no supplier involvement — a voucher void on a platform price list). Open string.\n"},"penalty":{"$ref":"#/components/schemas/Money"},"refundEstimate":{"$ref":"#/components/schemas/Money"},"lines":{"type":"array","description":"Per-item cancellation fees (connect-v1 v1.5.0, WEB-1 ask): one entry per line in scope with its own penalty and refund, in the order's terms (OD-367), summing to `penalty` and `refundEstimate`. Present when the supplier quotes per line or the platform can apportion; absent otherwise.\n","items":{"type":"object","required":["itemId","penalty","refund"],"properties":{"itemId":{"type":"string"},"penalty":{"$ref":"#/components/schemas/Money"},"refund":{"$ref":"#/components/schemas/Money"},"handledBy":{"type":"string","description":"As on the quote (SUPPLIER_API | OPS_DESK | PLATFORM) when it differs per line"}}}},"validUntil":{"type":"string","format":"date-time"},"policyCheck":{"$ref":"#/components/schemas/CancelPolicyCheck"},"amountsWithheld":{"type":"string","description":"Present only when the supplier's answer was not a quote (it acted, or may have) and its figures could not be restated at the order's booked FX snapshot; every amount is then withheld. The value is the reason; today always FX_SNAPSHOT_UNAVAILABLE (OD-367).\n"}}}}},"CancelPolicyCheck":{"type":"object","description":"Whether a cancel penalty was checked against the cancellation terms the order was booked on (OD-369, API-0006 §4 and §7): the order row carries a snapshot of the offer's cancellation policy taken at commit, and on cancel-quote and cancel the supplier's penalty is compared with what that snapshot charges now. Every cancel-quote and cancel answer carries one — never a silent pass. A penalty the snapshot contradicts is not answered here at all: it is 409 PENALTY_CHANGED, details.reason CONTRADICTS_BOOKED_TERMS. status — MATCHES_BOOKED_TERMS (checked, and the penalty is the one the booked terms charge now); NO_POLICY_SNAPSHOT (the order carries no snapshot: booked before OD-369, or its offer stated none); QUOTE_REQUIRED (the booked terms say the penalty is knowable only from a live quote); NOT_CHECKED_FOR_PRODUCT (this product's quotes are not checked; today only HTL is). checked is true exactly when status is MATCHES_BOOKED_TERMS. detail is a human sentence for every status but MATCHES_BOOKED_TERMS, where it is absent.\n","required":["checked","status"],"properties":{"checked":{"type":"boolean"},"status":{"type":"string","enum":["MATCHES_BOOKED_TERMS","NO_POLICY_SNAPSHOT","QUOTE_REQUIRED","NOT_CHECKED_FOR_PRODUCT"]},"detail":{"type":"string"}}},"CancelRequest":{"type":"object","description":"Optional cancelQuoteRef guarantees quoted penalty until validUntil (03 §3 #17)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","description":"Consent-first (DR-06, OD-341 (f)): acceptedPenalty is the penalty the traveller agreed to, from the cancel-quote. A cancel without it cancels nothing and is answered 409 PENALTY_CHANGED with details.penalty (the current one) and details.requoteRequired true; a current penalty other than the accepted one is the same 409 with details.previousPenalty.\n","properties":{"cancelQuoteRef":{"type":"string","description":"From POST /v1/orders/{orderRef}/cancel-quote; guarantees its penalty until validUntil"},"acceptedPenalty":{"$ref":"#/components/schemas/Money"},"itemIds":{"type":"array","description":"Lines to cancel (a partial cancel where the rate allows it); absent = the whole order","items":{"type":"string"}},"reasonCode":{"type":"string","description":"Open string; the cancel-reasons lookup type is [PROPOSED] (OD-340)"}}}}}}},"AmendRequest":{"type":"object","description":"mode QUOTE | COMMIT; amendType incl. ADD_ANCILLARY, ADD_DOCUMENT (03 §3 #18)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","additionalProperties":true,"description":"The amend intent, typed in v1.7.0 as built (EngineIntent): mode QUOTE | COMMIT, amendType (required on both), amendRef (required on COMMIT), itemIds (the lines to amend; absent = the whole order) and changes (the values a QUOTE asks for). Open: a per-domain key the platform does not know is ignored.\n","properties":{"mode":{"type":"string","description":"QUOTE | COMMIT; open string"},"amendType":{"type":"string","description":"One of the product's capabilities verbs.amend.amendTypes (FNB DATE_SHIFT)"},"amendRef":{"type":"string","description":"From the QUOTE answer; required on COMMIT"},"itemIds":{"type":"array","items":{"type":"string"}},"changes":{"$ref":"#/components/schemas/AmendChanges"},"payment":{"type":"object","description":"v1.7.0 (CARDS lane): the card for an amend whose target slot is card-secured. The token is never stored (D3, OD-875), so nothing carries over from QUOTE to COMMIT: the client MAY send it on the QUOTE, where it is only validated and then dropped (warning <SUPPLIER>_CARD_TOKEN_UNUSED), and the COMMIT MUST carry message.intent.payment.paymentMethod again. A COMMIT to a card-secured target without it is refused: 400 VALIDATION details.reason PAYMENT_METHOD_REQUIRED when the supplier can take a token on a modify (resend with one), or 409 CONFLICT details.reason CARD_SECURED_SLOT_NOT_AMENDABLE when it cannot (In-House capture, an experience slot): cancel and rebook instead. Nothing is sent to the supplier in either case. The QUOTE answer's message.targetSlot.creditCardRequired says whether the COMMIT needs it.\n","properties":{"paymentMethod":{"$ref":"#/components/schemas/PaymentMethodToken"}}}}}}}}},"AmendResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"amendRef":{"type":"string"},"deltaPrice":{"$ref":"#/components/schemas/Money"},"validUntil":{"type":"string","format":"date-time"},"outcome":{"type":"string","description":"QUOTED on mode QUOTE, APPLIED or PENDING on COMMIT; open string (OD-341 (m)). v1.7.0: a COMMIT the supplier DEFINITELY refused (its own answer says the booking is unchanged, e.g. an Example Dining modify answering 4xx) is recorded as amendment status REFUSED, never AMBIGUOUS: the client gets the refusal as a 409 CONFLICT details.reason SUPPLIER_REFUSED (example supplierRefused), not a 202 AMBIGUOUS_RESULT, nothing is reconciled and no money is parked. A commit whose read-back shows the change was not made is NOT_APPLIED, likewise final.\n"},"handledBy":{"type":"string","description":"SUPPLIER_API | OPS_DESK | PLATFORM — as on the cancel quote (IMP-P2-083); OPS_DESK keeps outcome PENDING until the desk closes it"},"amountsWithheld":{"type":"string","description":"As on the cancel answer (OD-367) — present when every amount is withheld; the value is the reason"},"targetSlot":{"$ref":"#/components/schemas/AmendTargetSlot"}}}}},"LookupResponse":{"type":"object","description":"Flat ApiResponse wrapper family (05); reference data, tenant-filtered","properties":{"requestId":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LookupRecord"}},"page":{"$ref":"#/components/schemas/Page"}}},"LookupRecord":{"type":"object","description":"One reference record (API-0008 §3, 05 §3/§4): identity and codes, never rich content. The codes map holds iata / icao / giata / acriss / … per type. Open by construction (additionalProperties true) so per-type fields can arrive without a contract change; the type-specific extras ride in attributes. OD-297 / OD-310.\n","required":["id","type"],"additionalProperties":true,"properties":{"timeZone":{"type":"string","x-status":"planned","description":"IANA time zone for place-like types (airport, city, station) — planned; promoted from attributes.timezone"},"id":{"type":"string","description":"Canonical id mondee:{type}:{id}; prov- ids stay permanent aliases after promotion (05 §4)"},"type":{"type":"string","description":"The lookup type key"},"name":{"type":"string","description":"The display name resolved for the requested locale (fallback pt-BR → pt → en, 05 §5)"},"names":{"type":"object","additionalProperties":{"type":"string"},"description":"Localized names keyed by BCP-47 locale"},"codes":{"type":"object","additionalProperties":{"type":["string","null"]},"description":"External codes by scheme (iata, icao, giata, acriss, …)"},"geo":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}}},"parents":{"type":"array","items":{"type":"string"},"description":"Canonical ids of the parent records, nearest first (geo hierarchy country → region → city)"},"version":{"type":"integer","format":"int64","description":"Per-type monotonic version; the export delta cursor (05 §6)"},"deprecated":{"type":"boolean","description":"A tombstone when true; see successorId on a merge"},"successorId":{"type":"string","description":"On a merge tombstone, the canonical id that replaces this record (reference.entity_merged, 05 §4)"},"attributes":{"type":"object","additionalProperties":true,"description":"Type-specific extras (city, country, timezone, roles, …)"}}},"LookupExport":{"type":"object","description":"Handle to a signed NDJSON export (API-0008 §3): the file at url holds one LookupRecord per line. A delta (since given) carries changed records and tombstones only. The link is on the platform's own origin and still needs the caller's credential (OD-450 (a)).\n","required":["type","url","asOfVersion","expiresAt"],"properties":{"type":{"type":"string"},"format":{"type":"string","description":"Always ndjson today"},"url":{"type":"string","format":"uri","description":"Signed download URL of the NDJSON file: GET /v1/lookup/{type}/export with asOf, expires and sig set. Not presigned — send the same credential as for the issue call (OD-450 (a)).\n"},"since":{"type":"integer","format":"int64","description":"Echo of the since parameter on a delta export"},"asOfVersion":{"type":"integer","format":"int64","description":"The type version this export is complete up to; the next delta's since"},"expiresAt":{"type":"string","format":"date-time","description":"When the signed url stops working (15 minutes after issue)"}}},"ContentRequest":{"description":"Static content of a product catalog, or the changes since a previous sync, in the request envelope.","type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` names the catalog and, for a delta, where the last sync stopped.","type":"object"}}},"ContentResponse":{"type":"object","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object"}}},"CapabilitiesResponse":{"type":"object","description":"Generated from the registry (04 §2, ADR-C03); per-tenant; versioned + diffable","required":["version","tenant","products"],"properties":{"version":{"type":"string","description":"The client-facing change stamp (04 §2 rule 4) that capabilities.changed and the changelog key off. The platform renders it as the registry snapshot number; 04 §2's date-based form has no generator behind it yet (OD-157 D-2).\n"},"snapshotVersion":{"type":"integer","format":"int64","description":"The registry row-set identity (FEAT-F05 §12) — a DIFFERENT fact from `version`: a document and a dispatch refusal resolved at the same snapshotVersion can be compared, and F-05 pages on drift between them. Declared additively (OD-157 D-2).\n"},"tenant":{"type":"string"},"generatedFrom":{"type":"string"},"generatedAt":{"type":"string","format":"date-time","description":"When this document was generated (OD-157 D-5, declared additively)"},"products":{"type":"array","description":"One entry per product this tenant is entitled to; omission means not entitled (04 §2 rule 2)","items":{"$ref":"#/components/schemas/CapabilityProduct"}},"ancillaryTypes":{"type":"array","items":{"$ref":"#/components/schemas/AncillaryTypeEntry"}},"regions":{"type":"array","description":"Per-region health (FEAT-016 §16.1, AC-016-16): a region whose suppliers are degraded is reported DEGRADED here rather than failing the document. Optional and additive; absent on a single-region deployment.\n","items":{"$ref":"#/components/schemas/RegionStatus"}}}},"RegionStatus":{"type":"object","description":"One logical region and its health (FEAT-016). The set of regions and statuses is open: the known regions are us-east and sa-east, the known statuses are HEALTHY and DEGRADED, and clients MUST tolerate unknown values for both (spec 11 §8).\n","required":["region","status"],"properties":{"region":{"type":"string","description":"Logical region code. Known values us-east, sa-east; clients MUST tolerate unknown values.","examples":["us-east","sa-east"]},"status":{"type":"string","description":"Region health. Known values HEALTHY, DEGRADED; clients MUST tolerate unknown values.","examples":["HEALTHY","DEGRADED"]}}},"CapabilityProduct":{"type":"object","description":"One product's entry in the capabilities document. Every field below is transcribed from the worked document in 04-endpoint-product-matrix.md §2; nothing is invented here. Until now `products[]` was an untyped object, so \"every action control is rendered from GET /v1/capabilities\" (09 §4.3) was unsatisfiable from a conforming response (OD-78 / OD-CONSOLE-02, and OD-25 before it). This document is GENERATED, never authored (04 §2 rule 1), and is the PLANNING truth level; an offer's `conditions` block narrows it and an order's `links` narrows that again (04 §2 rules 3 and 5). `verbs` is REQUIRED (OD-CONSOLE2-01 / OD-100). 04 §2 rule 2 is \"Omission ≠ 501\": a verb a product will never have is `NOT_APPLICABLE` and a verb waiting on a supplier contract is `PLANNED` — i.e. the document states verb availability with a STATUS and never with an absence, and every product entry in 04 §2's worked document carries the map. A conforming entry without it could satisfy neither, and left every capability-driven UI holding a fallback for a response the spec set never permitted.\n","required":["domain","verbs"],"properties":{"domain":{"type":"string","description":"Canonical product code (02 §1)"},"category":{"type":"string","description":"The product category from the taxonomy's DomainDescriptor (02 §1) — TRANSPORT, STAY, CRUISE, DO, SUPPORT, COMPOSITE, DATA. Declared additively (OD-157 D-4b); 04 §2's worked example omits it.\n"},"artifacts":{"type":"array","description":"What a booking of this product yields (02 §1 DomainDescriptor.artifacts), e.g. E_TICKET, VOUCHER, QR. Declared additively (OD-157 D-4b).\n","items":{"type":"string"}},"constraints":{"type":"object","additionalProperties":true,"description":"Product-level constraints from the registry rows (FEAT-F05 §7), e.g. settlementCurrencies. PLACEMENT IS OPEN (OD-157 D-4, OWNER): 04 §2 puts settlementCurrencies under suppliers[].flags and rate limits under `limits`; the platform has no per-supplier rows to hang flags off yet, so it publishes them here.\n"},"aliases":{"type":"array","description":"Per-vertical aliases that route here (03 §1 #1)","items":{"type":"string"}},"tier":{"type":"string","description":"GA | BETA | SANDBOX_ONLY (04 §2). OPEN string rather than a closed enum because this document is serialized from the registry (04 §2 rule 1) and 11 §8 only exempts open enums from a version bump — closing it would make a registry addition breaking\n"},"bookingModels":{"type":"array","description":"INSTANT | ON_REQUEST (04 §2)","items":{"type":"string"}},"verbs":{"type":"object","description":"Verb name -> capability entry. A MAP, not a fixed set of keys, because the verb set is the endpoint set (03 §3) and a new verb must be additive (11 §8). Keys are the verb names 04 §2 uses: search, poll, detail, schedules, seatmap, rules, ancillaries, reprice, hold, orders, cancelQuote, cancel, amend. THIS is the field a capability-driven control reads: `verbs[verb].status`.\n","additionalProperties":{"$ref":"#/components/schemas/CapabilityVerb"}},"suppliers":{"type":"array","description":"Per-supplier capability variance behind this product (04 §2, 08)","items":{"$ref":"#/components/schemas/CapabilitySupplier"}},"limits":{"$ref":"#/components/schemas/CapabilityLimits"},"requiredInfoBase":{"type":"array","description":"Fields every offer of this product will demand at POST /v1/orders, before the per-offer `requiredInfo` adds to them (03 §6, 04 §2)\n","items":{"type":"string"}},"dataProduct":{"$ref":"#/components/schemas/CapabilityDataProduct"}}},"CapabilityDataProduct":{"type":"object","description":"The shape of a DATA product entry (IMP-P2-090; SPI proposal §4.2): FSTAT, ALERT, WX, ENTRY, CO2, ROUTE, HOLIDAY. A data product has no booking verbs; it declares the enrichment kinds the tenant is entitled to, the quota it may consume, the horizon the data covers and the providers behind it. Never over-advertises: a kind whose provider has no credential is PLANNED.\n","required":["kinds"],"properties":{"kinds":{"type":"array","items":{"type":"object","required":["kind","status"],"properties":{"kind":{"type":"string","description":"An EnrichmentProvenance.kind value"},"status":{"type":"string","description":"GA | BETA | PLANNED | NOT_APPLICABLE"},"providers":{"type":"array","items":{"type":"string"}},"horizon":{"type":"object","properties":{"pastDays":{"type":"integer"},"futureDays":{"type":"integer"}}},"cacheTtlSeconds":{"type":"integer"}}}},"quota":{"type":"object","properties":{"dailyCalls":{"type":"integer"},"monthlyCalls":{"type":"integer"},"unit":{"type":"string","description":"CALL | ITEM | SUBSCRIPTION"}}},"subscribable":{"type":"boolean","description":"True when flight watches may be created (data:subscribe) and order.flight_* events ship"}},"example":{"kinds":[{"kind":"FLIGHT_STATUS","status":"GA","providers":["SUP-DAT-EXAMPLE","SUP-DAT-EXAMPLE6"],"horizon":{"pastDays":3,"futureDays":3},"cacheTtlSeconds":1800},{"kind":"FLIGHT_EMISSIONS","status":"GA","providers":["SUP-DAT-EXAMPLE2"],"horizon":{"pastDays":0,"futureDays":330}}],"quota":{"dailyCalls":5000,"unit":"CALL"},"subscribable":true}},"CapabilityVerb":{"type":"object","description":"One verb's status for a product (04 §2). Per rule 2: a verb a product will never have is NOT_APPLICABLE, a verb waiting on a supplier contract is PLANNED with no date promise, and a production 501 means this document said something wrong (sev-3 bug, metric unexpected_501_total, 13 §7). Carries per-verb extras beyond `status` — 04 §2 shows `async: \"AUTO\"` and `maxRooms: 8` on search — so additionalProperties stays true by design, and a client reads the extras it knows about and ignores the rest.\n","additionalProperties":true,"required":["status"],"properties":{"status":{"type":"string","description":"GA | BETA | PARTIAL | PLANNED | NOT_APPLICABLE (04 §2 rules 2 and 6). OPEN string, not a closed enum: the document is generated from the registry (rule 1) and a new status must not be a breaking wire change (11 §8)\n"},"note":{"type":"string","description":"Free text, e.g. 04 §2's \"per-supplier; see suppliers[]\" on a PARTIAL hold"},"blockedOn":{"type":"string","description":"Why a PLANNED verb is planned, e.g. \"supplier contract\" (04 §2 rule 6, D-21)"},"async":{"type":"string","description":"AUTO | ALWAYS | NEVER — whether this verb returns the 202 async form (03 §5). 04 §2 shows `\"async\": \"AUTO\"` on search\n"},"amendTypes":{"type":"array","description":"On the `amend` verb (connect-v1 v1.5.0): the amendType values this product supports — DATE_SHIFT | EXCHANGE | NAME_CORRECTION | ADD_ANCILLARY | ADD_DOCUMENT | PLATE_CHANGE | SCHEDULE_CHANGE_ACCEPT | SCHEDULE_CHANGE_DECLINE | SCHEDULE_CHANGE_ALTERNATIVE (CANONICAL-MODEL §2.9 amendType). The three SCHEDULE_CHANGE_* values gate the console's schedule-change screen; open strings (11 §8). FNB (v1.7.0) supports DATE_SHIFT only: a new time, date or party size, sent as message.intent.changes (AmendChanges).\n","items":{"type":"string"}}}},"CapabilitySupplier":{"type":"object","description":"One supplier behind a product, with its capability flags (04 §2)","required":["code"],"properties":{"code":{"type":"string","description":"Supplier registry code (08 §2)"},"credentialSets":{"type":"array","description":"MONDEE and/or tenant credential-set ids visible to this tenant (BYOC visibility rules, 08 §5). Open strings — DR-14 removed the closed [MONDEE, TENANT_OWN, SANDBOX] enum because it collapses every tenant's own credentials into one value\n","items":{"type":"string"}},"flags":{"$ref":"#/components/schemas/CapabilitySupplierFlags"}}},"CapabilitySupplierFlags":{"type":"object","description":"Per-supplier capability flags, transcribed field for field from the worked document in 04 §2. These are the PLANNING truth level: an offer's `conditions` block overrides any of them at rate level (04 §2 rule 5 — a RECHECK rate can be non-holdable even where supportsHold is true).\n","properties":{"supportsHold":{"type":"boolean"},"holdTtlSeconds":{"type":["integer","null"],"description":"null when this supplier does not support holds (04 §2)"},"supportsPartialCancel":{"type":"boolean"},"supportsNameChange":{"type":"string","description":"NONE | CORRECTION_ONLY | FULL — same axis as an offer's conditions.amendable.names (03 §6)"},"refundMode":{"type":"string","description":"INSTANT | QUEUED | MANUAL (04 §2)"},"instantConfirm":{"type":"boolean"},"confirmSlaSeconds":{"type":"integer"},"onRequestSlaHours":{"type":["integer","null"],"description":"null when this supplier is not on-request (04 §2)"},"minLeadTimeHours":{"type":"integer"},"maxPax":{"type":"integer"},"groupThresholdPax":{"type":"integer","description":"At or above this party size the booking is a group request, not a search (04 §2)"},"settlementCurrencies":{"type":"array","description":"ISO-4217 codes this supplier settles in. PKG's BRL-only constraint is exactly this field rather than a hidden convention (03 §2.3)\n","items":{"type":"string"}},"sandboxAvailable":{"type":"boolean"},"retrieveMode":{"type":"string","description":"How the platform can read a booking back from this supplier (IMP-P2-090; ancillary proposal G-13): BY_LOCATOR | BY_REFERENCE | PLATFORM_DERIVED (no supplier read exists — Example Lounges; reconciliation rests on attestations and webhooks) | OPTIONAL (a read exists but is not mandatory for the verb). Declared, never assumed. Open string.\n"},"supportsRetrieve":{"type":"boolean","description":"False exactly when retrieveMode is PLATFORM_DERIVED — the declared \"no supplier read\" capability"},"paymentFork":{"type":"string","description":"PLATFORM_COLLECTS | SUPPLIER_HOSTED | EITHER — where the money is collected for this supplier's products (IMP-P2-081/090)"},"searchMode":{"type":"string","description":"SYNC | ASYNC | POLLED — whether this supplier's search is synchronous, 202-async by default, or always polled (a transfer supplier)"},"sellFloorImposed":{"type":"boolean","description":"True when this supplier imposes a sell floor on its rates (CANONICAL-MODEL §7 C-5); offers then carry pricing.sellFloor"},"passengerEmailPolicy":{"type":"string","description":"SUPPLIER_EMAILS | PLATFORM_ONLY — whether the supplier emails travellers directly (a transfer supplier)"},"bookingModel":{"type":"string","description":"INSTANT | ON_REQUEST | REQUEST_TO_BOOK — the supplier's default booking model (IMP-P2-087)"},"syncedDatasets":{"type":"array","description":"Datasets synced from this supplier (FEAT-023; IMP-P2-154) and their freshness targets; absent for live-only suppliers","items":{"type":"object","required":["dataset"],"properties":{"dataset":{"type":"string"},"freshnessTargetSeconds":{"type":"integer"},"maxStalenessSeconds":{"type":"integer"},"dataAsOf":{"type":"string","format":"date-time"},"status":{"type":"string","description":"FRESH | STALE | FAILING | PAUSED"}}}}}},"CapabilityLimits":{"type":"object","description":"Tenant-specific rate limits for this product (04 §2, 12 §A4)","properties":{"searchRps":{"type":"integer"},"burst":{"type":"integer"}}},"AncillaryTypeEntry":{"type":"object","description":"One ancillary type and the products it can hang off — 04 §2 writes it as {\"type\": \"EARLY_CHECKIN\", \"parents\": [\"HTL\",\"VR\"]}. `type` is an open string: 11 §8 names ancillary types as an open enum explicitly.\n","required":["type"],"properties":{"type":{"type":"string"},"parents":{"type":"array","items":{"type":"string"}}}},"AppliedRule":{"type":"object","description":"One entry of the appliedRules[] stamp (FEAT-013 §7; CANONICAL-MODEL §2.6/§2.8): which rule, at which immutable rule-set version and layer, contributed what to the sell price. Carried on RepriceResponse, OrderItem and simulation results. Only ruleId/version/layer are required: `action`, `delta` and `floorClamped` are optional so the server can serialize the audit view to console principals and a narrower view to external API principals — a clamp is \"visible to audit, invisible to the client as anything but the price\" (FEAT-013 §11, kill-list CANONICAL-MODEL §6). Never carries a supplier net amount or a floor value. `layer` and `action` are OPEN strings (values below) because this stamp lives on order lines for 7 years and a new action kind must not be a breaking wire change (11 §8).\n","required":["ruleId","version","layer"],"properties":{"ruleId":{"type":"string"},"version":{"type":"integer","description":"Rule-set version the rule was evaluated at; 0 = an unpublished draft (simulation only)"},"layer":{"type":"string","description":"PLATFORM | TENANT | ORG | SITE (07 §3)"},"action":{"type":"string","description":"MARKUP_PCT | MARKUP_ABS | COMMISSION_PCT | FEE_LINE | ROUND | FLOOR (FEAT-013 §7)"},"delta":{"$ref":"#/components/schemas/Money"},"floorClamped":{"type":"boolean","description":"True when the sell floor clamped the price up after this rule (FEAT-013 §11, C-5)"}}},"PriceBreakdownLine":{"type":"object","description":"One sell-side breakdown line (CANONICAL-MODEL §2.1 breakdown[] {type, amount}); amount is integer minor units in the enclosing price's currency. `type` is an OPEN registered string: BASE, TAX, FEE, GRATUITY, ONBOARD_CREDIT, PORT_CHARGE (as built, PriceComponentType), and registered by IMP-P2-078: MEET_GREET, CHILD_SEAT, TOLL, EXTRA_STOP, WAITING_TIME, GUIDE_LANGUAGE, GST (with taxComponent), SECURITY_DEPOSIT, TOURIST_TAX, RESORT_FEE, CLEANING_FEE, SERVICE_FEE, PREMIUM, RIDER, DISCOUNT (negative amount); registered by v1.7.0 for FNB: EXPERIENCE (a restaurant experience price paid at the venue). Per-vertical types are registered, never free text. For FNB the restaurant is the property: a venue-paid line is collectedBy PROPERTY, payableAt PROPERTY, included false.\n","required":["type","amount"],"properties":{"type":{"description":"The kind of line, for example BASE, TAX, FEE or DISCOUNT.","type":"string"},"amount":{"type":"integer","description":"Minor units"},"feeType":{"type":"string","description":"For FEE lines produced by a FEE_LINE rule — the tenant's fee label"},"label":{"type":"string","description":"Display label in the request locale"},"taxComponent":{"type":"string","description":"For GST / TAX lines — CGST | SGST | IGST | UTGST | CESS | VAT | SALES_TAX | OCCUPANCY_TAX; open string (IMP-P2-078; India invoicing needs the split)"},"taxCode":{"$ref":"#/components/schemas/TaxCode"},"rateBasisPoints":{"type":"integer","minimum":0,"description":"The tax or fee rate in basis points when the line is rate-based (1800 = 18%)"},"collectedBy":{"type":"string","description":"PLATFORM | SUPPLIER | PROPERTY — who takes the money; a non-PLATFORM line outside Offer.price is a pay-at-property disclosure"},"payableAt":{"type":"string","description":"BOOKING | PROPERTY | DESK | AFTER_SERVICE"},"included":{"type":"boolean","description":"True when the line is inside the enclosing amount (default); false for a disclosed line outside it"},"quantity":{"description":"How many units the line covers, when more than one.","type":"integer","minimum":1},"unitAmount":{"type":"integer","description":"Minor units per unit when quantity is given"},"currency":{"type":"string","description":"ISO-4217 code of this line's amount and unitAmount (v1.25.0). Set ONLY on a disclosed line (included false) that is NOT in the enclosing price's currency, for example a resort fee payable at the property in its local currency. Absent means the enclosing price's currency, as before; a line inside the total never carries it.\n"}}},"MarkupRuleScope":{"type":"object","description":"Where a rule applies (FEAT-013 §7). Every field optional; an absent field matches everything; more fields = more specific. Tenant is implied by the token, never sent.\n","properties":{"domain":{"type":"string","description":"Canonical product code (02 §1)"},"supplier":{"description":"Only offers of this supplier.","type":"string"},"channel":{"type":"string","description":"Only sales through this channel, for example API, CONSOLE or AI."},"siteId":{"description":"Only sales on this site.","type":"string"},"pos":{"type":"string","description":"Point-of-sale market"},"currency":{"type":"string","description":"ISO-4217 of the resolved display currency"},"productClass":{"description":"Only offers of this product class.","type":"string"},"validFrom":{"description":"The rule applies from this time.","type":"string","format":"date-time"},"validTo":{"description":"The rule applies until this time.","type":"string","format":"date-time"}}},"MarkupRuleAction":{"type":"object","description":"What a rule does (FEAT-013 §7). `value` is always an integer — no floats anywhere near money: basis points for the *_PCT kinds (1250 = 12.50%); minor units of `currency` for MARKUP_ABS and FEE_LINE; the rounding increment in minor units for ROUND.\n","required":["kind","value"],"properties":{"kind":{"description":"What the rule does to the price, a percentage or absolute markup, a commission, a fee line or rounding.","type":"string","enum":["MARKUP_PCT","MARKUP_ABS","COMMISSION_PCT","FEE_LINE","ROUND"]},"value":{"description":"An integer, in basis points for the percentage kinds (1250 is 12.50%) and in minor units of the currency otherwise.","type":"integer"},"currency":{"type":"string","description":"ISO-4217; required for MARKUP_ABS and FEE_LINE when scope.currency is absent"},"feeType":{"type":"string","description":"FEE_LINE only — the breakdown label the fee renders under"},"roundingMode":{"type":"string","enum":["UP","DOWN","HALF_UP"],"description":"ROUND only; default UP [assumption]"}}},"MarkupRuleFloor":{"type":"object","description":"The floor primitive resolving conflict C-5 (FEAT-013 §11): after rule application sell := max(sell, floor). SUPPLIER_SELL_FLOOR takes the offer's supplier-imposed floor (hotel extension sellFloor, CANONICAL-MODEL §3.2); ABS is a tenant-policy floor in `value`. Floor enforcement is law — there is no flag to disable it (FEAT-013 §10).\n","required":["kind"],"properties":{"kind":{"description":"The floor to keep, the supplier's own sell floor or an absolute amount.","type":"string","enum":["SUPPLIER_SELL_FLOOR","ABS"]},"value":{"$ref":"#/components/schemas/Money"}}},"MarkupRule":{"type":"object","description":"One markup/commission/fee/rounding rule (FEAT-013 §7, 07 §3). Precedence is deterministic: layer (PLATFORM → TENANT → ORG → SITE, lower layers narrow and never widen), then priority, then specificity; a tie is rejected at publish time. SITE may set presentation-level rounding only.\n","required":["ruleId","layer","scope","action","priority"],"properties":{"ruleId":{"type":"string","description":"Stable across versions; the id appliedRules[] quotes"},"layer":{"description":"Where the rule sits; lower layers (ORG, SITE) can narrow a higher layer's rule, never widen it.","type":"string","enum":["PLATFORM","TENANT","ORG","SITE"]},"scope":{"$ref":"#/components/schemas/MarkupRuleScope"},"action":{"$ref":"#/components/schemas/MarkupRuleAction"},"floor":{"$ref":"#/components/schemas/MarkupRuleFloor"},"priority":{"type":"integer","description":"Higher wins within a layer at equal specificity"}}},"RuleSet":{"type":"object","description":"An immutable rule-set version (FEAT-013 §5/§7). Publishing mints a new version; nothing is edited in place; rollback republishes a prior version's rules.\n","required":["version","state","rules"],"properties":{"version":{"type":"integer","minimum":1},"state":{"type":"string","enum":["PUBLISHED","RETIRED"],"description":"The currently served version is PUBLISHED; superseded versions read back as RETIRED"},"publishedAt":{"type":"string","format":"date-time"},"publishedBy":{"type":"string","description":"Actor id (audit, FEAT-013 §8)"},"simulationId":{"type":"string","description":"The simulation this version was published against (FEAT-013 §5)"},"note":{"type":"string"},"simulateRequired":{"type":"boolean","description":"The per-tenant flag rules.simulateRequired (FEAT-013 §10, [PROPOSED] default true). While true, a publish must quote a simulationId run against the rules it publishes. Read-only here; absent means the platform default. Additive (OD-296).\n"},"rules":{"type":"array","items":{"$ref":"#/components/schemas/MarkupRule"}}}},"RuleSetPublishRequest":{"type":"object","description":"The full rule list for the next version plus the version it was authored against (optimistic concurrency) and the simulation that previewed exactly this list.\n","required":["baseVersion","rules"],"properties":{"baseVersion":{"type":"integer","minimum":1,"description":"Must equal the current published version, else 409 CONFLICT (details.currentVersion)"},"simulationId":{"type":"string","description":"Required while rules.simulateRequired is on [PROPOSED default true, FEAT-013 §10]"},"note":{"type":"string","description":"Change note shown in the console version history"},"reason":{"type":"string","maxLength":500,"description":"Why this version is published (09 §9, OD-340). Mandatory with negativeMarginOverride — an override with no reason is 400 VALIDATION details.reason REASON_REQUIRED (a note stands in for it on an ordinary publish); longer than 500 characters is 400 VALIDATION INVALID_RULE. Recorded on the version and on config.rules_published; not echoed on RuleSet.\n"},"negativeMarginOverride":{"type":"boolean","default":false,"description":"Explicit override of the negative-margin guard (09 §9 failure flows, OD-346): true publishes a rule list whose quoted simulation priced lines below their base. Without it such a publish is 409 CONFLICT details.reason NEGATIVE_MARGIN_OVERRIDE_REQUIRED.\n"},"rules":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/MarkupRule"}}}},"RuleSimulationSample":{"type":"object","description":"One sample to price. Either `repriceRef` (replay an RP- snapshot's stored inputs, FEAT-013 §5) or an inline `basePrice` (+ optional breakdown and supplier sellFloor). Inline inputs are the caller's own figures and are never echoed back.\n","properties":{"sampleId":{"type":"string","description":"Caller's correlation id, echoed on the result"},"repriceRef":{"description":"A price you have already quoted, replayed with the candidate rules.","type":"string"},"supplier":{"description":"The supplier of the inline sample.","type":"string"},"channel":{"description":"The sales channel of the inline sample, for example API.","type":"string"},"siteId":{"description":"The site of the inline sample.","type":"string"},"pos":{"description":"The point-of-sale market of the inline sample.","type":"string"},"productClass":{"description":"The product class of the inline sample.","type":"string"},"travelDate":{"description":"The travel date of the inline sample.","type":"string","format":"date"},"basePrice":{"$ref":"#/components/schemas/Money"},"breakdown":{"type":"array","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}},"sellFloor":{"$ref":"#/components/schemas/Money"}}},"RuleSimulationRequest":{"type":"object","description":"Envelope in (03 §1); context.domain is the samples' product","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` carries the candidate rules and the samples to price.","type":"object","required":["intent"],"properties":{"intent":{"type":"object","description":"`samples` or `sampleRecent` (OD-340). With both, the listed samples are priced and the recent ones added; with neither, 400 VALIDATION.\n","properties":{"ruleSet":{"type":"object","description":"Candidate — a draft `rules` list or a stored `version`; absent = current published","properties":{"version":{"description":"A stored rule-set version to simulate instead of a draft list of rules.","type":"integer","minimum":1},"rules":{"type":"array","items":{"$ref":"#/components/schemas/MarkupRule"}}}},"samples":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/RuleSimulationSample"}},"sampleRecent":{"type":"object","description":"Replay this tenant's recent RP- snapshots as samples instead of listing them (OD-340). Out-of-range bounds are 400 VALIDATION details.reason INVALID_SAMPLE; an empty window is 400 VALIDATION NO_RECENT_SAMPLES.\n","properties":{"days":{"description":"How many days back to take recent prices from.","type":"integer","minimum":1,"maximum":30,"default":7},"limit":{"description":"The most recent prices to take as samples.","type":"integer","minimum":1,"maximum":200,"default":50}}}}}}}}},"RuleSimulationResult":{"type":"object","required":["status"],"properties":{"sampleId":{"type":"string"},"status":{"type":"string","description":"PRICED | DROPPED | UNRESOLVED — DROPPED when the floor sits above the tenant's capped maximum sell (FEAT-013 §12 [PROPOSED]); UNRESOLVED when a recent sample's inputs could not be replayed (OD-340); open string\n"},"price":{"$ref":"#/components/schemas/Money"},"baselinePrice":{"$ref":"#/components/schemas/Money"},"breakdown":{"type":"array","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}},"appliedRules":{"type":"array","items":{"$ref":"#/components/schemas/AppliedRule"}},"dropReason":{"type":"string"}}},"RuleSimulationResponse":{"type":"object","description":"Envelope out (03 §1) — old-vs-new sell per sample + the simulationId a publish must quote","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","required":["simulationId","results"],"properties":{"simulationId":{"type":"string"},"candidateVersion":{"type":"integer","description":"Present when a stored version was simulated; absent for a draft"},"baselineVersion":{"type":"integer","description":"The published version baselinePrice was computed under"},"summary":{"type":"object","properties":{"samples":{"type":"integer"},"priced":{"type":"integer"},"dropped":{"type":"integer"},"floorClamped":{"type":"integer"},"negativeMargin":{"type":"integer","description":"Results priced below their base (OD-340). Non-zero makes a publish quoting this simulationId need negativeMarginOverride + reason (09 §9).\n"}}},"results":{"type":"array","items":{"$ref":"#/components/schemas/RuleSimulationResult"}}}}}},"PaymentIntentRequest":{"type":"object","description":"On the platform's own account the amount derives from repriceRef — clients never send free-form amounts (03 §8); repriceRef is then required (400 VALIDATION details.field repriceRef) and an amount is 400 VALIDATION details.reason AMOUNT_FROM_REPRICE_ONLY. On the tenant's OWN provider account (paymentSource, v1.49.0, PLAT-FINSUP phase 2) the payment is the tenant's own business: the caller states amount, sends no repriceRef (400 VALIDATION details.reason REPRICE_REF_WITH_PAYMENT_SOURCE), and must send an Idempotency-Key (400 VALIDATION details.reason IDEMPOTENCY_KEY_REQUIRED). Such an intent never pays an Apiq order, and its money never passes through the platform's account.\n","properties":{"repriceRef":{"type":"string","description":"Required unless paymentSource is present"},"amount":{"$ref":"#/components/schemas/Money","description":"v1.49.0: only with paymentSource — the amount to collect on the tenant's own account (integer minor units, ISO-4217). Absent with paymentSource is 400 VALIDATION details.reason AMOUNT_REQUIRED; not positive is AMOUNT_NOT_POSITIVE.\n"},"paymentSource":{"$ref":"#/components/schemas/PaymentSource"},"method":{"type":"string","description":"CARD (hosted fields) | PIX | UPI | CREDIT_LINE | INSTALLMENTS | BNPL | ACH | NETBANKING | WALLET | EMI | APM. Open string; a method the provider cannot run is 400 VALIDATION details.reason UNKNOWN_METHOD. NETBANKING, WALLET and EMI are the India rails (a payment provider, a payment provider; IMP-P2-081); EMI carries its plan in installmentPlan.\n"},"captureModel":{"type":"string","description":"MANUAL_CAPTURE | SALE_ONLY | BNPL_AUTH (CANONICAL-MODEL §3.6, API-0010 §2); absent = the provider's default for the method. A model the provider cannot run is 400 VALIDATION details.reason CAPTURE_MODEL_NOT_SUPPORTED.\n"},"installmentPlan":{"type":"object","description":"BR installments (PKG as-built, 03 §8, API-0010 §2)"},"session":{"$ref":"#/components/schemas/PaymentSession"}}},"PaymentSource":{"type":"object","description":"v1.49.0 (PLAT-FINSUP phase 2): the tenant's OWN account at a payment provider, registered as a tenant-owned credential set through POST /admin/v1/credential-sets (supplier SUP-PAY-EXAMPLE). The set is validated on first use (the key's mode against the set's environment, then a read-only provider call); a set that cannot serve payments is 409 CONFLICT details.reason PAYMENT_SOURCE_NOT_READY, an unknown one (or another tenant's) 404 NOT_FOUND details.reason PAYMENT_SOURCE_NOT_FOUND, a supplier with no source on this deployment 400 VALIDATION details.reason PAYMENT_SOURCE_NOT_OFFERED, and a deployment without payment sources 501 NOT_IMPLEMENTED details.reason PAYMENT_SOURCES_NOT_ENABLED. When the provider cannot be asked whether a new set's key works, the create is 502 UPSTREAM_UNAVAILABLE details.reason PAYMENT_SOURCE_UNVERIFIED (retryable) and the set is left as it was.\n","required":["supplier"],"properties":{"supplier":{"type":"string","maxLength":64,"description":"The provider's supplier id, e.g. SUP-PAY-EXAMPLE"},"credentialSet":{"type":"string","maxLength":160,"description":"The tenant's credential set id; absent = the tenant's own set for the supplier"}}},"PaymentSourceView":{"type":"object","description":"v1.49.0: present on a payment that runs on the tenant's own provider account (then fork is absent: the tenant collects, on its own account). Absent for a payment on the platform's account.\n","properties":{"kind":{"type":"string","description":"TENANT_OWN — the tenant's own credential set"},"credentialSet":{"type":"string","description":"The set's id (not a secret)"}}},"PaymentSession":{"type":"object","description":"Optional order context some providers need to open a payment session (OD-655/669). Amounts are integer minor units in the intent currency. A bad field is 400 VALIDATION details {field: session.*, reason: INVALID_SESSION}; a card number in any text field is 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED.\n","properties":{"country":{"type":"string","pattern":"^[A-Z]{2}$"},"locale":{"type":"string","maxLength":35},"orderRef":{"type":"string","maxLength":128},"taxAmount":{"$ref":"#/components/schemas/Money"},"orderLines":{"type":"array","maxItems":100,"items":{"type":"object","required":["name","quantity","unitPrice"],"properties":{"name":{"type":"string","maxLength":256},"quantity":{"type":"integer","minimum":1},"unitPrice":{"$ref":"#/components/schemas/Money"},"taxAmount":{"$ref":"#/components/schemas/Money"}}}}}},"PaymentIntentResponse":{"type":"object","properties":{"paymentId":{"type":"string"},"clientSecret":{"type":"string","description":"PSP hosted-fields handoff"},"pixQr":{"type":"string","description":"Pix QR payload (BR first-class, A-1)"},"amount":{"$ref":"#/components/schemas/Money"},"method":{"type":"string"},"captureModel":{"type":"string"},"state":{"type":"string","description":"CREATED | PENDING_RECONCILIATION | REQUIRES_ACTION | AUTHORIZED | PARTIALLY_CAPTURED | CAPTURED | PARTIALLY_REFUNDED | REFUNDED | CANCELED | FAILED (API-0010 §3; backend PaymentState); open string. REFUNDED (IMP-P2-081): everything captured was refunded (refundedAmount equals capturedAmount) — the terminal state OrderPayment.state already named. PARTIALLY_REFUNDED (v1.6.2, additive): some but not all of the captured amount was refunded (0 < refundedAmount < capturedAmount) on a payment with nothing left to capture — CAPTURED, or PARTIALLY_CAPTURED whose authorization has lapsed. Further refunds are allowed; when cumulative refundedAmount reaches capturedAmount the state becomes REFUNDED. A PARTIALLY_CAPTURED payment that can still capture keeps PARTIALLY_CAPTURED after a partial refund. It counts as funded wherever AUTHORIZED / PARTIALLY_CAPTURED / CAPTURED do. Clients must treat any unknown value as non-terminal. PENDING_RECONCILIATION (AS BUILT, OD-205 / OD-295): a provider call went out and its outcome is unknown, or the provider acknowledged a PENDING authorization (OD-655); the intent is reconciled against the provider, never re-sent. Transitions after confirm are webhook-driven — a synchronous value is provisional (API-0010 §1)\n"},"pendingOperation":{"type":"object","description":"Present only while a capture, refund or void is at the provider (OD-669). Absent otherwise.\n","required":["kind","amount","status"],"properties":{"kind":{"type":"string","enum":["CAPTURE","REFUND","VOID"]},"amount":{"$ref":"#/components/schemas/Money"},"status":{"type":"string","enum":["ACCEPTED","IN_FLIGHT"]}}},"threeDS":{"type":"object","properties":{"required":{"type":"boolean"},"clientSecret":{"type":"string"}}},"authValidUntil":{"type":"string","format":"date-time","description":"Authorization expiry — load-bearing for capture-on-ticketing (API-0010 §3)"},"capturedAmount":{"$ref":"#/components/schemas/Money"},"refundedAmount":{"$ref":"#/components/schemas/Money","description":"Cumulative amount refunded at the provider (POST /v1/payments/{paymentId}/refund, OD-308)"},"installmentPlan":{"type":"object"},"checkout":{"$ref":"#/components/schemas/CheckoutSessionRef","description":"Present only while a hosted checkout session is active on this intent (minted by POST /v1/payments/{paymentId}/checkout-session, not yet expired and not yet spent; API-0010 §11). Absent otherwise — an optional, additive field.\n"},"fork":{"type":"string","description":"PLATFORM_COLLECTS | SUPPLIER_HOSTED — which side collects (IMP-P2-081). A SUPPLIER_HOSTED intent carries no clientSecret: the supplier's checkout runs and the order carries the supplier's payment reference; the platform only records the outcome. Absent on a payment on the tenant's own account (paymentSource, v1.49.0).\n"},"paymentSource":{"$ref":"#/components/schemas/PaymentSourceView"},"attempts":{"type":"array","description":"The attempts made against this intent, oldest first (IMP-P2-081; a payment provider)","items":{"$ref":"#/components/schemas/PaymentAttempt"}},"winningAttemptId":{"type":"string","description":"The attempt whose capture the intent settled on; a second captured attempt is refunded in full and incident-tagged"}}},"CheckoutSessionRequest":{"type":"object","description":"Hosted checkout session body (API-0010 §11). Carries no instrument data of any kind — the card is collected on the provider's hosted page, never by the caller (15 §2, SAQ-A).\n","properties":{"returnUrl":{"type":"string","maxLength":2048,"description":"Where the hosted page sends the browser when it finishes, with `paymentId` and `status` query parameters appended. Allowed: any aikonic:// URL (always, at every rung, no configuration — the agent app uses aikonic://payment), or an https URL whose origin (scheme, host, port) exactly equals one of the deployment's aikonic.payments.checkout.allowed-return-origins (default empty). Anything else — http, another scheme, another origin, userinfo, a relative or malformed URL — is 400 VALIDATION with details.reason RETURN_URL_NOT_ALLOWED; never an open redirect. Absent = the hosted page ends on its own result screen.\n"}}},"CheckoutSessionRef":{"type":"object","description":"An active hosted checkout session as the intent answers carry it (API-0010 §11)","required":["checkoutSessionId","url","expiresAt"],"properties":{"checkoutSessionId":{"type":"string","description":"Opaque, unguessable, single-use session id (cs_…). A bearer of the hosted page, not a platform credential; it confers nothing on any API route.\n"},"url":{"type":"string","format":"uri","description":"The hosted checkout page to open in the system browser (never an embedded web view that the app can script)"},"expiresAt":{"type":"string","format":"date-time","description":"When the session stops being usable (~15 minutes after creation; deployment property aikonic.payments.checkout.session-ttl, default PT15M). After it, or after the first submit on the hosted page, create a new session with a new Idempotency-Key.\n"}}},"CheckoutSession":{"type":"object","description":"The 200 answer of POST /v1/payments/{paymentId}/checkout-session (API-0010 §11): the session plus the returnUrl it will redirect to, echoed only when one was sent.\n","required":["checkoutSessionId","url","expiresAt"],"properties":{"checkoutSessionId":{"type":"string","description":"Opaque, unguessable, single-use session id (cs_…); binds the hosted page to this one intent"},"url":{"type":"string","format":"uri","description":"The hosted checkout page to open in the system browser"},"expiresAt":{"type":"string","format":"date-time","description":"When the session stops being usable (~15 minutes; aikonic.payments.checkout.session-ttl)"},"returnUrl":{"type":"string","description":"The validated returnUrl, echoed when the request sent one; absent otherwise"}}},"PaymentCaptureRequest":{"type":"object","description":"Capture body (API-0010 §2 [PROPOSED]); amount absent = the whole authorized remainder","properties":{"amount":{"$ref":"#/components/schemas/Money","description":"At most the authorized amount not yet captured; the intent's currency"}}},"PaymentRefundRequest":{"type":"object","description":"Refund body (API-0010 §6 [PROPOSED]); amount absent = everything captured and not yet refunded","properties":{"amount":{"$ref":"#/components/schemas/Money","description":"At most the captured amount not yet refunded; the intent's currency"},"reason":{"type":"string","description":"Why — e.g. CANCELLATION | AMENDMENT | GOODWILL; open string, recorded for audit"}}},"PspWebhookAck":{"type":"object","description":"The ingestion edge's acknowledgement to the payment provider (F-10 pattern)","required":["status"],"properties":{"status":{"type":"string","description":"ACCEPTED (verified, new, applied once) | DUPLICATE (already seen, nothing applied)"},"providerEventId":{"type":"string","description":"The provider's own event id — the dedupe key"},"eventId":{"type":"string","description":"The platform event appended for an ACCEPTED notification"},"traceId":{"type":"string"}}},"PaymentConfirmRequest":{"type":"object","description":"Token references only — never instrument data (API-0010 §2)","properties":{"threeDSTransactionRefNumber":{"type":"string"},"pspTokenRef":{"type":"string","description":"Vaulted PSP token reference — never a PAN (18 §6)"},"pixConfirmation":{"type":"string"},"checkoutSignature":{"$ref":"#/components/schemas/CheckoutSignature","description":"The hosted-checkout signed triple (IMP-P2-081); verified against the platform's stored provider order id, then the provider is read"},"attemptId":{"type":"string","description":"The attempt this confirm belongs to, when the client made several"}}},"PaymentConfirmResponse":{"type":"object","properties":{"paymentRef":{"type":"string","description":"Usable as payment.paymentRef in POST /v1/orders"},"attemptId":{"type":"string","description":"The attempt this confirm resolved (IMP-P2-081)"},"status":{"type":"string","description":"CONFIRMED | REQUIRES_CHALLENGE | PENDING_RECONCILIATION | FAILED | CREATED | CANCELED; open string. PENDING_RECONCILIATION (AS BUILT, OD-295): the provider call's outcome is unknown and is being reconciled — never re-sent; the terminal answer arrives as payment.captured / payment.failed or on a later confirm.\n"}}},"ThreeDsCallbackRequest":{"type":"object","properties":{"paymentId":{"type":"string"},"threeDSTransactionRefNumber":{"type":"string","description":"The 3DS transaction reference from the challenge (token reference only, never instrument data)"},"challengeResult":{"type":"object"}}},"WebhookSubscriptionRequest":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["order.state_changed","order.schedule_changed","hold.expired","credential.expiring","capabilities.changed","reference.updated","order.item_failed","order.supplier_cancelled","order.docs_required","order.document_ready","order.refund_issued","payment.captured","reference.entity_merged","payment.failed","payment.refunded","payment.canceled","agent.proposal.created","agent.proposal.approved","agent.proposal.rejected","agent.proposal.expired","agent.proposal.execution_unknown","order.dispatch_updated","order.flight_status_changed","order.flight_disrupted","order.ticket_revoked","order.ticket_expired","order.price_adjusted","order.supplier_price_mismatch","order.supplier_event","enquiry.submitted","enquiry.unconfirmed","enquiry.failed","mcp.grant.approved","mcp.grant.denied","mcp.grant.revoked","mcp.grant.expired","mcp.settings.changed","key.minted","key.paused","key.resumed","key.revoked","key.rotated","key.expiring","signup.device.approved","agent.registered","agent.version_registered","agent.version_revoked","agent.key_rotated","agent.tier_changed","agent.suspended"]}},"description":{"type":"string","description":"Console label (API-0009 §2)"}}},"WebhookSubscription":{"type":"object","properties":{"webhookId":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"signingSecret":{"type":"string","description":"Returned once at creation; used for X-Mondee-Signature HMAC verification"},"description":{"type":"string"},"state":{"type":"string","description":"ACTIVE | DISABLED — auto-disabled after 72h of failed deliveries (API-0009 §3, §5); open string"},"createdAt":{"type":"string","format":"date-time"}}},"WebhookSecretRotationRequest":{"type":"object","description":"Optional rotation settings (API-0009 §5, 12 §A17)","properties":{"overlapSeconds":{"type":"integer","minimum":0,"maximum":604800,"description":"How long the previous secret keeps signing; absent = 86400 [PROPOSED]; 0 = stop at once"}}},"WebhookSecretRotation":{"type":"object","description":"The rotation result; signingSecret is returned ONCE, here","required":["webhookId","signingSecret","rotatedAt"],"properties":{"webhookId":{"type":"string"},"signingSecret":{"type":"string","description":"The new signing secret — returned once, never re-returned"},"rotatedAt":{"type":"string","format":"date-time"},"previousSecretExpiresAt":{"type":"string","format":"date-time","description":"When the previous secret stops signing (end of the dual-secret overlap window)"}}},"WebhookDeliveryAttempt":{"type":"object","description":"One delivery attempt as the tenant-visible delivery log records it (12 §A17)","required":["attempt","at","outcome"],"properties":{"attempt":{"type":"integer","minimum":1},"at":{"type":"string","format":"date-time"},"httpStatus":{"type":"integer","description":"The endpoint's status; absent when no response arrived"},"latencyMs":{"type":"integer","format":"int64"},"outcome":{"type":"string","description":"DELIVERED | HTTP_ERROR | NETWORK_ERROR | BLOCKED_TARGET | NO_SECRET | ENDPOINT_DISABLED | SUBSCRIPTION_DELETED; open string\n"},"detail":{"type":"string","maxLength":256,"description":"A bounded response snippet or failure reason — never a header, the secret or the request body"}}},"WebhookDelivery":{"type":"object","description":"One (subscription, event) delivery and its attempt log (API-0009 §5/§7)","required":["deliveryId","webhookId","eventId","state","attempts"],"properties":{"deliveryId":{"type":"string"},"webhookId":{"type":"string"},"eventId":{"type":"string"},"type":{"type":"string","description":"The event type"},"state":{"type":"string","description":"PENDING | DELIVERED | DEAD_LETTERED | CANCELLED; open string"},"attempts":{"type":"integer","description":"Total attempts made (the log keeps at most 50)"},"createdAt":{"type":"string","format":"date-time"},"nextAttemptAt":{"type":"string","format":"date-time","description":"Present while PENDING"},"log":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttempt"}}}},"WebhookDeliveryPage":{"type":"object","required":["deliveries"],"properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"nextCursor":{"type":"string"}}},"WebhookEnvelope":{"type":"object","description":"Signed event delivery envelope; replayable via GET /v1/events (03 §3 #26). Delivery order across events is NOT guaranteed (OD-709): the outbox drain is asynchronous, so for one order a payment.refunded delivery can arrive before that order's CONFIRMED and CANCELLED events. Receivers must order events by occurredAt, then eventId, and must tolerate any cross-event arrival order; a delivery is never evidence that an earlier event has already arrived.\n","required":["eventId","type","occurredAt","data"],"properties":{"eventId":{"type":"string"},"type":{"type":"string"},"occurredAt":{"type":"string","format":"date-time"},"tenant":{"type":"string"},"data":{"type":"object"},"actor":{"type":"object","description":"actor.kind = ai mandatory on AI-initiated (platform event envelope law)"},"seq":{"type":"integer","description":"Per-tenant monotonic sequence (C13, API-0009 §3/§5). Delivery order is best-effort only — consumers dedupe on eventId and use seq per orderRef to detect staleness\n"},"orderRef":{"type":"string","description":"Present on order-scoped events (C13 envelope, CANONICAL-MODEL §2.11)"},"traceId":{"type":"string"}}},"EventPage":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEnvelope"}},"nextCursor":{"type":"string"}}},"OrderEventsResponse":{"type":"object","description":"One order's timeline (03 §3 #26): message is exactly an EventPage, so a client renders it with the component it uses for GET /v1/events","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"$ref":"#/components/schemas/EventPage"}}},"LoginRequest":{"type":"object","description":"FEAT-020 §5.4. Plain JSON, not an envelope.","required":["username","password","siteId"],"properties":{"username":{"type":"string"},"password":{"type":"string","format":"password"},"siteId":{"type":"string","description":"The storefront signed in to; login is per site (07 §1)"}}},"SignInRequest":{"type":"object","description":"v1.14.0 (OD-882). One sign-in for every realm. Plain JSON, not an envelope.","required":["username","password"],"properties":{"username":{"type":"string","description":"The account's email (a customer user provisioned with a plain username signs in with it too)"},"password":{"type":"string","format":"password"},"siteId":{"type":"string","description":"Customer users only, and only after a 202 SITE_CHOICE: the site to sign in to. Omitted, a customer user with one site signs in to it. Ignored for staff and supplier users.\n"}}},"SignInResult":{"type":"object","description":"v1.14.0 (OD-882). Exactly one session is present — the one `realm` names.","required":["realm"],"properties":{"realm":{"type":"string","enum":["TENANT","OPERATOR","SUPPLIER"]},"tenantSession":{"$ref":"#/components/schemas/SessionResponse"},"operatorSession":{"$ref":"#/components/schemas/OperatorSessionResponse"},"supplierSession":{"$ref":"#/components/schemas/SupplierSessionResponse"}}},"SignInNextStep":{"type":"object","description":"v1.14.0 (OD-882). The password was accepted. MFA: complete `challenge` at the realm's own mfa/verify route (/v1/auth/mfa/verify, /ops/v1/auth/mfa/verify or /supplier/v1/auth/mfa/verify). SITE_CHOICE: repeat the sign-in with one of `sites`.\n","required":["realm","step"],"properties":{"realm":{"type":"string","enum":["TENANT","OPERATOR","SUPPLIER"]},"step":{"type":"string","enum":["MFA","SITE_CHOICE"]},"challenge":{"$ref":"#/components/schemas/MfaChallenge"},"sites":{"type":"array","items":{"$ref":"#/components/schemas/SignInSite"}}}},"SignInSite":{"type":"object","required":["siteId","name"],"properties":{"siteId":{"type":"string"},"name":{"type":"string"}}},"RefreshRequest":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}},"SessionResponse":{"type":"object","description":"15 §1.1 — access JWT (ES256, ≤ 15 min) + single-use rotating refresh token","required":["tokenType","accessToken","expiresAt","expiresIn","refreshToken","refreshExpiresAt","context"],"properties":{"tokenType":{"type":"string","enum":["Bearer"]},"accessToken":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"expiresIn":{"type":"integer","description":"Seconds until expiresAt"},"refreshToken":{"type":"string"},"refreshExpiresAt":{"type":"string","format":"date-time"},"context":{"$ref":"#/components/schemas/AccessContext"}}},"AccessContext":{"type":"object","description":"The verified caller (07 §1, FEAT-020 §3a): resolved from the credential, never the body","required":["userId","tenantId","siteId","permissions"],"properties":{"userId":{"type":"string"},"tenantId":{"type":"string"},"siteId":{"type":"string","description":"The storefront the session signed in to. AS BUILT (IMP-P2-041): an API key or a service token has no site, and GET /v1/me then answers `\"siteId\": null` for it — a client treats null as \"no site\" (the key's tenant is the scope). On a login or refresh answer it is always present. Declared nullable in the next date-based version; this release documents it (a type change is breaking under gate 1).\n"},"segment":{"type":["string","null"],"description":"Commercial segment — always null in FEAT-020 phase A"},"permissions":{"type":"array","items":{"type":"string"},"description":"15 §1.3 permission strings, e.g. search:hotels, orders:write"},"tenantStatus":{"type":"string","enum":["TRIAL","LIVE","SUSPENDED"],"description":"The credential's tenant's lifecycle state, read on this request (07 §5; FEAT-020 §18c). SUSPENDED — new business is refused 403 TENANT_SUSPENDED, while cancel-quote, cancel, amend and the order reads stay up. PROVISIONING and OFFBOARDED never appear: their credentials are refused 401 before any handler runs. On a login or refresh answer it is a snapshot taken when the session was issued; GET /v1/me reads it live.\n"},"productionWithheld":{"type":"boolean","description":"True only on a deployment whose keys are live (production) for a tenant not approved for it — TRIAL, a sandbox twin, or SUSPENDED (07 §5, D-60). New business is then refused 403 PERMISSION_DENIED with details.reason TENANT_NOT_LIVE, unless the tenant is SUSPENDED, whose TENANT_SUSPENDED answers first. Always false outside production. The same decision the travel routes make, so a badge drawn from it cannot disagree with a refusal; read tenantStatus first.\n"},"actorKind":{"type":"string","enum":["human","ai","system"],"description":"The kind of caller the credential is (DR-40): human for a signed-in session, ai for an API key minted actorKind ai and for every service token, system for an API key minted actorKind system. human is an interactive session (a human user, ConsoleActor.kind user); ai and system are the API key's actorKind. Resolved from the credential, never the body. A client that finds it absent treats the caller as ai.\n"},"passwordSet":{"type":"boolean","description":"Present on GET /v1/me for a human session: false for an account that has no password yet (FEAT-030 passwordless signup), which sets one with POST /v1/auth/password/set; absent for an API key or service token (OD-ENR-15).\n"},"accountType":{"$ref":"#/components/schemas/AccountType","description":"connect-v1 1.47.0 (FEAT-033): BUSINESS or INDIVIDUAL, the type the tenant was created with. A client that finds it absent (a tenant created before 1.47.0, or a server that does not send it) treats the tenant as BUSINESS.\n"},"givenName":{"type":"string","description":"connect-v1 1.47.0: the signed-in person's given name, present for a human session when it is known (set at signup); absent for an API key or service token.\n"},"familyName":{"type":"string","description":"connect-v1 1.47.0: the signed-in person's family name, present for a human session when it is known (set at signup); absent for an API key or service token.\n"}}},"ApiKeyMetadata":{"type":"object","description":"15 §1.1 — what is kept of a key after mint. Never the secret.","required":["keyPrefix","display","environment","scopes","status","createdAt","actorKind"],"properties":{"actorKind":{"type":"string","enum":["ai","system"],"default":"ai","description":"The channel this key acts as (DR-40), fixed at mint. ai: an AI-plane or agent credential, the AI channel, so it is subject to the D-50 approval gate. system: a non-AI server integration, the SYSTEM channel, exempt from the D-50 gate. A rotated key keeps the actorKind of the key it replaces.\n"},"keyPrefix":{"type":"string","description":"Environment prefix (mo_test_ | mo_live_, 15 §1.1) + a 16-character lookup id. Not secret."},"display":{"type":"string","description":"Prefix + last 4, for recognising a key in a list"},"name":{"type":"string"},"environment":{"type":"string","enum":["sandbox","live"]},"scopes":{"type":"array","items":{"type":"string"}},"ipAllowlist":{"type":"array","items":{"type":"string"}},"suppliers":{"type":"array","items":{"type":"string"},"description":"connect-v1 v1.46.0. The supplier registry codes this key may reach (e.g. SUP-HTL-EXAMPLE6), fixed at mint; a rotated key keeps the list of the key it replaces. Absent or empty means every supplier the key's scopes reach — every key minted before the field existed reads that way.\n"},"status":{"type":"string","enum":["ACTIVE","REVOKED"]},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"principalId":{"type":"string","description":"The service principal the key authenticates as (17 §1 user kind SERVICE)"},"overlapUntil":{"type":"string","format":"date-time","description":"Set once the key has been rotated — it keeps working until then (15 §1.1)"},"revokedAt":{"type":"string","format":"date-time","description":"When the key was revoked, for a REVOKED key"},"kind":{"$ref":"#/components/schemas/KeyKind","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034): own (minted by a session), agt (minted by a key), svc. Absent from an older server and on every key minted before 1.57.0, read as own.\n"},"writes":{"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). The key's rung on the write ladder (KeyWrites; none < hold < cancel < book-requires-approval < book); null or absent, the scopes alone decide.","oneOf":[{"$ref":"#/components/schemas/KeyWrites"},{"type":"null"}]},"products":{"type":["array","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Product tab keys the key may use, a view over its search:<tabKey> scopes; null is every product its scopes reach.","items":{"type":"string"}},"spendPerDay":{"description":"connect-v1 1.57.0 (PROPOSAL). The key's daily spend cap, a sub-cap under the tenant's; null for none.","oneOf":[{"$ref":"#/components/schemas/KeySpendCap"},{"type":"null"}]},"lookToBook":{"type":["integer","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Searches per booking per UTC day; null for none."},"reqPerSec":{"type":["integer","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Requests per second; null for none."},"holdsPerHour":{"type":["integer","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Holds per hour; null for none."},"cancelsPerDay":{"type":["integer","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Cancellations per UTC day; null for none."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"connect-v1 1.57.0 (PROPOSAL). When the key expires (state EXPIRED after); null, it does not. Never later than the minting key's expiresAt; a successor at rotation gets min(now + the key's lifetime, the minting key's expiresAt), so a child never outlives its parent [assumption].\n"},"principal":{"type":"string","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §9). The Member the key acts for, as a masked e-mail (first two characters of the local part, *** and the domain); stamped at mint. For a key minted before 1.57.0 the minting user where known, else the Owner; for the key a device sign-up hands an agent, the Owner (OD-AGT-20). X-On-Behalf-Of on a kind agt key must equal it; on a kind own key it is honoured only when this principal is an Owner or Admin [assumption]; anything else is 403 PRINCIPAL_UNKNOWN.\n"},"principalUserId":{"type":["string","null"],"description":"connect-v1 1.57.0 (PROPOSAL). The principal's user id; fixed at mint for a kind agt key."},"agent":{"type":["string","null"],"maxLength":120,"description":"connect-v1 1.57.0 (PROPOSAL). The agent label recorded at mint (X-Agent); null when none was sent."},"mintedBy":{"type":["string","null"],"description":"connect-v1 1.57.0 (PROPOSAL). The keyPrefix of the key that minted this one; null for a session mint."},"state":{"$ref":"#/components/schemas/KeyState","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). ACTIVE, PAUSED, REVOKED or EXPIRED, beside the unchanged status. Absent from an older server, read from status.\n"},"pausedReason":{"type":["string","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Why a PAUSED key was paused (KEY_PAUSED, LOOP_DETECTED, or an Owner's reason); null otherwise."},"pausedEvidence":{"description":"connect-v1 1.57.0 (PROPOSAL). The evidence of a PAUSED key's pause; null otherwise.","oneOf":[{"$ref":"#/components/schemas/ApiKeyPauseEvidence"},{"type":"null"}]},"successorKeyPrefix":{"type":["string","null"],"description":"connect-v1 1.57.0 (PROPOSAL). The key that replaced this one at rotation; null until rotated."},"usageToday":{"$ref":"#/components/schemas/ApiKeyUsage","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.7). Today's counters, on GET /v1/keys only."}}},"ApiKeyList":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyMetadata"}}}},"MintApiKeyRequest":{"description":"A new API key, what it may do and the limits it runs under.","type":"object","required":["scopes"],"properties":{"actorKind":{"type":"string","enum":["ai","system"],"default":"ai","description":"The channel the key acts as; default ai. system keys are for non-AI server integrations; they are the SYSTEM channel and skip the D-50 approval gate; minting one requires the tenant admin role; a system key can never file an agent proposal. Anyone else asking for system is 403 PERMISSION_DENIED details.reason SYSTEM_KEY_REQUIRES_ADMIN. Any other value is a 400. The mint is audited with the actorKind.\n"},"name":{"description":"A name for the key, shown in the key list.","type":"string"},"scopes":{"type":"array","minItems":1,"items":{"type":"string"},"description":"At least one and at most 32 (ApiKeyService.MAX_SCOPES; more is 400 VALIDATION details.field scopes); known permission strings, a subset of the minting user's (deny-by-default)"},"ipAllowlist":{"description":"The only addresses or ranges the key may call from; empty means any.","type":"array","items":{"type":"string"}},"suppliers":{"type":"array","maxItems":64,"items":{"type":"string"},"description":"connect-v1 v1.46.0. Optional supplier allowlist: the supplier registry codes (GET /v1/capabilities products[].suppliers[].code) this key may reach. Absent or empty means every supplier the scopes reach. When set: every code must be a known supplier (else 400 VALIDATION details.field suppliers, details.reason UNKNOWN_SUPPLIER); every supplier must serve a product one of the key's search:<tabKey> scopes reaches (else 400 VALIDATION details.reason SUPPLIER_OUTSIDE_SCOPES); and every search:<tabKey> scope must keep at least one allowed supplier (else 400 VALIDATION details.reason SCOPE_WITHOUT_SUPPLIER). A search fans out to the allowed suppliers only; any other verb on an offer, reprice, hold or order of a supplier outside the list is 403 PERMISSION_DENIED details.reason SUPPLIER_NOT_ALLOWED_FOR_KEY, and GET /v1/capabilities lists the allowed suppliers only.\n"},"environment":{"type":"string","enum":["sandbox","live"],"description":"Optional; defaults to this deployment's environment, and any other value is a 400. A live key is minted only for a tenant whose production activation has been approved (status LIVE, D-60) and never for a sandbox twin; otherwise 403 PERMISSION_DENIED, details.reason TENANT_NOT_LIVE (FEAT-020 phase C). A SUSPENDED tenant mints nothing: 403 TENANT_SUSPENDED."},"writes":{"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). The write ladder (KeyWrites); under the narrowing rule no higher than the minting key's, and the minting key's when left out or null. Every cap below follows the same rule on a key mint; on a session mint left out or null is no key cap.","oneOf":[{"$ref":"#/components/schemas/KeyWrites"},{"type":"null"}]},"products":{"type":["array","null"],"description":"connect-v1 1.57.0 (PROPOSAL). Product tab keys (hotels, flights), each reached by one of the key's search:<tabKey> scopes (else 400 VALIDATION details.field products).","items":{"type":"string"}},"spendPerDay":{"description":"connect-v1 1.57.0 (PROPOSAL). Daily spend cap, in the tenant's cap currency (else 400 VALIDATION CURRENCY_MISMATCH).","oneOf":[{"$ref":"#/components/schemas/KeySpendCap"},{"type":"null"}]},"lookToBook":{"description":"The most searches the key may make per booking, counted per UTC day; null means no cap.","type":["integer","null"],"minimum":1},"reqPerSec":{"description":"The most requests per second the key may make; null means no cap of its own.","type":["integer","null"],"minimum":1},"holdsPerHour":{"description":"The most holds the key may place per hour; null means no cap.","type":["integer","null"],"minimum":0},"cancelsPerDay":{"description":"The most cancellations the key may request per UTC day; null means no cap.","type":["integer","null"],"minimum":0},"expiresAt":{"type":["string","null"],"format":"date-time","description":"connect-v1 1.57.0 (PROPOSAL). In the future (else 400 VALIDATION details.field expiresAt)."},"onBehalfOf":{"type":"string","format":"email","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §9). The Member the key acts for (its principal); default the caller. A session may name itself, or another active Member of the tenant when it holds users:manage (else 403 PERMISSION_DENIED details.reason MISSING_PERMISSION) [assumption]; a key may name only its own stamped principal. Anyone else, or an address that is not an active Member, is 403 PERMISSION_DENIED details.reason PRINCIPAL_UNKNOWN.\n"}}},"MintedApiKey":{"type":"object","required":["apiKey","key"],"properties":{"apiKey":{"type":"string","description":"The secret. Returned here once; stored only as a salted hash."},"key":{"$ref":"#/components/schemas/ApiKeyMetadata"}}},"KeyKind":{"type":"string","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3, §7). Who minted the key: own — a person's session (every key minted before 1.57.0 reads own); agt — another key, under the narrowing rule (aikonic.keys.agent-mint.enabled); svc — a platform service key, never minted on POST /v1/keys [assumption: the derivation; FEAT-034 §7 names the values only]. Distinct from actorKind (DR-40), the channel the key acts as. x-extensible-enum.\n","x-extensible-enum":["own","agt","svc"]},"KeyWrites":{"type":"string","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). The writes a key may make, a ladder where each level includes the ones below it: none (reads, search and reprice only) < hold < cancel (a servicing key: holds and cancels, never books) < book-requires-approval (books with a person's approval, the D-50 gate; holds and cancels) < book (books, holds and cancels) [assumption: the order of the ladder, and that a key that books may cancel; FEAT-034 §5.3 lists the values only]. So the sandbox default writes book also cancels (the quick-start Cancel card). A key's writes never exceed what its scopes allow; narrowing moves down the ladder. x-extensible-enum.\n","x-extensible-enum":["none","hold","cancel","book-requires-approval","book"]},"KeyState":{"type":"string","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). The key's lifecycle: ACTIVE; PAUSED (by a velocity or loop rule or by an Owner, resumed only by a person); REVOKED; EXPIRED (past expiresAt, or a rotated key past overlapUntil). Beside the unchanged ApiKeyMetadata.status (ACTIVE | REVOKED), which a PAUSED or EXPIRED key keeps at ACTIVE and REVOKED respectively [assumption: EXPIRED reads status REVOKED]. x-extensible-enum.\n","x-extensible-enum":["ACTIVE","PAUSED","REVOKED","EXPIRED"]},"KeySpendCap":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). A key's daily spend cap, in minor units of an ISO 4217 currency.","required":["amount","currency"],"properties":{"amount":{"type":"integer","minimum":0,"description":"Minor units"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217"}}},"ApiKeyScope":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). The blast radius of a key, beside its scopes and suppliers. Its members are repeated on MintApiKeyRequest, ApiKeyMetadata and ApiKeyScopePatch (by field, not allOf); this schema names the set for the manifest's key defaults and the usage report's limits. A null or absent member sets no key cap, and the tenant's own limit (the LIVE_CAPPED daily cap, TIER_CAP_REACHED) still governs; on a key minted by a key it is the parent's value instead (the narrowing rule) [assumption]. spendPerDay is a sub-cap under the tenant cap, checked before any charge, hold or supplier byte (FEAT-034 §11): reserved atomically before the order and released if it fails. A minted key's calls and spend also count against every ancestor key's windows [assumption; OD-AGT-6].\n","properties":{"writes":{"description":"The write ladder (KeyWrites); null means the scopes alone decide","oneOf":[{"$ref":"#/components/schemas/KeyWrites"},{"type":"null"}]},"products":{"type":["array","null"],"description":"Product tab keys (hotels, flights), a view over the key's search:<tabKey> scopes, never a new permission (15 §1.3)","items":{"type":"string"}},"spendPerDay":{"description":"Daily spend cap of the key (UTC day), null for none","oneOf":[{"$ref":"#/components/schemas/KeySpendCap"},{"type":"null"}]},"lookToBook":{"type":["integer","null"],"minimum":1,"description":"Searches allowed per booking, counted per UTC day"},"reqPerSec":{"type":["integer","null"],"minimum":1},"holdsPerHour":{"type":["integer","null"],"minimum":0},"cancelsPerDay":{"type":["integer","null"],"minimum":0},"expiresAt":{"type":["string","null"],"format":"date-time"}}},"ApiKeyUsage":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.7). One key's counters for the current UTC day; replays of one Idempotency-Key are not counted.","required":["requests","errors","searches","bookings","holds","cancels","approvals"],"properties":{"requests":{"type":"integer","minimum":0},"errors":{"type":"integer","minimum":0},"searches":{"type":"integer","minimum":0},"bookings":{"type":"integer","minimum":0},"holds":{"type":"integer","minimum":0},"cancels":{"type":"integer","minimum":0},"approvals":{"type":"integer","minimum":0,"description":"Bookings filed for a person's approval (book-requires-approval)"},"spend":{"description":"Spend booked today in the cap currency; null when nothing was booked","oneOf":[{"$ref":"#/components/schemas/KeySpendCap"},{"type":"null"}]}}},"ApiKeyUsageReport":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.7). GET /v1/keys/{keyPrefix}/usage — today's counters beside the limits they count against.","required":["keyPrefix","day","usage","limits","softBreachesToday","state"],"properties":{"keyPrefix":{"type":"string"},"day":{"type":"string","format":"date","description":"The UTC day counted"},"usage":{"$ref":"#/components/schemas/ApiKeyUsage"},"limits":{"$ref":"#/components/schemas/ApiKeyScope"},"softBreachesToday":{"type":"integer","minimum":0,"description":"429 VELOCITY_LIMIT answers in the last 24 hours; the third pauses the key"},"state":{"$ref":"#/components/schemas/KeyState"}}},"ApiKeyPauseEvidence":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.4). Why a key was paused. rule is LOOP_IDENTICAL (identical requests, path and body hash, per minute) | LOOP_HOLDS (holds without a booking or release in 10 minutes) | VELOCITY_HARD (the third soft breach in 24 hours) | VELOCITY_BURST (above 5× a limit) | MANUAL (an Owner or Admin) | STOP_IGNORED (calls kept coming after stop), x-extensible-enum. summary is the line the console shows (\"212 holds in 9 min\"); requestIds are the traceIds of the requests that tripped the rule.\n","required":["rule","summary","requestIds","at"],"properties":{"rule":{"type":"string","x-extensible-enum":["LOOP_IDENTICAL","LOOP_HOLDS","VELOCITY_HARD","VELOCITY_BURST","MANUAL","STOP_IGNORED"]},"dimension":{"type":["string","null"],"description":"The scope or threshold that tripped (reqPerSec, holdsPerHour, identicalRequestsPerMinute …); null for MANUAL"},"observed":{"type":["integer","null"]},"limit":{"type":["integer","null"]},"windowSeconds":{"type":["integer","null"]},"summary":{"type":"string"},"requestIds":{"type":"array","items":{"type":"string"}},"at":{"type":"string","format":"date-time"}}},"ApiKeyScopePatch":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.3). PATCH /v1/keys/{keyPrefix}: any of the scope members, scopes, suppliers and name; only the members sent change, and each may only narrow (403 SCOPE_WIDENING otherwise).\n","minProperties":1,"properties":{"name":{"type":"string"},"scopes":{"type":"array","minItems":1,"items":{"type":"string"},"description":"A subset of the key's current scopes (at most 32, as MintApiKeyRequest.scopes)"},"suppliers":{"type":"array","maxItems":64,"items":{"type":"string"},"description":"A subset of the key's current supplier allowlist (absent there means every supplier)"},"writes":{"oneOf":[{"$ref":"#/components/schemas/KeyWrites"},{"type":"null"}]},"products":{"type":["array","null"],"items":{"type":"string"}},"spendPerDay":{"oneOf":[{"$ref":"#/components/schemas/KeySpendCap"},{"type":"null"}]},"lookToBook":{"type":["integer","null"],"minimum":1},"reqPerSec":{"type":["integer","null"],"minimum":1},"holdsPerHour":{"type":["integer","null"],"minimum":0},"cancelsPerDay":{"type":["integer","null"],"minimum":0},"expiresAt":{"type":["string","null"],"format":"date-time"}}},"ApiKeyStateChange":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.4). The reason for a pause or a resume, audited.","required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":500},"category":{"type":"string","description":"false_positive | fixed | limits_changed | other (x-extensible-enum); a resume within 10 minutes with false_positive feeds the false-positive pause metric","x-extensible-enum":["false_positive","fixed","limits_changed","other"]}}},"KeyEventData":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8). data of the key.* webhooks. Never a secret, never an e-mail. reason is the pause reason (KEY_PAUSED, LOOP_DETECTED), the resume or manual-pause reason as typed, or SYSTEM_STOP_IGNORED on a system revocation; the members a given event does not need are absent.\n","required":["keyPrefix","kind","environment","state"],"properties":{"keyPrefix":{"type":"string"},"kind":{"$ref":"#/components/schemas/KeyKind"},"environment":{"type":"string","x-extensible-enum":["sandbox","live"]},"principalUserId":{"type":["string","null"]},"agent":{"type":["string","null"]},"mintedBy":{"type":["string","null"]},"state":{"$ref":"#/components/schemas/KeyState"},"reason":{"type":"string"},"category":{"type":"string"},"resumeBy":{"type":"string","x-extensible-enum":["owner"]},"evidence":{"$ref":"#/components/schemas/ApiKeyPauseEvidence"},"successorKeyPrefix":{"type":"string"},"overlapUntil":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}}},"DeviceSignupRequest":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). POST /v1/signup/device: the person's work e-mail and the sign-up fields of SignupVerifyRequest (same rules), and the agent name (falls back to the X-Agent header).\n","required":["email"],"properties":{"email":{"type":"string","format":"email"},"organization":{"type":"string","maxLength":200},"accountType":{"$ref":"#/components/schemas/AccountType"},"givenName":{"type":"string","minLength":1,"maxLength":100},"familyName":{"type":"string","minLength":1,"maxLength":100},"country":{"type":"string","pattern":"^[A-Z]{2}$"},"agent":{"type":"string","maxLength":120}}},"DeviceSignupStart":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). The RFC 8628 §3.2 shaped answer of POST /v1/signup/device.","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"properties":{"device_code":{"type":"string","description":"The poll secret; shown here once"},"user_code":{"type":"string","pattern":"^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$","description":"8 letters of the RFC 8628 §6.1 base-20 alphabet, shown as XXXX-XXXX"},"verification_uri":{"type":"string","format":"uri"},"verification_uri_complete":{"type":"string","format":"uri","description":"verification_uri with the user_code filled in; the person still confirms the code on the page"},"expires_in":{"type":"integer","description":"Seconds the request lives (900)"},"interval":{"type":"integer","description":"Seconds to wait between polls (5); polling faster is slow_down"},"token_uri":{"type":"string","format":"uri","description":"Where to poll: POST /v1/signup/device:token with this device_code (an extension parameter, RFC 8628 §3.2 allows them; the manifest's signup.deviceToken names the same route)\n"}}},"DeviceSignupTokenRequest":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). POST /v1/signup/device:token.","required":["device_code"],"properties":{"device_code":{"type":"string"}}},"DeviceSignupResult":{"description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). The FEAT-030 §5.1 SignupResult of the new tenant plus where an agent goes next: the MCP endpoint of the public toolset, the onboarding manifest, and the tenant's origin (agent). It never carries session (OD-AGT-20 option (a)): the agent receives the tenant's SYSTEM sandbox key only (key, apiKey), with the sandbox default scope (aikonic.keys.defaults.sandbox, FEAT-034 §10), principal the Owner and agent the sign-up agent; the person signs in to the console themselves (the e-mailed code, or a password they set). Under aikonic.keys.agent-mint.enabled that key may mint narrower keys (POST /v1/keys, the narrowing rule); a child keeps its parent's actorKind unless it asks for ai, the narrower channel [assumption].\n","allOf":[{"$ref":"#/components/schemas/SignupResult"},{"type":"object","required":["mcp","manifest","origin"],"properties":{"mcp":{"type":"object","required":["url","toolset"],"properties":{"url":{"type":"string","format":"uri"},"toolset":{"type":"string","x-extensible-enum":["public"]}}},"manifest":{"type":"string","format":"uri"},"origin":{"type":"string","x-extensible-enum":["agent","human"]}}}]},"DeviceSignupRequestView":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2, §14). What the consent page shows. email is masked; state is PENDING | APPROVED | DENIED | REFUSED (the e-mail was proven and its domain already has a tenant, TENANT_EXISTS, OD-AGT-21) | EXPIRED | CANCELLED (x-extensible-enum); creates says what approving creates — ownerSession is always false, since no session is handed to the agent (OD-AGT-20).\n","required":["userCode","email","organization","agent","state","expiresAt","creates"],"properties":{"userCode":{"type":"string"},"email":{"type":"string","description":"Masked, e.g. pr***@skyhop.example"},"organization":{"type":["string","null"]},"agent":{"type":["string","null"]},"state":{"type":"string","x-extensible-enum":["PENDING","APPROVED","DENIED","REFUSED","EXPIRED","CANCELLED"]},"expiresAt":{"type":"string","format":"date-time"},"creates":{"type":"object","required":["tenant","sandboxKey","ownerSession"],"properties":{"tenant":{"type":"boolean"},"sandboxKey":{"type":"boolean"},"ownerSession":{"type":"boolean","description":"Always false (OD-AGT-20); the person signs in to the console themselves"}}}}},"DeviceSignupApproval":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). POST /v1/signup/device:approve — the user code, the e-mail in full and the e-mailed code.","required":["userCode","email","code"],"properties":{"userCode":{"type":"string"},"email":{"type":"string","format":"email"},"code":{"type":"string","pattern":"^[0-9]{6}$"}}},"DeviceSignupDenial":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). POST /v1/signup/device:deny.","required":["userCode"],"properties":{"userCode":{"type":"string"}}},"DeviceSignupDecision":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2). The request after the decision.","required":["userCode","state"],"properties":{"userCode":{"type":"string"},"state":{"type":"string","x-extensible-enum":["APPROVED","DENIED"]}}},"DeviceSignupEventData":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8). data of signup.device.approved, recorded on the new tenant when the agent's first poll after the approval creates it (never for a REFUSED request, OD-AGT-21). No e-mail: the identity outbox carries no e-mail address in an event (FEAT-020 §8); the tenant's Owner is the approver, and approvedAt is when they approved.\n","required":["userCode","origin","approvedAt"],"properties":{"userCode":{"type":"string"},"agent":{"type":["string","null"]},"origin":{"type":"string","x-extensible-enum":["agent"]},"approvedAt":{"type":"string","format":"date-time"}}},"NextCall":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.5). The one request that moves the tenant forward now: a valid request an agent can send as is, with why it is next and where it is documented. Never a human-session route.\n","required":["operationId","method","path","why"],"properties":{"operationId":{"type":"string"},"method":{"type":"string","x-extensible-enum":["GET","POST","PATCH","PUT","DELETE"]},"path":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"body":{"type":["object","null"]},"why":{"type":"string"},"docs":{"type":"string","format":"uri"}}},"OnboardState":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.5). GET /v1/onboard and the MCP tool onboard: the manifest without its signature, the tenant's origin, the rail (ActivationState, as GET /v1/activation), certification (CertificationState, as GET /v1/certification) and the next call, null when nothing is left for an agent.\n","required":["manifest","tenant","rail","certification","nextCall"],"properties":{"manifest":{"$ref":"#/components/schemas/AgentManifest"},"tenant":{"type":"object","required":["tenantId","origin","signupAgent"],"properties":{"tenantId":{"type":"string"},"origin":{"type":"string","x-extensible-enum":["human","agent"]},"signupAgent":{"type":["string","null"]}}},"rail":{"$ref":"#/components/schemas/ActivationState"},"certification":{"$ref":"#/components/schemas/CertificationState"},"nextCall":{"oneOf":[{"$ref":"#/components/schemas/NextCall"},{"type":"null"}]}}},"AgentManifest":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.5, §7). GET /.well-known/aikonic.json. Always signed there (jws); inside OnboardState it is carried without jws. velocity names the thresholds of OD-AGT-6; keys.defaults the scopes a sign-up key and a LIVE_CAPPED agent key get, keys.lifetimeDays their lifetimes and keys.liveCappedSpendPercentOfTenantCap the share of the tenant cap a live agent key may spend (OD-AGT-3) [assumption: the member names beyond FEAT-034 §7's abridged list].\n","required":["manifestVersion","issuedAt","expiresAt","environments","signup","keys","velocity","rail","certification","scenarios","products","pricing","sdks","docs"],"properties":{"manifestVersion":{"type":"string"},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","description":"issuedAt + 24 hours [assumption]. An agent refuses a document past expiresAt as it refuses one whose signature does not verify, so an old signed manifest cannot be replayed indefinitely.\n"},"environments":{"type":"object","required":["sandbox"],"properties":{"sandbox":{"type":"object","required":["api","mcp"],"properties":{"api":{"type":"string","format":"uri"},"mcp":{"type":"string","format":"uri"}}},"live":{"type":"object","required":["api","mcp"],"properties":{"api":{"type":"string","format":"uri"},"mcp":{"type":"string","format":"uri"}}}}},"signup":{"type":"object","required":["email","identities"],"properties":{"device":{"type":["string","null"],"format":"uri","description":"POST /v1/signup/device; null where the device flow is off"},"deviceToken":{"type":["string","null"],"format":"uri","description":"POST /v1/signup/device:token, where the agent polls with the device_code every interval seconds until the person approves; null where the device flow is off\n"},"agentPath":{"type":["string","null"],"description":"One sentence for a coding agent: start the device flow, show the person the user_code and verification_uri, poll deviceToken, then call GET /v1/onboard with the sandbox key it receives (never a session: the person signs in to the console themselves, OD-AGT-20); null where the device flow is off\n"},"harnessDomains":{"type":"array","description":"DEV and SANDBOX only: e-mail domains whose device sign-ups the platform's harness identity approves with no person and no inbox proof (aikonic.signup.device-flow.harness-approve, AC-034-16), each creating a sandbox tenant, limited per domain (20 starts a day); never treated as an existing tenant's domain (OD-AGT-21) [assumption]. Absent on every other rung.\n","items":{"type":"string"}},"email":{"type":"string","format":"uri","description":"POST /v1/signup"},"identities":{"type":"array","description":"How the person proves the e-mail (email_code; device_code when the device flow is on, approved with the e-mailed code; google and microsoft wait on OD-ENR-5)","items":{"type":"string","x-extensible-enum":["email_code","device_code","google","microsoft"]}}}},"keys":{"type":"object","required":["defaults"],"properties":{"defaults":{"type":"object","required":["sandbox"],"properties":{"sandbox":{"$ref":"#/components/schemas/ApiKeyScope"},"liveCapped":{"$ref":"#/components/schemas/ApiKeyScope"}}},"lifetimeDays":{"type":"object","additionalProperties":{"type":"integer"}},"liveCappedSpendPercentOfTenantCap":{"type":"integer","minimum":0,"maximum":100}}},"velocity":{"type":"object","additionalProperties":{"type":"integer"},"description":"The thresholds in force, as enforced: pauseOnSoftBreach (the soft breach that pauses the key — 3 is the third within softBreachWindowHours; the ones before it are 429 VELOCITY_LIMIT), softBreachWindowHours, burstMultiplier, identicalRequestsPerMinute, holdsWithoutOutcomePer10Minutes, callsAfterStopBeforeRevoke\n"},"rail":{"type":"array","description":"The rail step keys (ActivationStep.key), in order","items":{"type":"string"}},"certification":{"type":"object","required":["checks","advisory"],"properties":{"checks":{"type":"array","items":{"type":"string"}},"advisory":{"type":"array","description":"Reported, never counted in passed or total in phase 1","items":{"type":"string"}}}},"scenarios":{"type":"string","format":"uri"},"products":{"type":"array","items":{"type":"string"}},"pricing":{"type":"string","format":"uri"},"sdks":{"type":"object","additionalProperties":{"type":"string"},"description":"Package names by registry (npm, pypi, maven)"},"docs":{"type":"object","required":["llms","openapi","mcp"],"properties":{"llms":{"type":"string","format":"uri"},"openapi":{"type":"string","format":"uri"},"mcp":{"type":"string","format":"uri"}}},"jws":{"$ref":"#/components/schemas/ManifestSignature"}}},"ManifestSignature":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.5, OD-AGT-7). A detached-payload compact JWS (RFC 7515 Appendix F, header..signature) over the RFC 8785 canonical JSON of the manifest without jws; verify with the key of kid at GET /.well-known/aikonic-jwks.json, whose kid thumbprints are also pinned out of band (SDKs, docs) [verify]. For ES256 the signature part is the 64-byte R||S, 86 base64url characters (RFC 7518 §3.4).\n","required":["kid","alg","signature"],"properties":{"kid":{"type":"string"},"alg":{"type":"string","x-extensible-enum":["ES256"]},"signature":{"type":"string","pattern":"^[A-Za-z0-9_-]+\\.\\.[A-Za-z0-9_-]{86}$"}}},"ManifestJwks":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.5, OD-AGT-7). RFC 7517 JSON Web Key Set; two kids live during a rotation.","required":["keys"],"properties":{"keys":{"type":"array","minItems":1,"items":{"type":"object","required":["kty","crv","x","y","kid","use","alg"],"properties":{"kty":{"type":"string"},"crv":{"type":"string"},"x":{"type":"string"},"y":{"type":"string"},"kid":{"type":"string"},"use":{"type":"string"},"alg":{"type":"string"}}}}}},"Attribution":{"type":"object","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7, G3). Who a record is attributed to: the human principal (a Member's user id; the key's principal, or the Member X-On-Behalf-Of named), the agent label (X-Agent, or the key's agent) and the key. Set on records created while aikonic.keys.agent-mint.enabled is on; null where unknown (a session request has no keyPrefix, a request without X-Agent and a key without one has no agent). Never an e-mail.\n","properties":{"principalUserId":{"type":["string","null"]},"agent":{"type":["string","null"]},"keyPrefix":{"type":["string","null"]}}},"IdentityAccepted":{"type":"object","description":"The single answer of a request that may or may not have sent a message — deliberately the same for every input","required":["status"],"properties":{"status":{"type":"string"}}},"SignupRequest":{"type":"object","description":"Step 1 of self-serve signup. connect-v1 1.18.0, served since 1.18.1 (FEAT-030 §5.1, WP-1): organization is no longer required.\n","required":["email"],"properties":{"email":{"type":"string","format":"email"},"organization":{"type":"string","maxLength":200,"description":"The organisation name, 1 to 200 characters when sent. Optional (FEAT-030 §5.1): absent or blank, the platform prefills it from the e-mail domain — the label left of the public suffix, separators (- _ .) as spaces, each word capitalised, cut to 200 (founder@acme-travel.co.example → \"Acme Travel\") [assumption: the derivation; FEAT-030 §5.1 says only \"prefilled from the e-mail domain\"]. A consumer mailbox domain (a platform-held list) or a label that yields nothing prefills \"My organisation\" — the derived name never carries the address's local part. The console may still send it, typically the prefill as the person edited it.\n"}}},"SignupVerifyRequest":{"type":"object","description":"Step 2 of self-serve signup. connect-v1 1.18.0, served since 1.18.1 (FEAT-030 §5.1, WP-1): only email and code are required — password and organization are optional and buildingIntent is recorded.\n","required":["email","code"],"properties":{"email":{"type":"string","format":"email"},"code":{"type":"string","pattern":"^[0-9]{6}$"},"password":{"type":"string","format":"password","minLength":12,"maxLength":1024,"description":"Optional (FEAT-030 §5.1 step 2): the password moves out of signup; the person sets a password later from the console (POST /v1/auth/password/set). When sent, the password policy holds (12 to 1024 characters) and the account is created with it, as today.\n"},"organization":{"type":"string","maxLength":200,"description":"Optional: absent or blank, prefilled from the e-mail domain by the rule on SignupRequest.organization; sent, 1 to 200 characters.\n"},"buildingIntent":{"$ref":"#/components/schemas/BuildingIntent","description":"The answer to \"What are you building?\" (FEAT-030 §5.1 step 3), skippable — absent means skipped. Recorded on the new tenant's activation rail, where it reads back as ActivationState.buildingIntent and orders the rail and the quick-start (FEAT-030 WP-1).\n"},"accountType":{"$ref":"#/components/schemas/AccountType","description":"connect-v1 1.47.0 (FEAT-033): BUSINESS (an organisation, today's signup) or INDIVIDUAL (one person, no company). Absent means BUSINESS, unchanged. For INDIVIDUAL the server requires givenName and familyName (400 VALIDATION details.reason NAME_REQUIRED, details.field naming the missing one), ignores organization and names the tenant \"<givenName> <familyName>\"; buildingIntent stays optional.\n"},"givenName":{"type":"string","minLength":1,"maxLength":100,"description":"connect-v1 1.47.0: the person's given name. Required by the server when accountType is INDIVIDUAL; accepted for BUSINESS (it may be stored on the user).\n"},"familyName":{"type":"string","minLength":1,"maxLength":100,"description":"connect-v1 1.47.0: the person's family name. Required by the server when accountType is INDIVIDUAL; accepted for BUSINESS (it may be stored on the user).\n"},"country":{"type":"string","pattern":"^[A-Z]{2}$","description":"connect-v1 1.47.0. ISO 3166-1 alpha-2 country of the person or the business; optional for both types. connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): for a BUSINESS tenant it is also recorded on the business profile (BusinessProfile.country, as PATCH /v1/activation/profile would set it) and reads back as ActivationState.country; it decides whether the India-only steps (GST register look-up, signatory check, GST invoices) are offered.\n"}}},"BuildingIntent":{"type":"string","description":"connect-v1 1.18.0 [PROPOSED] (FEAT-030 §5.1 step 3): what the person is building — API_INTEGRATION (calling the API from their own code), AI_AGENT (an AI agent over MCP or the API), BOOKING_SITE (a booking site without code). A closed request enum here; the same values read back as the x-extensible-enum ActivationState.buildingIntent, which a client renders tolerantly.\n","enum":["API_INTEGRATION","AI_AGENT","BOOKING_SITE"]},"SignupResult":{"type":"object","description":"The evaluation tenant a signup created, and its first sandbox key — the secret shown once. AS SERVED (BE-5 !70): on a rung whose keys are LIVE (PROD) no key is minted and apiKey and key are ABSENT (the record is NON_NULL; sandboxTenantId likewise when null). They stay in `required` in this date-based version because relaxing a required response property is breaking under gate 1; a client must already treat them as optional, and the next date-based version declares it. connect-v1 1.18.0 adds the optional session, served since 1.18.1 (FEAT-030 §5.1: the console session starts on code verification). On a rung whose keys are SANDBOX the first key is a SYSTEM key (key.actorKind system, DR-40), so the quick-start books without a D-50 approval grant (FEAT-030 phase 5, 2026-10-10); its scopes (search:hotels, search:flights, orders:write, orders:read, orders:cancel) cover all four quick-start cards, Cancel included.\n","required":["tenantId","siteId","username","apiKey","key"],"properties":{"tenantId":{"type":"string"},"sandboxTenantId":{"type":"string"},"siteId":{"type":"string"},"username":{"type":"string"},"apiKey":{"type":"string"},"key":{"$ref":"#/components/schemas/ApiKeyMetadata"},"session":{"$ref":"#/components/schemas/SessionResponse","description":"FEAT-030 §5.1 step 2, WP-1 (served since 1.18.1): the console session that starts on code verification — a TENANT-realm session for the new admin (username) on siteId, the same SessionResponse POST /v1/auth/login answers, issued whether or not a password was sent (also where no key is minted). Absent where the tenant admin-MFA policy (DR-51) demands a second factor for the new admin (UAT, PROD), or when issuing it failed. A secret, like apiKey: a first-party console's BFF stores it server-side exactly as it stores a login's session and never sends it to the page. A client that gets none signs in with POST /v1/auth/login (which needs the password).\n"},"accountType":{"$ref":"#/components/schemas/AccountType","description":"connect-v1 1.47.0 (FEAT-033): the type of the tenant this signup created, BUSINESS or INDIVIDUAL. Absent from a server before 1.47.0; a client then treats it as BUSINESS.\n"}}},"AccountType":{"type":"string","description":"connect-v1 1.47.0 (FEAT-033, OD-906 proposed): what kind of customer a tenant is. BUSINESS is an organisation (the signup before 1.47.0); INDIVIDUAL is one person with no company, whose tenant is named after the person, who verifies with an identity check (POST /v1/activation/identity-checks) instead of a business verification and who pays each booking by card through the payment provider's hosted checkout. A tenant created before 1.47.0 reads as BUSINESS. An x-extensible-enum: in a response a client renders the values it knows and treats an unknown one as BUSINESS; in a request a value the server does not know is 400 VALIDATION with details.field accountType.\n","x-extensible-enum":["BUSINESS","INDIVIDUAL"],"example":"INDIVIDUAL"},"InviteRequest":{"type":"object","description":"Body of POST /v1/invites. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): exactly one of role or permissions (the backend enforces it, no oneOf; 400 VALIDATION details.field role); permissions is no longer schema-required. With role the grant is the role's bundle intersected with the inviter's permissions.\n","required":["email"],"properties":{"email":{"type":"string","format":"email"},"permissions":{"type":"array","minItems":1,"items":{"type":"string"},"description":"A subset of the inviter's permissions"},"role":{"$ref":"#/components/schemas/ConsoleRole","description":"connect-v1 1.63.0 (PROPOSAL): invite as this console role; the invitee gets the role's permission bundle intersected with what the inviter holds (never more than the inviter has)\n"}}},"Invited":{"type":"object","required":["email","permissions","siteIds","expiresAt"],"properties":{"email":{"type":"string"},"role":{"$ref":"#/components/schemas/ConsoleRole","description":"connect-v1 1.63.0 (PROPOSAL) — the role invited as, when the invite named one"},"permissions":{"type":"array","items":{"type":"string"}},"siteIds":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time"}}},"InviteAcceptRequest":{"type":"object","required":["token","password"],"properties":{"token":{"type":"string"},"password":{"type":"string","format":"password","minLength":12,"maxLength":1024}}},"InviteAccepted":{"type":"object","required":["username","tenantId","siteIds"],"properties":{"username":{"type":"string"},"tenantId":{"type":"string"},"siteIds":{"type":"array","items":{"type":"string"}}}},"ForgotPasswordRequest":{"type":"object","required":["username"],"properties":{"username":{"type":"string","description":"The account's email (or a customer user's plain username)"},"siteId":{"type":"string","description":"Optional since v1.14.0 (OD-882): omitted, the account is found by its email in the login directory, as POST /v1/auth/sign-in finds it.\n"}}},"ResetPasswordRequest":{"type":"object","required":["username","code","newPassword"],"properties":{"username":{"type":"string"},"siteId":{"type":"string","description":"Optional since v1.14.0 (OD-882), as on ForgotPasswordRequest."},"code":{"type":"string","pattern":"^[0-9]{6}$"},"newPassword":{"type":"string","format":"password","minLength":12,"maxLength":1024}}},"RotatedApiKey":{"type":"object","required":["apiKey","key","replaces","overlapUntil"],"properties":{"apiKey":{"type":"string","description":"The replacement's secret. Returned here once."},"key":{"$ref":"#/components/schemas/ApiKeyMetadata"},"replaces":{"type":"string","description":"The keyPrefix of the key being rotated out"},"overlapUntil":{"type":"string","format":"date-time"}}},"OAuthTokenRequest":{"type":"object","description":"RFC 6749 §4.4.2 access-token request, application/x-www-form-urlencoded, in the BODY only — never the query string — and each parameter at most once (§3.2). client_id and client_secret are present only when the client authenticates in the body instead of by HTTP Basic (oauthClientBasic); sending both methods is invalid_request. connect-v1 1.26.0 (OD-884 Phase 2, DR-67) adds four grant types, each taking only its own parameters (any other is invalid_request). authorization_code (MCP clients, public): code, redirect_uri, code_verifier, resource and client_id in the body, no secret. refresh_token (MCP clients): refresh_token, client_id, optionally resource. urn:ietf:params:oauth:grant-type:device_code (MCP clients, RFC 8628 §3.4): device_code and client_id. urn:ietf:params:oauth:grant-type:token-exchange (RFC 8693, the AI plane only, authenticated by HTTP Basic as a configured exchange client): subject_token, subject_token_type and resource. client_secret in the body is accepted for client_credentials only.\n","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["client_credentials","authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"]},"scope":{"type":"string","maxLength":2048,"description":"client_credentials: space-delimited permission strings, each one held by the key (a subset of its scopes; see the oauthClientCredentials scheme). Absent means all of the key's scopes. An empty item, an unknown or unheld string, or a permission no key can hold is invalid_scope. The MCP grant types ignore it: the grant's scope was fixed at authorize and consent.\n"},"client_id":{"type":"string","description":"client_credentials: the API key's keyPrefix (not secret), mo_test_ or mo_live_ followed by 16 base-32 characters. MCP grant types: the client id of the grant, a registered client id or the https URL of the client's metadata document; it must be the client the code, refresh token or device code was issued to (else invalid_grant).\n"},"client_secret":{"type":"string","format":"password","pattern":"^mo_(test|live)_[a-z2-7]{16}_[a-z2-7]{52}$","description":"The WHOLE API key as shown at mint, prefix included (client_credentials only)"},"code":{"type":"string","maxLength":256,"description":"authorization_code — the code from the redirect; single-use, 60 s; a second use revokes the grant"},"redirect_uri":{"type":"string","format":"uri","maxLength":2048,"description":"authorization_code — exactly the redirect_uri sent to GET /v1/oauth/authorize"},"code_verifier":{"type":"string","minLength":43,"maxLength":128,"pattern":"^[A-Za-z0-9._~-]{43,128}$","description":"authorization_code — the PKCE verifier (RFC 7636 §4.1); its S256 must equal the code_challenge"},"refresh_token":{"type":"string","format":"password","maxLength":256,"description":"refresh_token — the grant's current refresh token. It rotates: every use returns a new one and spends the old. Presenting a spent one revokes the WHOLE grant (reason REFRESH_REUSE). A refresh re-checks the bound session and re-caps the grant's expiry.\n"},"device_code":{"type":"string","format":"password","maxLength":256,"description":"urn:ietf:params:oauth:grant-type:device_code — the device_code from POST /v1/oauth/device_authorization"},"resource":{"type":"string","format":"uri","maxLength":2048,"description":"RFC 8707: the canonical URL of the MCP endpoint. Required for authorization_code (must equal the authorize request's) and token-exchange (must equal the subject token's audience, else invalid_target reason AUDIENCE_MISMATCH); optional for refresh_token (must equal the grant's).\n"},"subject_token":{"type":"string","format":"password","maxLength":4096,"description":"token-exchange — the MCP client's access token the AI plane received (RFC 8693 §2.1)"},"subject_token_type":{"type":"string","enum":["urn:ietf:params:oauth:token-type:access_token"],"description":"token-exchange — always the access-token type"}}},"OAuthTokenResponse":{"type":"object","description":"RFC 6749 §5.1 successful response. client_credentials issues no refresh_token (§4.4.3). connect-v1 1.26.0: authorization_code, refresh_token and device_code answer an access token of at most 10 minutes, never past the grant's expiry, whose audience is the MCP resource (the platform API refuses it, TOKEN_WRONG_AUDIENCE) plus a rotating refresh_token, and scope is the grant's (mcp:console:read, optionally with mcp:console:write; mcp:supplier:read; or mcp:operator:read). An operator grant's tokens never pass its 1-hour cap. token-exchange answers the downstream platform token the AI plane calls the platform with (at most 5 minutes and never past the subject token's or the grant's expiry, no refresh_token), issued_token_type, and aikonic_grant describing the grant it belongs to.\n","required":["access_token","token_type","expires_in","scope"],"additionalProperties":false,"properties":{"access_token":{"type":"string","description":"ES256 JWT (typ at+jwt); send as Authorization Bearer. client_credentials: carries client_id = the key's prefix. MCP grant types: audience = the MCP resource, never accepted by the platform API. token-exchange: the downstream token, accepted by the platform only on the grant's toolset's read operations.\n"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer","minimum":1,"maximum":900,"description":"Seconds until the token expires — never later than a rotating key's overlap end"},"scope":{"type":"string","description":"The granted permissions (or the grant's MCP scope), sorted and space-joined; always present"},"refresh_token":{"type":"string","format":"password","description":"MCP authorization_code, refresh_token and device_code grants only; rotates on every use"},"issued_token_type":{"type":"string","enum":["urn:ietf:params:oauth:token-type:access_token"],"description":"token-exchange only (RFC 8693 §2.2.1)"},"aikonic_grant":{"$ref":"#/components/schemas/AikonicGrant"}}},"OAuthError":{"type":"object","description":"RFC 6749 §5.2 error response of POST /v1/oauth/token — the one route whose 4xx are outside the ApiErrorResponse family, because OAuth client libraries parse {error} there. error_description is fixed text: nothing the caller sent is echoed. A 5xx from the same route is an ApiErrorResponse. connect-v1 1.26.0 (OD-884 Phase 2, DR-67): the same shape on the MCP grant routes (GET /v1/oauth/authorize when it cannot redirect, POST /v1/oauth/register, /v1/oauth/device_authorization, /v1/oauth/revoke) and for the new grant types of POST /v1/oauth/token; error becomes an OPEN list (x-extensible-enum, like the IANA OAuth error registry of RFC 6749 §8.5 it mirrors), and an optional reason names the platform's cause. A client_credentials request still answers only the six codes it always did; a client must treat an unknown error value as a refusal. connect-v1 1.57.0 (PROPOSAL, FEAT-034 §5.2): the same shape on the 400 of POST /v1/signup/device:token (authorization_pending, slow_down, expired_token, access_denied with reason EMAIL_MISMATCH when the request was cancelled, invalid_grant, invalid_request).\n","required":["error","error_description"],"additionalProperties":false,"properties":{"error":{"type":"string","description":"invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type and invalid_scope (RFC 6749 §5.2; all a client_credentials request can receive); since 1.26.0 also invalid_target (RFC 8707), authorization_pending, slow_down, expired_token and access_denied (RFC 8628 §3.5), invalid_redirect_uri and invalid_client_metadata (RFC 7591 §3.2.2), on the MCP grant routes only.\n","x-extensible-enum":["invalid_request","invalid_client","invalid_grant","unauthorized_client","unsupported_grant_type","invalid_scope","invalid_target","authorization_pending","slow_down","expired_token","access_denied","invalid_redirect_uri","invalid_client_metadata"]},"error_description":{"type":"string"},"reason":{"type":"string","description":"Optional machine-readable cause (connect-v1 1.26.0), an open list. On the token exchange: TOKEN_INVALID, AUDIENCE_MISMATCH, GRANT_REVOKED, GRANT_EXPIRED, SESSION_ENDED, PRINCIPAL_INACTIVE, TENANT_INACTIVE, TENANT_MISMATCH, TOOLSET_DISABLED, GRANTS_DISABLED, OPERATOR_GRANTS_DISABLED, OPERATOR_MFA_NOT_ENFORCED. On the MCP grants: REFRESH_REUSE, CODE_REUSED, PKCE_MISMATCH, PKCE_S256_REQUIRED, REDIRECT_URI_MISMATCH, CLIENT_UNKNOWN, CLIENT_METADATA_INVALID, DEVICE_FLOW_NOT_ALLOWED, OPERATOR_GRANTS_DISABLED (the operator resource where operator grants are off), OPERATOR_MFA_NOT_ENFORCED (the operator resource where aikonic.ops.mfa.required is false) and WRITES_DISABLED (mcp:console:write where the platform write ceiling is off). EXCHANGE_RATE_LIMITED, the per-grant exchange limit, is not an OAuthError: like every abuse limit of this route it answers 429 RATE_LIMITED as an ApiErrorResponse (IdentityRateLimited) with details.reason EXCHANGE_RATE_LIMITED.\n"}}},"OAuthServerMetadata":{"type":"object","description":"RFC 8414 §2 authorization server metadata, as GET /.well-known/oauth-authorization-server serves it. An MCP client must refuse a server whose code_challenge_methods_supported lacks S256 (MCP authorization 2026-07-28); this one lists S256 only. Other RFC 8414 members may be added without a contract change.\n","required":["issuer","authorization_endpoint","token_endpoint","response_types_supported","grant_types_supported","code_challenge_methods_supported"],"properties":{"issuer":{"type":"string","format":"uri","description":"The API origin, with no path; equals the iss on every authorization response (RFC 9207)"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri","description":"RFC 7591 dynamic registration (deprecated by MCP 2026-07-28, served)"},"device_authorization_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"scopes_supported":{"type":"array","items":{"type":"string"}},"response_types_supported":{"type":"array","items":{"type":"string","enum":["code"]}},"response_modes_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"revocation_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string","enum":["S256"]}},"client_id_metadata_document_supported":{"type":"boolean","description":"Client ID Metadata Documents are accepted as client_id (the MCP 2026-07-28 preferred registration)"},"authorization_response_iss_parameter_supported":{"type":"boolean","description":"Always true (RFC 9207)"}}},"OAuthClientRegistrationRequest":{"type":"object","description":"RFC 7591 §2 client metadata for POST /v1/oauth/register. Public clients only. Members not listed here are ignored and not echoed.\n","properties":{"client_name":{"type":"string","minLength":1,"maxLength":100,"description":"Shown on the consent screen with the UNVERIFIED badge; it is self-asserted"},"redirect_uris":{"type":"array","maxItems":10,"items":{"type":"string","format":"uri","maxLength":2048},"description":"https URLs or loopback IP literals; required unless grant_types is the device_code grant only"},"grant_types":{"type":"array","items":{"type":"string","enum":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code"]}},"response_types":{"type":"array","items":{"type":"string","enum":["code"]}},"token_endpoint_auth_method":{"type":"string","enum":["none"]},"client_uri":{"type":"string","format":"uri","maxLength":2048},"logo_uri":{"type":"string","format":"uri","maxLength":2048},"software_id":{"type":"string","maxLength":200},"software_version":{"type":"string","maxLength":100}}},"OAuthClientRegistrationResponse":{"type":"object","description":"RFC 7591 §3.2.1 — the registered metadata, with the platform-issued client_id and no secret","required":["client_id","client_id_issued_at","token_endpoint_auth_method","aikonic_client_verified"],"properties":{"client_id":{"type":"string"},"client_id_issued_at":{"type":"integer","description":"Seconds since the epoch"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string","enum":["none"]},"aikonic_client_verified":{"type":"boolean","description":"Always false for a dynamically registered client (ADR-C10 §Decision item 4)"}}},"DeviceAuthorizationRequest":{"type":"object","description":"RFC 8628 §3.1 device authorization request, form-encoded, in the body only","required":["client_id","resource"],"properties":{"client_id":{"type":"string","maxLength":2048},"resource":{"type":"string","format":"uri","maxLength":2048,"description":"The plane's /mcp/console or /mcp/supplier URL (RFC 8707); the operator resource is DEVICE_FLOW_NOT_ALLOWED"},"scope":{"type":"string","enum":["mcp:console:read","mcp:supplier:read"]}}},"DeviceAuthorizationResponse":{"type":"object","description":"RFC 8628 §3.2. verification_uri_complete is deliberately absent: the human always types the code (ADR-C10 T2).\n","required":["device_code","user_code","verification_uri","expires_in","interval"],"additionalProperties":false,"properties":{"device_code":{"type":"string","format":"password","description":"Secret to the client; poll POST /v1/oauth/token with it"},"user_code":{"type":"string","pattern":"^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$","description":"8 characters of the RFC 8628 §6.1 base-20 alphabet, shown as XXXX-XXXX"},"verification_uri":{"type":"string","format":"uri"},"expires_in":{"type":"integer","minimum":1,"maximum":600},"interval":{"type":"integer","minimum":5}}},"OAuthRevokeRequest":{"type":"object","description":"RFC 7009 §2.1 revocation request, form-encoded, in the body only. client_id is required: a public MCP client identifies itself (RFC 7009 §2.1), and a token issued to another client is not revoked.\n","required":["token","client_id"],"properties":{"token":{"type":"string","format":"password","maxLength":4096},"token_type_hint":{"type":"string","enum":["refresh_token","access_token"]},"client_id":{"type":"string","maxLength":2048}}},"AikonicGrant":{"type":"object","description":"The grant a token exchange belongs to (token-exchange answers only; the AI plane reads it to bind the call to one toolbox). Never shown to an agent. connect-v1 1.26.0 (ADR-C10 Phases 3 and 4): realm OPERATOR and toolset operator, writeOperations and crossTenantReads.\n","required":["grantId","realm","toolset","tenantId","supplierCode","onBehalfOf","clientId","clientName","clientVerified","operations","writeOperations","crossTenantReads","expiresAt"],"additionalProperties":false,"properties":{"grantId":{"type":"string"},"realm":{"type":"string","enum":["TENANT","SUPPLIER","OPERATOR"]},"toolset":{"type":"string","enum":["console","supplier","operator"]},"tenantId":{"type":["string","null"],"description":"The bound tenant (TENANT realm); null for a supplier or operator grant"},"supplierCode":{"type":["string","null"],"description":"The bound supplier (SUPPLIER realm); null for a console or operator grant"},"onBehalfOf":{"type":"string","description":"The approving human's user id (console), supplier principal id or operator id"},"clientId":{"type":"string"},"clientName":{"type":"string"},"clientVerified":{"type":"boolean"},"operations":{"type":"array","items":{"type":"string"},"description":"The x-mcp read names the grant may reach, a subset of the toolset's read tools and resources; for an operator grant it includes the cross-tenant reads ticked at consent\n"},"writeOperations":{"type":"array","items":{"type":"string"},"description":"Console only (ADR-C10 Phase 3, DR-68): the commit-lite x-mcp names ticked at consent that the tenant has ON in writesEnabled at this exchange; empty when the platform write ceiling is off, for every supplier and operator grant, and for a grant without write consent\n"},"crossTenantReads":{"type":"array","items":{"type":"string"},"description":"Operator only (ADR-C10 Phase 4): the cross-tenant reads ticked at consent (a subset of ops_list_tenants, ops_finance_summary, ops_list_supplier_cancellations, ops_list_approvals, ops_insights_environments and, from connect-v1 1.64.0 (FEAT-044), ops_list_registered_agents and ops_list_agent_traffic, each also in operations); every call of one is audited. Empty for every other grant\n"},"expiresAt":{"type":"string","format":"date-time"}}},"McpGrantRequest":{"type":"object","description":"A pending grant as the realm's Approve agent screen reads it (FEAT-031 §5.1, §5.2). It never carries a code, a verifier, a device code or a token. connect-v1 1.26.0 (ADR-C10 Phases 3 and 4, DR-68): realm OPERATOR, the write and cross-tenant offers, the step-up and second approval flags, and the second-approval state.\n","required":["requestId","flow","realm","toolset","state","expiresAt","client","scopes","operations","writeOperations","crossTenantReads","lifetimeSeconds","requesterIpCountry","stepUpRequired","secondApprovalRequired","approvalId","redirectTo"],"additionalProperties":false,"properties":{"requestId":{"type":"string"},"agent":{"description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.7): the agent identity of the requesting client, as the consent screen shows it (operator, agent and version, proofs, tier). Absent when the client carried no agent identity.\n","allOf":[{"$ref":"#/components/schemas/AgentStamp"}]},"agentConsent":{"description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.7): what the consent screen shows about the agent asking: verified, the warnings, the tools the tier ceiling removed and the longest grant offered (DR-167, DR-168). Present for every client while agent identity is on, so an unverified client is shown its warning, ceiling and one-hour cap; absent only while agent identity is off.\n","allOf":[{"$ref":"#/components/schemas/AgentConsent"}]},"flow":{"type":"string","enum":["AUTH_CODE","DEVICE_CODE"],"description":"An operator request is always AUTH_CODE (the operator realm never offers a device flow)"},"realm":{"type":"string","enum":["TENANT","SUPPLIER","OPERATOR"]},"toolset":{"type":"string","enum":["console","supplier","operator"]},"state":{"type":"string","enum":["PENDING","AWAITING_SECOND_APPROVAL","APPROVED","DENIED","EXPIRED"],"description":"AWAITING_SECOND_APPROVAL (operator only): the first operator approved on a rung that needs a second approver, and the MCP_OPERATOR_GRANT ops_approval approvalId is open\n"},"expiresAt":{"type":"string","format":"date-time"},"client":{"type":"object","required":["clientId","name","verified","registration","redirectHost","logoUri"],"additionalProperties":false,"properties":{"clientId":{"type":"string"},"name":{"type":"string"},"verified":{"type":"boolean","description":"True only for a PLATFORM client; DYNAMIC and METADATA_DOCUMENT clients are UNVERIFIED"},"registration":{"type":"string","enum":["DYNAMIC","METADATA_DOCUMENT","PLATFORM"]},"redirectHost":{"type":["string","null"],"description":"The host the browser returns to; null for a device flow"},"logoUri":{"type":["string","null"]}}},"scopes":{"type":"array","items":{"type":"object","required":["scope","description"],"additionalProperties":false,"properties":{"scope":{"type":"string"},"description":{"type":"string"}}}},"operations":{"type":"array","description":"The toolset's read tools and resources (from the generated allow-list), all pre-ticked; for an operator request the cross-tenant reads are not here but in crossTenantReads\n","items":{"type":"object","required":["name","kind","title"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The x-mcp name"},"kind":{"type":"string","enum":["tool","resource"]},"title":{"type":"string"}}}},"writeOperations":{"type":"array","description":"Console only (ADR-C10 Phase 3, DR-68): the commit-lite tools offered for write consent, every one UNTICKED on the screen. Offered only when the client asked for mcp:console:write, the platform write ceiling is on, the tenant has the tool ON in writesEnabled and the approving human holds the tool's permission; empty otherwise and for every supplier and operator request (an operator grant is reads only, ADR-C10 R3)\n","items":{"type":"object","required":["name","title","permission"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The x-mcp name of a console commit-lite tool"},"title":{"type":"string"},"permission":{"type":"string","description":"The console permission the tool needs, for example orders:write"}}}},"crossTenantReads":{"type":"array","description":"Operator only (ADR-C10 Phase 4): the seven reads that see every tenant (ops_list_tenants, ops_finance_summary, ops_list_supplier_cancellations, ops_list_approvals, ops_list_registered_agents, ops_list_agent_traffic, resource ops_insights_environments), offered UNTICKED in their own group; each call of a ticked one is audited. Empty for every other request\n","items":{"type":"object","required":["name","kind","title"],"additionalProperties":false,"properties":{"name":{"type":"string"},"kind":{"type":"string","enum":["tool","resource"]},"title":{"type":"string"}}}},"lifetimeSeconds":{"type":"integer","minimum":1,"description":"The longest the grant can live, shortened to the human's session if that ends sooner: 8 hours for console and supplier, 1 hour for operator (ADR-C10 Phase 4)\n"},"requesterIpCountry":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country of the client that started a device flow; null for authorization code"},"stepUpRequired":{"type":"boolean","description":"Whether approving needs a step-up proof (stepUpCode or stepUpAssertion): always true for an operator request; for a console request false here, and required once any write box is ticked (the screen shows the field then); false for supplier\n"},"secondApprovalRequired":{"type":"boolean","description":"Operator only: true on a rung named in aikonic.mcp.operator.second-approver-rungs (default UAT and PROD), where the grant activates only after a different OPS_SECURITY operator approves the MCP_OPERATOR_GRANT ops_approval (FEAT-024 §7)\n"},"approvalId":{"type":["string","null"],"description":"The MCP_OPERATOR_GRANT ops_approval id while AWAITING_SECOND_APPROVAL or after; null otherwise"},"redirectTo":{"type":["string","null"],"format":"uri","description":"Operator only, once a request that awaited a second approval has ended, shown only to the operator who approved first (the screen polls this request and sends the browser there). APPROVED: the client's redirect_uri with code, state and iss; every read mints a fresh code and cancels the previous one, so the screen stops polling once it has it. DENIED or EXPIRED: the redirect_uri with error=access_denied, state and iss. Null otherwise\n"}}},"McpGrantRequestLookup":{"type":"object","required":["userCode"],"additionalProperties":false,"properties":{"userCode":{"type":"string","pattern":"^[A-Za-z]{4}-?[A-Za-z]{4}$","description":"The code the human typed; case and the dash are ignored"}}},"McpGrantRequestApproval":{"type":"object","description":"connect-v1 1.26.0 (ADR-C10 Phases 3 and 4, DR-68): writeOperations, crossTenantReads and the step-up pair. The step-up pair follows DR-53 / DR-54 (exactly one of stepUpCode and stepUpAssertion; both optional in the schema, the backend enforces the rule). It is required when writeOperations is non-empty and on every operator approval: absent is 403 PERMISSION_DENIED details.reason STEP_UP_REQUIRED; wrong or doubled is STEP_UP_FAILED; a locked principal is STEP_UP_LOCKED.\n","required":["toolset"],"additionalProperties":false,"properties":{"toolset":{"type":"string","enum":["console","supplier","operator"],"description":"Must equal the request's toolset; on a device flow the human selects it by hand"},"operations":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"},"description":"x-mcp names to grant, a subset of the request's operations; absent = all of them"},"writeOperations":{"type":"array","maxItems":10,"items":{"type":"string"},"description":"Console only: the write tools ticked, a subset of the request's writeOperations (any other name is 400 VALIDATION details.reason OPERATION_NOT_ALLOWED, field writeOperations); absent or empty = no write consent. Non-empty needs the step-up proof\n"},"crossTenantReads":{"type":"array","maxItems":5,"items":{"type":"string"},"description":"Operator only: the cross-tenant reads ticked, a subset of the request's crossTenantReads; absent or empty = none (the default)\n"},"confirmOwnDevice":{"type":"boolean","description":"Required true on a device flow (\"I started this on my own device\")"},"stepUpCode":{"type":"string","writeOnly":true,"pattern":"^[0-9]{6}$","description":"The approving human's current TOTP code. Send this or stepUpAssertion, not both"},"stepUpAssertion":{"writeOnly":true,"allOf":[{"$ref":"#/components/schemas/WebAuthnAssertion"}],"description":"A WebAuthn step-up assertion (DR-54). Send this or stepUpCode, not both"}}},"McpGrantRequestDecision":{"type":"object","description":"Exactly one of: redirectTo (authorization code: the client's redirect_uri with code or error=access_denied, state and iss; the browser goes exactly there and nowhere else), approved (device flow, approved), denied (device flow, denied), or, since connect-v1 1.26.0 for an operator approval on a second-approver rung, awaitingSecondApproval with approvalId (no code is issued until a different OPS_SECURITY operator approves the MCP_OPERATOR_GRANT ops_approval; the screen then polls opsGetMcpGrantRequest).\n","additionalProperties":false,"properties":{"redirectTo":{"type":"string","format":"uri"},"approved":{"type":"boolean","enum":[true]},"denied":{"type":"boolean","enum":[true]},"awaitingSecondApproval":{"type":"boolean","enum":[true]},"approvalId":{"type":"string","description":"The MCP_OPERATOR_GRANT ops_approval id; present only with awaitingSecondApproval"}}},"McpGrantCall":{"type":"object","description":"One call a grant made — never its arguments or result","required":["tool","outcome","at"],"additionalProperties":false,"properties":{"tool":{"type":"string","description":"The x-mcp name called (refused names included)"},"outcome":{"type":"string","description":"OK, REFUSED or ERROR (open list)"},"at":{"type":"string","format":"date-time"}}},"McpGrant":{"type":"object","description":"One MCP grant (FEAT-031 §7). recentCalls and refusalCounts are present on the single-grant read only. No token, code or hash is ever returned. connect-v1 1.26.0 (ADR-C10 Phases 3 and 4): realm OPERATOR, writeOperations, crossTenantReads and approvedBy.\n","required":["grantId","realm","toolset","boundUser","client","flow","scopes","operations","writeOperations","crossTenantReads","approvedBy","createdAt","approvedAt","lastUsedAt","lastUsedIpCountry","expiresAt","status","revokedAt","revokedBy","revokeReason"],"additionalProperties":false,"properties":{"grantId":{"type":"string"},"agent":{"description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.3): the agent identity the platform resolved for the request, grant or record (operator, agent and version, proofs, tier). Absent when the caller carried no agent identity.\n","allOf":[{"$ref":"#/components/schemas/AgentStamp"}]},"realm":{"type":"string","enum":["TENANT","SUPPLIER","OPERATOR"]},"toolset":{"type":"string","enum":["console","supplier","operator"]},"boundUser":{"type":"object","required":["id","displayName"],"additionalProperties":false,"properties":{"id":{"type":"string"},"displayName":{"type":"string"}}},"client":{"type":"object","required":["clientId","name","verified"],"additionalProperties":false,"properties":{"clientId":{"type":"string"},"name":{"type":"string"},"verified":{"type":"boolean"}}},"flow":{"type":"string","enum":["AUTH_CODE","DEVICE_CODE"]},"scopes":{"type":"array","items":{"type":"string"}},"operations":{"type":"array","items":{"type":"string"},"description":"The read x-mcp names ticked at consent (an operator grant's ticked cross-tenant reads included)"},"writeOperations":{"type":"array","items":{"type":"string"},"description":"Console only: the write tools ticked at consent with a step-up (ADR-C10 Phase 3). Each runs only while the tenant has it ON in writesEnabled; empty for a grant without write consent and for every supplier and operator grant\n"},"crossTenantReads":{"type":"array","items":{"type":"string"},"description":"Operator only, the cross-tenant reads ticked at consent; empty otherwise"},"approvedBy":{"type":"array","minItems":1,"maxItems":2,"description":"Who approved: the bound human; for an operator grant on a second-approver rung, also the OPS_SECURITY operator who approved the MCP_OPERATOR_GRANT ops_approval\n","items":{"type":"object","required":["id","displayName"],"additionalProperties":false,"properties":{"id":{"type":"string"},"displayName":{"type":"string"}}}},"createdAt":{"type":"string","format":"date-time"},"approvedAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"lastUsedIpCountry":{"type":["string","null"]},"expiresAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["ACTIVE","REVOKED","EXPIRED"]},"revokedAt":{"type":["string","null"],"format":"date-time"},"revokedBy":{"type":["string","null"],"description":"The user or principal id that revoked it; null when the platform did (session ended, reuse)"},"revokeReason":{"type":["string","null"]},"recentCalls":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/McpGrantCall"}},"refusalCounts":{"type":"object","additionalProperties":{"type":"integer","minimum":0},"description":"Refused requests by reason, for example SESSION_REQUIRED"}}},"McpGrantPage":{"type":"object","required":["items","nextCursor"],"additionalProperties":false,"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/McpGrant"}},"nextCursor":{"type":["string","null"]}}},"McpGrantRevokeRequest":{"type":"object","additionalProperties":false,"properties":{"reason":{"type":"string","maxLength":200}}},"McpGrantRevokeAllRequest":{"type":"object","additionalProperties":false,"properties":{"userId":{"type":"string","description":"Absent = the caller's own grants; another user's id needs users:manage"},"reason":{"type":"string","maxLength":200}}},"McpGrantRevokeAllResult":{"type":"object","required":["revoked"],"additionalProperties":false,"properties":{"revoked":{"type":"integer","minimum":0}}},"McpSettings":{"type":"object","description":"The tenant's MCP settings (FEAT-031 §7 tenant_mcp_settings): the console toolset switch and, since connect-v1 1.26.0 (ADR-C10 Phase 3, DR-68), the per-tool write opt-in. updatedBy and updatedAt are read-only. Written through McpSettingsUpdate.\n","required":["toolsets","writesEnabled"],"additionalProperties":false,"properties":{"toolsets":{"type":"object","required":["console"],"additionalProperties":false,"properties":{"console":{"type":"string","enum":["ON","OFF"]}}},"writesEnabled":{"type":"object","description":"One entry per console commit-lite x-mcp tool; ON lets an MCP grant whose human ticked that tool at consent (with a step-up) run it, OFF (the default) refuses it. A tenant that never changed it reads all OFF; the platform answers all ten names. Money and decisions are never in this list. The writes also need the platform ceiling aikonic.mcp.grants.writes.enabled and the AI plane's AIKONIC_MCP_AUTONOMOUS_WRITES.\n","additionalProperties":false,"properties":{"claim_queue_item":{"type":"string","enum":["ON","OFF"]},"release_queue_item":{"type":"string","enum":["ON","OFF"]},"test_webhook":{"type":"string","enum":["ON","OFF"]},"replay_webhook_delivery":{"type":"string","enum":["ON","OFF"]},"update_supplier_data_profile":{"type":"string","enum":["ON","OFF"]},"start_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"cancel_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"pause_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"resume_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"escalate_console_case":{"type":"string","enum":["ON","OFF"]}}},"updatedBy":{"type":["string","null"],"readOnly":true},"updatedAt":{"type":["string","null"],"format":"date-time","readOnly":true}}},"McpSettingsUpdate":{"type":"object","description":"The body of updateMcpSettings (connect-v1 1.26.0, ADR-C10 Phase 3, DR-68). Each member is optional and changes only what it names: toolsets.console sets the switch; writesEnabled sets only the tools it lists (a tool not listed keeps its value). At least one of toolsets and writesEnabled is required (else 400 VALIDATION). Turning any write ON needs the Owner role and the step-up pair (DR-53 / DR-54: exactly one of stepUpCode and stepUpAssertion); turning a write OFF needs users:manage only and no proof (a stop must be cheap).\n","additionalProperties":false,"properties":{"toolsets":{"type":"object","required":["console"],"additionalProperties":false,"properties":{"console":{"type":"string","enum":["ON","OFF"]}}},"writesEnabled":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"claim_queue_item":{"type":"string","enum":["ON","OFF"]},"release_queue_item":{"type":"string","enum":["ON","OFF"]},"test_webhook":{"type":"string","enum":["ON","OFF"]},"replay_webhook_delivery":{"type":"string","enum":["ON","OFF"]},"update_supplier_data_profile":{"type":"string","enum":["ON","OFF"]},"start_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"cancel_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"pause_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"resume_supplier_data_run":{"type":"string","enum":["ON","OFF"]},"escalate_console_case":{"type":"string","enum":["ON","OFF"]}}},"stepUpCode":{"type":"string","writeOnly":true,"pattern":"^[0-9]{6}$","description":"The Owner's current TOTP code; required with any write turned ON. Send this or stepUpAssertion, not both"},"stepUpAssertion":{"writeOnly":true,"allOf":[{"$ref":"#/components/schemas/WebAuthnAssertion"}],"description":"A WebAuthn step-up assertion (DR-54). Send this or stepUpCode, not both"}}},"ConsoleApprovalErrorResponse":{"type":"object","description":"ApiErrorResponse's shape, with `code` carrying the D-50 approval token's two rejections (12 §B2.4, OD-403) alongside PERMISSION_DENIED, on the two routes that can return them — POST /admin/v1/orders/{orderRef}/reconcile:fail and POST /admin/v1/agent-proposals/{proposalId}:approve (components/responses/ ConsoleApprovalForbidden). Both routes are new in this publish, so declaring the extra codes here rather than on the shared ApiErrorResponse.code enum keeps gate 1 (schema-diff) honestly additive: no already-published response's declared `code` set widens (API-0012 §10). See ApiErrorResponse.code's own description and OD-WP-1.\n","required":["code","message","traceId","retryable"],"properties":{"code":{"type":"string","enum":["PERMISSION_DENIED","APPROVAL_TOKEN_EXPIRED","APPROVAL_TOKEN_USED"]},"message":{"type":"string"},"traceId":{"type":"string","description":"The support handle (13 §3)"},"retryable":{"type":"boolean"},"details":{"type":"object","additionalProperties":true,"description":"reason (SELF_APPROVAL on PERMISSION_DENIED), approvalTokenId, expiredAt (EXPIRED), usedAt (USED) — see ConsoleApprovalForbidden's examples for the shapes.\n"}}},"ConsoleActor":{"type":"object","description":"Who did it (13 §1.1 `actor`): kind user | system | ai, with actor.kind = ai mandatory on AI-initiated actions (C4 law). `agentId` names the agent when kind is ai; `onBehalfOf` is the human approver of a D-50 approval-token action, or the customer an agent-desk booking was made for (09 §5).\n","required":["kind","id"],"properties":{"kind":{"type":"string","enum":["user","system","ai"]},"id":{"type":"string"},"agentId":{"type":"string"},"onBehalfOf":{"type":"string"}}},"ConsoleResourceRef":{"type":"object","description":"13 §1.1 `resource` — {type, ref}: order MO-…, credential_set …, ruleset <version>, agent-proposal prop_…","required":["type","ref"],"properties":{"type":{"type":"string","description":"Open string — order | credential_set | ruleset | webhook | user | invite | agent-proposal | branding"},"ref":{"type":"string"}}},"ConsoleTimeWindow":{"type":"object","description":"A closed [from, to] window; date-time on live views, date on reports","required":["from","to"],"properties":{"from":{"type":"string"},"to":{"type":"string"}}},"ConsoleRole":{"type":"string","description":"The 8-role console model of 09 §10 / §12 (07 §5 [assumption]): Owner, Admin, Developer, OpsAgent, Finance, RevenueManager, ContentEditor, Auditor — spelled UPPER_SNAKE like every other enum here. Permissions (15 §1.3 strings) are resolved from the role bundle by the platform; a client reads `permissions`, never derives them.\n","enum":["OWNER","ADMIN","DEVELOPER","OPS_AGENT","FINANCE","REVENUE_MANAGER","CONTENT_EDITOR","AUDITOR"]},"ConsoleTeamMember":{"type":"object","description":"A tenant user as the console sees it (FEAT-020 §7 user, kind HUMAN) — never a credential","required":["userId","email","kind","roles","permissions","status","siteIds","createdAt"],"properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"displayName":{"type":"string"},"kind":{"type":"string","enum":["HUMAN"],"description":"Service principals are API keys (GET /v1/keys), never listed here"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleRole"}},"permissions":{"type":"array","items":{"type":"string"},"description":"Resolved 15 §1.3 permission strings — what AccessContext.permissions will hold for this user"},"status":{"type":"string","enum":["ACTIVE","DEACTIVATED","LOCKED"],"description":"LOCKED = FEAT-020 §12 lockout; clears itself or by an Admin re-activation"},"siteIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"lastLoginAt":{"type":["string","null"],"format":"date-time"}}},"ConsoleTeamMemberList":{"type":"object","required":["members","page"],"properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleTeamMember"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleTeamMemberUpdate":{"type":"object","description":"PATCH body — at least one of roles / active; reason is recorded in the audit row","minProperties":1,"properties":{"roles":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ConsoleRole"},"description":"Replaces the role set"},"active":{"type":"boolean","description":"false deactivates (sessions revoked at once); true re-activates a DEACTIVATED or LOCKED member"},"reason":{"type":"string","maxLength":500}}},"ConsoleInvite":{"type":"object","description":"FEAT-020 §7 `invite` as the inviter sees it — never the token","required":["inviteId","email","roles","siteIds","invitedBy","createdAt","expiresAt","status"],"properties":{"inviteId":{"type":"string"},"email":{"type":"string","format":"email"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleRole"}},"permissions":{"type":"array","items":{"type":"string"},"description":"The explicit permission subset granted by POST /v1/invites, when one was given"},"siteIds":{"type":"array","items":{"type":"string"}},"invitedBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":["string","null"],"format":"date-time","description":"null until the invite is accepted (a PENDING/EXPIRED/REVOKED invite has none)"},"status":{"type":"string","enum":["PENDING","ACCEPTED","EXPIRED","REVOKED"]}}},"ConsoleInviteList":{"type":"object","required":["invites","page"],"properties":{"invites":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleInvite"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleAuditRecord":{"type":"object","description":"One 13 §1.1 audit record, tenant slice (13 §1.4). before/after diffs are not carried on the list row; `details` is the code-specific payload (open map, the one place this contract allows one). `breakGlass` is present on ADR-003 disclosures only.\n","required":["auditId","occurredAt","actor","tenantId","action","resource","outcome"],"properties":{"auditId":{"type":"string","description":"ULID — time-ordered, unguessable"},"agent":{"description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.3): the agent identity the platform resolved for the request, grant or record (operator, agent and version, proofs, tier). Absent when the caller carried no agent identity.\n","allOf":[{"$ref":"#/components/schemas/AgentStamp"}]},"occurredAt":{"type":"string","format":"date-time"},"actor":{"$ref":"#/components/schemas/ConsoleActor"},"tenantId":{"type":"string"},"siteId":{"type":"string"},"action":{"type":"string","description":"Closed-registry verb (13 §1.1) — orders.create, credential.rotate, config.ruleset.publish, breakglass.grant …"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"outcome":{"type":"string","description":"SUCCESS, FAILED, or PENDING (a write-ahead row for an /admin/v1 write that has not been closed yet, 13 §1.2). UNKNOWN_OUTCOME (v1.6.8, OD-842 (b)): the write-ahead PENDING row was never closed by its own request (the process died, or the closing write failed), and the audit-pending-sweep resolved it after its threshold (default 5 minutes). The action began; nobody recorded how it ended. A row of this outcome carries no outcomeCode.\n","enum":["SUCCESS","FAILED","PENDING","UNKNOWN_OUTCOME"]},"outcomeCode":{"type":"string","description":"The ApiErrorResponse.code of a FAILED outcome; absent otherwise, including for UNKNOWN_OUTCOME (v1.6.8)"},"ipPrefix":{"type":"string","description":"v1.6.8 (OD-843 (b)): the caller's network, never a full address. An IPv4 address is truncated to its /24 and written a.b.c.0/24; an IPv6 address to its /48, written x:y:z::/48. Absent when the row has none (rows filed before OD-843 (b)).\n","examples":["203.0.113.0/24","2001:db8:85a3::/48"]},"userAgentFamily":{"type":"string","description":"v1.6.8 (OD-843 (b)): the family of the caller's User-Agent, never the raw header. One of the backend's closed list (UserAgentFamily): Edge, Firefox, Chrome, Safari, curl, okhttp, python-requests, Java-http-client, other. Described as a string, not an enum, because the list is a Java set of strings that can grow additively; a client must tolerate a value it does not know. Absent when the row has none.\n","examples":["Chrome"]},"traceId":{"type":"string"},"approvalTokenId":{"type":"string","description":"Stamped on D-50 approval-token actions (12 §B2.4 step 3)"},"apiKeyId":{"type":"string","description":"keyPrefix of the key that made the call, when a key did"},"breakGlass":{"type":"object","description":"ADR-003 disclosure — every platform-side cross-tenant access of this tenant's data","required":["reason","approver","ttlUntil"],"properties":{"reason":{"type":"string"},"approver":{"type":"string"},"ttlUntil":{"type":"string","format":"date-time"}}},"details":{"type":"object","additionalProperties":true,"description":"Code-specific payload — an explicit open map"}}},"ConsoleAuditRecordList":{"type":"object","required":["records","page"],"properties":{"records":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAuditRecord"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleQueueName":{"type":"string","description":"The fulfilment queues (09 §4). The four v1 queues, plus — connect-v1 v1.5.0 (BE-4) — OFFLINE_SUPPLIER_ACTION (a cancel or amend handledBy OPS_DESK awaiting the desk), REQUEST_TO_BOOK (a request-to-book order awaiting the owner's answer; the supplier realm answers it, the desk can confirm or decline on behalf) and VOUCHER_REISSUE (a revoked or failed voucher to reissue). Declared as an x-extensible-enum (the Zalando convention), not a closed enum: adding a queue is additive under gate 1 and a client must tolerate a queue name it does not know (11 §8). Generated clients see a string.\n","x-extensible-enum":["ON_REQUEST_AGING","RECONCILIATION","DOC_GEN_FAILED","SCHEDULE_CHANGES","OFFLINE_SUPPLIER_ACTION","REQUEST_TO_BOOK","VOUCHER_REISSUE"]},"ConsoleSlaState":{"type":"string","description":"SLA colouring of a queue row — WITHIN, WARNING (last quarter of the SLA), BREACHED (past slaDueAt)","enum":["WITHIN","WARNING","BREACHED"]},"ConsoleQueueAction":{"type":"string","description":"A verb the caller may take on a queue row, each backed by a contract operation: CLAIM / RELEASE (this queue), RECONCILE_RETRY / RECONCILE_ADOPT / RECONCILE_FAIL (POST /admin/v1/orders/{orderRef}/reconcile:*), REGENERATE_DOCUMENTS (POST /v1/orders/{orderRef}/documents), AMEND (POST /v1/orders/{orderRef}/amend — accept or re-accommodate a schedule change, QUOTE then COMMIT), CANCEL (POST …/cancel-quote → cancel, the on-request decline path). Added in connect-v1 v1.5.0 (BE-4): CONFIRM / DECLINE (REQUEST_TO_BOOK — POST /supplier/v1/bookings/{bookingRef}:confirm | :decline on behalf, reason-stamped), MARK_DONE / ESCALATE (OFFLINE_SUPPLIER_ACTION — close or escalate the desk ticket), REISSUE_VOUCHER (VOUCHER_REISSUE — POST /v1/orders/{orderRef}/documents). Declared as an x-extensible-enum, not a closed enum (11 §8): a client renders the actions it knows and ignores the rest; generated clients see a string.\n","x-extensible-enum":["CLAIM","RELEASE","RECONCILE_RETRY","RECONCILE_ADOPT","RECONCILE_FAIL","REGENERATE_DOCUMENTS","AMEND","CANCEL","CONFIRM","DECLINE","MARK_DONE","ESCALATE","REISSUE_VOUCHER"]},"ConsoleAssignee":{"type":"object","description":"Who holds the row (09 §4 \"claimable\")","required":["userId","claimedAt"],"properties":{"userId":{"type":"string"},"displayName":{"type":"string"},"claimedAt":{"type":"string","format":"date-time"}}},"ConsoleQueueItem":{"type":"object","description":"One claimable, SLA-coloured queue row (09 §4). `state` is the order's state (03 §7 FSM) or ON_REQUEST; `atRisk` is the server's not-yet-settled total for the order (a field, never a client sum). Queue-specific fields: attempt / nextRetryAt (RECONCILIATION — 06 §8 step 4 schedule position), reason (DOC_GEN_FAILED template error), eventId (SCHEDULE_CHANGES SupplierEvent). `actions` is the server's list of what THIS caller may do now.\n","required":["itemId","queue","orderRef","domain","supplier","state","enteredAt","slaState","actions"],"properties":{"itemId":{"type":"string"},"queue":{"$ref":"#/components/schemas/ConsoleQueueName"},"orderRef":{"type":"string"},"domain":{"type":"string"},"supplier":{"type":"string"},"credentialSet":{"type":"string","description":"MONDEE or the tenant's own set id (08 §5 attribution)"},"state":{"type":"string","description":"Order FSM state (PENDING_RECONCILIATION, ON_REQUEST, CONFIRMED …) — open string"},"enteredAt":{"type":"string","format":"date-time"},"slaDueAt":{"type":["string","null"],"format":"date-time","description":"From the supplier registry's confirmSlaHours (08 §2) or the reconciliation window; null when no SLA applies"},"slaState":{"$ref":"#/components/schemas/ConsoleSlaState"},"atRisk":{"$ref":"#/components/schemas/Money"},"assignee":{"$ref":"#/components/schemas/ConsoleAssignee","description":"Absent when the row is unclaimed"},"attempt":{"type":"integer","description":"Reconciliation attempts so far (RECONCILIATION)"},"nextRetryAt":{"type":["string","null"],"format":"date-time","description":"Next scheduled retrieve (RECONCILIATION); null once the schedule is exhausted"},"reason":{"type":"string","description":"The failure text (DOC_GEN_FAILED) or change summary (SCHEDULE_CHANGES)"},"eventId":{"type":"string","description":"The normalised SupplierEvent (06 §7) behind a SCHEDULE_CHANGES row"},"watching":{"type":"boolean","description":"True when the caller watches this row (notified on every change); connect-v1 v1.5.0 (WEB-1)"},"bookingRef":{"type":"string","description":"The supplier-realm booking (SB-…) behind a REQUEST_TO_BOOK row"},"actions":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleQueueAction"}}}},"ConsoleQueueCounts":{"type":"object","required":["open","claimed","breached"],"properties":{"open":{"type":"integer"},"claimed":{"type":"integer"},"breached":{"type":"integer"}}},"ConsoleQueue":{"type":"object","required":["queue","counts","items","page"],"properties":{"queue":{"$ref":"#/components/schemas/ConsoleQueueName"},"counts":{"$ref":"#/components/schemas/ConsoleQueueCounts"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleQueueItem"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleReconcileRetryRequest":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500,"description":"Posted into the order timeline (09 §4)"}}},"ConsoleReconcileAdoptRequest":{"type":"object","required":["supplierLocator","reason"],"properties":{"supplierLocator":{"type":"string","description":"The PNR / booking id found by desk or GDS lookup"},"reason":{"type":"string","minLength":8,"maxLength":500}}},"ConsoleReconcileFailRequest":{"type":"object","description":"Reason + the second approver's D-50 token (09 §3, ADR-003)","required":["reason","approvalToken"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500},"approvalToken":{"type":"string","description":"Minted by ANOTHER human at POST /admin/v1/approvals for scope orders:cancel, action reconcile.fail, this orderRef and the amount to compensate"}}},"ConsoleReconcileOutcome":{"type":"object","description":"What a reconciliation action did and where the order stands after it (06 §8, 18 §10)","required":["orderRef","outcome","state","attempt","performedAt"],"properties":{"orderRef":{"type":"string"},"outcome":{"type":"string","enum":["FOUND_CONFIRMED","FOUND_UNSETTLED","NOT_FOUND","SUPPLIER_UNAVAILABLE","FAILED_COMPENSATED"],"description":"FOUND_CONFIRMED: the supplier holds the booking and it is confirmed; the order is CONFIRMED. FOUND_UNSETTLED (v1.6.6): the supplier holds the booking but has not settled it (held, pending, or a state that is not CONFIRMED); the order stays as it is and the reconcile can be retried. NOT_FOUND: the supplier does not have the booking. SUPPLIER_UNAVAILABLE: the supplier could not say. FAILED_COMPENSATED: the order was marked failed and compensated.\n"},"state":{"type":"string","description":"The order's FSM state after the action (CONFIRMED, PENDING_RECONCILIATION, FAILED_COMPENSATED)"},"attempt":{"type":"integer"},"supplierLocator":{"type":["string","null"]},"nextRetryAt":{"type":["string","null"],"format":"date-time"},"performedAt":{"type":"string","format":"date-time"},"compensated":{"$ref":"#/components/schemas/Money"}}},"ConsoleAnalyticsReportName":{"type":"string","description":"The five v1 reports of 09 §6","enum":["funnel","l2b","routes","supplier-mix","margin"]},"ConsoleAnalyticsCellKind":{"type":"string","enum":["COUNT","MONEY","RATIO_BPS","DURATION_MS","TEXT"]},"ConsoleAnalyticsColumn":{"type":"object","required":["key","label","kind"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"kind":{"$ref":"#/components/schemas/ConsoleAnalyticsCellKind"}}},"ConsoleAnalyticsCell":{"type":"object","description":"One typed cell; exactly the field its `kind` names is set (others absent or null). Money is Money; ratios are basis points (10000 = 100%); durations are ms.\n","required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/ConsoleAnalyticsCellKind"},"count":{"type":["integer","null"]},"money":{"$ref":"#/components/schemas/Money"},"ratioBps":{"type":["integer","null"]},"durationMs":{"type":["integer","null"]},"text":{"type":["string","null"]}}},"ConsoleAnalyticsRow":{"type":"object","required":["key","label","cells"],"properties":{"key":{"type":"string","description":"Stable row id — a funnel stage, supplier code, route, money model"},"label":{"type":"string"},"cells":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAnalyticsCell"},"description":"Aligned index-for-index with `columns`"}}},"ConsoleAnalyticsReport":{"type":"object","description":"One report as a typed table (09 §6; OD-82 ≤ 4 columns). `totals` is the server's total row or null when a total is meaningless (funnel); the console never sums a column (web hard rule 1).\n","required":["report","window","dataThrough","currency","filters","columns","rows"],"properties":{"report":{"$ref":"#/components/schemas/ConsoleAnalyticsReportName"},"window":{"$ref":"#/components/schemas/ConsoleTimeWindow"},"dataThrough":{"type":"string","format":"date-time","description":"Freshness stamp — \"data through\" (09 §6)"},"currency":{"type":"string","description":"ISO-4217 of every MONEY cell (the tenant's reporting currency)"},"filters":{"type":"object","description":"The filters applied, echoed","properties":{"domain":{"type":"string"},"supplier":{"type":"string"},"credentialSet":{"type":"string"},"pos":{"type":"string"}}},"columns":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/ConsoleAnalyticsColumn"}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAnalyticsRow"}},"totals":{"$ref":"#/components/schemas/ConsoleAnalyticsRow","description":"The server's total row; absent when a total is meaningless (funnel)"}}},"ConsoleHomeQueueCount":{"type":"object","required":["queue","open","claimed","breached"],"properties":{"queue":{"$ref":"#/components/schemas/ConsoleQueueName"},"open":{"type":"integer"},"claimed":{"type":"integer"},"breached":{"type":"integer"}}},"ConsoleSupplierHealth":{"type":"object","description":"Registry status (08 §2) plus the last hour's error rate and p95 for suppliers this tenant sells","required":["supplier","status"],"properties":{"supplier":{"type":"string"},"status":{"type":"string","enum":["ONBOARDING","CERTIFYING","LIVE","DEGRADED","SUSPENDED","OFFBOARDED"]},"errorRateBps":{"type":["integer","null"]},"p95Ms":{"type":["integer","null"]}}},"ConsoleHomeOps":{"type":"object","description":"The ops Home (09 §4): queue counts, the rows needing attention most, at-risk total, supplier health","required":["queues","atRiskTotal","attention","supplierHealth"],"properties":{"queues":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleHomeQueueCount"}},"atRiskTotal":{"$ref":"#/components/schemas/Money"},"attention":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/ConsoleQueueItem"},"description":"Money at risk descending, then soonest SLA — the server's order"},"supplierHealth":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleSupplierHealth"}}}},"ConsoleFunnelStage":{"type":"object","required":["stage","count"],"properties":{"stage":{"type":"string","enum":["SEARCH","DETAIL","REPRICE","ORDER","CONFIRMED"]},"count":{"type":"integer"}}},"ConsoleMoneyToday":{"type":"object","description":"Today's money so far, server-computed per 08 §7 (gross, net, margin/fee, refunds)","required":["gross","net","margin","refunds"],"properties":{"gross":{"$ref":"#/components/schemas/Money"},"net":{"$ref":"#/components/schemas/Money"},"margin":{"$ref":"#/components/schemas/Money"},"refunds":{"$ref":"#/components/schemas/Money"}}},"ConsoleLatencyRow":{"type":"object","required":["supplier","p50Ms","p95Ms"],"properties":{"supplier":{"type":"string"},"p50Ms":{"type":["integer","null"]},"p95Ms":{"type":["integer","null"]}}},"ConsoleVolumePoint":{"type":"object","required":["date","orders"],"properties":{"date":{"type":"string","format":"date"},"orders":{"type":"integer"}}},"ConsoleHomeAnalytics":{"type":"object","description":"The analytical Home (09 §6, §7): today's funnel, money, supplier latency, seven days of volume","required":["funnel","moneyToday","latency","volume"],"properties":{"funnel":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleFunnelStage"}},"moneyToday":{"$ref":"#/components/schemas/ConsoleMoneyToday"},"latency":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleLatencyRow"}},"volume":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleVolumePoint"}}}},"ConsoleHome":{"type":"object","description":"Both Homes in one read (OD-81) — a console_projections entry per tenant and window (OD-401)","required":["generatedAt","dataThrough","currency","window","ops","analytics"],"properties":{"generatedAt":{"type":"string","format":"date-time"},"dataThrough":{"type":"string","format":"date-time"},"currency":{"type":"string"},"window":{"$ref":"#/components/schemas/ConsoleTimeWindow"},"ops":{"$ref":"#/components/schemas/ConsoleHomeOps"},"analytics":{"$ref":"#/components/schemas/ConsoleHomeAnalytics"}}},"ConsoleMoneyModel":{"type":"string","description":"The 08 §7 money models a statement is split by (FEE_ONLY has no statement lines in this cut)","enum":["MERCHANT","AGENCY","PASS_THROUGH"]},"ConsoleStatementLine":{"type":"object","description":"One derived statement line (09 §7) — one order item, carrying its `{moneyModel, credentialSet, fxSnapshotId}` stamp. Which money columns are present follows the model: MERCHANT gross/net/margin; AGENCY gross/commission; PASS_THROUGH gross/platformFee. `provisional` lines (order PENDING_RECONCILIATION) are excluded from totals.\n","required":["orderRef","domain","supplier","credentialSet","moneyModel","fxSnapshotId","bookedAt","state","provisional","gross"],"properties":{"orderRef":{"type":"string"},"itemRef":{"type":"string"},"domain":{"type":"string"},"supplier":{"type":"string"},"credentialSet":{"type":"string"},"moneyModel":{"$ref":"#/components/schemas/ConsoleMoneyModel"},"fxSnapshotId":{"type":"string"},"bookedAt":{"type":"string","format":"date-time"},"state":{"type":"string","description":"The order's FSM state at dataThrough"},"provisional":{"type":"boolean"},"gross":{"$ref":"#/components/schemas/Money"},"net":{"$ref":"#/components/schemas/Money"},"margin":{"$ref":"#/components/schemas/Money"},"commission":{"$ref":"#/components/schemas/Money"},"platformFee":{"$ref":"#/components/schemas/Money"}}},"ConsoleStatementTotals":{"type":"object","description":"Server-computed over the NON-provisional lines of the whole period (not the page)","required":["lines","provisionalLines","gross"],"properties":{"lines":{"type":"integer"},"provisionalLines":{"type":"integer"},"gross":{"$ref":"#/components/schemas/Money"},"net":{"$ref":"#/components/schemas/Money"},"margin":{"$ref":"#/components/schemas/Money"},"commission":{"$ref":"#/components/schemas/Money"},"platformFee":{"$ref":"#/components/schemas/Money"}}},"ConsoleStatement":{"type":"object","required":["model","period","currency","derived","dataThrough","lines","totals","page"],"properties":{"model":{"$ref":"#/components/schemas/ConsoleMoneyModel"},"period":{"type":"string","description":"YYYY-MM"},"currency":{"type":"string"},"derived":{"type":"boolean","description":"Always true in this cut (OD-411) — a view over orders + payments, not a ledger read"},"dataThrough":{"type":"string","format":"date-time"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleStatementLine"}},"totals":{"$ref":"#/components/schemas/ConsoleStatementTotals"},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleLedgerExportRequest":{"type":"object","required":["period","format"],"properties":{"period":{"type":"string","pattern":"^[0-9]{4}-(0[1-9]|1[0-2])$"},"model":{"$ref":"#/components/schemas/ConsoleMoneyModel"},"format":{"type":"string","enum":["CSV"]}}},"ConsoleJobError":{"type":"object","required":["code","message","traceId"],"properties":{"code":{"type":"string","description":"An ApiErrorResponse.code, or EXPORT_TOO_LARGE when the export exceeded the blob cap (BE-4 !63)"},"message":{"type":"string"},"traceId":{"type":"string"}}},"ConsoleJobResult":{"type":"object","required":["downloadUrl","contentType","rows","expiresAt"],"properties":{"downloadUrl":{"type":"string","description":"As served (BE-4 !63): {base}/admin/v1/jobs/{jobId}/download — NOT a bearer link and it carries no signature or expiry. Call it (GET, finance:export) for a fresh signed ConsoleJobDownload (≤ 15 minutes, never past expiresAt); the signed link itself is never stored. expiresAt is the artifact's own TTL (24 h after SUCCEEDED).\n"},"contentType":{"type":"string"},"rows":{"type":"integer"},"expiresAt":{"type":"string","format":"date-time"}}},"ConsoleJob":{"type":"object","description":"An asynchronous console job (ledger export today); poll GET /admin/v1/jobs/{jobId}","required":["jobId","kind","state","createdAt","updatedAt"],"properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["LEDGER_EXPORT"]},"state":{"type":"string","enum":["QUEUED","RUNNING","SUCCEEDED","FAILED"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"progressPct":{"type":"integer","minimum":0,"maximum":100},"result":{"$ref":"#/components/schemas/ConsoleJobResult","description":"Present once SUCCEEDED"},"error":{"$ref":"#/components/schemas/ConsoleJobError","description":"Present once FAILED"}}},"ConsoleRuleSetVersion":{"type":"object","description":"One row of the version history (FEAT-013 §5, §7) — read the body with GET /admin/v1/rules/versions/{version}","required":["version","state","publishedAt","publishedBy","ruleCount"],"properties":{"version":{"type":"integer","minimum":1},"state":{"type":"string","enum":["PUBLISHED","RETIRED"]},"publishedAt":{"type":["string","null"],"format":"date-time","description":"null on the platform-seeded version 1, which was never actually published"},"publishedBy":{"type":"string"},"note":{"type":"string"},"simulationId":{"type":["string","null"],"description":"null on a rollback (OD-408) and on the seeded version 1"},"ruleCount":{"type":"integer"},"rolledBackFrom":{"type":["integer","null"],"description":"Set when this version was minted by POST …/{version}:rollback — the version it replaced"}}},"ConsoleRuleSetVersionList":{"type":"object","required":["current","versions","page"],"properties":{"current":{"type":"integer","description":"The PUBLISHED version number"},"versions":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleRuleSetVersion"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleRuleSetRollbackRequest":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500,"description":"Publish-class reason string (09 §12); becomes the new version's note prefix"}}},"ConsoleWebhookDeliveryOutcome":{"type":"string","description":"DELIVERED 2xx · RETRYING on the 12 §A17 ladder · DEAD_LETTERED ladder exhausted (replayable at GET /v1/events) · REPLAYED re-sent by :replay","enum":["DELIVERED","RETRYING","DEAD_LETTERED","REPLAYED"]},"ConsoleWebhookDelivery":{"type":"object","description":"One delivery attempt (09 §10; API-0009 §7) — status, attempt, response code, latency, payload hash","required":["deliveryId","webhookId","eventId","type","attemptedAt","attempt","httpStatus","latencyMs","outcome","payloadHash"],"properties":{"deliveryId":{"type":"string"},"webhookId":{"type":"string"},"eventId":{"type":"string"},"type":{"type":"string","description":"The event type delivered"},"attemptedAt":{"type":"string","format":"date-time"},"attempt":{"type":"integer","minimum":1,"description":"Position on the retry ladder for this event (12 §A17); a replay starts at 1"},"httpStatus":{"type":["integer","null"],"description":"null when the endpoint never answered (timeout, connection refused) or the attempt is still queued"},"latencyMs":{"type":["integer","null"]},"outcome":{"$ref":"#/components/schemas/ConsoleWebhookDeliveryOutcome"},"responseSnippet":{"type":["string","null"],"maxLength":512,"description":"First bytes of the receiver's response body, redacted (13 §6)"},"payloadHash":{"type":"string","description":"sha256 of the signed body — what the receiver can match its own log against"},"nextAttemptAt":{"type":["string","null"],"format":"date-time"},"replayOf":{"type":["string","null"],"description":"The deliveryId this attempt replays, on a REPLAYED row"}}},"ConsoleWebhookTestRequest":{"description":"Which event to send as the test.","type":"object","properties":{"type":{"type":"string","default":"webhook.test","description":"webhook.test, or one of the subscription's event types to deliver a fixture sample of (data carries test true)"}}},"ConsoleUsagePoint":{"type":"object","required":["at","requests","rateLimited"],"properties":{"at":{"type":"string","format":"date-time","description":"Bucket start"},"requests":{"type":"integer"},"rateLimited":{"type":"integer","description":"Requests answered 429 RATE_LIMITED in the bucket"},"attribution":{"type":"array","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7, §11, AC-034-11; when aikonic.keys.agent-mint.enabled is on, else absent). The bucket's requests and 429s split by Attribution (principal, agent, key); the rows sum to the bucket. A bucket is an aggregate, so attribution is a breakdown here, not one Attribution.\n","items":{"allOf":[{"$ref":"#/components/schemas/Attribution"},{"type":"object","required":["requests","rateLimited"],"properties":{"requests":{"type":"integer"},"rateLimited":{"type":"integer"}}}]}}}},"ConsoleUsageClass":{"type":"object","description":"One 12 §A4 endpoint class with its limits and its series","required":["class","sustainedRps","burst","points"],"properties":{"class":{"type":"string","enum":["search","money","reference","account"]},"sustainedRps":{"type":"integer"},"burst":{"type":"integer"},"peakRps":{"type":"number","description":"Since 1.58.0. The busiest one-minute bucket of the window, as requests per second (requests in that minute / 60, one decimal), every key together — the class's headroom against `sustainedRps`. A burst shorter than a minute is averaged into its minute.\n"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleUsagePoint"}}}},"ConsoleQuota":{"type":"object","description":"A monthly quota (12 §A4 \"Monthly quotas (searches, orders)\") and where it stands","required":["metric","used","limit","resetsAt"],"properties":{"metric":{"type":"string","enum":["searches","orders"]},"used":{"type":"integer"},"limit":{"type":["integer","null"],"description":"null = unmetered on this tenant's plan"},"resetsAt":{"type":"string","format":"date-time"}}},"ConsoleUsage":{"type":"object","required":["granularity","window","dataThrough","classes","quotas"],"properties":{"granularity":{"type":"string","enum":["hour","day"]},"window":{"$ref":"#/components/schemas/ConsoleTimeWindow"},"dataThrough":{"type":"string","format":"date-time"},"classes":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleUsageClass"}},"quotas":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleQuota"}},"summary":{"$ref":"#/components/schemas/ConsoleUsageSummary"},"previous":{"$ref":"#/components/schemas/ConsoleUsageSummary","description":"The equal window immediately before this one. Absent when that window reaches past the meter's retention (35 days): a comparison with nothing is not a comparison.\n"},"latencySeries":{"type":"array","description":"The p95 latency per bucket (`granularity`); a bucket with no latency data is absent","items":{"$ref":"#/components/schemas/ConsoleUsageLatencyPoint"}},"statuses":{"type":"array","description":"Requests per HTTP status code over the window, ascending by status","items":{"$ref":"#/components/schemas/ConsoleUsageStatus"}},"endpoints":{"type":"array","description":"The ten busiest route templates over the window, busiest first","maxItems":10,"items":{"$ref":"#/components/schemas/ConsoleUsageEndpoint"}},"keys":{"type":"array","description":"The ten busiest callers over the window (API keys, console sessions, MCP grants), busiest first","maxItems":10,"items":{"$ref":"#/components/schemas/ConsoleUsageKey"}},"rateLimitEvents":{"type":"array","description":"The window's 429s grouped by minute, key and route, newest first (at most 25)","maxItems":25,"items":{"$ref":"#/components/schemas/ConsoleRateLimitEvent"}},"weekHours":{"type":"array","description":"The seven days before `window.to`, by hour, every class and key together — whatever the window and granularity. An hour with no traffic is absent.\n","items":{"$ref":"#/components/schemas/ConsoleUsagePoint"}},"funnel":{"$ref":"#/components/schemas/ConsoleUsageFunnel"}}},"ConsoleUsageSummary":{"type":"object","description":"One window's totals (since 1.58.0). The latency figures are absent when the window has no request with latency data; `p95LatencyMs` is the upper bound of the latency bucket the 95th percentile falls in, `meanLatencyMs` and `maxLatencyMs` are exact.\n","required":["requests","rateLimited","serverErrors"],"properties":{"requests":{"type":"integer"},"rateLimited":{"type":"integer","description":"Requests answered 429"},"serverErrors":{"type":"integer","description":"Requests answered 5xx"},"meanLatencyMs":{"type":"integer"},"p95LatencyMs":{"type":"integer"},"maxLatencyMs":{"type":"integer"}}},"ConsoleUsageLatencyPoint":{"type":"object","required":["at","p95Ms"],"properties":{"at":{"type":"string","format":"date-time","description":"Bucket start"},"p95Ms":{"type":"integer","description":"Upper bound of the latency bucket the bucket's 95th percentile falls in"}}},"ConsoleUsageStatus":{"type":"object","required":["status","requests"],"properties":{"status":{"type":"integer","description":"The HTTP status the platform answered"},"requests":{"type":"integer"}}},"ConsoleUsageEndpoint":{"type":"object","description":"One route template (`POST /v1/search`, `GET /v1/orders/{orderRef}`) over the window. A request that matched no template is not listed.\n","required":["method","path","class","requests","rateLimited","serverErrors","points"],"properties":{"method":{"type":"string"},"path":{"type":"string","description":"The route template, as the contract names it"},"class":{"type":"string","enum":["search","money","reference","account"]},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"},"meanLatencyMs":{"type":"integer"},"points":{"type":"array","description":"The route's own series, bucketed by `granularity`","items":{"$ref":"#/components/schemas/ConsoleUsagePoint"}}}},"ConsoleUsageKey":{"type":"object","description":"One caller over the window. `keyId` is the key's `ApiKeyMetadata.keyPrefix` (as GET /v1/keys lists it) for `source: api_key`; `session` and `mcp-grant` for the other two sources.\n","required":["keyId","source","requests","rateLimited","serverErrors"],"properties":{"keyId":{"type":"string"},"source":{"type":"string","enum":["api_key","session","mcp_grant"]},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"},"lastSeenAt":{"type":"string","format":"date-time","description":"Start of the last minute in the window this caller made a request"}}},"ConsoleRateLimitEvent":{"type":"object","description":"The 429s of one minute, one key and one route. `method` and `path` are absent for 429s metered before 1.58.0, which carry no route.\n","required":["at","keyId","class","count"],"properties":{"at":{"type":"string","format":"date-time","description":"Minute start"},"keyId":{"type":"string"},"class":{"type":"string","enum":["search","money","reference","account"]},"method":{"type":"string"},"path":{"type":"string"},"count":{"type":"integer"}}},"ConsoleUsageFunnel":{"type":"object","description":"The shopping funnel over the window: 2xx answers of POST /v1/search, /v1/detail (and /v1/flights/returns), /v1/reprice, /v1/hold and /v1/orders. Searches over orders is the look-to-book ratio.\n","required":["searches","details","reprices","holds","orders"],"properties":{"searches":{"type":"integer"},"details":{"type":"integer"},"reprices":{"type":"integer"},"holds":{"type":"integer"},"orders":{"type":"integer"}}},"ConsoleUsageDetailPoint":{"type":"object","required":["at","requests","rateLimited","serverErrors"],"properties":{"at":{"type":"string","format":"date-time","description":"Bucket start"},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"},"meanLatencyMs":{"type":"integer","description":"Absent for a status code, and for a bucket with no requests"}}},"ConsoleUsageClassCount":{"type":"object","required":["class","requests","rateLimited","serverErrors"],"properties":{"class":{"type":"string","enum":["search","money","reference","account"]},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"}}},"ConsoleUsageDetail":{"type":"object","description":"One dimension over a usage window (since 1.59.0). `byKey` is present for every dimension but key; `byEndpoint` and `byStatus` for key and class; `byClass` for key and status; `rateLimitEvents` for every dimension but status. Lists are busiest first, at most 50 (events: 25, newest first).\n","required":["dimension","id","granularity","window","dataThrough","summary","points"],"properties":{"dimension":{"type":"string","enum":["endpoint","key","class","status"]},"id":{"type":"string"},"granularity":{"type":"string","enum":["hour","day"]},"window":{"$ref":"#/components/schemas/ConsoleTimeWindow"},"dataThrough":{"type":"string","format":"date-time"},"summary":{"$ref":"#/components/schemas/ConsoleUsageSummary"},"previous":{"$ref":"#/components/schemas/ConsoleUsageSummary"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleUsageDetailPoint"}},"byKey":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/ConsoleUsageKey"}},"byEndpoint":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/ConsoleUsageDetailEndpoint"}},"byStatus":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleUsageStatus"}},"byClass":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleUsageClassCount"}},"rateLimitEvents":{"type":"array","maxItems":25,"items":{"$ref":"#/components/schemas/ConsoleRateLimitEvent"}}}},"ConsoleUsageDetailEndpoint":{"type":"object","description":"One route template within a detail; no series of its own","required":["method","path","class","requests","rateLimited","serverErrors"],"properties":{"method":{"type":"string"},"path":{"type":"string"},"class":{"type":"string","enum":["search","money","reference","account"]},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"},"meanLatencyMs":{"type":"integer"}}},"ConsoleUsageRow":{"type":"object","description":"One row of GET /admin/v1/usage/rows. `id` is what GET /admin/v1/usage/detail takes for the row's dimension (an event's id is `at|keyId|METHOD path`, not a detail id). Endpoint rows carry method, path, class, meanLatencyMs and lastSeenAt; key rows keyId, source and lastSeenAt; event rows at, keyId, class and (when metered with a route) method and path, with `requests` and `rateLimited` both the burst's 429 count.\n","required":["id","requests","rateLimited","serverErrors"],"properties":{"id":{"type":"string"},"method":{"type":"string"},"path":{"type":"string"},"class":{"type":"string","enum":["search","money","reference","account"]},"keyId":{"type":"string"},"source":{"type":"string","enum":["api_key","session","mcp_grant"]},"at":{"type":"string","format":"date-time"},"requests":{"type":"integer"},"rateLimited":{"type":"integer"},"serverErrors":{"type":"integer"},"meanLatencyMs":{"type":"integer"},"lastSeenAt":{"type":"string","format":"date-time"}}},"ConsoleUsageRows":{"type":"object","required":["dimension","window","total","rows"],"properties":{"dimension":{"type":"string","enum":["endpoint","key","event"]},"window":{"$ref":"#/components/schemas/ConsoleTimeWindow"},"total":{"type":"integer","description":"Rows matching `q` and `class`, over every page"},"classCounts":{"type":"object","description":"Rows matching `q` per class, before the class filter (endpoint and event)","properties":{"search":{"type":"integer"},"money":{"type":"integer"},"reference":{"type":"integer"},"account":{"type":"integer"}}},"rows":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ConsoleUsageRow"}},"nextCursor":{"type":"string","description":"Present when another page follows"}}},"ConsoleColor":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","description":"#RRGGBB"},"ConsoleBrandingLogo":{"type":"object","description":"https URLs the platform serves; every field optional so a brand can start with a mark only","properties":{"lightUrl":{"type":"string","format":"uri"},"darkUrl":{"type":"string","format":"uri"},"markUrl":{"type":"string","format":"uri"}}},"ConsoleBrandingPalette":{"type":"object","description":"The six theme tokens the shell maps to CSS variables (OD-406); the console's contrast validator runs on them before apply","required":["primary","onPrimary","accent","background","surface","text"],"properties":{"primary":{"$ref":"#/components/schemas/ConsoleColor"},"onPrimary":{"$ref":"#/components/schemas/ConsoleColor"},"accent":{"$ref":"#/components/schemas/ConsoleColor"},"background":{"$ref":"#/components/schemas/ConsoleColor"},"surface":{"$ref":"#/components/schemas/ConsoleColor"},"text":{"$ref":"#/components/schemas/ConsoleColor"}}},"ConsoleBrandingTypography":{"type":"object","properties":{"fontFamily":{"type":"string","description":"A font the platform ships; open string"}}},"ConsoleBrandingSupport":{"type":"object","properties":{"email":{"type":"string","format":"email"},"url":{"type":"string","format":"uri"}}},"ConsoleBrandingUpdate":{"type":"object","description":"PUT body — the whole brand; `version` is the version being replaced (optimistic concurrency)","required":["version","displayName","palette"],"properties":{"version":{"type":"integer","minimum":1},"displayName":{"type":"string","maxLength":80},"shortName":{"type":"string","maxLength":24},"logo":{"$ref":"#/components/schemas/ConsoleBrandingLogo"},"faviconUrl":{"type":"string","format":"uri"},"palette":{"$ref":"#/components/schemas/ConsoleBrandingPalette"},"typography":{"$ref":"#/components/schemas/ConsoleBrandingTypography"},"support":{"$ref":"#/components/schemas/ConsoleBrandingSupport"},"defaultLocale":{"type":"string","description":"BCP-47; the tenant default in OD-405's locale resolution (/v1/me → tenant default → Accept-Language)"}}},"ConsoleBranding":{"type":"object","description":"The site's brand as data (07 §6, 09 §9; OD-406); version increments on every PUT","required":["siteId","version","displayName","palette","updatedAt","updatedBy"],"properties":{"siteId":{"type":"string"},"version":{"type":"integer","minimum":1},"displayName":{"type":"string"},"shortName":{"type":"string"},"logo":{"$ref":"#/components/schemas/ConsoleBrandingLogo"},"faviconUrl":{"type":"string","format":"uri"},"palette":{"$ref":"#/components/schemas/ConsoleBrandingPalette"},"typography":{"$ref":"#/components/schemas/ConsoleBrandingTypography"},"support":{"$ref":"#/components/schemas/ConsoleBrandingSupport"},"defaultLocale":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"ConsoleSupplierDirectory":{"type":"object","description":"GET /admin/v1/suppliers — every supplier adapter the platform serves, as one tenant sees it (09 §8; v1.8.0)","required":["suppliers"],"properties":{"suppliers":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleSupplier"}}}},"ConsoleSupplier":{"type":"object","description":"One supplier adapter of the directory (09 §8; v1.8.0): what its registry descriptor declares, and the credential sets this tenant may use for it. verbs and flags are open strings (the adapter SDK's SupplierVerb and CapabilityFlag names) so a new one is not a breaking change.\n","required":["supplierId","domains","adapterVersion","status","verbs","flags","pricingCurrencies","credentialSets"],"properties":{"supplierId":{"type":"string","description":"Supplier registry code, SUP-<DOMAIN>-<NAME> (08 §2)"},"domains":{"type":"array","items":{"type":"string"},"description":"Canonical domain codes the adapter serves (02 §1)"},"adapterVersion":{"type":"string"},"status":{"type":"string","enum":["AVAILABLE","DEGRADED","NO_CREDENTIAL"],"description":"The tenant's standing on this supplier, from its usable credential sets: AVAILABLE — a set serves traffic (ACTIVE, EXPIRING); DEGRADED — none serves traffic and one is out only transiently (RATE_LIMITED, FAILED_HEALTH); NO_CREDENTIAL — no set this tenant may use is in service.\n"},"verbs":{"type":"array","items":{"type":"string"},"description":"The supplier verbs the descriptor declares (SEARCH, DETAIL, REPRICE, HOLD, ORDER, POST_BOOKING, RETRIEVE)"},"flags":{"type":"array","items":{"type":"string"},"description":"The capability flags the descriptor declares (SUPPORTS_HOLD, RETURNS_CANCEL_POLICY, CATALOG_SYNC…)"},"pricingCurrencies":{"type":"array","items":{"type":"string"},"description":"ISO-4217 currencies the supplier prices in; empty = it prices in the requested currency"},"credentialSets":{"type":"array","description":"The sets this tenant may use for the supplier — its own first, then the platform's","items":{"$ref":"#/components/schemas/ConsoleSupplierCredentialSet"}}}},"ConsoleSupplierCredentialSet":{"type":"object","description":"A credential set as the directory names it — identity, owner, FSM status and cert state; never a secret (08 §3, §6)","required":["credentialSetId","owner","environment","status","certification"],"properties":{"credentialSetId":{"type":"string","description":"MONDEE for the platform's set; the tenant's own set id otherwise (GET /admin/v1/credential-sets)"},"owner":{"type":"string","enum":["PLATFORM","TENANT"]},"environment":{"type":"string","enum":["sandbox","live"]},"status":{"$ref":"#/components/schemas/ConsoleCredentialSetStatus"},"certification":{"$ref":"#/components/schemas/ConsoleCertificationSummary"}}},"FareSourceStrategy":{"type":"string","description":"08 §4 strategies — PREFER_OWN calls the own key and ONLY the own key; BLEND calls both (doubles metered spend)","enum":["PREFER_OWN","OWN_ONLY","MONDEE_ONLY","BLEND"]},"FareSourceFallback":{"type":"string","description":"08 §4 — what to do when the own key cannot serve; MONDEE falls back to the platform key, tagged and warned","enum":["MONDEE","NONE"]},"FareSourceBlendDedupe":{"type":"string","description":"08 §4 — BLEND only; which offer survives when both keys return the same product","enum":["CHEAPEST_WINS","TAG_BOTH"]},"FareSourceRule":{"type":"object","description":"A per-supplier override of the product-level strategy and fallback (08 §4 perSupplier)","required":["strategy","fallback"],"properties":{"strategy":{"$ref":"#/components/schemas/FareSourceStrategy"},"fallback":{"$ref":"#/components/schemas/FareSourceFallback"}}},"FareSourceConfig":{"type":"object","description":"08 §4's resolution object for one (tenant, domain), with its version (v1.8.0; OD-528)","required":["domain","strategy","fallback","blendDedupe","perSupplier","version","updatedAt","updatedBy"],"properties":{"domain":{"type":"string","description":"Canonical domain code (02 §1)"},"strategy":{"$ref":"#/components/schemas/FareSourceStrategy"},"fallback":{"$ref":"#/components/schemas/FareSourceFallback"},"blendDedupe":{"$ref":"#/components/schemas/FareSourceBlendDedupe"},"perSupplier":{"type":"object","description":"Overrides keyed by supplier registry code","additionalProperties":{"$ref":"#/components/schemas/FareSourceRule"}},"version":{"type":"integer","minimum":0,"description":"0 = nothing saved, the platform default; every PUT adds one"},"updatedAt":{"type":["string","null"],"format":"date-time"},"updatedBy":{"type":["string","null"],"description":"The user id of the last writer; null on the platform default"}}},"FareSourceConfigUpdate":{"type":"object","description":"PUT /admin/v1/fare-source-config/{domain} — the whole resolution object, a reason and the version it was read at","required":["strategy","fallback","blendDedupe","perSupplier","reason","expectedVersion"],"properties":{"strategy":{"$ref":"#/components/schemas/FareSourceStrategy"},"fallback":{"$ref":"#/components/schemas/FareSourceFallback"},"blendDedupe":{"$ref":"#/components/schemas/FareSourceBlendDedupe"},"perSupplier":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FareSourceRule"}},"reason":{"type":"string","minLength":5,"maxLength":500,"description":"Why — recorded in the audit row (09 §8)"},"expectedVersion":{"type":"integer","minimum":0,"description":"The version the editor read; a different current version is 409 CONFLICT VERSION_MOVED"}}},"ConsoleCredentialSetStatus":{"type":"string","description":"The credential FSM of 08 §3","enum":["PENDING_VALIDATION","CERTIFYING","ACTIVE","RATE_LIMITED","EXPIRING","REVOKED","FAILED_HEALTH"]},"ConsoleCredentialSecretDisplay":{"type":"object","description":"What the console may ever see of a stored secret (08 §6, 09 §8) — last-4 + fingerprint","required":["kind","last4","fingerprint"],"properties":{"kind":{"type":"string","enum":["API_KEY","BASIC","OAUTH_CLIENT","CERTIFICATE"]},"last4":{"type":"string"},"fingerprint":{"type":"string","description":"sha256:<12 hex> of the secret material"}}},"ConsoleCredentialRotation":{"type":"object","description":"08 §3 `rotation {policy, lastRotated, expiresAt}`","required":["policy"],"properties":{"policy":{"type":"string","enum":["MANUAL","AUTO"]},"lastRotatedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Drives the T-30/7/1d expiry alerts (08 §6) and the EXPIRING status"}}},"ConsoleCredentialHealth":{"type":"object","description":"08 §3 `health {lastProbe, errorRate}`","properties":{"lastProbeAt":{"type":["string","null"],"format":"date-time"},"errorRateBps":{"type":["integer","null"]}}},"ConsoleCredentialLimits":{"type":"object","description":"08 §3 `limits {rps, l2bCap}` — the supplier contract's, as configured","properties":{"rps":{"type":["integer","null"]},"l2bCap":{"type":["integer","null"]}}},"ConsoleCertificationSummary":{"type":"object","description":"The latest cert run in brief; the report card is GET …/{credentialSetId}/certification","required":["state"],"properties":{"state":{"type":"string","enum":["NOT_STARTED","RUNNING","PASSED","FAILED"]},"runId":{"type":["string","null"]},"completedAt":{"type":["string","null"],"format":"date-time"}}},"ConsoleCredentialSet":{"type":"object","description":"08 §3's credential_set as the tenant sees it — vaultRef replaced by the secret display","required":["credentialSetId","supplier","products","environment","status","secret","rotation","health","limits","certification","createdAt","createdBy"],"properties":{"credentialSetId":{"type":"string"},"supplier":{"type":"string","description":"Supplier registry code (08 §2)"},"label":{"type":"string"},"products":{"type":"array","items":{"type":"string"},"description":"Canonical domain codes this set is used for (02 §1)"},"environment":{"type":"string","enum":["sandbox","live"]},"status":{"$ref":"#/components/schemas/ConsoleCredentialSetStatus"},"secret":{"$ref":"#/components/schemas/ConsoleCredentialSecretDisplay"},"rotation":{"$ref":"#/components/schemas/ConsoleCredentialRotation"},"health":{"$ref":"#/components/schemas/ConsoleCredentialHealth"},"limits":{"$ref":"#/components/schemas/ConsoleCredentialLimits"},"certification":{"$ref":"#/components/schemas/ConsoleCertificationSummary"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"ConsoleCredentialSetList":{"type":"object","required":["credentialSets","page"],"properties":{"credentialSets":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleCredentialSet"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleCredentialSecret":{"type":"object","description":"The secret material, WRITE-ONLY (08 §6): accepted here, vaulted, never echoed. Exactly the fields the `kind` needs — API_KEY: apiKey; BASIC: username + password; OAUTH_CLIENT: clientId + clientSecret; CERTIFICATE: certificatePem + privateKeyPem. v1.50.0: with any kind, an optional webhookSecret (see the property).\n","required":["kind"],"writeOnly":true,"properties":{"kind":{"type":"string","enum":["API_KEY","BASIC","OAUTH_CLIENT","CERTIFICATE"]},"webhookSecret":{"type":"string","format":"password","maxLength":512,"description":"v1.50.0 (PLAT-FINSUP): optional, with any kind. The signing secret of the webhook endpoint the tenant registered at the supplier for this set, so the platform can verify the supplier's notifications about the tenant's own account. For a payment source (supplier SUP-PAY-EXAMPLE) it is the Example Pay endpoint's signing secret (whsec_…) for POST /v1/payments/psp-webhook/{provider}/{sourceRef}, sourceRef being this set's id. Without it the set still serves payments; its webhooks answer 401 REJECTED_UNVERIFIABLE. Write-only like the rest: never echoed, not even as a last4.\n"},"apiKey":{"type":"string","format":"password"},"username":{"type":"string"},"password":{"type":"string","format":"password"},"clientId":{"type":"string"},"clientSecret":{"type":"string","format":"password"},"certificatePem":{"type":"string"},"privateKeyPem":{"type":"string","format":"password"}}},"ConsoleCredentialSetCreate":{"type":"object","description":"Step 1 of 08 §8 — enter a BYOC key","required":["supplier","products","environment","secret"],"properties":{"supplier":{"type":"string"},"label":{"type":"string","maxLength":80},"products":{"type":"array","minItems":1,"items":{"type":"string"}},"environment":{"type":"string","enum":["sandbox","live"],"description":"A live set is accepted only for a tenant whose activation is approved (D-60) — else 403 PERMISSION_DENIED, TENANT_NOT_LIVE"},"secret":{"$ref":"#/components/schemas/ConsoleCredentialSecret"},"rotation":{"$ref":"#/components/schemas/ConsoleCredentialRotation"}}},"ConsoleCertificationCheck":{"type":"object","description":"One of the nine 08 §8 step 2 checks","required":["check","status"],"properties":{"check":{"type":"string","enum":["AUTH","SEARCH_SHAPE","REPRICE_HONESTY","HOLD_EXPIRY","BOOK_SANDBOX","CANCEL_PENALTY","RETRIEVE_BY_REFERENCE","IDEMPOTENCY_ECHO","ERROR_TAXONOMY"]},"status":{"type":"string","enum":["PENDING","RUNNING","PASSED","FAILED","SKIPPED"]},"durationMs":{"type":["integer","null"]},"message":{"type":["string","null"],"description":"Why it failed or was skipped; never a secret or a supplier net amount"},"captureId":{"type":["string","null"],"description":"The redacted supplier-call capture (13 §2) the console links to on failure"}}},"ConsoleCanaryWindow":{"type":"object","description":"08 §8 step 3 — the first N live bookings flagged for ops review before full ACTIVE","required":["required","completed","flagged"],"properties":{"required":{"type":"integer"},"completed":{"type":"integer"},"flagged":{"type":"integer"}}},"ConsoleCertificationReport":{"type":"object","description":"The report card (08 §8, 09 §8)","required":["credentialSetId","state","checks"],"properties":{"credentialSetId":{"type":"string"},"runId":{"type":["string","null"]},"state":{"type":"string","enum":["NOT_STARTED","RUNNING","PASSED","FAILED"]},"startedAt":{"type":["string","null"],"format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"checks":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleCertificationCheck"}},"canary":{"$ref":"#/components/schemas/ConsoleCanaryWindow","description":"Present once the run PASSED and the canary window opened"}}},"ConsoleAgent":{"type":"object","description":"FEAT-019 §7 `agent_registry` as the tenant sees it, plus its pause state (D-54 kill switch)","required":["agentId","name","useCase","journey","autonomyLevel","state","owner","openProposals","pausedAt","pausedBy"],"properties":{"agentId":{"type":"string"},"name":{"type":"string"},"useCase":{"type":"string","description":"The FEAT-019 §5 catalogue id (AG-01 … AG-30)"},"journey":{"type":"string","enum":["J1","J2","J3","J4","J5"]},"autonomyLevel":{"type":"string","enum":["L0","L1","L2","L3"],"description":"Production rung on the FEAT-019 §5 ladder; money-touching agents are L1 forever"},"state":{"type":"string","enum":["ACTIVE","PAUSED"]},"owner":{"type":"string"},"policyDocRef":{"type":"string"},"lastActiveAt":{"type":["string","null"],"format":"date-time"},"openProposals":{"type":"integer"},"pausedAt":{"type":["string","null"],"format":"date-time"},"pausedBy":{"type":["string","null"]}}},"ConsoleAgentList":{"type":"object","required":["agents"],"properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAgent"}}}},"ConsoleAgentPauseRequest":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500}}},"AgentProposalEventData":{"type":"object","description":"The data of every agent.proposal.* webhook event","required":["proposalId","state"],"properties":{"proposalId":{"type":"string"},"state":{"$ref":"#/components/schemas/ConsoleAgentProposalState"},"approvalAction":{"type":"string","description":"The D-50 action of a money proposal (orders.create | orders.cancel)"},"resourceRef":{"type":"string","description":"The proposal's resource.ref"},"orderRef":{"type":"string","description":"Present on agent.proposal.approved — the order the executed verb created, cancelled or amended"}}},"McpGrantEventData":{"type":"object","description":"The data of every mcp.grant.* webhook event (FEAT-031 §8). Only TENANT-realm (console) grants are delivered, to the grant's own tenant. No token, authorization code, user code, refresh hash, session id or IP address is ever carried, and no person's name.\n","required":["grantId","toolset","state","clientId","clientVerified"],"properties":{"grantId":{"type":"string","description":"The grant's id; on mcp.grant.denied the grant request's requestId, as no grant exists"},"toolset":{"type":"string","description":"console (the only tenant-delivered toolset); open string"},"state":{"type":"string","description":"ACTIVE (approved) | DENIED | REVOKED | EXPIRED; open string"},"reason":{"type":"string","description":"On mcp.grant.revoked only: USER | ADMIN | OPERATOR | SESSION_ENDED | REFRESH_REUSE | USER_DISABLED | TENANT_SUSPENDED; open string, a receiver tolerates a new value\n"},"clientId":{"type":"string","description":"The MCP client's id (its client metadata document URL, or a registered id)"},"clientVerified":{"type":"boolean"},"operations":{"type":"array","items":{"type":"string"},"description":"The read x-mcp names ticked at consent; absent on mcp.grant.denied"},"writeOperations":{"type":"array","items":{"type":"string"},"description":"The console write tools ticked at consent with a step-up; empty without write consent, absent on mcp.grant.denied"},"expiresAt":{"type":"string","format":"date-time","description":"The grant's expiry; absent on mcp.grant.denied"}}},"McpSettingsEventData":{"type":"object","description":"The data of mcp.settings.changed: one changed tenant MCP setting (McpSettings). No token, code, hash, session id or address is ever carried.\n","required":["tool","from","to"],"properties":{"tool":{"type":"string","description":"console for the toolset switch (toolsets.console), otherwise the write tool's x-mcp name in writesEnabled; open string"},"from":{"type":"string","enum":["ON","OFF"]},"to":{"type":"string","enum":["ON","OFF"]}}},"ConsoleAiAssistSearchIntentRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","minLength":1,"maxLength":2000,"description":"The agent's natural-language request. A traveler name typed here is redacted by the model gateway."}}},"ConsoleAiAssistSearchIntent":{"type":"object","description":"Only grounded slots; a slot that is not grounded has NO key (never null) and is asked back in clarifications instead. Prefill the search form with what is present.\n","properties":{"destinationRef":{"type":"string","description":"A destination the tenant's golden index retrieved for this query"},"checkIn":{"type":"string","format":"date","description":"ISO date that appears in the query; always before checkOut, and present only together with it"},"checkOut":{"type":"string","format":"date","description":"ISO date that appears in the query; always after checkIn, and present only together with it"},"adults":{"type":"integer","minimum":1,"maximum":9,"description":"An adult count the query states (digits or a number word)"}}},"ConsoleAiAssistSearchIntentResponse":{"type":"object","required":["intent","clarifications","blockedToolCalls","degraded","injectionSuspected"],"properties":{"intent":{"$ref":"#/components/schemas/ConsoleAiAssistSearchIntent"},"clarifications":{"type":"array","items":{"type":"string"},"description":"One question per slot that could not be grounded, in the order destination, dates, adults"},"blockedToolCalls":{"type":"integer","minimum":0,"description":"Commit-class tool calls the read-only allow-list dropped on this call (an assist never commits)"},"degraded":{"type":"boolean","description":"The model gateway reported this call as degraded; the answer may be less complete"},"injectionSuspected":{"type":"boolean","description":"The model gateway flagged a suspected prompt injection in this call's data; treat the answer with care"}}},"ConsoleAiAssistRequiredInfoItem":{"type":"object","required":["field"],"properties":{"field":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z][A-Za-z0-9_]*$","description":"A traveler field name the offer's requiredInfo demands (an identifier)"},"required":{"type":"boolean","default":false,"description":"true when the field must be present on every traveler"}}},"ConsoleAiAssistAutofillDraft":{"type":"object","properties":{"travelers":{"type":"array","maxItems":20,"default":[],"items":{"type":"object","description":"One traveler of the draft, as a flat map: at most 40 fields, each named by an identifier and holding a string of at most 200 characters, an integer or null. A list, an object, a boolean or a longer string is 400 VALIDATION; the value type is left open here only so every generated client can carry the map. The plane reads four keys itself: type (ADT, CHD or INF, or null; any other value is 400 VALIDATION; it drives the age-band check INF 0-1, CHD 2-11, ADT 12-120), age (integer years), and nationality and documentCountry (compared with each other). Every other key is matched against requiredInfo by name.\n","maxProperties":40,"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9_]{0,63}$"},"additionalProperties":true}}}},"ConsoleAiAssistAutofillRequest":{"type":"object","properties":{"requiredInfo":{"type":"array","maxItems":50,"default":[],"items":{"$ref":"#/components/schemas/ConsoleAiAssistRequiredInfoItem"}},"draft":{"$ref":"#/components/schemas/ConsoleAiAssistAutofillDraft"}}},"ConsoleAiAssistAutofillFinding":{"type":"object","required":["code","traveler","field"],"properties":{"code":{"type":"string","enum":["MISSING","NATIONALITY_DOCUMENT_MISMATCH","AGE_BAND_MISMATCH"],"description":"MISSING — a required field is absent or blank; NATIONALITY_DOCUMENT_MISMATCH — nationality differs from documentCountry (field documentCountry); AGE_BAND_MISMATCH — age is outside the type's band (field age)\n"},"traveler":{"type":"integer","minimum":0,"description":"0-based index into draft.travelers"},"field":{"type":"string","description":"The traveler field the finding is about"}}},"ConsoleAiAssistAutofillSuggestion":{"type":"object","description":"A field worth confirming with the traveler. Names the field only; the value is never echoed.","required":["traveler","field"],"properties":{"traveler":{"type":"integer","minimum":0,"description":"0-based index into draft.travelers"},"field":{"type":"string"}}},"ConsoleAiAssistAutofillResponse":{"type":"object","required":["findings","suggestions","droppedSuggestions","degraded","injectionSuspected"],"properties":{"findings":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAiAssistAutofillFinding"}},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAiAssistAutofillSuggestion"}},"droppedSuggestions":{"type":"integer","minimum":0,"description":"Model suggestions dropped because their value was not already in the draft (the no-invention filter)"},"degraded":{"type":"boolean","description":"The model gateway reported this call as degraded; the answer may be less complete"},"injectionSuspected":{"type":"boolean","description":"The model gateway flagged a suspected prompt injection in this call's data; treat the answer with care"}}},"ConsoleAiAssistErrorExplainRequest":{"type":"object","required":["code"],"properties":{"code":{"type":"string","minLength":3,"maxLength":64,"pattern":"^[A-Z][A-Z0-9_]{2,63}$","description":"A platform error code (an ApiErrorResponse.code value); any other shape is 400 VALIDATION"},"trace":{"type":"object","additionalProperties":true,"default":{},"description":"The error's context, e.g. its traceId and details. At most 16 KiB serialized and 6 levels deep; redacted before the model sees it."}}},"ConsoleAiAssistErrorExplainResponse":{"type":"object","required":["code","narrative","blockedToolCalls","degraded","injectionSuspected"],"properties":{"code":{"type":"string","description":"The request's code, echoed"},"narrative":{"type":"string","description":"One or two plain sentences and the next step, redacted on the way out"},"blockedToolCalls":{"type":"integer","minimum":0,"description":"Commit-class tool calls the read-only allow-list dropped on this call (an assist never commits)"},"degraded":{"type":"boolean","description":"The model gateway reported this call as degraded; the answer may be less complete"},"injectionSuspected":{"type":"boolean","description":"The model gateway flagged a suspected prompt injection in this call's data; treat the answer with care"}}},"ConsoleAgentProposalState":{"type":"string","description":"FEAT-019 §7 — an OPEN proposal expires (default 48h, capped at the idempotency window, DR-43) so stale drafts never execute. EXECUTING — approve claimed the proposal and its verb is running. EXECUTION_UNKNOWN — terminal for the API: the verb's outcome is unknown, the D-50 grant stays spent, and the desk resolves it; it is never retried by the platform. Approve claims the proposal (OPEN to EXECUTING) and runs its verb once. A definitive failure proven NOT dispatched (PRICE_CHANGED, OFFER_EXPIRED, a 402 PAYMENT_FAILED before dispatch, a supplier refusal that was not sent) returns the proposal to OPEN, gives the D-50 grant back and answers the verb's own error. Any failure that may have dispatched makes it EXECUTION_UNKNOWN: terminal for the API, the grant stays spent, the verb is never retried, and the desk resolves it. A stale EXECUTING (a crash mid-run) is likewise resolved by the desk to EXECUTION_UNKNOWN. Expiry applies only to OPEN: an EXECUTING or EXECUTION_UNKNOWN proposal never expires. Any failure after the claim that is not proven undispatched answers 500 INTERNAL details.reason EXECUTION_UNKNOWN, never a retryable INTERNAL. Strict enum clients must tolerate unknown values: EXECUTING and EXECUTION_UNKNOWN were added in v1.4.0.\n","enum":["OPEN","EXECUTING","APPROVED","REJECTED","EXPIRED","EXECUTION_UNKNOWN"]},"ConsoleEvidenceRef":{"type":"object","description":"One piece of a proposal's evidence (FEAT-019 §6) — a thing the console can link to","required":["kind","ref"],"properties":{"kind":{"type":"string","enum":["TRACE","ORDER","EVENT","CAPTURE","DOCUMENT","URL"]},"ref":{"type":"string"},"label":{"type":"string"}}},"ConsoleProposedChange":{"type":"object","description":"One line of the proposal's diff — a path into the resource and its before/after, rendered as text","required":["path"],"properties":{"path":{"type":"string"},"before":{"type":["string","null"]},"after":{"type":["string","null"]}}},"ConsoleProjectedEffect":{"type":"object","description":"What executing the proposal does (FEAT-019 §6 \"projected effect\"); money as Money","required":["description"],"properties":{"description":{"type":"string"},"amountDelta":{"$ref":"#/components/schemas/Money"}}},"ConsoleAgentProposal":{"type":"object","description":"FEAT-019 §6's uniform L1 approval envelope — one reviewable object: evidence, the change, projected effect, undo plan (§7 `agent_proposal`). `requiresApprovalToken` is true for every money-touching proposal (12 §B2.5 — no tenant override) and `amount` is then the sum the D-50 token must be bound to. actor.kind is always ai here.\n","required":["proposalId","agentId","type","title","summary","state","actor","resource","requiresApprovalToken","evidence","changes","createdAt","expiresAt"],"properties":{"proposalId":{"type":"string"},"agent":{"description":"connect-v1 1.64.0 (PROPOSAL, FEAT-044 §5.3): the agent identity the platform resolved for the request, grant or record (operator, agent and version, proofs, tier). Absent when the caller carried no agent identity.\n","allOf":[{"$ref":"#/components/schemas/AgentStamp"}]},"agentId":{"type":"string"},"type":{"type":"string","description":"Open string — REBOOKING (AG-22), ADOPTION_BRIEF (AG-24), RECONCILIATION (AG-13), CONFIG_CHANGE …"},"title":{"type":"string"},"summary":{"type":"string"},"state":{"$ref":"#/components/schemas/ConsoleAgentProposalState"},"actor":{"$ref":"#/components/schemas/ConsoleActor"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"requiresApprovalToken":{"type":"boolean"},"approvalAction":{"type":"string","description":"Present when requiresApprovalToken — the D-50 action the approving grant is minted for (POST /v1/approvals action); the grant's resourceRef is resource.ref, its amount `amount` (OD-457). A stored proposal is never payments.* or amend.commit: those are refused at submit (ACTION_NOT_EXECUTABLE, AMEND_COMMIT_NOT_EXECUTABLE, DR-44)\n"},"amount":{"$ref":"#/components/schemas/Money","description":"Present when requiresApprovalToken — the sum the token is bound to"},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleEvidenceRef"}},"changes":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleProposedChange"}},"projectedEffect":{"$ref":"#/components/schemas/ConsoleProjectedEffect"},"undoPlan":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"decidedBy":{"type":["string","null"]},"decidedAt":{"type":["string","null"],"format":"date-time"},"decisionReason":{"type":["string","null"]},"approvalTokenId":{"type":"string","description":"Set once APPROVED with a token — the id audit rows quote"},"executionSummary":{"$ref":"#/components/schemas/ConsoleProposalExecutionSummary"},"executionResult":{"$ref":"#/components/schemas/ConsoleProposalExecutionResult"}}},"ConsoleProposalExecutionSummary":{"type":"object","description":"What approving the proposal will actually execute, derived by the server at read time from the stored execution and never from agent text — this, not title or summary, is what the human approves. Present on a money proposal that carries an execution.\n","required":["action","resourceRef","serverAmount","travelerNames","paymentRef","holdRef"],"properties":{"action":{"type":"string","enum":["orders.create","orders.cancel","amend.commit"]},"resourceRef":{"type":"string","description":"The hold, order or reference the verb acts on"},"serverAmount":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The server-derived total of the verb; null when the reference expired"},"travelerNames":{"type":"array","items":{"type":"string"}},"paymentRef":{"type":["string","null"]},"holdRef":{"type":["string","null"]}}},"ConsoleProposalExecutionResult":{"type":"object","description":"What approving a money proposal did (DR-38): approve executed the verb named by the submission's `execution` exactly once and this is its outcome. Present once the proposal is APPROVED and executed; absent otherwise.\n","required":["orderRef","orderStatus","executedAt"],"properties":{"orderRef":{"type":"string","description":"The order the executed verb created, cancelled or amended (MO-…)"},"orderStatus":{"type":"string","description":"The order's status after the verb ran"},"executedAt":{"type":"string","format":"date-time"}}},"ConsoleAgentProposalList":{"type":"object","required":["proposals","page"],"properties":{"proposals":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleAgentProposal"}},"page":{"$ref":"#/components/schemas/Page"}}},"ConsoleAgentProposalCreate":{"type":"object","description":"Ask an agent to draft a proposal for a resource","required":["agentId","type","resource"],"properties":{"agentId":{"type":"string"},"type":{"type":"string"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"instructions":{"type":"string","maxLength":2000,"description":"Free text for the human's constraints; passed to the agent as data, never as instructions to the platform"}}},"ConsoleAgentProposalDecision":{"type":"object","description":"Approve body — the D-50 token when the proposal moves money","properties":{"approvalToken":{"type":"string","description":"Required when requiresApprovalToken is true (400 VALIDATION otherwise)"},"reason":{"type":"string","maxLength":500}}},"ConsoleAgentProposalRejection":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":4,"maxLength":500}}},"ConsoleApprovalRequest":{"type":"object","description":"What a D-50 token is bound to (12 §B2.4 step 2, 15 §1.4) — scope + action + resource + amount","required":["scope","action","resource"],"properties":{"scope":{"type":"string","description":"The 15 §1.3 permission the action needs (orders:amend, orders:cancel …); the minter must hold it"},"action":{"type":"string","description":"agent-proposal.approve | reconcile.fail | … — the closed-registry verb the token unlocks"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"resourceRef":{"type":"string","description":"AS BUILT (IMP-P2-041, AdminApprovalController.MintApprovalRequest): the backend reads a flat resourceRef and ignores `resource`. Send both through the reconciliation window: resourceRef = resource.ref. The backend is to read `resource` (BE-7); until then a request without resourceRef is 400 VALIDATION there.\n"},"amount":{"$ref":"#/components/schemas/Money"}}},"ConsoleApprovalToken":{"type":"object","description":"The minted token — value returned once (12 §B2.4); approvalTokenId is what audit rows carry","required":["approvalTokenId","token","scope","action","resource","singleUse","mintedBy","mintedAt","expiresAt"],"properties":{"approvalTokenId":{"type":"string"},"token":{"type":"string","description":"Present it in the approving request's body; never logged, never re-returned"},"scope":{"type":"string"},"action":{"type":"string"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"amount":{"$ref":"#/components/schemas/Money"},"singleUse":{"type":"boolean","enum":[true]},"mintedBy":{"type":"string"},"mintedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","description":"~2 minutes after mintedAt"},"approvalToken":{"type":"string","description":"AS BUILT alias of `token` (IMP-P2-041, MintApprovalResponse.approvalToken): the backend answers {approvalToken, expiresAt, binding} today and is to answer this schema's required fields (BE-7); through the window a client reads token, else approvalToken. The as-built value is prefixed apc_; the /v1/approvals grant is apt_ — the two are not interchangeable.\n"},"binding":{"type":"object","description":"AS BUILT alias of {scope, action, resource.ref, amount} (MintApprovalResponse.Binding)","properties":{"scope":{"type":"string"},"action":{"type":"string"},"resourceRef":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"}}}}},"ApprovalGrantRequest":{"type":"object","description":"What a D-50 grant is bound to (12 §B2.4 step 2, 15 §1.4, API-0010 §5): one action on one resource for one exact amount. The binding the redeeming verb checks, in the form the platform's verifier stores it: flat resourceRef, Money amount.\n","required":["action","resourceRef","amount"],"properties":{"action":{"type":"string","description":"The verb the grant unlocks. Open string; values today orders.create | orders.cancel | amend.commit | payments.confirm | payments.capture | payments.void | payments.refund (API-0010 §2). An unknown value is 400 VALIDATION.\n"},"resourceRef":{"type":"string","description":"The resource the verb acts on: the paymentId for payments.*, the repriceRef for orders.create, the orderRef for orders.cancel and amend.commit\n"},"amount":{"$ref":"#/components/schemas/Money"},"scope":{"type":"string","description":"Optional. The 15 §1.3 permission the action needs; derived from action when absent. A scope that does not match the action is 400 VALIDATION.\n"}}},"ApprovalGrant":{"type":"object","description":"A minted D-50 grant (12 §B2.4): single use, about 2 minutes, bound to tenant + action + resourceRef + amount. approvalToken is opaque — its prefix is not a contract — and is returned exactly once; present it as X-Approval-Token on the bound verb. approvalTokenId is the audit stamp the redeemed action carries (13 §1.1).\n","required":["approvalToken","approvalTokenId","action","resourceRef","amount","singleUse","expiresAt"],"properties":{"approvalToken":{"type":"string"},"approvalTokenId":{"type":"string"},"action":{"type":"string"},"resourceRef":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"},"scope":{"type":"string"},"singleUse":{"type":"boolean","enum":[true]},"mintedBy":{"type":"string","description":"The approving person's user id"},"mintedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","description":"About 2 minutes after mintedAt"}}},"AgentProposalSubmission":{"type":"object","description":"What an agent submits (FEAT-019 §6 \"evidence, diff, projected effect, undo plan\"). The platform adds proposalId, state OPEN, actor (from the credential), createdAt and expiresAt and answers the stored ConsoleAgentProposal — the one envelope the console and the API share.\n","required":["agentId","type","title","summary","resource","requiresApprovalToken","evidence","changes"],"properties":{"agentId":{"type":"string","description":"Must be the agent the credential belongs to (403 PERMISSION_DENIED AGENT_MISMATCH otherwise)"},"type":{"type":"string","description":"Open string — REBOOKING (AG-22), ADOPTION_BRIEF (AG-24), RECONCILIATION (AG-13), CONFIG_CHANGE"},"title":{"type":"string","maxLength":200},"summary":{"type":"string","maxLength":2000,"description":"Shown to the approver as data; amounts are rendered from `amount`, never from this text (FEAT-019 §11)"},"resource":{"$ref":"#/components/schemas/ConsoleResourceRef"},"requiresApprovalToken":{"type":"boolean","description":"True for every money-touching or irreversible proposal (12 §B2.5, FEAT-019 §5 — no tenant override). The PLATFORM decides the stored value from the proposal's type, its changes and its approvalAction, never from this field alone: a proposal the platform classes as money-touching is stored true whatever is sent, and one submitted with false is refused 400 VALIDATION details.field requiresApprovalToken. The submitted value can only raise the flag, never lower it\n"},"approvalAction":{"type":"string","description":"The D-50 action executing the proposal commits — the `action` its grant is minted for (ApprovalGrantRequest.action: orders.create | orders.cancel | amend.commit | payments.confirm | payments.capture | payments.void | payments.refund). Required whenever the proposal moves money: a money-touching proposal (by type, changes or requiresApprovalToken) without it is 400 VALIDATION details.field approvalAction. The grant's resourceRef is resource.ref and its amount is `amount`. payments.* is a valid grant action but is not executable by a proposal: a money proposal whose approvalAction is payments.* is 400 VALIDATION details.reason ACTION_NOT_EXECUTABLE; amend.commit is 400 AMEND_COMMIT_NOT_EXECUTABLE (DR-44). Only orders.create and orders.cancel are accepted\n"},"amount":{"$ref":"#/components/schemas/Money","description":"Required with approvalAction — the exact sum the D-50 grant will be bound to"},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleEvidenceRef"}},"changes":{"type":"array","items":{"$ref":"#/components/schemas/ConsoleProposedChange"}},"projectedEffect":{"$ref":"#/components/schemas/ConsoleProjectedEffect"},"undoPlan":{"type":["string","null"]},"expiresAt":{"type":"string","format":"date-time","description":"Optional, earlier than the default; absent = 48h after submission, and never later than the idempotency window (FEAT-019 §7)"},"execution":{"$ref":"#/components/schemas/AgentProposalExecution"}}},"AgentProposalExecution":{"type":"object","description":"What approve runs (DR-38). Required when the proposal is money-touching (requiresApprovalToken true): a money proposal without it is 400 VALIDATION details.field execution. execution.action must equal approvalAction and execution.currency must equal amount.currency, else 400 VALIDATION details.reason EXECUTION_INCONSISTENT. Never echoed back; the outcome is the proposal's `executionResult`.\n","required":["action","domain","currency","intent"],"properties":{"action":{"type":"string","enum":["orders.create","orders.cancel","amend.commit"]},"domain":{"type":"string","description":"The travel domain the verb runs in (flights, hotels, …)"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 currency of the verb's money; must equal the proposal's amount.currency"},"intent":{"type":"object","additionalProperties":true,"description":"The verb's message.intent exactly as POST /v1/orders, the cancel or the amend call takes it"}}},"PlaceRef":{"type":"object","description":"One way to name a place, used by every intent that takes a location (ground waypoints, experiences pickup, lounge and airport-service stations, weather). Send exactly one of placeId (an API-0008 `mondee:place:…` id or a Google place id passed through verbatim), iata (an airport or rail station code) or geo; text is free text the platform resolves and is never a booking input on its own (a transfer supplier).\n","properties":{"timeZone":{"type":"string","x-status":"planned","description":"IANA time zone of the place (e.g. America/Sao_Paulo) — planned; lookups carry it as attributes.timezone today"},"placeId":{"description":"A place id from the lookup calls, or a map provider's place id passed through as it is.","type":"string","maxLength":256},"iata":{"description":"An airport or rail station code.","type":"string","pattern":"^[A-Z]{3}$"},"geo":{"description":"A point on the map.","$ref":"#/components/schemas/GeoPoint"},"text":{"description":"Free text the platform resolves; never enough on its own to book.","type":"string","maxLength":512},"countryCode":{"description":"The country, as its ISO 3166-1 alpha-2 code, to narrow the match.","type":"string","pattern":"^[A-Z]{2}$"}},"example":{"placeId":"mondee:place:gru-airport","iata":"GRU"}},"GeoPoint":{"description":"A point on the map, in decimal degrees.","type":"object","required":["lat","lon"],"properties":{"lat":{"description":"Latitude, -90 to 90.","type":"number","minimum":-90,"maximum":90},"lon":{"description":"Longitude, -180 to 180.","type":"number","minimum":-180,"maximum":180}},"example":{"lat":-23.4356,"lon":-46.4731}},"GeoArea":{"type":"object","description":"A geographic search area (HTL/VR, IMP-P2-075): a centre with a radius, or a bounding box. Send one form.\n","properties":{"center":{"$ref":"#/components/schemas/GeoPoint"},"radiusKm":{"type":"number","minimum":0.1,"maximum":200},"bbox":{"type":"object","description":"South-west and north-east corners","required":["sw","ne"],"properties":{"sw":{"$ref":"#/components/schemas/GeoPoint"},"ne":{"$ref":"#/components/schemas/GeoPoint"}}}},"example":{"center":{"lat":25.7617,"lon":-80.1918},"radiusKm":5}},"PartySpec":{"type":"object","description":"Who travels, as a search input (every vertical). Child ages are mandatory wherever a child is counted: suppliers price by age, never by count (Example Rentals, EXAMPLEHOTELS4, iOL X).\n","required":["adults"],"properties":{"adults":{"type":"integer","minimum":1,"maximum":50},"childAges":{"type":"array","description":"One entry per child, age at travel date","items":{"type":"integer","minimum":0,"maximum":17}},"infants":{"type":"integer","minimum":0,"description":"Lap infants (FLT); elsewhere count them in childAges"},"pets":{"type":"integer","minimum":0,"description":"HTL/VR only — pets travelling (Example Rentals `pets`); absent = 0"}},"example":{"adults":2,"childAges":[4,9]}},"FlightLinkRef":{"type":"object","description":"The flight an item is keyed to (CANONICAL-MODEL §3.3 `flightLink`, extended by the ancillary proposal §2.1 with terminals and a parent link). Used by ground waypoints, airport services, lounges and insurance.\n","required":["carrier","flightNumber","date"],"properties":{"carrier":{"type":"string","pattern":"^[A-Z0-9]{2,3}$"},"flightNumber":{"type":"string","maxLength":6},"date":{"type":"string","format":"date","description":"Departure date, airport-local"},"origin":{"type":"string","pattern":"^[A-Z]{3}$"},"destination":{"type":"string","pattern":"^[A-Z]{3}$"},"departureTerminal":{"type":"string","maxLength":16},"arrivalTerminal":{"type":"string","maxLength":16},"parent":{"type":"object","description":"The platform order the flight was booked on, when it was","properties":{"orderRef":{"type":"string"},"itemId":{"type":"string"}}}},"example":{"carrier":"LA","flightNumber":"8084","date":"2026-11-10","origin":"GRU","destination":"MIA","arrivalTerminal":"N","parent":{"orderRef":"MO-8KD-2FQ-9XT","itemId":"OI-1"}}},"DateRange":{"description":"A range of days, both ends inclusive.","type":"object","required":["from","to"],"properties":{"from":{"description":"First day (inclusive).","type":"string","format":"date"},"to":{"description":"Last day (inclusive).","type":"string","format":"date"}},"example":{"from":"2026-11-10","to":"2026-11-14"}},"LuggageSpec":{"type":"object","description":"Luggage the vehicle must take (XFR/TXI/CAR); a filter on vehicle class, never a supplier field","properties":{"checked":{"type":"integer","minimum":0},"carryOn":{"type":"integer","minimum":0},"oversized":{"type":"integer","minimum":0,"description":"Skis, golf bags, bicycles — items that need a larger class"}},"example":{"checked":3,"carryOn":2}},"GroundWaypointSpec":{"type":"object","description":"One stop of a ground journey as a search input (CANONICAL-MODEL §3.3 waypoints[])","required":["type","location"],"properties":{"type":{"type":"string","enum":["PICKUP","DROPOFF","STOP"]},"location":{"$ref":"#/components/schemas/PlaceRef"},"scheduledAt":{"type":"string","format":"date-time","description":"Local time with offset at the waypoint; required on PICKUP unless flightLink is given"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"}},"example":{"type":"PICKUP","location":{"iata":"GRU"},"flightLink":{"carrier":"LA","flightNumber":"8084","date":"2026-11-10"}}},"FlightSearchIntent":{"type":"object","description":"A flight search: the slices (origin, destination and date of each leg), the travellers and the cabin. Optional filters (carriers, alliance, refundableOnly, changeableOnly, excludeBasicEconomy, a per-slice cabin and fareTypes) are sent to each supplier; the platform does not re-filter the answer. A filter that is absent, false or empty changes nothing. A supplier that cannot apply a filter it was given searches without it (never narrower than asked) and adds a context.warnings[] entry SEARCH_FILTER_UNSUPPORTED (details.filter names the field, details.reason says why). A supplier that does not read a filter at all ignores it without a warning, so a client that must be sure checks each offer (extension.air segments, fare and conditions).\n","required":["slices","travelers"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"slices":{"type":"array","minItems":1,"maxItems":6,"items":{"type":"object","required":["origin","destination","date"],"properties":{"origin":{"type":"string","pattern":"^[A-Z]{3}$"},"destination":{"type":"string","pattern":"^[A-Z]{3}$"},"date":{"type":"string","format":"date"},"cabin":{"type":"string","description":"This slice's cabin (v1.37.0), overriding the journey cabin for this slice only. Same values as the journey cabin: ECONOMY | PREMIUM_ECONOMY | BUSINESS | FIRST; open string. Absent means the journey cabin. A value the supplier does not know is searched with the journey cabin and a SEARCH_FILTER_UNSUPPORTED warning.\n"}}}},"travelers":{"type":"array","minItems":1,"items":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["ADT","CHD","INF"]},"age":{"type":"integer","minimum":0,"maximum":17}}}},"cabin":{"type":"string","description":"ECONOMY | PREMIUM_ECONOMY | BUSINESS | FIRST; open string"},"fareTypes":{"type":"array","description":"PUBLISHED | PRIVATE | NDC | LCC — narrows sourcing (D-30); open strings. v1.37.0 adds COMMISSIONABLE (published fares that pay the agency a commission) and states how a supplier applies the list: it narrows only when it has a filter for EVERY value given, and a list it cannot apply whole is searched without a fare-type filter plus a SEARCH_FILTER_UNSUPPORTED warning (details.filter fareTypes).\n","items":{"type":"string"}},"directOnly":{"type":"boolean"},"carriers":{"type":"array","minItems":1,"maxItems":20,"uniqueItems":true,"description":"Only these airlines (v1.37.0): IATA two-character airline designators, an include list. Which carrier of an itinerary the supplier matches (marketing, operating or validating) is the supplier's rule; a flight supplier states what is known. Given with alliance, a supplier that takes only one of the two applies alliance and warns SEARCH_FILTER_UNSUPPORTED for carriers.\n","items":{"type":"string","pattern":"^[A-Z0-9]{2}$"}},"alliance":{"type":"string","description":"Only airlines of this alliance (v1.37.0). STAR_ALLIANCE | SKYTEAM | ONEWORLD; x-extensible-enum, a client tolerates a value it does not know. A value the supplier has no code for is searched without an alliance filter and a SEARCH_FILTER_UNSUPPORTED warning.\n","x-extensible-enum":["STAR_ALLIANCE","SKYTEAM","ONEWORLD"]},"refundableOnly":{"type":"boolean","description":"Only fares the supplier marks refundable (v1.37.0). The refund penalty, if any, is in the offer's conditions; refundable is the supplier's flag, not a promise of a full refund.\n"},"changeableOnly":{"type":"boolean","description":"Only fares the supplier marks changeable (v1.37.0); change fees are in the offer's conditions."},"excludeBasicEconomy":{"type":"boolean","description":"Leave out the supplier's basic-economy fares (v1.37.0). What counts as basic economy is the supplier's classification.\n"},"journeyMode":{"type":"string","default":"COMPLETE","description":"How the offers of a round-trip or multi-city search are built (v1.27.0). COMPLETE, the default and the behaviour before v1.27.0: every offer carries every requested slice, priced as one journey. OUTBOUND_FIRST: each offer is an OUTBOUND journey (slice 0) whose later slices are still to be chosen. It carries extension.air.journey with complete false; its segments and slices hold only the chosen slices; its price is the round-trip price of that outbound as the supplier states it (a \"from\" price, after the tenant's pricing rules like any Offer.price) with pricing.indicative true. POST /v1/flights/returns with the offerRef lists the options for the next slice (AirReturnOption[]); the same route with returnOption chooses one and narrows the offer under the same offerRef, and once every slice is chosen it reprices and books like a COMPLETE offer. On a one-way search the value changes nothing. A supplier that cannot build outbound-first offers answers COMPLETE offers instead, with a context.warnings[] entry JOURNEY_MODE_UNSUPPORTED (supplier set), so a client reads extension.air.journey (absent means complete) on each offer rather than trusting the request. COMPLETE | OUTBOUND_FIRST; x-extensible-enum, a client tolerates a value it does not know.\n","x-extensible-enum":["COMPLETE","OUTBOUND_FIRST"]}},"example":{"slices":[{"origin":"GRU","destination":"MIA","date":"2026-11-10"}],"travelers":[{"type":"ADT"},{"type":"CHD","age":9}],"cabin":"ECONOMY"}},"StaySearchIntent":{"type":"object","description":"A stay search for hotels, vacation rentals and day use: a destination (a place, a geo area or a list of property ids), the dates, the rooms with child ages, and pets. More than one entry in `rooms[]` is refused for a whole-property vacation rental (400 VALIDATION).\n","required":["stay","rooms"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"area":{"$ref":"#/components/schemas/GeoArea"},"canonicalIds":{"type":"array","maxItems":200,"description":"Search these properties only (FEAT-014 canonical ids)","items":{"type":"string"}},"stay":{"type":"object","required":["checkIn","checkOut"],"properties":{"checkIn":{"type":"string","format":"date"},"checkOut":{"type":"string","format":"date","description":"Equal to checkIn is legal for DAYUSE (02 §3.2)"}}},"rooms":{"type":"array","minItems":1,"maxItems":8,"items":{"$ref":"#/components/schemas/PartySpec"}},"unitType":{"type":"string","description":"ROOM | WHOLE_PROPERTY | APARTMENT | VILLA — VR searches say WHOLE_PROPERTY; open string"},"quantity":{"type":"integer","minimum":1,"description":"Units wanted of a whole-property type (a vacation-rental supplier)"},"bookingModels":{"type":"array","description":"INSTANT | REQUEST_TO_BOOK — absent = both (a vacation-rental supplier)","items":{"type":"string"}},"petsAllowed":{"type":"boolean","description":"Only pet-friendly units (rooms[].pets says how many)"},"boards":{"type":"array","items":{"type":"string"}}},"example":{"destination":{"placeId":"mondee:place:miami"},"stay":{"checkIn":"2026-11-10","checkOut":"2026-11-14"},"rooms":[{"adults":2,"childAges":[4],"pets":1}],"unitType":"WHOLE_PROPERTY","petsAllowed":true}},"GroundSearchIntent":{"type":"object","description":"A ground transport search for transfers, taxis and cars: waypoints with a place id or coordinates, the party with child ages, luggage, an optional flight link on the pickup (the supplier may derive the pickup time from it), the service type and duration for hourly hire, and a round trip as two legs. A car search sends pickup and dropoff waypoints with a driver age band instead of a party.\n","required":["waypoints"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"waypoints":{"type":"array","minItems":2,"maxItems":6,"items":{"$ref":"#/components/schemas/GroundWaypointSpec"}},"party":{"$ref":"#/components/schemas/PartySpec"},"luggage":{"$ref":"#/components/schemas/LuggageSpec"},"serviceType":{"type":"string","description":"TRANSFER | HOURLY | RIDE | RENTAL; open string (CANONICAL-MODEL §3.3)"},"durationHours":{"type":"integer","minimum":1,"description":"HOURLY hire length"},"roundTrip":{"type":"object","description":"A second leg back; the offer answers with legs[] (a transfer supplier)","properties":{"returnAt":{"type":"string","format":"date-time"},"returnFlightLink":{"$ref":"#/components/schemas/FlightLinkRef"}}},"shared":{"type":"string","description":"PRIVATE | SHARED | ANY — shuttle vs private car (a transfer supplier); absent = ANY"},"vehicleClasses":{"type":"array","items":{"type":"string"}},"driverAgeBand":{"type":"string","description":"CAR only — the renter's age band (ACRISS pricing)"},"childSeats":{"type":"array","description":"Child seats wanted, one entry per seat with the child's age","items":{"type":"object","properties":{"childAge":{"type":"integer"}}}}},"example":{"waypoints":[{"type":"PICKUP","location":{"iata":"GRU"},"flightLink":{"carrier":"LA","flightNumber":"8084","date":"2026-11-10"}},{"type":"DROPOFF","location":{"placeId":"ChIJ0WGkg4FEzpQRrlsz_whLqZs"}}],"party":{"adults":2,"childAges":[4]},"luggage":{"checked":3},"serviceType":"TRANSFER","shared":"PRIVATE"}},"BusSearchIntent":{"type":"object","description":"A bus search: one leg, city to city, on a departure date, with the party by fare category. A place is a PlaceRef: `text` (a city name, resolved by the supplier's own city lookup) or `cityId` (the supplier's own city id, sent as given). The search price is indicative (`Offer.pricing.indicative`): the supplier's cart adds fees, so POST /v1/reprice states the bookable total and sets `priceChanged` when it differs. A GroundSearchIntent (pickup and dropoff waypoints and the pickup's date) is accepted too. A bus search with no departureDate (or waypoints), or no origin or destination, is 400 VALIDATION naming the field. A past date, or a city the supplier does not know, answers no offers with a named warning (<SUPPLIER>_DATE_IN_PAST, <SUPPLIER>_PLACE_UNRESOLVED).\n","required":["origin","destination","departureDate"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"origin":{"$ref":"#/components/schemas/PlaceRef"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"departureDate":{"type":"string","format":"date","description":"The departure date, station-local"},"party":{"$ref":"#/components/schemas/BusPartySpec"}}},"CheckinSearchIntent":{"type":"object","description":"An automated online check-in search. Check-in is an ancillary product bought for a flight booked elsewhere, and fulfilled when the airline's check-in window opens. The search answers whether check-in can be automated for this carrier, flight and route and, when the supplier knows, when the window opens; it never searches flights. `flightLink` names the flight (origin and destination are required here); `departureTime` is the local departure time at the origin airport, sent to the supplier as given. The airline booking reference (PNR) and the travellers are order inputs (requiredInfo), not search inputs. No flightLink, origin, destination or departureTime is 400 VALIDATION naming the field.\n","required":["flightLink","departureTime"],"properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"departureTime":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","description":"Local departure time (HH:MM) at flightLink.origin"},"operatingCarrier":{"type":"string","minLength":2,"maxLength":3,"description":"The operating carrier's IATA code for a codeshare; defaults to flightLink.carrier"},"party":{"$ref":"#/components/schemas/PartySpec"}}},"GiftSearchIntent":{"type":"object","description":"A gift card search. A digital gift card is a brand's card for one face value, delivered as a hosted link and non-refundable. Every field narrows the brand catalogue and none is required. The offer currency is context.currency: a brand sells in one currency, and a brand in another currency is not offered (never relabelled). faceValue asks for one value (its currency must be context.currency); without it the platform offers the brand's fixed denominations, or a few values inside its open limits.\n","properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"brandIds":{"type":"array","maxItems":20,"items":{"type":"string"},"description":"Canonical ids of brands from a previous answer (an offer's canonicalId)"},"faceValue":{"$ref":"#/components/schemas/Money"},"country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country where the card is redeemed"},"category":{"type":"string","description":"A brand category code (for example beauty, food-and-drink, travel-and-leisure)"},"text":{"type":"string","maxLength":100,"description":"Free text matched against brand names"}}},"GiftExtension":{"type":"object","description":"GIFT block (v1.31.0; CANONICAL-MODEL §3.12, proposed; a gift-card supplier): the display facts of a gift-card offer and, on the order line, the issued card's own expiry. The card itself (the hosted link, a code, a PIN, a barcode) is never here: it is the order's VOUCHER document, read live. The discount and the cost a supplier charges the platform are never here either.\n","required":["brandName","faceValue"],"properties":{"brandName":{"type":"string"},"faceValue":{"$ref":"#/components/schemas/Money"},"countries":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$"}},"categories":{"type":"array","items":{"type":"string"}},"delivery":{"type":"string","enum":["HOSTED_LINK"],"description":"How the card reaches the holder; HOSTED_LINK is a link to the brand-approved card page"},"deliveredLater":{"type":"boolean","description":"True when the card arrives after the order is placed (an asynchronous issue)"},"expiryText":{"type":"string","description":"The brand's own expiry rule as text (for example \"1 Year\"); the issued card's date is cardExpiresAt"},"redemptionMethods":{"type":"array","items":{"type":"string"},"description":"Where the card is redeemed (for example Online, Instore)"},"restrictions":{"type":"array","items":{"type":"string"},"description":"The brand's usage restrictions, as text"},"termsUrl":{"type":"string","format":"uri"},"termsText":{"type":"string"},"logoUrl":{"type":"string","format":"uri"},"cardImageUrl":{"type":"string","format":"uri"},"cardExpiresAt":{"type":"string","format":"date-time","description":"Order line only — the issued card's expiry, from the supplier's answer"}}},"EsimSearchIntent":{"type":"object","description":"An eSIM data plan search: a plan for one country, a region or global coverage, fulfilled at once as an eSIM whose QR code is read from the order's tickets (GET /v1/orders/{orderRef}/tickets, one ticket per eSIM). Coverage is esim.country, else destination.countryCode; or esim.region; or esim.type GLOBAL. Not every supplier serves region and global plans. Validity is esim.minDays, else the inclusive span of dates; the number of eSIMs is esim.quantity, else party.adults plus party.childAges, else 1. Offers are priced in USD only: another context.currency answers no offers with the warning <SUPPLIER>_CURRENCY_NOT_SERVED, and a search naming no coverage answers no offers with <SUPPLIER>_NO_DESTINATION. Every field narrows the plan list; none is required.\n","properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"dates":{"$ref":"#/components/schemas/DateRange"},"party":{"$ref":"#/components/schemas/PartySpec"},"esim":{"type":"object","description":"The plan filter both eSIM suppliers read","properties":{"country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country where the eSIM is used"},"region":{"type":"string","maxLength":64,"description":"A regional plan listing, for example europe or asia (an eSIM supplier only)"},"type":{"type":"string","enum":["GLOBAL"],"description":"GLOBAL asks for global plans (an eSIM supplier only)"},"minDays":{"type":"integer","minimum":1,"maximum":365,"description":"The shortest plan validity in days; without it, the span of dates"},"minDataGb":{"type":"number","minimum":0,"description":"The smallest data allowance in GB; unlimited plans always match; 0 means no floor"},"quantity":{"type":"integer","minimum":1,"maximum":50,"description":"eSIMs in one order (an eSIM supplier serves up to 10)"}}}}},"ParkingSearchIntent":{"type":"object","description":"An airport or cruise-port parking search for one stay: where (an airport, or a cruise port the supplier reaches through its airport) and the entry and exit times. A time without an offset is the wall-clock time at the location, which is what a traveller reads on the lot's sign; a time with an offset is converted to the location's own zone. A supplier that books on a time grid (for example 15 minutes) widens the stay to the grid, entry earlier and exit later, and echoes the times it priced on every offer (ParkingExtension.entryAt, exitAt, intervalAdjusted). Changing the times is a new search, never a reprice. A port the supplier cannot reach answers no offers and the warning <SUPPLIER>_PORT_NOT_SERVED (details.field message.intent.location.portCode), and nothing is sent. Prices on search are indicative and may exclude taxes (extension.parking.taxesIncluded false); reprice states the firm, tax-inclusive total.\n","required":["location","entryAt","exitAt"],"properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"location":{"type":"object","description":"Exactly one of iata or portCode","properties":{"iata":{"type":"string","pattern":"^[A-Z]{3}$","description":"The airport the lots serve"},"portCode":{"type":"string","maxLength":16,"description":"A cruise port code (UN/LOCODE, for example USMIA). The supplier adapter maps it to the airport its supplier searches by; an unmapped port answers no offers and a warning\n"}}},"entryAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2})?([Zz]|[+-]\\d{2}:\\d{2})?$","description":"Entry time, local at the location (2027-06-01T09:00) or with an offset"},"exitAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2})?([Zz]|[+-]\\d{2}:\\d{2})?$","description":"Exit time, after entryAt; same forms as entryAt"},"filters":{"type":"object","description":"Narrow the lots; applied by the platform after the supplier answers","properties":{"parkingClasses":{"type":"array","items":{"type":"string","enum":["SELF_PARK","VALET"]}},"covered":{"type":"boolean","description":"True keeps covered lots only (garage, covered or indoor)"},"shuttle":{"type":"boolean","description":"True keeps lots with a shuttle to the airport or port"}}}},"example":{"location":{"iata":"LAX"},"entryAt":"2027-06-01T09:00","exitAt":"2027-06-03T18:00"}},"ParkingExtension":{"type":"object","description":"PARK block (v1.55.0; PLAT-PARK; a parking supplier): what a traveller needs to choose a lot and find it on the day. On an offer it describes the lot product the price is for; detail adds the lot's description, hours and amenities. On the order line it carries the supplier's confirmation number (the order's supplierLocator stays absent, as on every order line). The gate barcode is never here: it is the order's BARCODE document, format PNG (GET /v1/orders/{orderRef}/documents).\n","required":["facility","entryAt","exitAt"],"properties":{"facility":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"address":{"type":"string","description":"One display line"},"city":{"type":"string"},"countryCode":{"type":"string","pattern":"^[A-Z]{2}$"},"geo":{"$ref":"#/components/schemas/GeoPoint"},"timeZone":{"type":"string","description":"IANA zone of the lot, the zone of entryAt and exitAt"},"airport":{"type":"string","pattern":"^[A-Z]{3}$","description":"The airport the lot serves"},"distanceMiles":{"type":"number","minimum":0,"description":"Distance from the lot to the airport, in miles, as the supplier states it"},"phone":{"type":"string","description":"Detail only — the lot's phone, as the supplier states it"}}},"parkingType":{"type":"string","description":"The supplier's own name for the product (for example \"Covered Self Park\")"},"parkingClass":{"type":"string","enum":["SELF_PARK","VALET","OTHER"]},"covered":{"type":"boolean"},"shuttle":{"type":"object","properties":{"available":{"type":"boolean"},"frequency":{"type":"string"},"description":{"type":"string"}}},"rating":{"type":"object","properties":{"score":{"type":"number","minimum":0,"maximum":5},"count":{"type":"integer","minimum":0}}},"entryAt":{"type":"string","description":"The entry time priced, lot-local (yyyy-MM-ddTHH:mm:ss)"},"exitAt":{"type":"string","description":"The exit time priced, lot-local"},"intervalAdjusted":{"type":"boolean","description":"True when the supplier's time grid widened the requested stay"},"taxesIncluded":{"type":"boolean","description":"False when the offer's price excludes the lot's taxes (a parking supplier's search); reprice states the tax-inclusive total"},"accessInstructions":{"type":"string","description":"How to enter and leave the lot, as plain text"},"cancellationText":{"type":"string","description":"The supplier's cancellation terms, verbatim"},"description":{"type":"string","description":"Detail only — the lot's own description, plain text"},"amenities":{"type":"array","description":"Detail only","items":{"type":"string"}},"hours":{"type":"array","description":"Detail only — opening hours per weekday, lot-local","items":{"type":"object","properties":{"day":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}}},"confirmationNumber":{"type":"string","description":"Order line only — the supplier's reservation number, shown at the lot"}}},"CruiseSearchIntent":{"type":"object","description":"A cruise search: sailings departing inside a window, for one cabin's guests. Ids are lookup ids (`GET /v1/lookup/cruise-regions|ports|cruise-lines|ships|waterways|river-countries|river-cities`), never supplier ids. A departure port narrows the results for suppliers that cannot filter by it. More than one cabin is refused 501 NOT_IMPLEMENTED details.reason MULTI_CABIN_NOT_SUPPORTED. The search price is indicative (`Offer.pricing.indicative`): the cabin ladder of POST /v1/detail and the hold make it firm.\n","required":["departureWindow"],"properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"cruiseType":{"type":"string","enum":["OCEAN","RIVER"],"default":"OCEAN"},"departureWindow":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}}},"nights":{"type":"object","properties":{"min":{"type":"integer","minimum":1},"max":{"type":"integer","minimum":1}}},"regions":{"type":"array","items":{"type":"string"},"description":"cruise-regions lookup ids"},"departurePorts":{"type":"array","items":{"type":"string"},"description":"ports lookup ids"},"cruiseLines":{"type":"array","items":{"type":"string"},"description":"cruise-lines lookup ids"},"ships":{"type":"array","items":{"type":"string"},"description":"ships lookup ids"},"waterways":{"type":"array","items":{"type":"string"},"description":"waterways lookup ids (RIVER)"},"riverCountries":{"type":"array","items":{"type":"string"},"description":"river-countries lookup ids (RIVER)"},"riverCities":{"type":"array","items":{"type":"string"},"description":"river-cities lookup ids (RIVER)"},"party":{"type":"object","description":"One cabin's guests; absent means two adults","properties":{"adults":{"type":"integer","minimum":1},"childAges":{"type":"array","items":{"type":"integer","minimum":0}}}},"cabins":{"type":"integer","minimum":1,"default":1},"residencyAirport":{"type":"string","pattern":"^[A-Z]{3}$","description":"IATA code of the guests' residence, for lines that price by residency"}}},"AicontSearchIntent":{"type":"object","description":"An AI content search: the operations of an experience-content service (catalogue reads, scrapes, enrichment) over the unified routes. `operation` names one and `arguments` holds that operation's own arguments, which the service validates (400 VALIDATION). A catalogue read answers one offer per enriched service at that service's price (amount 0 when it is free), and such an offer is not orderable. An operation that spends answers one quote offer at its cost cap plus a fixed fee, priced in USD and provisional: reprice confirms the quote, order runs it, and an order answered PENDING settles on read at the real cost. A read-only operation that would scrape (find_experiences) is quoted the same way.\n","required":["operation"],"properties":{"operation":{"type":"string","maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"The operation's name: list_services and get_service (free catalogue reads), find_experiences (free unless it would scrape), run_city or run_agent (always quoted)\n"},"arguments":{"type":"object","additionalProperties":true,"description":"The operation's own arguments; absent or empty when it takes none"}}},"PackageSearchIntent":{"type":"object","description":"A package search for a pre-bundled trip: hotel and flight, hotel and bus, or a service package. `origin` and `destination` are PlaceRefs; a package-origins or package-destinations lookup id goes in `placeId`. An input a supplier cannot resolve skips that supplier with the warning SUPPLIER_INPUT_UNRESOLVED. More than one room is refused 501 NOT_IMPLEMENTED details.reason MULTI_ROOM_NOT_SUPPORTED. Prices are indicative until POST /v1/reprice; the date calendar is GET /v1/lookup/package-calendar, not resultShape CALENDAR.\n","required":["packageType","destination","departureDate","returnDate"],"properties":{"resultShape":{"type":"string","enum":["OFFERS"],"default":"OFFERS"},"packageType":{"type":"string","enum":["HOTEL_FLIGHT","HOTEL_BUS","SERVICE"]},"origin":{"$ref":"#/components/schemas/PlaceRef"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"departureDate":{"type":"string","format":"date"},"returnDate":{"type":"string","format":"date"},"party":{"type":"object","description":"One room's guests; absent means two adults","properties":{"adults":{"type":"integer","minimum":1},"childAges":{"type":"array","items":{"type":"integer","minimum":0}}}},"rooms":{"type":"integer","minimum":1,"default":1}}},"BusPartySpec":{"type":"object","description":"Who rides, by bus fare category (v1.15.0): adults, children (Example Bus `youth`) and seniors, each child and senior with an age. At least one rider; `adults` defaults to 1 when the party names nobody. The order is the booking's: travelers[] lists adults, then children, then seniors, one traveler per rider.\n","properties":{"adults":{"type":"integer","minimum":0,"maximum":20},"childAges":{"type":"array","items":{"type":"integer","minimum":0,"maximum":17}},"seniorAges":{"type":"array","items":{"type":"integer","minimum":55,"maximum":120}}}},"BusExtension":{"type":"object","description":"BUS block `bus` of ItemExtension on an order line (v1.15.0): the operator's booking reference, the one-PDF ticket bundle (a supplier-hosted link covering every ticket) and each ticket's id and reference. A ticket's own PDF link is not carried: the supplier names that file after the passenger. Absent while the purchase is settling or reconciling. The e-ticket document (GET /v1/orders/{orderRef}/documents) states the same reference and link.\n","properties":{"bookingReference":{"type":"string","maxLength":128},"ticketBundlePdf":{"type":"string","format":"uri"},"tickets":{"type":"array","items":{"type":"object","required":["ticketId"],"properties":{"ticketId":{"type":"string","maxLength":64},"reference":{"type":"string","maxLength":128}}}}},"example":{"bookingReference":"EXAMPLEBUS_BOOK_123","ticketBundlePdf":"https://tickets.example.test/8656999.pdf","tickets":[{"ticketId":"13429786","reference":"EXAMPLEBUS_BOOK_123"}]}},"ExperienceSearchIntent":{"type":"object","description":"An experience search for activities, attractions, tours, events and shows: where and when, the party, an optional pickup point (chosen before the dates where the supplier requires it), option choices that set the price, the guide language, and event filters (section, quantity).\n","required":["dates","party"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"productIds":{"type":"array","description":"Canonical product ids to search directly","items":{"type":"string"}},"dates":{"$ref":"#/components/schemas/DateRange"},"party":{"$ref":"#/components/schemas/PartySpec"},"pickup":{"type":"object","description":"The pickup point when options and dates depend on it","properties":{"mode":{"type":"string","description":"PICKUP | MEET_ON_LOCATION"},"point":{"$ref":"#/components/schemas/PlaceRef"}}},"options":{"type":"array","description":"Pre-price product configuration answers (ExperienceOption)","items":{"type":"object","required":["optionId","value"],"properties":{"optionId":{"type":"string"},"value":{"type":"string"}}}},"guideLanguage":{"type":"string","description":"BCP-47 tag; narrows timeslots to that language"},"event":{"type":"object","description":"EVT filters (an event-ticket supplier)","properties":{"eventId":{"type":"string"},"quantity":{"type":"integer","minimum":1},"section":{"type":"string"},"row":{"type":"string"},"priceRange":{"type":"object","properties":{"min":{"$ref":"#/components/schemas/Money"},"max":{"$ref":"#/components/schemas/Money"}}}}},"categories":{"type":"array","items":{"type":"string"}}},"example":{"destination":{"placeId":"mondee:place:rome"},"dates":{"from":"2026-11-12","to":"2026-11-12"},"party":{"adults":2,"childAges":[9]},"pickup":{"mode":"PICKUP","point":{"placeId":"mondee:place:rome-termini"}},"guideLanguage":"en"}},"InsuranceSearchIntent":{"type":"object","description":"A travel insurance search: the trip to insure and the insured people with their ages. The quote may come from a local rating table (firmness PLATFORM_EMULATED, basis LOCAL_TABLE).\n","required":["trip","insured"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"trip":{"type":"object","required":["start","end","destinations"],"properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"origin":{"type":"string","pattern":"^[A-Z]{2}$"},"destinations":{"type":"array","minItems":1,"items":{"type":"string","pattern":"^[A-Z]{2}$"}},"tripType":{"type":"string","description":"SINGLE | ROUND_TRIP | ANNUAL"},"purpose":{"type":"string","description":"LEISURE | BUSINESS | STUDENT"}}},"insured":{"type":"array","minItems":1,"items":{"type":"object","required":["ageAtDeparture"],"properties":{"travelerIndex":{"type":"integer"},"ageAtDeparture":{"type":"integer","minimum":0,"maximum":120},"residenceCountry":{"type":"string","pattern":"^[A-Z]{2}$"}}}},"categories":{"type":"array","description":"DOMESTIC | INBOUND | OVERSEAS | STUDENT | SCHENGEN | CANCELLATION | DOMESTIC_CANCELLATION","items":{"type":"string"}},"sumInsuredBand":{"type":"string","description":"Supplier plan band label when the market sells by band"},"attachTo":{"type":"object","description":"The parent booking an INS_ADDON attaches to","properties":{"orderRef":{"type":"string"},"itemId":{"type":"string"}}}},"example":{"trip":{"start":"2026-11-10","end":"2026-11-20","origin":"IN","destinations":["US"],"tripType":"SINGLE"},"insured":[{"travelerIndex":0,"ageAtDeparture":34},{"travelerIndex":1,"ageAtDeparture":9}],"categories":["OVERSEAS"]}},"AirportServiceSearchIntent":{"type":"object","description":"An airport service search (meet and greet, fast track): the service is keyed to a flight and a station, and the supplier's search cannot run without the flight link.\n","required":["flightLink","direction","party"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"direction":{"type":"string","enum":["DEPARTURE","ARRIVAL","TRANSIT"]},"airport":{"type":"string","pattern":"^[A-Z]{3}$","description":"Absent = taken from flightLink by direction"},"categories":{"type":"array","description":"MEET_ASSIST | PORTER | BUGGY | WHEELCHAIR | FAST_TRACK | TRANSFER | BUNDLE","items":{"type":"string"}},"party":{"$ref":"#/components/schemas/PartySpec"}},"example":{"flightLink":{"carrier":"AI","flightNumber":"131","date":"2026-11-10","origin":"BOM","destination":"LHR","departureTerminal":"2"},"direction":"DEPARTURE","categories":["MEET_ASSIST","PORTER"],"party":{"adults":2,"childAges":[6]}}},"LoungeSearchIntent":{"type":"object","description":"An airport lounge search: a station or a flight link, the visit window and the guests. Voucher products need only the station; pre-booked products need the flight link.\n","required":["party"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"station":{"$ref":"#/components/schemas/Station"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"visitAt":{"type":"string","format":"date-time"},"party":{"$ref":"#/components/schemas/PartySpec"},"entitlementModels":{"type":"array","description":"VOUCHER | PREBOOK — absent = both","items":{"type":"string"}},"attachTo":{"type":"object","properties":{"orderRef":{"type":"string"},"itemId":{"type":"string"}}}},"example":{"station":{"airport":"DEL","terminal":"3","terminalType":"INTERNATIONAL","direction":"DEPARTURE"},"visitAt":"2026-11-10T20:00:00+05:30","party":{"adults":2},"entitlementModels":["VOUCHER"]}},"VisaSearchIntent":{"type":"object","description":"A visa search: the corridor (nationality, residence, origin, destinations and purpose). A VISA_FREE answer is advisory and never orderable.\n","required":["corridor"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"corridor":{"type":"object","required":["nationality","destinations"],"properties":{"nationality":{"type":"string","pattern":"^[A-Z]{2}$"},"residence":{"type":"string","pattern":"^[A-Z]{2}$"},"origin":{"type":"string","pattern":"^[A-Z]{2}$"},"destinations":{"type":"array","minItems":1,"items":{"type":"string","pattern":"^[A-Z]{2}$"}},"purpose":{"type":"string","description":"TOURISM | BUSINESS | TRANSIT | STUDY | WORK; open string"}}},"travel":{"$ref":"#/components/schemas/DateRange"},"party":{"$ref":"#/components/schemas/PartySpec"},"entry":{"type":"string","description":"SINGLE | DOUBLE | MULTI — absent = any"}},"example":{"corridor":{"nationality":"IN","residence":"IN","destinations":["AE"],"purpose":"TOURISM"},"travel":{"from":"2026-12-01","to":"2026-12-10"},"party":{"adults":2}}},"Station":{"type":"object","description":"An airport or rail station position (ancillary proposal §2.2)","required":["airport"],"properties":{"airport":{"type":"string","description":"IATA airport code, or the rail station code when siteType is RAIL"},"siteType":{"type":"string","description":"AIRPORT | RAIL; absent = AIRPORT"},"terminal":{"type":"string","maxLength":16},"terminalType":{"type":"string","description":"DOMESTIC | INTERNATIONAL | MIXED"},"direction":{"type":"string","description":"DEPARTURE | ARRIVAL | TRANSIT"},"airside":{"type":"boolean"}},"example":{"airport":"DEL","terminal":"3","terminalType":"INTERNATIONAL","direction":"DEPARTURE","airside":true}},"RequiredInfoAnswerOption":{"type":"object","description":"One choice of a SELECT / ENUM question, with the rows it unlocks (the Example Activities CONDITIONAL chain)","required":["value"],"properties":{"value":{"type":"string"},"label":{"type":"string"},"followUp":{"type":"array","description":"RequiredInfo.field values demanded only when this answer is chosen","items":{"type":"string"}}},"example":{"value":"PICKUP","label":"Pick me up at my hotel","followUp":["pickupHotelName"]}},"FileAnswerSpec":{"type":"object","description":"Constraints on a FILE-typed answer (IMP-P2-077; ancillary proposal G-3): the client uploads through POST /v1/uploads and answers with the uploadId, never with bytes or a public URL.\n","properties":{"accept":{"type":"array","description":"Media types accepted, for example application/pdf and image/jpeg","items":{"type":"string"}},"maxBytes":{"type":"integer","minimum":1},"maxFiles":{"type":"integer","minimum":1,"default":1},"purpose":{"type":"string","description":"ETICKET | PASSPORT_SCAN | PHOTO | MEDICAL_EVIDENCE | RESIDENCE_PROOF | OTHER; open string"}},"example":{"accept":["application/pdf","image/jpeg"],"maxBytes":5242880,"maxFiles":1,"purpose":"PASSPORT_SCAN"}},"UploadRequest":{"type":"object","description":"Ask for a pre-signed upload slot for one file answer (POST /v1/uploads)","required":["fileName","contentType","sizeBytes"],"properties":{"fileName":{"description":"The file's name, for the record.","type":"string","maxLength":255},"contentType":{"description":"The file's media type, for example application/pdf.","type":"string","maxLength":128},"sizeBytes":{"description":"The file's size in bytes.","type":"integer","minimum":1},"purpose":{"type":"string","description":"Mirrors FileAnswerSpec.purpose; the platform pins retention and data class from it"},"field":{"type":"string","description":"The RequiredInfo.field this upload answers, when known"},"offerRef":{"type":"string","description":"The offer the answer is for, when known"}},"example":{"fileName":"passport.pdf","contentType":"application/pdf","sizeBytes":1834022,"purpose":"PASSPORT_SCAN","field":"passportScan","offerRef":"OF-V-2d81aa"}},"Upload":{"type":"object","description":"A pre-signed upload slot (03 §10.4 promised pre-signed URLs). The client PUTs the bytes to `url` with exactly `headers`, then references `uploadId` in requiredInfoAnswers. The slot is single-use, tenant-walled, and the object is encrypted at rest with its data class pinned from purpose; it is deleted when unused past expiresAt.\n","required":["uploadId","url","method","expiresAt","state"],"properties":{"uploadId":{"type":"string","description":"up_… opaque handle; the only thing that goes into an answer"},"url":{"type":"string","format":"uri","description":"Pre-signed object-store URL; never logged"},"method":{"type":"string","enum":["PUT"]},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Headers the PUT must carry verbatim (Content-Type, Content-Length)"},"expiresAt":{"type":"string","format":"date-time"},"maxBytes":{"type":"integer"},"state":{"type":"string","description":"PENDING (slot issued) | RECEIVED (bytes landed) | SCANNED (clean) | REJECTED (malware or type mismatch) | EXPIRED; open string"},"dataClass":{"type":"string","description":"DATA-GOVERNANCE §2 class pinned from purpose (C2 for identity documents)"}},"example":{"uploadId":"up_01JA7F3K2Q","url":"https://uploads.example/t/acme-travel/up_01JA7F3K2Q?X-Amz-Signature=redacted","method":"PUT","headers":{"Content-Type":"application/pdf","Content-Length":"1834022"},"expiresAt":"2026-11-01T10:15:00Z","maxBytes":5242880,"state":"PENDING","dataClass":"C2"}},"Consent":{"type":"object","description":"One consent captured at order time (IMP-P2-077; ancillary proposal §5, G-2): which text (consentId + version), who accepted it and when. The platform stores the consent text versions; the order keeps the evidence (CANONICAL-MODEL §5 P4).\n","required":["consentId","version","acceptedAt"],"properties":{"consentId":{"type":"string","description":"HEALTH_DATA_SHARING | CROSS_BORDER_TRANSFER | THIRD_PARTY_HANDLER | INSURER_TERMS | MARKETING | SUPPLIER_TERMS; open string"},"version":{"type":"string"},"acceptedAt":{"type":"string","format":"date-time"},"scope":{"type":"string","description":"ORDER | TRAVELER; absent = ORDER"},"travelerIndex":{"type":"integer","description":"When scope is TRAVELER"},"actor":{"type":"string","description":"Who accepted — TRAVELER, LEAD_BOOKER or AGENT (then onBehalfOf applies); open string"},"supplier":{"type":"string","description":"The supplier the consent is for, when supplier-specific"}},"example":{"consentId":"HEALTH_DATA_SHARING","version":"2026-09","acceptedAt":"2026-11-01T10:12:00Z","scope":"TRAVELER","travelerIndex":0,"actor":"TRAVELER","supplier":"EXAMPLEINSURANCE"}},"PostalAddress":{"type":"object","description":"One postal address (Traveler residence, billing, shipping — IMP-P2-089)","required":["countryCode"],"properties":{"line1":{"type":"string","maxLength":200},"line2":{"type":"string","maxLength":200},"city":{"type":"string","maxLength":100},"region":{"type":"string","maxLength":100,"description":"State, province or region; ISO 3166-2 code where one exists"},"postalCode":{"type":"string","maxLength":20},"countryCode":{"type":"string","pattern":"^[A-Z]{2}$"}},"example":{"line1":"221B Baker Street","city":"London","postalCode":"NW1 6XE","countryCode":"GB"}},"TravelerDocument":{"type":"object","description":"A passport or identity document of a traveller (CANONICAL-MODEL §2.4 documents[]; backend TravelDocument)","required":["type","number"],"properties":{"type":{"type":"string","description":"PASSPORT | NATIONAL_ID | DRIVING_LICENCE | PAN | TAX_ID | OTHER; open string"},"number":{"type":"string","maxLength":64},"issuingCountry":{"type":"string","pattern":"^[A-Z]{2}$"},"expiry":{"type":"string","format":"date"},"uploadId":{"type":"string","description":"A scan of the document uploaded through POST /v1/uploads, when demanded"}},"example":{"type":"PASSPORT","number":"Z1234567","issuingCountry":"IN","expiry":"2031-05-01"}},"Traveler":{"type":"object","description":"The flat canonical traveller record (CANONICAL-MODEL §2.4): verticals never fork it; they demand extra fields through requiredInfo. Field-level encrypted at rest (17 §4); a card number in any text field is 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED (PanGuard). Addresses are additive (IMP-P2-089): residence for entry requirements and insurance rating (a data provider, an insurance supplier), billing for event tickets (an event-ticket supplier), shipping for physically delivered documents.\n","required":["firstName","lastName"],"properties":{"type":{"type":"string","description":"ADT | CHD | INF (backend PassengerType); absent = ADT"},"age":{"type":"integer","minimum":0,"description":"Required when type is CHD or INF"},"title":{"type":"string","maxLength":10},"firstName":{"type":"string","maxLength":100},"middleName":{"type":"string","maxLength":100},"lastName":{"type":"string","maxLength":100},"email":{"type":"string","format":"email"},"phoneNumber":{"type":"string","maxLength":32,"description":"E.164"},"dateOfBirth":{"type":"string","format":"date"},"gender":{"type":"string","description":"M | F | X | U; open string"},"nationality":{"type":"string","pattern":"^[A-Z]{2}$"},"residenceCountry":{"type":"string","pattern":"^[A-Z]{2}$","description":"Country of residence (ISO 3166-1); what entry requirements and insurance rating key on"},"residenceAddress":{"$ref":"#/components/schemas/PostalAddress"},"billingAddress":{"$ref":"#/components/schemas/PostalAddress"},"shippingAddress":{"$ref":"#/components/schemas/PostalAddress"},"documents":{"type":"array","items":{"$ref":"#/components/schemas/TravelerDocument"}},"loyalty":{"type":"array","items":{"type":"object","required":["program","number"],"properties":{"program":{"type":"string"},"number":{"type":"string"}}}},"answers":{"type":"object","additionalProperties":true,"description":"PER_TRAVELER requiredInfo answers keyed by RequiredInfo.field; a FILE answer is an uploadId"},"lead":{"type":"boolean","description":"The lead booker (contact for the order); at most one traveller"}},"example":{"type":"ADT","firstName":"Ana","lastName":"Silva","email":"ana.silva@example.com","phoneNumber":"+5511999990000","dateOfBirth":"1992-03-14","nationality":"BR","residenceCountry":"BR","documents":[{"type":"PASSPORT","number":"FX123456","issuingCountry":"BR","expiry":"2031-05-01"}],"answers":{"WEIGHT":"70"},"lead":true}},"TaxCode":{"type":"object","description":"The statutory classification of a taxed line (SAC/HSN in India, VAT codes elsewhere)","required":["scheme","code"],"properties":{"scheme":{"type":"string","description":"SAC | HSN | VAT | GST | OTHER; open string"},"code":{"description":"The tax code, for example a SAC or HSN code in India or a VAT code elsewhere.","type":"string","maxLength":32}},"example":{"scheme":"HSN","code":"996761"}},"PricingBasis":{"type":"object","description":"What the price is per (IMP-P2-078; an activity supplier): per unit (OCTO pricingPer UNIT), per booking, per person, per group, per night, per hour; and the units the quoted total covers.\n","required":["per"],"properties":{"per":{"type":"string","description":"UNIT | PERSON | BOOKING | GROUP | NIGHT | HOUR | POLICY; open string"},"units":{"type":"integer","minimum":1,"description":"How many of `per` the quoted price covers"},"tiers":{"type":"array","description":"Volume or pax-count price tiers when the supplier publishes them","items":{"type":"object","properties":{"fromUnits":{"type":"integer"},"toUnits":{"type":"integer"},"unitPrice":{"$ref":"#/components/schemas/Money"}}}}},"example":{"per":"PERSON","units":2}},"PricedAddOn":{"type":"object","description":"A priced extra on an offer (IMP-P2-078): child seat, meet-and-greet sign, toll, extra stop, guide language, accommodation choice. `included` lines are already in `price` and may be removable; optional lines are added at order time through intent.addOns[] and re-priced.\n","required":["code","label","price"],"properties":{"code":{"type":"string","description":"Canonical add-on code — CHILD_SEAT | MEET_GREET | TOLL | EXTRA_STOP | GUIDE_LANGUAGE | ACCOMMODATION | WAITING_TIME | INSURANCE_RIDER; open string"},"label":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"},"basis":{"$ref":"#/components/schemas/PricingBasis"},"included":{"type":"boolean","description":"Already inside Offer.price"},"removable":{"type":"boolean","description":"An included line the traveller may drop"},"quantity":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"},"default":{"type":"integer"}}},"ageBand":{"type":"object","description":"For per-age lines (child seats)","properties":{"minAge":{"type":"integer"},"maxAge":{"type":"integer"}}},"choices":{"type":"array","description":"For a choice-shaped add-on (guide language, accommodation class)","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"}}}},"taxCode":{"$ref":"#/components/schemas/TaxCode"}},"example":{"code":"CHILD_SEAT","label":"Child seat (1-4 years)","price":{"amount":1500,"currency":"USD"},"basis":{"per":"UNIT"},"included":false,"quantity":{"min":0,"max":2,"default":0},"ageBand":{"minAge":1,"maxAge":4}}},"SecurityDeposit":{"type":"object","description":"A refundable deposit the guest pays outside Offer.price (VR — SUP-HTL-EXAMPLE4, SUP-HTL-EXAMPLE5, a vacation-rental supplier): disclosed, never collected by the platform unless collectedBy is PLATFORM.\n","required":["amount"],"properties":{"amount":{"$ref":"#/components/schemas/Money"},"collectedBy":{"type":"string","description":"PROPERTY | SUPPLIER | PLATFORM; open string"},"collectedAt":{"type":"string","description":"CHECK_IN | BEFORE_CHECK_IN | BOOKING; open string"},"refundable":{"type":"boolean"},"refundWithinDays":{"type":"integer"},"method":{"type":"string","description":"CARD_HOLD | CASH | TRANSFER | UNKNOWN; open string"}},"example":{"amount":{"amount":2000000,"currency":"INR"},"collectedBy":"SUPPLIER","collectedAt":"BEFORE_CHECK_IN","refundable":true,"refundWithinDays":7}},"OfferPricing":{"type":"object","description":"Price facts beside Offer.price (IMP-P2-078): whether the figure is indicative (a \"from\" price built from synced or rating-table data, FEAT-023 §11 — reprice always firms it), what it is priced per, the supplier-imposed sell floor the markup engine must respect (CANONICAL-MODEL §7 C-5; the floor is the lowest SELL price, never a net), the security deposit and the pay-at-property lines outside the total, the all-in display obligation and a bound tax quote. connect-v1 v1.41.0: the supplier's reference (\"was\") price the offer can be shown against (referencePrice).\n","properties":{"indicative":{"type":"boolean","description":"True when price is a \"from\" figure; Offer.dataAsOf says how fresh"},"basis":{"$ref":"#/components/schemas/PricingBasis"},"sellFloor":{"$ref":"#/components/schemas/Money","description":"The supplier's minimum sell price for this rate (EXAMPLEHOTELS4 RecommendedSellingRate, GlobalTix); the rule engine clamps up, never down"},"referencePrice":{"$ref":"#/components/schemas/Money","description":"connect-v1 v1.41.0. The supplier's reference (\"was\", strike-through) price for the same stay, rooms and party, on the same basis as Offer.price (all nights, taxes included as price includes them) and in its currency. It is restated at the tenant's SELL level by the same ratio the platform priced Offer.price with (the cancellation ladder's rule, DR-69), so the two figures are comparable after markup; a reference that cannot be restated (no FX rate at the price's snapshot) is withheld, never shown at the supplier's figure. Present only when it is above price. Display only: it is never charged, never a sell floor and never a promise of a future price. The saving is referencePrice.amount − price.amount (the client computes it; no supplier savings figure is relayed). Absent when the supplier states none. SUP-HTL-EXAMPLE2: (stirkedOffFare + the rate's per-night taxes) × nights, the supplier's strike being per-night base before taxes.\n"},"securityDeposit":{"$ref":"#/components/schemas/SecurityDeposit"},"payAtProperty":{"type":"array","description":"Charges collected by the property or supplier on arrival, outside price (fees on arrival, consumption costs, tourist taxes). FNB (v1.7.0): a priced experience that is not prepaid is paid at the restaurant and appears here as an EXPERIENCE line (quantity and unitAmount per guest) with a GRATUITY line when the experience states one; Offer.price stays zero.\n","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}},"allInDisplayRequired":{"type":"boolean","description":"The market requires the all-in price to be shown up front (an event-ticket supplier)"},"taxQuote":{"type":"object","description":"A supplier tax quote bound to this price; the signature must travel reprice → order (G-8)","properties":{"signature":{"type":"string"},"basis":{"type":"string","description":"WHOLESALE | RETAIL"},"validUntil":{"type":"string","format":"date-time"}}}},"example":{"indicative":true,"basis":{"per":"NIGHT","units":4},"sellFloor":{"amount":79000,"currency":"USD"},"referencePrice":{"amount":98400,"currency":"USD"},"payAtProperty":[{"type":"TOURIST_TAX","amount":1200,"label":"City tax","collectedBy":"PROPERTY","payableAt":"PROPERTY"}]}},"DocumentDelivery":{"type":"object","description":"How a document reaches the traveller (IMP-P2-084; an activity supplier): downloaded, sent by email, transferred to a mobile ticketing app, shipped (FedEx), collected at will-call, or re-hosted by the platform from a supplier link. `inHandAt` is the date the ticket will be in the traveller's hands for deliveries that lag (event tickets).\n","properties":{"method":{"type":"string","description":"DOWNLOAD | EMAIL | MOBILE_TRANSFER | SHIPMENT | WILL_CALL | APP_WALLET | SUPPLIER_LINK; open string"},"carrier":{"type":"string","description":"Shipping carrier when method is SHIPMENT"},"trackingRef":{"type":"string"},"pickupInstructions":{"type":"string"},"inHandAt":{"type":"string","format":"date"},"rehosted":{"type":"boolean","description":"True when the platform re-hosted a supplier-served file in-region (data residency)"},"recipientTravelerIndex":{"type":"integer"}},"example":{"method":"MOBILE_TRANSFER","inHandAt":"2026-11-20","rehosted":false}},"SupplierProgressDetail":{"type":"object","description":"One supplier of the fan-out and where it is (IMP-P4-018; OD-CONSOLE2-02 answered the honest way: the counts stay counts, and this list is the per-supplier truth a warming screen renders and retries from). Present when the server tracks per-supplier state (the platform's SupplierOutcome record); a client must still tolerate its absence. `code`, `status` and `message` are the fields the console reads; the rest are additive.\n","required":["code","status"],"properties":{"code":{"type":"string","description":"Supplier registry code (08 §2), the same value CapabilitySupplier.code and Warning.supplier carry"},"status":{"type":"string","description":"PENDING | DONE | FAILED | DROPPED | SKIPPED; open string"},"message":{"type":"string","description":"Human-readable detail for the row (\"circuit open\", \"timed out after 2.0s\"); never the thing a client branches on"},"credentialSet":{"type":"string"},"offers":{"type":"integer","minimum":0},"tookMs":{"type":"integer"},"failureClass":{"type":"string","description":"TIMEOUT | CIRCUIT_OPEN | CREDENTIAL_DEGRADED | CURRENCY_UNAVAILABLE | UPSTREAM_ERROR | RATE_LIMITED | QUOTA_EXHAUSTED; open string"},"retryable":{"type":"boolean","description":"True when a client may ask the platform to retry this supplier on the same searchRef"},"warningCode":{"type":"string","description":"The context.warnings[] code attributed to this supplier, when one is"},"dataAsOf":{"type":"string","format":"date-time","description":"When this supplier's results were served from a synced store (FEAT-023), the freshness of that store"}},"example":{"code":"EXAMPLEHOTELS3_RAPID","status":"FAILED","message":"circuit open","credentialSet":"MONDEE","offers":0,"tookMs":2004,"failureClass":"CIRCUIT_OPEN","retryable":true,"warningCode":"SUPPLIER_DROPPED"}},"AddOnSelection":{"type":"object","required":["code"],"properties":{"code":{"type":"string"},"quantity":{"type":"integer","minimum":0},"choice":{"type":"string","description":"The chosen value for a choice-shaped add-on"},"meetSignText":{"type":"string","maxLength":60,"description":"MEET_GREET only — the text on the sign"}},"example":{"code":"CHILD_SEAT","quantity":1}},"HoldExtendRequest":{"type":"object","description":"Ask for more time on a hold (IMP-P2-083; an activity supplier's OCTO extend)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` says how long you want the hold.","type":"object","properties":{"intent":{"description":"The time you want the hold until, as requestedUntil; the supplier decides the actual expiry.","type":"object","properties":{"requestedUntil":{"type":"string","format":"date-time","description":"Absent = the supplier's default extension"}}}}}},"example":{"context":{"domain":"EXP"},"message":{"intent":{"requestedUntil":"2026-09-14T17:35:00Z"}}}},"PaymentAttempt":{"type":"object","description":"One attempt to pay an intent (IMP-P2-081; a payment provider — the order is the intent, payments are attempts). The platform's attempt journal is internal; this is the client view of it. A failed attempt is not a failed intent while another attempt is possible.\n","required":["attemptId","state","createdAt"],"properties":{"attemptId":{"type":"string","description":"Opaque platform id; never the provider's raw payment id"},"state":{"type":"string","description":"OPEN | SUCCEEDED | FAILED | SUPERSEDED | REFUNDED_DUPLICATE; open string"},"method":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":"string","format":"date-time"},"failureReason":{"type":"string","description":"Canonical decline class; never the provider's raw code"}},"example":{"attemptId":"att_01JA7G2","state":"SUCCEEDED","method":"UPI","createdAt":"2026-11-01T10:20:00Z","resolvedAt":"2026-11-01T10:20:41Z"}},"CheckoutSignature":{"type":"object","description":"The signed triple a hosted checkout hands the browser (IMP-P2-081; Example Pay razorpay_order_id / razorpay_payment_id / razorpay_signature). The platform verifies it against ITS stored provider order id, then reads the provider — the triple is a hint, never the truth. Token references only; never instrument data.\n","required":["providerPaymentRef","signature"],"properties":{"providerOrderRef":{"type":"string"},"providerPaymentRef":{"type":"string"},"signature":{"type":"string","description":"HMAC the provider computed; opaque to the client"}},"example":{"providerOrderRef":"order_redacted01","providerPaymentRef":"pay_redacted01","signature":"9ef2c3redacted"}},"ItemExtension":{"type":"object","description":"The one typed per-vertical block, keyed by block name — exactly one key is present (CANONICAL-MODEL §3). The domain picks the block: FLT → air; HTL/VR/DAYUSE → hotel; TXI/XFR/CAR → ground; EXP/ATT/TOUR/EVT/MOV → experiences; CRU → cruise; INS → insurance; FASTTRK/APTSVC → airportServices; LOUNGE → lounge; VISA → visa; FNB → dining (v1.7.0); BUS → bus (v1.15.0); GIFT → gift (v1.31.0); PKG → package (v1.35.0); PARK → parking (v1.55.0). Open so a block the client does not know is ignored (11 §8). `enrichment` is the provenance of every decorated value.\n","additionalProperties":true,"properties":{"air":{"$ref":"#/components/schemas/AirExtension"},"hotel":{"$ref":"#/components/schemas/HotelExtension"},"ground":{"$ref":"#/components/schemas/GroundExtension"},"experiences":{"$ref":"#/components/schemas/ExperiencesExtension"},"cruise":{"$ref":"#/components/schemas/CruiseExtension"},"insurance":{"$ref":"#/components/schemas/InsuranceExtension"},"airportServices":{"$ref":"#/components/schemas/AirportServicesExtension"},"lounge":{"$ref":"#/components/schemas/LoungeExtension"},"visa":{"$ref":"#/components/schemas/VisaExtension"},"dining":{"$ref":"#/components/schemas/DiningExtension"},"bus":{"$ref":"#/components/schemas/BusExtension"},"gift":{"$ref":"#/components/schemas/GiftExtension"},"package":{"$ref":"#/components/schemas/PackageExtension"},"parking":{"$ref":"#/components/schemas/ParkingExtension"},"enrichment":{"$ref":"#/components/schemas/EnrichmentBlock"}},"example":{"ground":{"service":{"class":"SEDAN","serviceType":"TRANSFER"},"waypoints":[{"type":"PICKUP","location":{"iata":"GRU"},"scheduledAt":"2026-11-10T15:40:00-03:00"},{"type":"DROPOFF","location":{"text":"Hotel Unique, São Paulo"}}]}}},"AirSegment":{"type":"object","description":"One operated leg (CANONICAL-MODEL §3.1 segments[]); terminals added (ancillary proposal G-6); enrichment fields per the EnrichmentProvider SPI §4.1. Since v1.23.0 a segment may also carry display names, its own duration, fare basis and brand, the connection after it (layoverMinutes, selfTransfer, bagsRecheckRequired, longLayover), technical stops, a red-eye flag and the seats left; all optional, absent when the supplier does not state them. Since v1.24.0 also connecting and operatedByText, and, on ORDER lines only (never on an offer), the airline record locator (airlineLocator) and the segment status (status).\n","required":["marketingCarrier","flightNumber","origin","destination","departure","arrival"],"properties":{"marketingCarrier":{"type":"string"},"operatingCarrier":{"type":"string"},"flightNumber":{"type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"departure":{"type":"string","format":"date-time"},"arrival":{"type":"string","format":"date-time"},"departureTerminal":{"type":"string"},"arrivalTerminal":{"type":"string"},"cabin":{"type":"string"},"bookingClass":{"type":"string"},"equipment":{"type":"string"},"emissions":{"$ref":"#/components/schemas/FlightEmissions"},"ratings":{"$ref":"#/components/schemas/FlightRatings"},"amenities":{"type":"array","items":{"$ref":"#/components/schemas/FlightAmenities"}},"flightStatus":{"$ref":"#/components/schemas/FlightStatus"},"durationMinutes":{"type":"integer","minimum":0,"description":"Flying time of this segment in minutes, gate to gate as the supplier states it (v1.23.0)"},"originName":{"type":"string","description":"Display name of the origin airport (v1.23.0)"},"destinationName":{"type":"string","description":"Display name of the destination airport (v1.23.0)"},"originCity":{"type":"string","description":"Display name of the origin city (v1.23.0)"},"destinationCity":{"type":"string","description":"Display name of the destination city (v1.23.0)"},"marketingCarrierName":{"type":"string","description":"Display name of the marketing carrier (v1.23.0)"},"operatingCarrierName":{"type":"string","description":"Display name of the operating carrier, when it differs or the supplier states it (v1.23.0)"},"fareBasis":{"type":"string","description":"Fare basis code that prices this segment (v1.23.0); the itinerary-level value stays in AirExtension.fare"},"brandName":{"type":"string","description":"Fare brand of this segment as the carrier names it, for example a basic or flexible economy brand (v1.23.0)"},"layoverMinutes":{"type":"integer","minimum":0,"description":"Connection time in minutes AFTER this segment, before the next segment of the same slice departs (v1.23.0). Absent on the last segment of a slice.\n"},"intermediateStops":{"type":"array","description":"Technical stops inside this one flight number, where the aircraft lands without a change of flight (v1.23.0)","items":{"$ref":"#/components/schemas/AirIntermediateStop"}},"redEye":{"type":"boolean","description":"True when the supplier flags the segment as an overnight (red-eye) flight (v1.23.0)"},"selfTransfer":{"type":"boolean","description":"True when the connection after this segment is not protected by one ticket: the traveller moves themselves (and their bags) to the next flight (v1.23.0)\n"},"bagsRecheckRequired":{"type":"boolean","description":"True when checked bags must be collected and checked in again at the connection after this segment (v1.23.0)"},"longLayover":{"type":"boolean","description":"True when the supplier flags the connection after this segment as a long layover (v1.23.0)"},"seatsRemaining":{"type":"integer","minimum":0,"description":"Seats the supplier reports as still bookable in this booking class; indicative, not a hold (v1.23.0)"},"connecting":{"type":"boolean","description":"True when this segment connects onward to the next segment of the same slice (v1.24.0)"},"operatedByText":{"type":"string","description":"The supplier's \"operated by …\" display text for a codeshare, as given, for example \"Operated by LATAM Airlines Brasil\" (v1.24.0); operatingCarrier stays the coded value\n"},"airlineLocator":{"type":"string","description":"The operating or validating airline's own record locator for this segment (v1.24.0). ORDER lines only: never present on an offer.\n"},"status":{"type":"string","description":"The segment status as the airline reports it on the booking, for example HK (confirmed), UN (unable, flight not operating), TK (schedule change, reconfirm) (v1.24.0). Open string; ORDER lines only: never present on an offer.\n"},"departureTimeZone":{"type":"string","description":"The IANA time zone departure's local time is in, as the supplier states it, for example America/Los_Angeles (v1.42.0)"},"arrivalTimeZone":{"type":"string","description":"The IANA time zone arrival's local time is in, as the supplier states it (v1.42.0)"}},"example":{"marketingCarrier":"LA","operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departure":"2026-11-10T23:50:00-03:00","arrival":"2026-11-11T07:10:00-05:00","departureTerminal":"3","arrivalTerminal":"N","cabin":"ECONOMY","bookingClass":"Q","durationMinutes":560,"originName":"Sao Paulo Guarulhos International Airport","destinationName":"Miami International Airport","originCity":"Sao Paulo","destinationCity":"Miami","marketingCarrierName":"LATAM Airlines","operatingCarrierName":"LATAM Airlines","fareBasis":"QL7X0NB1","brandName":"LIGHT","redEye":true,"seatsRemaining":4}},"AirIntermediateStop":{"type":"object","description":"A technical stop inside one flight number (AirSegment.intermediateStops[], v1.23.0)","properties":{"airport":{"type":"string","description":"IATA airport code of the stop"},"arrival":{"type":"string","format":"date-time","description":"Arrival at the stop, local time with offset"},"departure":{"type":"string","format":"date-time","description":"Departure from the stop, local time with offset"},"durationMinutes":{"type":"integer","minimum":0,"description":"Ground time at the stop in minutes"}},"example":{"airport":"SAL","arrival":"2026-11-10T09:05:00-06:00","departure":"2026-11-10T10:00:00-06:00","durationMinutes":55}},"AirSlice":{"type":"object","description":"One journey direction of a flight offer (v1.23.0): one slice per requested slice of FlightSearchIntent.slices, in the same order (a round trip has two). It summarises the segments it groups; the segments themselves stay in AirExtension.segments and are referenced by index. Every property is optional.\n","properties":{"index":{"type":"integer","minimum":0,"description":"0-based position of this slice, matching the order of the intent's slices"},"origin":{"type":"string","description":"IATA code of the first departure airport of the slice"},"destination":{"type":"string","description":"IATA code of the last arrival airport of the slice"},"departure":{"type":"string","format":"date-time","description":"Departure of the first segment, local time with offset"},"arrival":{"type":"string","format":"date-time","description":"Arrival of the last segment, local time with offset"},"durationMinutes":{"type":"integer","minimum":0,"description":"Elapsed journey time of the slice in minutes, first departure to last arrival, layovers included"},"stops":{"type":"integer","minimum":0,"description":"Number of connections in the slice (segments minus one; technical stops are not counted)"},"segmentIndexes":{"type":"array","description":"0-based indexes into AirExtension.segments of this slice's segments, in flying order","items":{"type":"integer","minimum":0}},"baggage":{"$ref":"#/components/schemas/AirBaggageAllowance"},"fareRules":{"$ref":"#/components/schemas/AirFareRules","description":"The fare rules of this journey only, when the supplier states rules per route (v1.24.0); absent when the supplier states them for the whole itinerary (AirExtension.fareRules)\n"},"meals":{"type":"array","description":"The meal service on this journey as the supplier states it, display strings as given (v1.24.0)","items":{"type":"string"}}},"example":{"index":0,"origin":"GRU","destination":"MIA","departure":"2026-11-10T08:00:00-03:00","arrival":"2026-11-10T19:25:00-05:00","durationMinutes":805,"stops":1,"segmentIndexes":[0,1],"baggage":{"checked":{"pieces":1,"weightKg":23,"included":true},"carryOn":{"pieces":1,"weightKg":10,"included":true}}}},"AirBaggageAllowance":{"type":"object","description":"The baggage a fare allows on a slice, per passenger (v1.23.0). Each member is optional; an absent member means the supplier stated nothing for that kind of bag, not that none is allowed.\n","properties":{"checked":{"$ref":"#/components/schemas/AirBaggageItem"},"carryOn":{"$ref":"#/components/schemas/AirBaggageItem"},"personalItem":{"$ref":"#/components/schemas/AirBaggageItem"}},"example":{"checked":{"pieces":0,"included":false,"charge":{"amount":3500,"currency":"USD"},"description":"First checked bag for a fee"},"carryOn":{"pieces":1,"weightKg":10,"included":true},"personalItem":{"pieces":1,"included":true,"description":"Fits under the seat in front"}}},"AirBaggageItem":{"type":"object","description":"One kind of bag in an AirBaggageAllowance, per passenger (v1.23.0). Every property is optional.","properties":{"pieces":{"type":"integer","minimum":0,"description":"Number of bags of this kind the fare includes"},"weightKg":{"type":"number","minimum":0,"description":"Maximum weight per bag in kilograms"},"description":{"type":"string","description":"The supplier's text for the allowance (size or weight limits), as given"},"charge":{"$ref":"#/components/schemas/Money","description":"The fee for the FIRST bag of this kind when the fare does not include it; indicative, paid to the carrier or bought as an ancillary, never inside Offer.price\n"},"included":{"type":"boolean","description":"True when at least one bag of this kind is included in the fare"}},"example":{"pieces":1,"weightKg":23,"description":"Up to 23 kg and 158 linear cm","included":true}},"AirPenaltyRule":{"type":"object","description":"Whether one fare action (a refund or a change, before or after departure) is allowed and at what penalty, as the supplier states it (v1.24.0). amount is the penalty as Money when the supplier states an amount; percentBasisPoints is set when the supplier states a percentage (of the fare). Both may be absent when only allowed is known. Every property is optional.\n","properties":{"allowed":{"type":"boolean","description":"True when the action is permitted by the fare"},"amount":{"$ref":"#/components/schemas/Money","description":"The penalty as Money, when the supplier states an amount"},"percentBasisPoints":{"type":"integer","minimum":0,"maximum":10000,"description":"The penalty as a percentage of the fare in basis points (1000 = 10%), when the supplier states a percentage"},"description":{"type":"string","description":"The supplier's text for the rule, as given"}},"example":{"allowed":true,"amount":{"amount":15000,"currency":"USD"},"description":"Change permitted before departure for a fee, plus any fare difference"}},"AirFareRules":{"type":"object","description":"The full per-phase picture of a fare's rules (v1.24.0): refund and change, each before and after departure, including change fees. conditions.cancellation stays the AUTHORITATIVE refund answer (and conditions.refundable / conditions.amendable the authoritative flags); AirFareRules is the supplier's fuller detail for display. Every member is optional; an absent member means the supplier stated nothing for that phase.\n","properties":{"refundBeforeDeparture":{"$ref":"#/components/schemas/AirPenaltyRule"},"refundAfterDeparture":{"$ref":"#/components/schemas/AirPenaltyRule"},"changeBeforeDeparture":{"$ref":"#/components/schemas/AirPenaltyRule"},"changeAfterDeparture":{"$ref":"#/components/schemas/AirPenaltyRule"}},"example":{"refundBeforeDeparture":{"allowed":true,"amount":{"amount":20000,"currency":"USD"}},"refundAfterDeparture":{"allowed":false},"changeBeforeDeparture":{"allowed":true,"percentBasisPoints":1000},"changeAfterDeparture":{"allowed":false}}},"AirExtension":{"type":"object","description":"FLT block (CANONICAL-MODEL §3.1) — on an offer and on an order line (ticketing, void window, schedule change). Since v1.23.0 an offer may also carry slices[] (one per journey direction, grouping segments[] by index, with the slice's baggage allowance) and the documented keys of the open fare object below. Since v1.24.0 an offer may also carry fareRules (AirFareRules, the whole itinerary). Since v1.27.0 an offer may also carry journey (AirJourneyState): an OUTBOUND_FIRST offer whose later slices are still to be chosen through POST /v1/flights/returns.\n","properties":{"journey":{"$ref":"#/components/schemas/AirJourneyState","description":"Which slices of the requested journey this offer carries (v1.27.0). Present on every offer of an OUTBOUND_FIRST search and on the offer POST /v1/flights/returns narrows from it; absent on a COMPLETE offer, and absent means complete. While journey.complete is false, segments and slices hold only the chosen slices, price is the supplier's \"from\" price for the whole journey with pricing.indicative true, and /v1/reprice and /v1/orders refuse the offer (409 CONFLICT details.reason JOURNEY_INCOMPLETE, details.nextSliceIndex).\n"},"brandOptions":{"type":"array","description":"The offered itinerary's brands (fare families), each with its own SELL price and terms (v1.42.0), on a search card and a detail of a single-itinerary FLT offer. Each price is priced by the platform with the rules that priced the offer, never a supplier net; a brand the rules cannot price is left out. Send one key back as message.intent.selection.brand on POST /v1/detail to narrow the offer to that brand: its price, segments' fare basis and booking class, fare rules and conditions become the brand's, and /v1/reprice and /v1/orders then price and book it. Absent on a journey priced across several itineraries and on order lines.\n","items":{"$ref":"#/components/schemas/AirBrandOption"}},"confirmations":{"type":"array","description":"What the booking system confirmed the line under (v1.42.0): one entry per supplier booking the line holds (a round trip a supplier books as two itineraries carries two), with the record locator (PNR), the supplier's trip number and its reference number for the flight — the identifiers a traveller or an agent quotes to the airline or the booking desk, as the upstream platform shows them. Set on ORDER lines only, after booking; an offer never carries it. The servicing envelope stays server-side (OrderItem has no supplierLocator, OD-CONTRACT-03): servicing verbs take the order and item refs, never these values.\n","items":{"$ref":"#/components/schemas/AirBookingConfirmation"}},"segments":{"type":"array","items":{"$ref":"#/components/schemas/AirSegment"}},"slices":{"type":"array","description":"One entry per journey direction, in the order of the intent's slices (v1.23.0): a one-way offer has one, a round trip two. Optional; absent when the adapter does not group segments.\n","items":{"$ref":"#/components/schemas/AirSlice"}},"fareRules":{"$ref":"#/components/schemas/AirFareRules","description":"The fare rules that apply to the whole itinerary (v1.24.0). A slice's own fareRules, when present, apply to that journey only. conditions.cancellation stays the authoritative refund answer.\n"},"fare":{"type":"object","additionalProperties":true,"description":"Fare facts of the offer (D-30). The object stays OPEN: a client must ignore keys it does not know, and every key is optional. Documented keys: brandName, fareBasis, sourceType (GDS | NDC | LCC | CHARTER), validatingCarrier; since v1.23.0 also fareType (open string, the supplier's fare class of contract; known values PUB, NET, PRIVATE), basicEconomy (boolean, a basic-economy fare with its usual restrictions), nonRefundable (boolean, the supplier's own flag; conditions.refundable stays the authoritative refund answer), gdsType (open string, the distribution system that priced it, for example EXAMPLEGDS or EXAMPLEGDS2), and brandFares, an array of the fare brands the carrier sells on the same flights, each {brandName, price (Money, the total for the party, like Offer.price), cabin, baggageSummary (string), features {included[], chargeable[], notAvailable[]} (feature codes or display labels, as strings), selected (boolean, true on the brand this offer is priced at)}. brandFares is INFORMATIONAL ONLY in v1.23.0: the offer is priced and booked at the selected brand; choosing another brand is a later contract change (until then a client displays the list and cannot reprice or book a different brand from it). Since v1.24.0 also validatingCarrierName (string, display name of validatingCarrier); the booleans virtualInterline (a self-connected itinerary across carriers without an interline agreement), lowCostCarrier, alternateAirport (a nearby-airport result, not the airport asked for), alternateDate (a flexible-date result, not the date asked for), vfrFare (a visiting-friends-and-relatives fare), missionaryFare, infantSeatRequired (an infant SSR: the infant needs a seat of its own) and nonBrandedFare; milesEarned (string, the frequent-flyer miles as the supplier states them); cardFee {amount (Money, per booking), percentBasisPoints (integer)}, INFORMATIONAL: a surcharge when paying by card, never inside Offer.price; and on each brandFares[] item tier (integer, the carrier's brand tier, for ordering the list) and meals (array of strings, as stated).\n","example":{"fareBasis":"QL7X0NB1","brandName":"LIGHT","sourceType":"GDS","validatingCarrier":"LA","validatingCarrierName":"LATAM Airlines","fareType":"PUB","basicEconomy":false,"nonRefundable":true,"gdsType":"EXAMPLEGDS","lowCostCarrier":false,"alternateDate":false,"milesEarned":"1,230 miles","cardFee":{"amount":{"amount":750,"currency":"USD"}},"brandFares":[{"brandName":"LIGHT","price":{"amount":128400,"currency":"USD"},"cabin":"ECONOMY","baggageSummary":"1 carry-on bag; checked bag for a fee","features":{"included":["CARRY_ON"],"chargeable":["CHECKED_BAG","SEAT_SELECTION"],"notAvailable":["REFUND"]},"selected":true,"tier":1},{"brandName":"STANDARD","price":{"amount":151800,"currency":"USD"},"cabin":"ECONOMY","baggageSummary":"1 carry-on bag and 1 checked bag of 23 kg","features":{"included":["CARRY_ON","CHECKED_BAG","SEAT_SELECTION"],"chargeable":["CHANGE"],"notAvailable":["REFUND"]},"selected":false,"tier":2,"meals":["Complimentary meal"]}]}},"ticketing":{"type":"object","description":"Order line only — create-vs-ticket is two supplier steps under NDC, one under LCC instant purchase","properties":{"status":{"type":"string","description":"UNFULFILLED | PARTIALLY_TICKETED | TICKETED | VOIDED | EXCHANGED | REVOKED; open string"},"deadline":{"type":"string","format":"date-time"},"expected":{"type":"integer","description":"Documents expected (one per traveller per ticketable unit)"},"issued":{"type":"integer"}}},"voidWindow":{"type":"object","properties":{"until":{"type":"string","format":"date-time"},"settlementPlan":{"type":"string","description":"ARC | BSP | NONE"}}},"scheduleChange":{"type":"object","additionalProperties":true,"description":"Pending supplier-initiated change with accept/amend options (order.schedule_changed)"},"emissions":{"type":"object","description":"Itinerary total per passenger (derived from segments[].emissions)","properties":{"grams":{"type":"integer"},"kg":{"type":"number"},"lb":{"type":"number"},"deltaVsTypicalPercent":{"type":"integer"}}}},"example":{"segments":[{"marketingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departure":"2026-11-10T23:50:00-03:00","arrival":"2026-11-11T07:10:00-05:00","cabin":"ECONOMY"}],"fare":{"fareBasis":"QL7X0NB1","sourceType":"NDC","validatingCarrier":"LA"},"ticketing":{"status":"TICKETED","expected":2,"issued":2},"emissions":{"grams":612000,"kg":612,"lb":1349.2,"deltaVsTypicalPercent":-8}}},"AirJourneyState":{"type":"object","description":"How much of the requested journey an FLT offer carries (v1.27.0, AirExtension.journey). Slice indexes are the 0-based positions of FlightSearchIntent.slices, the same index as AirSlice.index. On an OUTBOUND_FIRST offer complete stays false until every slice is chosen through POST /v1/flights/returns. mode and complete are always set when the block is present; every property is optional in the schema.\n","properties":{"mode":{"type":"string","description":"The journeyMode the offer was built in: COMPLETE | OUTBOUND_FIRST; x-extensible-enum, a client tolerates a value it does not know\n","x-extensible-enum":["COMPLETE","OUTBOUND_FIRST"]},"complete":{"type":"boolean","description":"True when every requested slice is chosen; only then can the offer be repriced and booked"},"selectedSlices":{"type":"array","description":"The slice indexes already chosen, ascending (an OUTBOUND_FIRST search offer has [0])","items":{"type":"integer","minimum":0}},"nextSliceIndex":{"type":"integer","minimum":0,"description":"The slice to choose next through POST /v1/flights/returns; absent when complete is true"}},"example":{"mode":"OUTBOUND_FIRST","complete":false,"selectedSlices":[0],"nextSliceIndex":1}},"AirBookingConfirmation":{"type":"object","description":"One supplier booking behind an air order line (v1.42.0; AirExtension.confirmations), as the booking system answered it. ORDER lines only. Each field is absent when the supplier did not state it; none is a servicing key.\n","properties":{"recordLocator":{"type":"string","description":"The record locator (PNR) the booking system created, e.g. JDZCK2. The airline's own locator, when it differs, is segments[].airlineLocator.\n"},"tripId":{"type":"string","description":"The supplier's trip number the booking was filed under (Example Air tripId)"},"referenceNumber":{"type":"string","description":"The supplier's reference number for this flight on the trip (Example Air ReferenceNumber / productId)\n"}},"example":{"recordLocator":"QX7PLM","tripId":"2000100001","referenceNumber":"20001000010101"}},"AirBrandOption":{"type":"object","description":"One brand (fare family) of an offered itinerary (v1.42.0). price is the TOTAL journey price for the party with this brand chosen: a SELL price after the tenant's pricing rules, exactly like Offer.price, never a supplier net; priceDelta is price minus the offer's current price and may be zero or negative. key, price and priceDelta are always set.\n","required":["key","price","priceDelta"],"properties":{"key":{"type":"string","description":"Opaque; send it back as message.intent.selection.brand. Not a supplier token; never parse it"},"brandName":{"type":"string","description":"The brand as the airline names it, for example Main or Refundable Main"},"cabin":{"type":"string","description":"ECONOMY | PREMIUM_ECONOMY | BUSINESS | FIRST; open string"},"price":{"$ref":"#/components/schemas/Money"},"priceDelta":{"$ref":"#/components/schemas/Money"},"segments":{"type":"array","description":"Per offer segment, in AirExtension.segments order, the brand's fare basis, booking class and cabin","items":{"type":"object","properties":{"fareBasis":{"type":"string"},"bookingClass":{"type":"string"},"cabin":{"type":"string"}}}},"fareRules":{"$ref":"#/components/schemas/AirFareRules"},"fareType":{"type":"string","description":"The fare type as the supplier states it (PUB, NET…); open string"},"sourceType":{"type":"string","description":"GDS | NDC | LCC; open string"},"holdable":{"type":"boolean","description":"The brand can be held without payment (POST /v1/hold)"},"basicEconomy":{"type":"boolean","description":"The airline's basic-economy restrictions apply"},"baggageSummary":{"type":"string","description":"One line describing the bags this brand includes, for display"},"features":{"type":"object","additionalProperties":true,"description":"The brand's features as the supplier codes them — included, chargeable and notAvailable lists"},"selected":{"type":"boolean","description":"True on the brand the offer is priced at"}},"example":{"key":"BR-3","brandName":"Main","cabin":"ECONOMY","price":{"amount":21960,"currency":"USD"},"priceDelta":{"amount":4500,"currency":"USD"},"segments":[{"fareBasis":"GH4OGVMN","bookingClass":"G","cabin":"ECONOMY"}],"fareType":"PUB","sourceType":"GDS","holdable":true,"basicEconomy":false,"baggageSummary":"1 personal item, 1 carry-on bag, checked bag for a fee","selected":false}},"AirReturnOption":{"type":"object","description":"One choice for the next slice of an incomplete FLT offer, listed by POST /v1/flights/returns (v1.27.0). The list is the supplier's answer for THIS outbound (the slices already chosen): another outbound has other options at other prices. key is valid only for the offerRef it was listed on and only until that offer's expiresAt. price is the TOTAL journey price for the whole party if this option is chosen, a sell price after the tenant's pricing rules exactly like Offer.price and never a supplier net; priceDelta is price minus the offer's current \"from\" price (Offer.price) and may be zero or negative. slices[].segmentIndexes index into this option's own segments. Every property is optional in the schema; key, sliceIndex, price and priceDelta are always set.\n","properties":{"key":{"type":"string","description":"Opaque; send it back as message.intent.returnOption to choose this option. Not a supplier token; never parse it\n"},"sliceIndex":{"type":"integer","minimum":0,"description":"The slice this option fills (the offer's journey.nextSliceIndex)"},"slices":{"type":"array","description":"The slice this option fills (one entry); segmentIndexes point into segments below","items":{"$ref":"#/components/schemas/AirSlice"}},"segments":{"type":"array","description":"The flights of this option, in flying order","items":{"$ref":"#/components/schemas/AirSegment"}},"fare":{"type":"object","additionalProperties":true,"description":"Fare facts of this option: the same OPEN object, with the same documented keys, as AirExtension.fare. A client ignores keys it does not know.\n"},"fareRules":{"$ref":"#/components/schemas/AirFareRules","description":"The fare rules of the journey if this option is chosen, when the supplier states them per option"},"price":{"$ref":"#/components/schemas/Money","description":"The TOTAL journey price for the party if this option is chosen, after the tenant's pricing rules (like Offer.price); never a supplier net. /v1/reprice still firms it.\n"},"priceDelta":{"$ref":"#/components/schemas/Money","description":"price minus the outbound offer's \"from\" price; may be zero or negative"},"upstream":{"$ref":"#/components/schemas/UpstreamPassThrough","description":"The sector answer this option was read from, verbatim (OD-901: one call, the itinerariesBySector itinerary with sector 2 or later). Present only when the request sent context.includeUpstream true on a deployment that serves it.\n"},"conditions":{"$ref":"#/components/schemas/Conditions","description":"The offer's conditions if this option is chosen, when they differ per option; absent means the offer's own"}},"example":{"key":"ro_00000101_2","sliceIndex":1,"slices":[{"index":1,"origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"stops":0,"segmentIndexes":[0]}],"segments":[{"marketingCarrier":"AI","flightNumber":"9173","origin":"BOM","destination":"SFO","departure":"2027-01-06T13:30:00+05:30","arrival":"2027-01-06T17:30:00-08:00","durationMinutes":1050,"cabin":"ECONOMY","bookingClass":"S"}],"price":{"amount":201400,"currency":"USD"},"priceDelta":{"amount":12400,"currency":"USD"}}},"FlightReturnsRequest":{"type":"object","description":"POST /v1/flights/returns (v1.27.0): the unified envelope, context.domain FLT. message.intent {offerRef} lists the options for the offer's next slice; {offerRef, returnOption} chooses one.\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` names the outbound offer and, to choose one, the return.","type":"object","required":["intent"],"properties":{"intent":{"description":"Send offerRef alone to list the returns of that outbound; add returnOption to choose one.","type":"object","required":["offerRef"],"properties":{"offerRef":{"type":"string","description":"An OUTBOUND_FIRST FLT offer whose journey is not complete (OF-F-...)"},"returnOption":{"type":"string","description":"A key from the latest listing of this offer (AirReturnOption.key). Absent: list the options. Present: choose it.\n"}}}}}}},"FlightReturnsResponse":{"type":"object","description":"POST /v1/flights/returns answer (v1.27.0). Listing: offer is the outbound offer as it stands (journey complete false), with returnOptions and returnOptionsComplete. Choosing: offer is the narrowed offer under the SAME offerRef, and returnOptions and returnOptionsComplete are absent.\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","properties":{"offer":{"$ref":"#/components/schemas/Offer"},"returnOptions":{"type":"array","description":"Listing only: the options for slice offer.extension.air.journey.nextSliceIndex, in the supplier's order. An empty list with a warning NO_RETURN_OPTIONS means the supplier said there are none for this outbound: choose another outbound.\n","items":{"$ref":"#/components/schemas/AirReturnOption"}},"returnOptionsComplete":{"type":"boolean","description":"Listing only: true when returnOptions is everything the supplier returned for this outbound; false when it was cut (a supplier page or a platform cap)\n"},"warnings":{"type":"array","description":"As served, the same list as context.warnings","items":{"$ref":"#/components/schemas/Warning"}}}}}},"HotelImage":{"type":"object","description":"One property or room photo (v1.25.0). `url` is a public CDN URL the supplier publishes; the platform passes it through and does not proxy or re-host it.\n","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"caption":{"type":"string","description":"The supplier's caption or category, for example Lobby or Deluxe King room"},"isPrimary":{"type":"boolean","description":"True on the image to show first; at most one per list"}},"example":{"url":"https://images.example.com/hotels/syn-1001/lobby.jpg","caption":"Lobby","isPrimary":true}},"HotelAmenity":{"type":"object","description":"One amenity or rate inclusion (v1.25.0). `name` is the display text in the request locale where the supplier provides it; `code` is the supplier's or the platform's amenity code when one exists (an open string, never a closed list). No icon URL is shipped: a client maps `code` or `name` to its own icons.\n","required":["name"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"top":{"type":"boolean","description":"True on the headline amenities a compact card shows first"}},"example":{"code":"WIFI","name":"Free Wi-Fi","top":true}},"HotelRoomRateOption":{"type":"object","description":"connect-v1 v1.41.0. One room rate an HTL detail lists (HotelExtension.roomRateOptions). key is opaque and valid only for the offerRef it was listed on, until that offer's expiresAt: send it back as message.intent.selection.roomRate to narrow the offer to this rate (the narrowed offer's price, conditions and room are this rate's). price is the TOTAL stay price for the rooms and party searched if this rate is chosen: a SELL price after the tenant's pricing rules, exactly like Offer.price (the platform prices every option with the rules that priced the offer), never a supplier net; priceDelta is price minus the offer's current price and may be zero or negative. A rate the rules cannot price is left out and counted in a warning (OFFER_UNSELLABLE). key, price and priceDelta are always set.\n","required":["key","price","priceDelta"],"properties":{"key":{"type":"string","description":"Opaque; send it back as message.intent.selection.roomRate. Not a supplier token; never parse it"},"room":{"$ref":"#/components/schemas/HotelRoom"},"rate":{"type":"object","additionalProperties":true,"description":"The rate's terms, the same OPEN object with the same documented keys as HotelExtension.rate"},"price":{"$ref":"#/components/schemas/Money"},"priceDelta":{"$ref":"#/components/schemas/Money"},"referencePrice":{"$ref":"#/components/schemas/Money","description":"This rate's reference (\"was\") price, on the same terms as OfferPricing.referencePrice"}},"example":{"key":"327203388-2","room":{"roomGroup":"Standard Room, 1 Queen Bed, Non Smoking","bedConfig":"1 Queen Bed","maxOccupancy":2,"board":"RO"},"rate":{"refundability":"NON_REFUNDABLE","payType":"PREPAID"},"price":{"amount":5512,"currency":"USD"},"priceDelta":{"amount":-324,"currency":"USD"}}},"HotelRoom":{"type":"object","description":"One room, or the whole property as one unit (IMP-P2-087; CANONICAL-MODEL §3.2 rooms[])","properties":{"roomGroup":{"type":"string","description":"The supplier's room group, or WHOLE_PROPERTY for a VR unit (a vacation-rental supplier, a vacation-rental supplier, a vacation-rental supplier)"},"description":{"type":"string"},"quantity":{"type":"integer","minimum":1,"description":"Units of this room or property booked; a VR is one unit"},"occupancy":{"$ref":"#/components/schemas/PartySpec"},"bedConfig":{"type":"string"},"board":{"type":"string","description":"Canonical board code (RO, BB, HB, FB, AI); open string"},"rateNotes":{"type":"array","items":{"type":"string"}},"images":{"type":"array","description":"Room photos as the supplier lists them (v1.25.0); public CDN URLs","items":{"$ref":"#/components/schemas/HotelImage"}},"amenities":{"type":"array","description":"Room amenities (v1.25.0)","items":{"$ref":"#/components/schemas/HotelAmenity"}},"inclusions":{"type":"array","description":"What the rate includes beyond the room, for example breakfast or parking (v1.25.0)","items":{"$ref":"#/components/schemas/HotelAmenity"}},"maxOccupancy":{"type":"integer","minimum":1,"description":"The most guests the room sleeps, as the supplier states it (v1.25.0)"},"bedCount":{"type":"integer","minimum":0,"description":"Number of beds in the room (v1.25.0); bedConfig keeps the descriptive text"},"size":{"type":"object","description":"Room floor area (v1.25.0)","required":["value","unit"],"properties":{"value":{"type":"number","minimum":0},"unit":{"type":"string","description":"SQFT | SQM; x-extensible-enum","x-extensible-enum":["SQFT","SQM"]}}},"view":{"type":"string","description":"The room's view as the supplier states it, for example City view (v1.25.0)"},"unitsAvailable":{"type":"integer","minimum":0,"description":"Rooms of this type the supplier reported free when the offer was built (v1.25.0). INDICATIVE only, for a \"few left\" hint: never a reservation and never re-checked before reprice.\n"},"petsAllowed":{"type":"boolean","description":"True when the supplier states pets are allowed in this room (v1.25.0)"},"confirmationNumber":{"type":"string","description":"The hotel's own confirmation number for this room (v1.25.0). Set on ORDER lines only, after booking, when the supplier returns one per room; an offer never carries it.\n"}},"example":{"roomGroup":"WHOLE_PROPERTY","description":"3-bedroom villa with pool","quantity":1,"occupancy":{"adults":4,"childAges":[6,9]},"board":"RO"}},"HotelExtension":{"type":"object","description":"HTL / VR / DAYUSE block (CANONICAL-MODEL §3.2; IMP-P2-087): property cross-walk, rooms or the whole-property unit and quantity, the rate's refundability / pay type / revalidation, the booking model (INSTANT or REQUEST_TO_BOOK — the Example Villas model where the order waits on an email-bound owner answer), the stay, and the sell floor the markup engine must honour.\n","properties":{"property":{"type":"object","properties":{"canonicalId":{"type":"string"},"provisional":{"type":"boolean"},"name":{"type":"string"},"propertyType":{"type":"string","description":"Hotel, Resort, Apartment, Hostel and so on, as the supplier states it; open string (v1.25.0)"},"starRating":{"type":"number","minimum":0,"maximum":5,"description":"The official or supplier star rating, halves allowed (v1.25.0)"},"address":{"$ref":"#/components/schemas/PostalAddress"},"geo":{"$ref":"#/components/schemas/GeoPoint"},"chain":{"type":"object","description":"The hotel chain or brand (v1.25.0)","properties":{"code":{"type":"string"},"name":{"type":"string"}}},"guestRating":{"type":"object","description":"Guest review score as the supplier reports it (v1.25.0). `score` is on `scale` (for example 4.3 of 5 or 8.6 of 10); a client rescales, it never assumes a scale.\n","properties":{"score":{"type":"number","minimum":0},"scale":{"type":"number","minimum":0,"description":"The maximum possible score, for example 5 or 10"},"count":{"type":"integer","minimum":0,"description":"Number of reviews behind the score"},"source":{"type":"string","description":"Who collected the reviews, for example the supplier or a review provider"},"label":{"type":"string","description":"The supplier's word for the score, for example Excellent or Pleasant (v1.41.0); display text, never a score"},"scores":{"type":"array","description":"Sub-scores by category (cleanliness, location, service), on the same scale","items":{"type":"object","properties":{"name":{"type":"string"},"score":{"type":"number","minimum":0}}}}}},"images":{"type":"array","description":"Property photos (v1.25.0); public CDN URLs, at most one isPrimary","items":{"$ref":"#/components/schemas/HotelImage"}},"amenities":{"type":"array","description":"Property amenities (v1.25.0); `top` marks the headline ones for a compact list","items":{"$ref":"#/components/schemas/HotelAmenity"}},"checkInTime":{"type":"string","description":"Earliest check-in, local time at the property, as the supplier states it (for example 15:00) (v1.25.0)"},"checkOutTime":{"type":"string","description":"Latest check-out, local time at the property (for example 11:00) (v1.25.0)"},"phone":{"type":"string","description":"The property's public front-desk phone number (v1.25.0); never a traveller's"},"description":{"type":"string","description":"The property description as the supplier writes it; plain text (v1.25.0)"},"timeZone":{"type":"string","description":"IANA time zone of the property, for example America/New_York (v1.25.0)"},"policies":{"type":"array","description":"The property's house policies as the supplier writes them (pets, children, payment, deposits) (v1.25.0)","items":{"type":"object","properties":{"name":{"type":"string"},"lines":{"type":"array","items":{"type":"string"}}}}},"nearby":{"type":"array","description":"Points of interest near the property (v1.25.0)","items":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string","description":"Airport, attraction, transit and so on; open string"},"distanceKm":{"type":"number","minimum":0}}}},"distanceKm":{"type":"number","minimum":0,"description":"Distance from the searched point or area centre, in kilometres (v1.25.0)"}}},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelRoom"}},"roomRateOptions":{"type":"array","description":"connect-v1 v1.41.0. The room rates a POST /v1/detail of an HTL offer lists (no selection sent), each priced: HotelRoomRateOption. Present only on that listing; a detail with selection.roomRate narrows the offer to one rate and carries none. The hotel supplier's supplierExtensions.mondeeHotels.roomRates (keys and rooms, no price) stays as it was.\n","items":{"$ref":"#/components/schemas/HotelRoomRateOption"}},"unit":{"type":"object","description":"The bookable unit when it is not a room","properties":{"type":{"type":"string","description":"ROOM | WHOLE_PROPERTY | APARTMENT | VILLA; open string"},"quantity":{"type":"integer"},"bedrooms":{"type":"integer"},"maxOccupancy":{"type":"integer"}}},"rate":{"type":"object","properties":{"refundability":{"type":"string","description":"FREE_UNTIL | NON_REFUNDABLE | PARTIAL; open string"},"payType":{"type":"string","description":"PREPAID | PAY_AT_PROPERTY; open string"},"revalidation":{"type":"string","description":"NONE | MANDATORY — MANDATORY ⇔ the supplier requires its own pre-book check (a recheck or price check) before booking"},"bookingModel":{"type":"string","description":"INSTANT | REQUEST_TO_BOOK — a REQUEST_TO_BOOK rate books into PENDING_SUPPLIER with conditions.confirmSlaSeconds (a vacation-rental supplier)"},"rateCode":{"type":"string"},"ratePlanName":{"type":"string","description":"The rate plan's display name as the supplier states it, for example Flexible rate, breakfast included (v1.25.0)"},"policies":{"type":"array","description":"The rate's policies as the supplier writes them, each a name and its lines (v1.41.0): age restriction, check-in, occupancy, guarantee, pets, mandatory fees, no-show and the like, in the shape of property.policies. Display text: the binding cancellation terms stay Offer.conditions.cancellation, the binding charges Offer.price and pricing.payAtProperty.\n","items":{"type":"object","properties":{"name":{"type":"string"},"lines":{"type":"array","items":{"type":"string"}}}}}}},"confirmationNumber":{"type":"string","description":"The hotel's own confirmation number for the stay (v1.25.0). Set on ORDER lines only, after booking; an offer never carries it. When the supplier confirms per room, rooms[].confirmationNumber carries each room's number and this one, when present, is the booking-level number.\n"},"stay":{"type":"object","properties":{"checkIn":{"type":"string","format":"date"},"checkOut":{"type":"string","format":"date"},"nights":{"type":"integer"}}},"sellFloor":{"$ref":"#/components/schemas/Money"},"securityDeposit":{"$ref":"#/components/schemas/SecurityDeposit"},"payAtProperty":{"type":"array","items":{"$ref":"#/components/schemas/PriceBreakdownLine"}}},"example":{"property":{"canonicalId":"PROP-88431","provisional":false,"name":"Casa Branca"},"rooms":[{"roomGroup":"WHOLE_PROPERTY","quantity":1,"occupancy":{"adults":4,"childAges":[6]},"board":"RO"}],"unit":{"type":"VILLA","quantity":1,"bedrooms":3,"maxOccupancy":7},"rate":{"refundability":"FREE_UNTIL","payType":"PREPAID","revalidation":"NONE","bookingModel":"REQUEST_TO_BOOK"},"stay":{"checkIn":"2026-11-10","checkOut":"2026-11-14","nights":4},"securityDeposit":{"amount":{"amount":2000000,"currency":"INR"},"collectedBy":"SUPPLIER","refundable":true}}},"GroundWaypoint":{"type":"object","description":"One stop of a ground journey on an offer or order line (CANONICAL-MODEL §3.3 waypoints[]; IMP-P2-086)","required":["type"],"properties":{"type":{"type":"string","enum":["PICKUP","DROPOFF","STOP"]},"location":{"$ref":"#/components/schemas/PlaceRef"},"scheduledAt":{"type":"string","format":"date-time"},"derivedFromFlight":{"type":"boolean","description":"True when the supplier set scheduledAt from flightLink (Example Transfers +30/+60 min; i'way recommended-time — a transfer supplier)"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"flightStatus":{"$ref":"#/components/schemas/FlightStatus"},"routeEstimate":{"$ref":"#/components/schemas/RouteEstimate"},"legId":{"type":"string","description":"The leg this waypoint belongs to on a round trip"}},"example":{"type":"PICKUP","location":{"iata":"GRU"},"scheduledAt":"2026-11-11T08:10:00-03:00","derivedFromFlight":true,"flightLink":{"carrier":"LA","flightNumber":"8085","date":"2026-11-11"},"legId":"LEG-1"}},"GroundDispatch":{"type":"object","description":"Post-booking is dispatch, not documents (CANONICAL-MODEL §3.3): the driver lifecycle on an order line, updated by order.dispatch_updated. NO_SHOW and NOT_COMPLETED added (a transfer supplier).\n","required":["state"],"properties":{"state":{"type":"string","description":"REQUESTED | DRIVER_ASSIGNED | EN_ROUTE | ARRIVED | IN_PROGRESS | COMPLETED | NO_SHOW | NOT_COMPLETED | CANCELLED; open string"},"driver":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"rating":{"type":"number"}}},"vehicle":{"type":"object","properties":{"make":{"type":"string"},"model":{"type":"string"},"colour":{"type":"string"},"plate":{"type":"string"}}},"trackingUrl":{"type":"string","format":"uri"},"guestContact":{"type":"string","description":"The number the traveller calls; supplier-provided"},"meetSignText":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}},"example":{"state":"DRIVER_ASSIGNED","driver":{"name":"Carlos","phone":"+5511988880000"},"vehicle":{"make":"Toyota","model":"Corolla","colour":"Black","plate":"ABC1D23"},"trackingUrl":"https://track.example.com/t/9f2a","meetSignText":"SILVA","updatedAt":"2026-11-11T07:30:00-03:00"}},"GroundExtension":{"type":"object","description":"TXI / XFR / CAR block (CANONICAL-MODEL §3.3; IMP-P2-086; a transfer supplier, a transfer supplier): waypoints (flat) and legs (a round trip is two), the service with hourly duration, free waiting time and shared-vs-private, dispatch on the order line, the operating provider under an aggregator, the toll policy, the supplier's passenger-email policy, and how the search ran (sync or polled).\n","properties":{"emissions":{"$ref":"#/components/schemas/EmissionsFigure","x-status":"planned","description":"Estimated CO2 of the ride (route-estimate based; planned — beyond extension.air)"},"waypoints":{"type":"array","items":{"$ref":"#/components/schemas/GroundWaypoint"}},"legs":{"type":"array","description":"Journeys of a round trip or multi-leg booking; waypoints[].legId points here","items":{"type":"object","required":["legId"],"properties":{"legId":{"type":"string"},"direction":{"type":"string","description":"OUTBOUND | RETURN"},"scheduledAt":{"type":"string","format":"date-time"}}}},"service":{"type":"object","properties":{"class":{"type":"string"},"serviceType":{"type":"string","description":"TRANSFER | HOURLY | RIDE | RENTAL; open string"},"durationHours":{"type":"integer","description":"HOURLY hire length (a transfer supplier)"},"freeWaitingMinutes":{"type":"integer","description":"Included waiting after landing or at pickup (G-15)"},"shared":{"type":"boolean","description":"True for a shuttle or shared ride (a transfer supplier)"},"leadTimeCutoff":{"type":"string","format":"date-time"},"maxPassengers":{"type":"integer"},"maxLuggage":{"type":"integer"}}},"dispatch":{"$ref":"#/components/schemas/GroundDispatch"},"operatingProvider":{"type":"object","description":"The company that drives, when fareSource.supplier is an aggregator (G-18); display only","properties":{"name":{"type":"string"},"code":{"type":"string"}}},"tollPolicy":{"type":"string","description":"INCLUDED | BILLED_AFTER | BILLED_AFTER_WITH_APPROVAL | FORBIDDEN; open string (G-3)"},"passengerEmailPolicy":{"type":"string","description":"SUPPLIER_EMAILS | PLATFORM_ONLY — whether the supplier emails the passenger directly (G-21)"},"searchMode":{"type":"string","description":"SYNC | POLLED — how this offer was obtained (G-20)"},"rental":{"type":"object","additionalProperties":true,"description":"CAR: {acriss, payType: PAY_NOW|PAY_AT_DESK, driverAgeBand, counter}"}},"example":{"waypoints":[{"type":"PICKUP","location":{"iata":"GRU"},"scheduledAt":"2026-11-11T08:10:00-03:00","derivedFromFlight":true,"legId":"LEG-1"},{"type":"DROPOFF","location":{"text":"Hotel Unique, São Paulo"},"legId":"LEG-1"}],"legs":[{"legId":"LEG-1","direction":"OUTBOUND"}],"service":{"class":"SEDAN","serviceType":"TRANSFER","freeWaitingMinutes":60,"shared":false},"operatingProvider":{"name":"Blacklane Partner SP"},"tollPolicy":"INCLUDED","passengerEmailPolicy":"PLATFORM_ONLY","searchMode":"SYNC"}},"ExperienceOption":{"type":"object","description":"A product configuration choice made before pricing (an activity supplier; Example GDS Discover, Example Activities)","required":["optionId","type"],"properties":{"optionId":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","description":"OPTIONS | TEXT | BOOLEAN | NUMBER; open string"},"required":{"type":"boolean"},"affectsPrice":{"type":"boolean"},"choices":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"priceDelta":{"$ref":"#/components/schemas/Money"}}}},"selected":{"type":"string"}},"example":{"optionId":"OPT-LANG","label":"Guide language","type":"OPTIONS","required":true,"affectsPrice":false,"choices":[{"value":"en","label":"English"},{"value":"it","label":"Italian"}]}},"ExperienceVariant":{"type":"object","description":"One ticket variant (CANONICAL-MODEL §3.4 variants[]) with age range, unit type and pax count (an activity supplier)","required":["variantId","label"],"properties":{"variantId":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer"},"ageRange":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}}},"unitType":{"type":"string","description":"PERSON | GROUP | VEHICLE | SEAT | FAMILY; open string"},"paxCount":{"type":"integer","description":"Travellers one unit admits (a FAMILY ticket admits 4)"},"accompaniedBy":{"type":"string","description":"The variant an INFANT or CHILD must travel with, when the supplier requires it"},"price":{"$ref":"#/components/schemas/Money"}},"example":{"variantId":"CHILD","label":"Child","count":1,"ageRange":{"min":4,"max":12},"unitType":"PERSON","paxCount":1,"accompaniedBy":"ADULT"}},"EventSeating":{"type":"object","description":"Event ticket seating (an event-ticket supplier) — section, row, seats, the quantities the listing can be split into, the seat map","properties":{"section":{"type":"string"},"row":{"type":"string"},"seats":{"type":"array","items":{"type":"string"}},"allowedQuantities":{"type":"array","description":"The exact counts purchasable from this listing (Example Tickets splits[])","items":{"type":"integer"}},"seatMapRef":{"type":"string"},"ticketFormat":{"type":"string","description":"ETICKET | MOBILE_TRANSFER | PHYSICAL | WILL_CALL | GUEST_LIST; open string"},"wheelchair":{"type":"boolean"},"viewType":{"type":"string"}},"example":{"section":"114","row":"C","seats":["7","8"],"allowedQuantities":[1,2,4],"ticketFormat":"MOBILE_TRANSFER"}},"ExperiencesExtension":{"type":"object","description":"EXP / ATT / TOUR / EVT / MOV block (CANONICAL-MODEL §3.4; IMP-P2-085): options before pricing, variants with age ranges, guide language per timeslot, event seating, the tax quote bound to the price and the all-in display flag, open-dated validity, bundles, the prefunded supplier balance a hold spends, and redemption on the order line.\n","properties":{"title":{"type":"string","maxLength":500,"description":"The supplier's display title of the product (the tour, attraction or activity), as the supplier states it. Display text only: never an identity, never translated by the platform.\n"},"travelDate":{"type":"string","format":"date","description":"The local calendar day the experience takes place, as searched and booked. Present when the supplier sells by day; the start time, when known, is `timeslot.start`.\n"},"productCode":{"type":"string","maxLength":100,"description":"The supplier's own code for the product (Example Activities `productCode`, e.g. `3327P1`), verbatim. The option chosen within it is `options[].optionId`. A supplier identifier for display and servicing, never a platform identity: the offer's identity stays `canonicalId`.\n"},"startTime":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","description":"The local start time of the booked slot (`HH:mm`), as the supplier states it, on `travelDate`. Present when the supplier states a time but no zone, so `timeslot.start` cannot be written.\n"},"destination":{"type":"string","maxLength":200,"description":"The destination the experience takes place in (a city or region name), as the supplier names it. Display text only.\n"},"imageUrl":{"type":"string","format":"uri","maxLength":2000,"description":"The supplier's lead image of the product. Display only; never re-hosted by the platform."},"priceBasis":{"type":"string","description":"connect-v1 v1.48.0, additive. What `Offer.price` prices. PARTY_TOTAL (also when absent, as before): the whole party, as asked. PER_TRAVELLER_FROM: the supplier's lowest price for ONE traveller across the product's options (Example Activities `pricing.summary.fromPrice`); the reprice answers the party's firm total for the chosen option, so the two differ by design. A client labels the price (\"from, per person\") and never multiplies it: the party total is the reprice's. x-extensible-enum: a client tolerates a value it does not know and shows the price unlabelled.\n","x-extensible-enum":["PARTY_TOTAL","PER_TRAVELLER_FROM"],"example":"PER_TRAVELLER_FROM"},"voucher":{"type":"object","description":"Order line only: the voucher the supplier issued at book, which the traveller presents. Only ever the supplier's own URL — never synthesised.\n","properties":{"url":{"type":"string","format":"uri"},"format":{"type":"string","description":"HTML | PDF | QR | …; the supplier's own word, open string"},"type":{"type":"string","description":"STANDARD | MOBILE | PAPER | …; the supplier's own word, open string"}}},"supplierCancellationPolicy":{"type":"object","description":"The cancellation terms as the supplier states them, verbatim (Example Activities `cancellationPolicy`). The platform's own refund terms stay the offer's `cancellationPolicy`; this is the supplier's text and refund schedule, kept for servicing and display.\n","properties":{"type":{"type":"string","description":"STANDARD | CUSTOM | ALL_SALES_FINAL | …; open string"},"description":{"type":"string"},"cancelIfBadWeather":{"type":"boolean"},"cancelIfInsufficientTravelers":{"type":"boolean"},"refundEligibility":{"type":"array","items":{"type":"object","properties":{"dayRangeMin":{"type":"integer"},"dayRangeMax":{"type":"integer"},"percentageRefundable":{"type":"number"},"startTimestamp":{"type":"string","format":"date-time"},"endTimestamp":{"type":"string","format":"date-time"}}}}}},"timeslot":{"type":"object","properties":{"slotId":{"type":"string"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"capacityState":{"type":"string","description":"AVAILABLE | LIMITED | SOLD_OUT | UNKNOWN; open string"},"guideLanguages":{"type":"array","description":"BCP-47 tags this slot runs in (G-5)","items":{"type":"string"}}}},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ExperienceVariant"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ExperienceOption"}},"seating":{"$ref":"#/components/schemas/EventSeating"},"taxQuote":{"type":"object","description":"Tax-quote binding (G-8); the signature travels reprice → order unchanged","properties":{"signature":{"type":"string"},"basis":{"type":"string","description":"WHOLESALE | RETAIL"},"allInPriceDisplayRequired":{"type":"boolean"}}},"validity":{"type":"object","description":"Open-dated tickets (G-13)","properties":{"openDated":{"type":"boolean"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"}}},"bundle":{"type":"object","description":"A package of several products sold as one (G-20)","properties":{"bundleId":{"type":"string"},"components":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"label":{"type":"string"}}}}}},"supplierBalance":{"type":"object","description":"Prefunded supplier balance (GlobalTix credit, EvoPay — G-14); amounts never shown to clients, only whether a hold spends it","properties":{"prefunded":{"type":"boolean"},"holdSpendsBalance":{"type":"boolean"}}},"pickup":{"type":"object","properties":{"mode":{"type":"string","description":"PICKUP | MEET_ON_LOCATION"},"point":{"$ref":"#/components/schemas/PlaceRef"}}},"fulfillment":{"type":"object","properties":{"instantDelivery":{"type":"boolean"},"barcodeFormat":{"type":"string","description":"PDF_ONLY | RAW | PKPASS | CODE128 | QR | ZIP; open string"},"documentScope":{"type":"string","description":"PER_UNIT | PER_BOOKING"}}},"redemption":{"type":"object","description":"Order line only (G-10)","properties":{"state":{"type":"string","description":"UNKNOWN | ISSUED | REDEEMED | PARTIALLY_REDEEMED | EXPIRED | REVOKED; open string"},"redeemedAt":{"type":"string","format":"date-time"},"tracking":{"type":"string","description":"NONE | POLL | PUSH"}}}},"example":{"timeslot":{"slotId":"TS-0930","start":"2026-11-12T09:30:00+01:00","capacityState":"AVAILABLE","guideLanguages":["en","it"]},"variants":[{"variantId":"ADULT","label":"Adult","count":2,"ageRange":{"min":13,"max":99},"unitType":"PERSON"},{"variantId":"CHILD","label":"Child","count":1,"ageRange":{"min":4,"max":12},"unitType":"PERSON","accompaniedBy":"ADULT"}],"options":[{"optionId":"OPT-LANG","label":"Guide language","type":"OPTIONS","required":true,"affectsPrice":false,"selected":"en"}],"validity":{"openDated":false},"fulfillment":{"instantDelivery":true,"barcodeFormat":"PDF_ONLY","documentScope":"PER_UNIT"},"redemption":{"state":"ISSUED","tracking":"POLL"}}},"CruiseExtension":{"type":"object","description":"CRU block (CANONICAL-MODEL §3.5); open beyond the named fields. Since v1.35.0 the named fields carry the sailing and its itinerary, the ship's content, the category directory, the detail ladder's choices (keys are opaque and short-lived, never supplier ids), the chosen cabin, the fare lines and the payment schedule. connect-v1 v1.44.0 (upstream parity, CHANGE-NOTE-connect-v1-2026-10-23-v1.44.0) names the rest of what the cruise service answers: the vendor session's configuration (detail level F), the rate rows' remarks, terms and credits and the category content on the CATEGORY choices, the itemised taxes and rate description on the fare, the booked line's confirmation number, vendor code, status and consumer advice (`booking`), and the payment options the line offered at booking. Never present: commission, net fare, supplier session ids, order and item ids.\n","additionalProperties":true,"properties":{"sailing":{"type":"object","additionalProperties":true,"properties":{"cruiseType":{"type":"string","enum":["OCEAN","RIVER"]},"name":{"type":"string"},"line":{"$ref":"#/components/schemas/CruiseNamedRef"},"ship":{"type":"object","properties":{"id":{"type":"string"},"shipId":{"type":"string"},"name":{"type":"string"},"imo":{"type":"string"},"contentSlug":{"type":"string"},"image":{"type":"string","description":"The supplier's ship image (root-relative path or URL), as answered"}}},"embarkPort":{"$ref":"#/components/schemas/CruiseNamedRef"},"disembarkPort":{"$ref":"#/components/schemas/CruiseNamedRef"},"embarkDate":{"type":"string","format":"date"},"disembarkDate":{"type":"string","format":"date"},"nights":{"type":"integer"},"cancellationNotice":{"type":"object","description":"The heading and language of the line's cancellation policy (its text is conditions.cancellation.policyText)","properties":{"title":{"type":"string"},"languageCode":{"type":"string"}}},"supplier":{"type":"object","additionalProperties":true,"description":"The supplier's own facts of the sailing row (v1.44.0), as the cruise service states them: sailingCode (the line's short code), planId / planCode (the Revelex sailing plan), packageId / packageTypeId (0 = cruise only), marketName / marketNames[] (the selling market), tourOnly and segment (\"yes\" / \"no\" as answered), status, createdDate (the supplier's ingestion date). Reference data for a client that mirrors the supplier's catalogue; nothing here is a servicing key.\n","properties":{"sailingCode":{"type":"string"},"planId":{"type":"string"},"planCode":{"type":"string"},"packageId":{"type":"string"},"packageTypeId":{"type":"string"},"marketName":{"type":"string"},"marketNames":{"type":"array","items":{"type":"string"}},"tourOnly":{"type":"string"},"segment":{"type":"string"},"status":{"type":"string"},"createdDate":{"type":"string"}}},"region":{"$ref":"#/components/schemas/CruiseNamedRef"},"itineraryDays":{"type":"array","items":{"type":"object","properties":{"day":{"type":"integer"},"date":{"type":"string","format":"date"},"port":{"$ref":"#/components/schemas/CruiseNamedRef"},"arrive":{"type":"string"},"depart":{"type":"string"},"atSea":{"type":"boolean"}}}}}},"ship":{"type":"object","additionalProperties":true,"description":"Ship content (detail only)","properties":{"content":{"type":"object","additionalProperties":true,"description":"The content provider's ship page (v1.44.0): teaser, introduction, uniqueFeature, gratuities, videoUrl, shipClass, size, style, shipType, shipKind, adultsOnly, childrensFacilities, nursery, smoking, celebrityTies, usefulToKnow, revelaxShipFacts (HTML), socialLinks {twitter, facebook, website}, accommodationIntro, diningIntro, entertainmentIntro, twitterUrl, facebookUrl, websiteUrl, coverImage {size: url} / coverImageUrl, the sections accommodationTypes[], enrichmentTypes[], healthFitnessTypes[], kidTeenTypes[], usefulTypes[] (each row: name, description, types[], stats, gradeCodes[], facilities[], accessibleCabin, images[]), ports[] (dayNumber, portCode, description, latitude, longitude, arrivalTime, departureTime, departsOn) and cruises[] (ref, name, cruise, holidayDate: the provider's other cruises on this ship). Marketing content for display; nothing here is a servicing key.\n"},"facts":{"type":"object","additionalProperties":true},"images":{"type":"array","items":{"type":"string"}},"deckPlans":{"type":"array","items":{"type":"object","properties":{"deck":{"type":"string"},"image":{"type":"string"}}}},"dining":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"categoryDirectory":{"type":"array","description":"The ship's cabin categories, unpriced (detail without selection)","items":{"type":"object","properties":{"supplierCategoryId":{"type":"string","description":"The line's own category id"},"color":{"type":"string","description":"The deck-plan colour of the category"},"image":{"type":"string","description":"The stateroom image path"},"validFrom":{"type":"string"},"validTo":{"type":"string"},"createdDate":{"type":"string"},"modifiedDate":{"type":"string"},"cabinType":{"type":"string"},"categoryCode":{"type":"string"},"name":{"type":"string"}}}},"choices":{"type":"object","description":"The next rung of the detail ladder: CATEGORY (priced for the party) or CABIN. Pass an item's key back in intent.selection; a list longer than one page carries nextCursor, passed back as intent.selection.page.\n","properties":{"level":{"type":"string","enum":["CATEGORY","CABIN"]},"items":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"A CATEGORY item carries cabinType, categoryCode, categoryName, berthedCategoryCode, rateCode, rateDescription, price, perPerson, taxesPerPerson, portChargesPerPerson, nonRefundable, status, bookable and, since connect-v1 v1.44.0, the rate row's other facts (rateRemarks, rateTerms, rateTermsMustAccept, waitlisted, taxesIncluded, nccfIncluded, onboardCredit, shoreExcursionCredit) and `content`, and — since the upstream parity pass — the rest of the rate row as the service states it: taxesAndPortChargesPerPerson (Money), rateInfoAvailable, groupNumber, applicablePromotions, promotionAdjustments[], airProviderName, booked (each only when stated; null and empty on every answer seen) and supplierComponentId (the line's own component id, shown as the supplier relays it; `selection.category` still takes `key`). A CABIN item carries cabinNumber, deck, bedType, minGuests, maxGuests, location, accessible, connecting, obstructed and — since the upstream parity pass — supplierDeckId (the line's deck row id, a catalogue id) and cabinInfoAvailable, or `guarantee: true` with a label.\n","properties":{"key":{"type":"string"},"supplierComponentId":{"type":"string"},"taxesAndPortChargesPerPerson":{"$ref":"#/components/schemas/Money"},"content":{"type":"array","description":"The category's content rows: `source` (widgety | line), `name`, `description` (HTML), `facilities[]`, `images[]` with `imageNames[]`, `floorplans[]` with `floorplanNames[]`, `accessible`, `stats {type, minOccupancy, maxOccupancy, minSize, maxSize}`, `cabinType`, `color`, and on a line row `supplierCategoryId` and `languageCode` (the line's own row id and language, as the supplier relays them). Display content; nothing priced.\n","items":{"type":"object","additionalProperties":true}}}}},"nextCursor":{"type":"string"},"rateAnswer":{"type":"object","additionalProperties":true,"description":"The rate answer's own facts beside the CATEGORY items: convertedCurrency, vendorInfo, convertedVendorInfo, promotions[] — each only when stated (null or empty on every answer seen). Absent when the answer states none.\n"}}},"cabinSelection":{"type":"object","additionalProperties":true,"properties":{"cabinType":{"type":"string"},"categoryCode":{"type":"string"},"categoryName":{"type":"string"},"deck":{"type":"string"},"cabinNumber":{"type":"string"},"guarantee":{"type":"boolean"},"bedType":{"type":"string"},"maxGuests":{"type":"integer"},"location":{"type":"string"},"accessible":{"type":"boolean"},"obstructed":{"type":"boolean"}}},"fare":{"type":"object","additionalProperties":true,"description":"Since the upstream parity pass the figure rows' own labels and ids ride beside the figures: lineFacts {tariff | nccf | tax | total: {typeId, name, travelerIndex; total also totalTravelerFare}}, taxBreakdown[].typeId / travelerIndex, cancellationSchedule[].time / amountTypeId / description, perGuest[].travelerIndex and perGuest[].lineFacts — as the line states them (null or empty on every answer seen where so).\n","properties":{"rateCode":{"type":"string"},"nonRefundable":{"type":"boolean"},"leadInPerPerson":{"$ref":"#/components/schemas/Money"},"tariff":{"$ref":"#/components/schemas/Money"},"nccf":{"$ref":"#/components/schemas/Money"},"taxes":{"$ref":"#/components/schemas/Money"},"total":{"$ref":"#/components/schemas/Money"},"rateDescription":{"type":"string"},"taxBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"}}}},"taxesPerPerson":{"$ref":"#/components/schemas/Money"},"portChargesPerPerson":{"$ref":"#/components/schemas/Money"},"perGuestTotals":{"type":"array","items":{"$ref":"#/components/schemas/Money"}},"perGuest":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"guest":{"type":"string","description":"The guest's number as the line states it (passenger index or name number)"},"tariff":{"$ref":"#/components/schemas/Money"},"nccf":{"$ref":"#/components/schemas/Money"},"taxes":{"$ref":"#/components/schemas/Money"},"taxBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"}}}},"total":{"$ref":"#/components/schemas/Money"}}}},"leadIns":{"type":"array","description":"The search answer's per-person lead-in fares, one per cabin category (cabinType, categoryCode, rateCode, description, perPerson, nccf, nccfIncluded, taxes, taxesIncluded); the cheapest is `leadInPerPerson`.\n","items":{"type":"object","additionalProperties":true,"properties":{"supplierPriceId":{"type":"string","description":"The supplier's id of this lead-in price row"},"priceCode":{"type":"string","description":"The line's short code on the price row"},"marketName":{"type":"string"},"createdDate":{"type":"string","description":"The supplier's ingestion date of the row"}}}},"cancellationSchedule":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string","format":"date"},"amount":{"$ref":"#/components/schemas/Money"},"percentBasisPoints":{"type":"integer"}}}}}},"optionHold":{"type":"object","additionalProperties":true,"properties":{"expiresAt":{"type":"string","format":"date-time"},"status":{"type":"string"},"alternateDiningRequired":{"type":"boolean","description":"The hold said an alternate dining choice is required"},"nonRefundableDepositSavings":{},"convertedPricing":{},"vendorOptionSets":{"type":"array","items":{}},"vendorAddedOptionSets":{"type":"array","items":{}}}},"depositSchedule":{"type":"object","properties":{"depositDue":{"type":"string","format":"date"},"balanceDue":{"type":"string","format":"date"}}},"paymentSchedule":{"type":"array","items":{"type":"object","properties":{"typeId":{},"time":{"type":"string"},"timeZone":{"type":"string"},"dueDate":{"type":"string","format":"date"},"amount":{"$ref":"#/components/schemas/Money"},"name":{"type":"string"}}}},"vendorConfiguration":{"type":"object","additionalProperties":true,"description":"The line's rules for this sailing, as its vendor session states them (detail with selection.fares): the hold lifetime, the party rules and the titles and suffixes it accepts, and the session's reference (`supplierSessionRefId`, what the upstream platform's client holds as `sessionId`; a reference only — the vendor's own session material behind it is kept by the platform and never on the wire). The flags object carries every other vendor_configuration value under the supplier's own key.\n","properties":{"supplierSessionRefId":{"type":"string","description":"The supplier's reference for the vendor session this detail opened (what the upstream platform's client holds as `sessionId`). The booked line repeats it as `booking.supplierSessionRefId`.\n"},"notices":{"type":"array","description":"The line's notices on the session answer (empty on every answer seen); items as answered","items":{}},"errors":{"type":"array","description":"The session answer's non-fatal errors (a fatal one is the typed error); items as answered","items":{}},"holdTimeoutSeconds":{"type":"integer"},"minAgeSupervisor":{"type":"integer"},"passengerTypes":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"The supplier's passenger type code as it states it (ADT, SRC, CHD and INF on Holland America); open string"},"maxCount":{"type":"integer"},"minAge":{"type":"integer"},"minAgeUnit":{"type":"string"},"maxAge":{"type":"integer"},"maxAgeUnit":{"type":"string"},"minBirthDate":{"type":"string","format":"date"},"maxBirthDate":{"type":"string","format":"date"}}}},"titles":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"code":{"type":"string"},"name":{"type":"string"},"gender":{"type":"string"}}}},"suffixes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}},"pastPassengerFields":{"type":"object","additionalProperties":true},"passengerFields":{"type":"object","additionalProperties":true},"flags":{"type":"object","additionalProperties":true}}},"booking":{"type":"object","additionalProperties":true,"description":"On cruise ORDER lines only: the cruise line's booking as the upstream platform's trip product shows it — the confirmation number and vendor code, the line's own booking status, whether the confirm step succeeded (false: the option exists at the line and the order is PENDING_SUPPLIER), the line's consumer advice, and the supplier session the booking was made in (`supplierSessionRefId`). An offer never carries it. The order and item ids stay on the order's supplier reference and in the booking answer copy (`OrderItem.upstream`).\n","properties":{"confirmationNumber":{"type":"string"},"vendorCode":{"type":"string"},"status":{"type":"string"},"confirmed":{"type":"boolean"},"consumerAdvice":{"type":"array","items":{"type":"string"}},"supplierSessionRefId":{"type":"string","description":"The supplier's reference for the vendor session the booking was made in — what the upstream platform's client holds as `sessionId` and quotes to the booking desk. A reference only: the vendor's own session material behind it (the Revelex session ids the supplier answers under `data.internal`) is kept by the platform under this reference and is never on the wire.\n"}}},"paymentOptions":{"type":"array","description":"The payment options the line offered when the booking was priced, with the amount each would take (NO_PAYMENT is the HOLD option this platform books; DEPOSIT and FULL_PAYMENT are stated for the customer, not collected here: D-CRU-4). Card methods are not carried.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The supplier's own name for the option (NO_PAYMENT, DEPOSIT, FULL_PAYMENT seen); open string"},"amount":{"$ref":"#/components/schemas/Money"},"typeId":{},"labelToken":{"type":"string"}}}},"paymentConfiguration":{"type":"object","additionalProperties":true,"description":"What the line's checkout stated beside the payment options (order lines, from the pre-book pricing answer): methods[] — the card methods it would take, each {type {id, name, labelToken}, allowElectronic, allowManual, creditCard {code, name, typeId, fee, feeCap, feePercentage, mandatoryCvv, mandatoryValidFromDate}} — and mandatoryBillingAddress / mandatoryShippingAddress. Stated, not used: card payment is NOT_IMPLEMENTED for cruise (D-CRU-4). Never a component id.\n"}},"example":{"sailing":{"cruiseType":"OCEAN","name":"7 Night Western Caribbean","line":{"id":"mondee:cruise-line:4kd8s1","name":"MSC Cruises"},"ship":{"name":"MSC Seaside"},"embarkPort":{"id":"mondee:port:2jd81k","name":"Miami, Florida"},"embarkDate":"2027-01-09","disembarkDate":"2027-01-16","nights":7},"cabinSelection":{"cabinType":"BALCONY","categoryCode":"B2","cabinNumber":"9202"},"optionHold":{"expiresAt":"2026-11-15T23:59:00Z","status":"Option"},"booking":{"confirmationNumber":"SBXCONF0001","vendorCode":"SYN","status":"Option","confirmed":true,"supplierSessionRefId":"0b1e2c3d-0000-4000-8000-000000000001"},"paymentOptions":[{"type":"NO_PAYMENT"},{"type":"DEPOSIT","amount":{"amount":50000,"currency":"USD"}}]}},"CruiseNamedRef":{"type":"object","description":"A lookup id and its display name (v1.35.0)","properties":{"id":{"type":"string"},"name":{"type":"string"},"vendorId":{"type":"string"},"destinationId":{"type":"string"}}},"PackageExtension":{"type":"object","description":"PKG block (v1.35.0; a package supplier); open beyond the named fields. The package, its hotel, its flights or bus legs, its transfer; on detail the keyed choices (paged); on order lines one component per hotel, flight or bus leg and transfer. Never present: supplier keys, booking-shell ids, commission, net figures, document links.\n","additionalProperties":true,"properties":{"package":{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","enum":["HOTEL_FLIGHT","HOTEL_BUS","SERVICE"]},"title":{"type":"string"},"nights":{"type":"integer"},"origin":{"$ref":"#/components/schemas/CruiseNamedRef"},"destination":{"$ref":"#/components/schemas/CruiseNamedRef"},"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}}},"hotel":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"stars":{"type":"number"},"address":{"type":"string"},"geo":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}}},"images":{"type":"array","items":{"type":"string"}},"facilities":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"room":{"type":"object","properties":{"name":{"type":"string"},"board":{"type":"string"},"refundable":{"type":"boolean"}}}}},"flights":{"type":"object","additionalProperties":true,"properties":{"inventory":{"type":"string","enum":["BLOCK","DYNAMIC"]},"outbound":{"$ref":"#/components/schemas/PackageLeg"},"inbound":{"$ref":"#/components/schemas/PackageLeg"}}},"bus":{"type":"object","additionalProperties":true,"properties":{"outbound":{"$ref":"#/components/schemas/PackageLeg"},"inbound":{"$ref":"#/components/schemas/PackageLeg"}}},"transfer":{"type":"object","properties":{"type":{"type":"string"},"vehicle":{"type":"string"},"included":{"type":"boolean"}}},"servicePackage":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"}}},"choices":{"type":"object","description":"Detail only: keyed choices per kind (rooms, outbound, inbound, transfers), first page each; pass a key back in intent.selection, a kind's nextCursor in intent.selection.page.\n","additionalProperties":true},"components":{"type":"array","description":"Order lines only — one entry per hotel, flight or bus leg and transfer","items":{"type":"object","additionalProperties":true,"properties":{"kind":{"type":"string","enum":["HOTEL","FLIGHT","BUS","TRANSFER","SERVICE"]},"name":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"location":{"type":"string"},"fare":{"$ref":"#/components/schemas/Money"}}}}}},"PackageLeg":{"type":"object","description":"One flight or bus direction of a package (v1.35.0)","additionalProperties":true,"properties":{"departs":{"type":"string"},"arrives":{"type":"string"},"carrier":{"type":"string"},"numbers":{"type":"array","items":{"type":"string"}},"stops":{"type":"integer"},"baggage":{"type":"string"}}},"InsuranceExtension":{"type":"object","description":"INS block (ancillary proposal §3.7, applied to CANONICAL-MODEL §3.7; IMP-P2-088; SUP-INS-EXAMPLE): the plan, headline coverage, riders, period, the insured people with a premium each (one policy per insured), where the rating came from, underwriting gates, regulatory facts, the parent booking of an INS_ADDON, and the policy state on the order line.\n","properties":{"plan":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string","description":"DOMESTIC | INBOUND | OVERSEAS | STUDENT | SCHENGEN | CANCELLATION | DOMESTIC_CANCELLATION"},"tripType":{"type":"string","description":"SINGLE | ROUND_TRIP | ANNUAL"},"sumInsured":{"$ref":"#/components/schemas/Money"},"coverageArea":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"}},"excludes":{"type":"array","items":{"type":"string"}}}}}},"coverage":{"type":"array","items":{"type":"object","properties":{"benefit":{"type":"string"},"limit":{"$ref":"#/components/schemas/Money"},"deductible":{"$ref":"#/components/schemas/Money"}}}},"riders":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"cover":{"$ref":"#/components/schemas/Money"},"deductible":{"$ref":"#/components/schemas/Money"},"premium":{"$ref":"#/components/schemas/Money"},"optional":{"type":"boolean"},"selected":{"type":"boolean"}}}},"period":{"type":"object","properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"days":{"type":"integer"},"dayBand":{"type":"string"}}},"insured":{"type":"array","items":{"type":"object","properties":{"travelerIndex":{"type":"integer"},"ageAtDeparture":{"type":"integer"},"premium":{"$ref":"#/components/schemas/Money"},"eligible":{"type":"boolean"},"ineligibleReason":{"type":"string"},"itemId":{"type":"string","description":"Order only — the line this insured person's policy became (one offer → N items)"}}}},"rating":{"type":"object","properties":{"source":{"type":"string","description":"SUPPLIER_QUOTE | LOCAL_TABLE"},"tableVersion":{"type":"string"},"tableEffectiveFrom":{"type":"string","format":"date"}}},"underwriting":{"type":"object","properties":{"declarations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"requiredAnswer":{"type":"string"}}}},"medicalEvidence":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"whenAgeAtLeast":{"type":"integer"}}}}}},"regulatory":{"type":"object","properties":{"insurer":{"type":"string"},"distributorRole":{"type":"string","description":"CORPORATE_AGENT | REFERRAL | MERCHANT_OF_RECORD | UNKNOWN"},"freeLook":{"type":"object","properties":{"days":{"type":"integer"},"from":{"type":"string","description":"ISSUE | RECEIPT"}}},"wordingDocRef":{"type":"string"},"disclosures":{"type":"array","items":{"type":"string"}}}},"attachTo":{"type":"object","properties":{"orderRef":{"type":"string"},"itemId":{"type":"string"}}},"policy":{"type":"object","description":"Order line only","properties":{"status":{"type":"string","description":"ISSUED | PENDING_ISSUE | ENDORSEMENT_PENDING | CANCELLATION_PENDING; open string"},"endorsementState":{"type":"string"}}}},"example":{"plan":{"name":"Overseas Explorer","category":"OVERSEAS","tripType":"SINGLE","sumInsured":{"amount":25000000,"currency":"USD"},"coverageArea":{"regions":["WORLDWIDE"],"excludes":[]}},"period":{"start":"2026-11-10","end":"2026-11-20","days":11},"insured":[{"travelerIndex":0,"ageAtDeparture":34,"premium":{"amount":189000,"currency":"INR"},"eligible":true}],"rating":{"source":"LOCAL_TABLE","tableVersion":"2026-09"},"regulatory":{"insurer":"Example Insurance (underwriter per wording)","distributorRole":"CORPORATE_AGENT","freeLook":{"days":15,"from":"ISSUE"}},"policy":{"status":"ISSUED"}}},"AirportServicesExtension":{"type":"object","description":"FASTTRK / APTSVC block (ancillary proposal §3.8, applied to CANONICAL-MODEL §3.8; IMP-P2-088; an airport-services supplier): the service, station and flight link, when it is served, the party, the tax code for the GST invoice, verbatim policy links, the e-ticket evidence the handler needs, and fulfilment on the order line. One supplier booking may back several lines (lounge + porter): they share OrderItem.supplierBookingGroup.\n","properties":{"flightStatus":{"$ref":"#/components/schemas/FlightStatus","x-status":"planned","description":"Live status of the linked flight (planned — beyond extension.air)"},"service":{"type":"object","properties":{"category":{"type":"string","description":"MEET_ASSIST | PORTER | BUGGY | WHEELCHAIR | FAST_TRACK | TRANSFER | BUNDLE"},"name":{"type":"string"},"features":{"type":"array","items":{"type":"string"}},"fulfilledBy":{"type":"string","description":"The ground handler; display only"}}},"station":{"$ref":"#/components/schemas/Station"},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"serviceAt":{"type":"string","format":"date-time"},"pax":{"type":"object","properties":{"adults":{"type":"integer"},"children":{"type":"integer"},"childMaxAge":{"type":"integer"},"additionalAdults":{"type":"integer"},"additionalChildren":{"type":"integer"}}},"taxCode":{"$ref":"#/components/schemas/TaxCode"},"policies":{"type":"object","properties":{"rescheduleText":{"type":"string"},"links":{"type":"object","additionalProperties":{"type":"string"}}}},"evidence":{"type":"object","properties":{"eticketRequired":{"type":"boolean"},"transport":{"type":"string","description":"FILE | URL"},"maxBytes":{"type":"integer"}}},"fulfillment":{"type":"object","description":"Order line only","properties":{"state":{"type":"string","description":"PENDING | CONFIRMED | DELIVERED | CANCELLED; open string — PENDING is a real long state"},"handlerContact":{"type":"string"}}}},"example":{"service":{"category":"MEET_ASSIST","name":"Departure meet and assist","features":["porter","fast-track security"],"fulfilledBy":"Encalm"},"station":{"airport":"DEL","terminal":"3","terminalType":"INTERNATIONAL","direction":"DEPARTURE"},"flightLink":{"carrier":"AI","flightNumber":"131","date":"2026-11-10","departureTerminal":"3"},"serviceAt":"2026-11-10T20:30:00+05:30","pax":{"adults":2,"children":1,"childMaxAge":12},"taxCode":{"scheme":"HSN","code":"996761"},"evidence":{"eticketRequired":true,"transport":"FILE","maxBytes":5242880},"fulfillment":{"state":"PENDING"}}},"LoungeExtension":{"type":"object","description":"LOUNGE block (ancillary proposal §3.9, applied to CANONICAL-MODEL §3.9; IMP-P2-088; SUP-FNB-EXAMPLE2, an airport-services supplier): the canonical outlet, the entitlement model (VOUCHER or PREBOOK), eligibility, the flight link when pre-booked, where the price came from, and the voucher's redemption state on the order line. The code itself is on the VOUCHER / QR document, never here.\n","properties":{"flightStatus":{"$ref":"#/components/schemas/FlightStatus","x-status":"planned","description":"Live status of the linked flight when pre-booked (planned — beyond extension.air)"},"outlet":{"type":"object","properties":{"canonicalId":{"type":"string"},"provisional":{"type":"boolean"},"name":{"type":"string"},"site":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"AIRPORT | RAIL"}}},"access":{"type":"object","properties":{"terminal":{"type":"string"},"terminalType":{"type":"string"},"airside":{"type":"boolean"}}},"content":{"type":"object","additionalProperties":true}}},"entitlement":{"type":"object","properties":{"model":{"type":"string","description":"VOUCHER | PREBOOK"},"visits":{"type":"integer"},"guests":{"type":"integer"},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"}}},"eligibility":{"type":"object","properties":{"flightLinked":{"type":"boolean"},"windowBeforeDepartureMinutes":{"type":"integer"},"terminalRestriction":{"type":"string"}}},"flightLink":{"$ref":"#/components/schemas/FlightLinkRef"},"pricing":{"type":"object","properties":{"source":{"type":"string","description":"SUPPLIER | PLATFORM_PRICE_LIST"},"priceListVersion":{"type":"string"},"walkInComparable":{"$ref":"#/components/schemas/Money"}}},"voucher":{"type":"object","description":"Order line only","properties":{"redemptionState":{"type":"string","description":"UNKNOWN | ISSUED | REDEEMED | EXPIRED | VOIDED; open string"},"redemptionTracking":{"type":"string","description":"NONE | POLL | PUSH"}}}},"example":{"outlet":{"canonicalId":"LNG-DEL-T3-PLAZA","name":"Plaza Premium Lounge","site":{"code":"DEL","name":"Indira Gandhi International","type":"AIRPORT"},"access":{"terminal":"3","terminalType":"INTERNATIONAL","airside":true}},"entitlement":{"model":"VOUCHER","visits":1,"guests":1,"validUntil":"2027-05-10T00:00:00+05:30"},"pricing":{"source":"PLATFORM_PRICE_LIST","priceListVersion":"2026-Q4"},"voucher":{"redemptionState":"ISSUED","redemptionTracking":"NONE"}}},"VisaExtension":{"type":"object","description":"VISA block (ancillary proposal §3.10, applied to CANONICAL-MODEL §3.10; IMP-P2-088; SUP-VIS-EXAMPLE2, a visa supplier): the corridor, the visa type, exemptions, the requirement set the answers were collected against and when it was shown (evidence), processing facts, the advisory level, and the application stage on the order line.\n","properties":{"corridor":{"type":"object","properties":{"nationality":{"type":"string"},"residence":{"type":"string"},"origin":{"type":"string"},"destinations":{"type":"array","items":{"type":"string"}},"purpose":{"type":"string"}}},"visaType":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string"},"entry":{"type":"string","description":"SINGLE | DOUBLE | MULTI"},"validity":{"type":"object","properties":{"value":{"type":"integer"},"unit":{"type":"string"}}},"stay":{"type":"object","properties":{"value":{"type":"integer"},"unit":{"type":"string"}}},"applicationMethod":{"type":"string","description":"ONLINE | EMBASSY | ON_ARRIVAL | VISA_FREE | NOT_APPLICABLE"}}},"exemptions":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","description":"RESIDENCE_PERMIT | THIRD_COUNTRY_VISA"},"documentCountry":{"type":"string"},"minimumValidityMonths":{"type":"integer"},"conditionsText":{"type":"string"}}}},"requirementSet":{"type":"object","properties":{"version":{"type":"string"},"shownAt":{"type":"string","format":"date-time"}}},"processing":{"type":"object","properties":{"expectedCompletionDate":{"type":"string","format":"date"},"slaDays":{"type":"integer"},"dueDate":{"type":"string","format":"date"},"appointmentRequired":{"type":"boolean"}}},"advisory":{"type":"object","properties":{"enforcement":{"type":"string","description":"MANDATORY | NOT_REQUIRED | MAY_BE_REQUIRED"}}},"application":{"type":"object","description":"Order line only","properties":{"stage":{"type":"string","description":"COLLECTING | PAYMENT | REVIEW | REVIEW_FAILED | APPOINTMENT | FILED | DECIDED; open string"},"decision":{"type":"string","description":"APPROVED | REJECTED"},"decidedAt":{"type":"string","format":"date-time"},"appointments":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"date":{"type":"string","format":"date"},"time":{"type":"string"},"address":{"type":"string"}}}}}}},"example":{"corridor":{"nationality":"IN","residence":"IN","destinations":["AE"],"purpose":"TOURISM"},"visaType":{"name":"Tourist visa 30 days","entry":"SINGLE","validity":{"value":60,"unit":"DAYS"},"stay":{"value":30,"unit":"DAYS"},"applicationMethod":"ONLINE"},"requirementSet":{"version":"2026-09-18","shownAt":"2026-11-01T10:05:00Z"},"processing":{"slaDays":4,"appointmentRequired":false},"advisory":{"enforcement":"MANDATORY"},"application":{"stage":"FILED"}}},"EnrichmentProvenance":{"type":"object","description":"Where a decorated value came from and how fresh it is (SPI proposal §1 law 5); one entry per source used","required":["kind","provider","retrievedAt"],"properties":{"kind":{"type":"string","description":"FLIGHT_EMISSIONS | TYPICAL_EMISSIONS | FLIGHT_STATUS | FLIGHT_RATINGS | FLIGHT_AMENITIES | FLIGHT_TRACK | ROUTE_ESTIMATE | ROUTE_MATRIX | WEATHER_CURRENT | WEATHER_FORECAST | WEATHER_HISTORICAL | HOLIDAYS | ENTRY_REQUIREMENTS | TRANSIT_REQUIREMENTS; open string"},"provider":{"type":"string","description":"Supplier registry id of the data provider"},"providerVersion":{"type":"string"},"modelVersion":{"type":"string"},"retrievedAt":{"type":"string","format":"date-time"},"asOf":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"stale":{"type":"boolean"},"attribution":{"type":"string","description":"Display text the data terms require (TIM CC BY-SA)"},"license":{"type":"string"}},"example":{"kind":"FLIGHT_EMISSIONS","provider":"SUP-DAT-EXAMPLE2","modelVersion":"2025.3","retrievedAt":"2026-11-01T02:00:00Z","asOf":"2026-11-01T02:00:00Z","expiresAt":"2026-11-08T02:00:00Z","stale":false,"attribution":"Travel Impact Model, Google (CC BY-SA 4.0)"}},"EnrichmentBlock":{"type":"object","description":"The provenance list of every enrichment on an offer or order line; absent when nothing was decorated","properties":{"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}}},"example":{"provenance":[{"kind":"FLIGHT_EMISSIONS","provider":"SUP-DAT-EXAMPLE2","retrievedAt":"2026-11-01T02:00:00Z","stale":false}]}},"FlightKey":{"description":"One flight, by carrier, number, route and date.","type":"object","required":["operatingCarrier","flightNumber","origin","destination","departureDate"],"properties":{"operatingCarrier":{"description":"The airline that flies the aircraft, as its two-character IATA code.","type":"string"},"marketingCarrier":{"description":"The airline that sells the flight, when it differs from the operating one.","type":"string"},"flightNumber":{"type":"string","description":"Normalised, no leading zeros"},"origin":{"description":"The departure airport, as its IATA code.","type":"string"},"destination":{"description":"The arrival airport, as its IATA code.","type":"string"},"departureDate":{"type":"string","format":"date","description":"Airport-local date"}},"example":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"}},"EmissionsFigure":{"type":"object","required":["grams"],"properties":{"grams":{"type":"integer","description":"Authoritative"},"kg":{"type":"number"},"lb":{"type":"number"}},"example":{"grams":306000,"kg":306,"lb":674.6}},"FlightEmissions":{"type":"object","description":"CO2e per passenger for one operated leg (SPI proposal §3.1)","required":["flight","perPax"],"properties":{"flight":{"$ref":"#/components/schemas/FlightKey"},"perPax":{"type":"object","description":"Cabin → figure; cabins ECONOMY, PREMIUM_ECONOMY, BUSINESS, FIRST","additionalProperties":{"$ref":"#/components/schemas/EmissionsFigure"}},"selectedCabin":{"type":"string"},"typical":{"type":"object","description":"Cabin → market-typical grams","additionalProperties":{"type":"integer"}},"deltaVsTypicalPercent":{"type":"integer"},"contrailsImpact":{"type":"string","description":"LOW | MODERATE | HIGH | UNKNOWN"},"methodology":{"type":"object","properties":{"source":{"type":"string"},"modelVersion":{"type":"string"}}},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"perPax":{"ECONOMY":{"grams":306000,"kg":306,"lb":674.6},"BUSINESS":{"grams":918000,"kg":918,"lb":2023.8}},"selectedCabin":"ECONOMY","typical":{"ECONOMY":332000},"deltaVsTypicalPercent":-8,"contrailsImpact":"MODERATE","methodology":{"source":"TIM","modelVersion":"2025.3"}}},"FlightStatus":{"type":"object","description":"Operational status of one leg (SPI proposal §3.2); never changes order state","required":["flight","status","lastUpdatedAt"],"properties":{"flight":{"$ref":"#/components/schemas/FlightKey"},"codeshares":{"type":"array","items":{"type":"string"}},"status":{"type":"string","description":"SCHEDULED | DELAYED | BOARDING | DEPARTED | AIRBORNE | LANDED | ARRIVED | CANCELLED | DIVERTED | RETURNED | NOT_OPERATING | UNKNOWN; open string"},"boarding":{"type":"string","description":"CHECK_IN_OPEN | CHECK_IN_CLOSED | BOARDING | FINAL_CALL | GATE_CLOSED"},"departure":{"$ref":"#/components/schemas/FlightStatusPoint"},"arrival":{"$ref":"#/components/schemas/FlightStatusPoint"},"delayMinutes":{"type":"object","properties":{"departure":{"type":"integer"},"arrival":{"type":"integer"}}},"divertedTo":{"type":"string"},"equipment":{"type":"object","properties":{"iataCode":{"type":"string"}}},"travelDay":{"type":"object","properties":{"date":{"type":"string","format":"date"},"phase":{"type":"string","description":"PRE_DEPARTURE | DAY_OF | IN_FLIGHT | COMPLETED"}}},"lastUpdatedAt":{"type":"string","format":"date-time","description":"The provider's data timestamp, not the retrieval time"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"status":"DELAYED","departure":{"airport":"GRU","terminal":"3","gate":"318","scheduled":"2026-11-10T23:50:00-03:00","estimated":"2026-11-11T00:35:00-03:00"},"arrival":{"airport":"MIA","terminal":"N","scheduled":"2026-11-11T07:10:00-05:00","estimated":"2026-11-11T07:50:00-05:00","baggageBelt":"12"},"delayMinutes":{"departure":45,"arrival":40},"travelDay":{"date":"2026-11-10","phase":"DAY_OF"},"lastUpdatedAt":"2026-11-10T21:02:00Z"}},"FlightStatusPoint":{"type":"object","properties":{"airport":{"type":"string"},"terminal":{"type":"string"},"gate":{"type":"string"},"scheduled":{"type":"string","format":"date-time"},"estimated":{"type":"string","format":"date-time"},"actual":{"type":"string","format":"date-time"},"baggageBelt":{"type":"string"}},"example":{"airport":"MIA","terminal":"N","scheduled":"2026-11-11T07:10:00-05:00","estimated":"2026-11-11T07:50:00-05:00","baggageBelt":"12"}},"FlightStatusAlert":{"type":"object","description":"A platform-derived change between two FlightStatus snapshots (SPI proposal §3.2); identical for every provider","required":["alertId","kind","severity","detectedAt"],"properties":{"alertId":{"type":"string"},"flight":{"$ref":"#/components/schemas/FlightKey"},"kind":{"type":"string","description":"DELAY | CANCELLATION | DIVERSION | RETURN | GATE_CHANGE | TERMINAL_CHANGE | BAGGAGE_BELT | BOARDING | DEPARTED | LANDED"},"severity":{"type":"string","description":"INFO | WARN | CRITICAL"},"previous":{"type":"object","additionalProperties":true,"description":"A partial FlightStatus (the fields that changed; planned typing, the object stays open)","properties":{"status":{"type":"string"},"departure":{"$ref":"#/components/schemas/FlightStatusPoint"},"arrival":{"$ref":"#/components/schemas/FlightStatusPoint"},"delayMinutes":{"type":"object","properties":{"departure":{"type":"integer"},"arrival":{"type":"integer"}}},"divertedTo":{"type":"string"},"departureEstimated":{"type":"string","format":"date-time"}}},"current":{"type":"object","additionalProperties":true,"description":"A partial FlightStatus (the fields that changed; planned typing, the object stays open)","properties":{"status":{"type":"string"},"departure":{"$ref":"#/components/schemas/FlightStatusPoint"},"arrival":{"$ref":"#/components/schemas/FlightStatusPoint"},"delayMinutes":{"type":"object","properties":{"departure":{"type":"integer"},"arrival":{"type":"integer"}}},"divertedTo":{"type":"string"},"departureEstimated":{"type":"string","format":"date-time"}}},"detectedAt":{"type":"string","format":"date-time"},"via":{"type":"string","description":"PUSH | POLL"}},"example":{"alertId":"fsa_01JA7H","kind":"DELAY","severity":"WARN","previous":{"departureEstimated":"2026-11-10T23:50:00-03:00"},"current":{"departureEstimated":"2026-11-11T00:35:00-03:00"},"detectedAt":"2026-11-10T21:02:10Z","via":"PUSH"}},"FlightRatings":{"type":"object","description":"On-time statistics for a flight number or route (SPI proposal §3.3)","properties":{"flight":{"$ref":"#/components/schemas/FlightKey"},"route":{"type":"object","properties":{"origin":{"type":"string"},"destination":{"type":"string"}}},"sample":{"type":"object","properties":{"observations":{"type":"integer"},"codeshares":{"type":"integer"},"directs":{"type":"integer"}}},"onTimePercent":{"type":"number"},"distribution":{"type":"object","additionalProperties":{"type":"number"},"description":"onTime, late15, late30, late45, cancelled, diverted — percentages"},"delayMinutes":{"type":"object","properties":{"mean":{"type":"number"},"stdDev":{"type":"number"},"min":{"type":"integer"},"max":{"type":"integer"}}},"stars":{"type":"object","properties":{"onTime":{"type":"number"},"delay":{"type":"number"},"overall":{"type":"number"}}},"cancelProbabilityPercent":{"type":"number"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"route":{"origin":"GRU","destination":"MIA"},"sample":{"observations":412},"onTimePercent":81.5,"distribution":{"onTime":81.5,"late15":9.2,"late30":4.1,"late45":3,"cancelled":1.4,"diverted":0.8},"delayMinutes":{"mean":11.4,"stdDev":22.1},"stars":{"overall":4.1}}},"FlightAmenities":{"type":"object","description":"Cabin amenities of one leg (SPI proposal §3.3); confidence says whether published or predicted","required":["cabin"],"properties":{"cabin":{"type":"string"},"seat":{"type":"object","properties":{"pitchInches":{"type":"number"},"widthInches":{"type":"number"},"type":{"type":"string","description":"STANDARD | RECLINER | ANGLE_FLAT | FLAT_BED | SUITE"}}},"wifi":{"type":"string","description":"FREE | PAID | AVAILABLE | NONE | UNKNOWN"},"power":{"type":"string","description":"FREE | PAID | AVAILABLE | NONE | UNKNOWN"},"ife":{"type":"string","description":"SEATBACK | STREAMING | NONE | UNKNOWN"},"meal":{"type":"string"},"confidence":{"type":"string","description":"PUBLISHED | PREDICTED | UNKNOWN"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"cabin":"ECONOMY","seat":{"pitchInches":31,"type":"STANDARD"},"wifi":"PAID","power":"AVAILABLE","ife":"SEATBACK","confidence":"PREDICTED"}},"RouteEstimate":{"type":"object","description":"Door-to-door estimate for one leg (SPI proposal §3.4)","required":["mode","distanceMeters","durationSeconds"],"properties":{"legRef":{"type":"string"},"origin":{"$ref":"#/components/schemas/PlaceRef"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"mode":{"type":"string","description":"CAR | TAXI | BICYCLE | WALK"},"distanceMeters":{"type":"integer"},"durationSeconds":{"type":"integer"},"trafficDelaySeconds":{"type":"integer"},"noTrafficDurationSeconds":{"type":"integer"},"trafficModel":{"type":"string","description":"LIVE | HISTORICAL | NONE"},"departAt":{"type":"string","format":"date-time"},"arriveAt":{"type":"string","format":"date-time"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"mode":"CAR","distanceMeters":31200,"durationSeconds":2880,"trafficDelaySeconds":600,"trafficModel":"LIVE","departAt":"2026-11-11T08:10:00-03:00","arriveAt":"2026-11-11T08:58:00-03:00"}},"RouteMatrix":{"type":"object","required":["origins","destinations","mode","cells"],"properties":{"origins":{"type":"array","items":{"$ref":"#/components/schemas/PlaceRef"}},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/PlaceRef"}},"mode":{"type":"string"},"cells":{"type":"array","items":{"type":"object","required":["originIndex","destinationIndex"],"properties":{"originIndex":{"type":"integer"},"destinationIndex":{"type":"integer"},"estimate":{"$ref":"#/components/schemas/RouteEstimate"},"error":{"type":"string"}}}}},"example":{"origins":[{"iata":"GRU"}],"destinations":[{"placeId":"mondee:place:sp-hotel-unique"}],"mode":"CAR","cells":[{"originIndex":0,"destinationIndex":0,"estimate":{"mode":"CAR","distanceMeters":31200,"durationSeconds":2880}}]}},"Temperature":{"type":"object","required":["c","f"],"properties":{"c":{"type":"number"},"f":{"type":"number"}},"example":{"c":27.5,"f":81.5}},"WeatherForecast":{"type":"object","description":"Weather at a place (SPI proposal §3.5); min/max in C and F from one metric call","required":["place","kind"],"properties":{"place":{"$ref":"#/components/schemas/PlaceRef"},"kind":{"type":"string","description":"CURRENT | FORECAST | HISTORICAL"},"current":{"type":"object","properties":{"observedAt":{"type":"string","format":"date-time"},"temp":{"$ref":"#/components/schemas/Temperature"},"condition":{"type":"string"},"conditionText":{"type":"string"}}},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"min":{"$ref":"#/components/schemas/Temperature"},"max":{"$ref":"#/components/schemas/Temperature"},"avg":{"$ref":"#/components/schemas/Temperature"},"condition":{"type":"string","description":"CLEAR | PARTLY_CLOUDY | CLOUDY | FOG | DRIZZLE | RAIN | HEAVY_RAIN | THUNDERSTORM | SNOW | SLEET | HAIL | WINDY | UNKNOWN"},"conditionText":{"type":"string"}}}},"horizonExceeded":{"type":"boolean"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"place":{"iata":"MIA"},"kind":"FORECAST","days":[{"date":"2026-11-11","min":{"c":22,"f":71.6},"max":{"c":29,"f":84.2},"condition":"PARTLY_CLOUDY","conditionText":"Partly cloudy"}]}},"HolidayCalendar":{"type":"object","description":"Public holidays and platform-computed long weekends (SPI proposal §3.6)","required":["country","range","holidays"],"properties":{"country":{"type":"string"},"subdivision":{"type":"string"},"range":{"$ref":"#/components/schemas/DateRange"},"holidays":{"type":"array","items":{"type":"object","required":["date","name"],"properties":{"date":{"type":"string","format":"date"},"observedDate":{"type":"string","format":"date"},"name":{"type":"string"},"scope":{"type":"string","description":"NATIONAL | REGIONAL | LOCAL"},"public":{"type":"boolean"},"subdivisions":{"type":"array","items":{"type":"string"}}}}},"longWeekends":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"days":{"type":"integer"},"holidayDates":{"type":"array","items":{"type":"string","format":"date"}},"bridgeDays":{"type":"array","items":{"type":"string","format":"date"}}}}},"cells":{"type":"array","x-status":"planned","description":"Planned (web date-picker): one cell per day of `range` when include names CELLS — what a calendar renders without recomputing the long weekends.\n","items":{"$ref":"#/components/schemas/HolidayCalendarCell"}},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"country":"BR","range":{"from":"2026-11-01","to":"2026-11-30"},"holidays":[{"date":"2026-11-15","name":"Proclamação da República","scope":"NATIONAL","public":true},{"date":"2026-11-20","name":"Dia da Consciência Negra","scope":"NATIONAL","public":true}],"longWeekends":[{"start":"2026-11-20","end":"2026-11-22","days":3,"holidayDates":["2026-11-20"],"bridgeDays":[]}]}},"EntryRequirements":{"type":"object","description":"Documents a traveller needs for a corridor (SPI proposal §3.7). `verdict` is NOT_PROVIDED for every provider that lists documents without an eligibility verdict (Example Data); a client must never render an absent document as \"no visa needed\" without the disclaimer. `shownAt` is the liability timestamp kept on the order.\n","required":["query","groups","verdict","shownAt"],"properties":{"query":{"type":"object","additionalProperties":true},"groups":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","description":"ENTRY | EXIT | TRANSIT"},"country":{"type":"string"},"profiles":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"Typed per the SPI §3.7 profile (planned typing; the object stays open)","properties":{"profileId":{"type":"string"},"conditions":{"type":"array","items":{"type":"string"}},"documentOptions":{"type":"array","description":"Alternatives (outer array, any one satisfies) of document sets (inner array, all required)","items":{"type":"array","items":{"$ref":"#/components/schemas/EntryRequirementDocument"}}},"warning":{"type":"string"},"disclaimer":{"type":"string"},"insuranceLink":{"type":"string","format":"uri"}}}},"docsRequired":{"type":"boolean"}}}},"verdict":{"type":"string","description":"NOT_PROVIDED | ALLOWED | NOT_ALLOWED; open string"},"shownAt":{"type":"string","format":"date-time"},"disclaimerText":{"type":"string"},"provenance":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"example":{"query":{"origin":"BR","destination":"US","nationality":"BR","residence":"BR"},"groups":[{"kind":"ENTRY","country":"US","docsRequired":true,"profiles":[{"profileId":"default","documentOptions":[[{"name":"Visa B1/B2","type":"VISA","requirementLevel":"REQUIRED"}]]}]}],"verdict":"NOT_PROVIDED","shownAt":"2026-11-01T10:05:00Z","disclaimerText":"Requirements change; verify with the consulate before travel."}},"CarbonSummary":{"type":"object","description":"Account-level emissions over a period, from snapshots taken at order commit (SPI proposal §3.1)","required":["period","flownSegments","totalGrams"],"properties":{"tenant":{"type":"string"},"period":{"$ref":"#/components/schemas/DateRange"},"flownSegments":{"type":"integer"},"totalGrams":{"type":"integer"},"typicalGrams":{"type":"integer"},"savedGrams":{"type":"integer"},"kg":{"type":"number"},"lb":{"type":"number"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}}},"example":{"tenant":"acme-travel","period":{"from":"2026-10-01","to":"2026-10-31"},"flownSegments":1240,"totalGrams":384000000,"typicalGrams":401000000,"savedGrams":17000000,"kg":384000,"lb":846575}},"OrderEnrichment":{"type":"object","description":"The trip-page aggregate for one order (GET /v1/orders/{orderRef}/enrichment)","properties":{"flightStatus":{"type":"array","items":{"$ref":"#/components/schemas/FlightStatus"}},"alerts":{"type":"array","items":{"$ref":"#/components/schemas/FlightStatusAlert"}},"weather":{"type":"array","items":{"$ref":"#/components/schemas/WeatherForecast"}},"entryRequirements":{"type":"array","items":{"$ref":"#/components/schemas/EntryRequirements"}},"routeEstimates":{"type":"array","items":{"$ref":"#/components/schemas/RouteEstimate"}},"holidays":{"type":"array","items":{"$ref":"#/components/schemas/HolidayCalendar"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}}},"example":{"flightStatus":[{"flight":{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"},"status":"SCHEDULED","lastUpdatedAt":"2026-11-09T21:00:00Z"}],"weather":[{"place":{"iata":"MIA"},"kind":"FORECAST","days":[{"date":"2026-11-11","min":{"c":22,"f":71.6},"max":{"c":29,"f":84.2},"condition":"PARTLY_CLOUDY"}]}],"provenance":[{"kind":"FLIGHT_STATUS","provider":"SUP-DAT-EXAMPLE","retrievedAt":"2026-11-09T21:00:30Z","stale":false}]}},"FlightEmissionsResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is FlightEmissions[], provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/FlightEmissions"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":[],"provenance":[],"warnings":[]}},"FlightStatusResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is FlightStatus[] (one per operated leg), provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/FlightStatus"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":[],"provenance":[],"warnings":[]}},"FlightRatingsResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is FlightRatings, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/FlightRatings"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"onTimePercent":81.5},"provenance":[],"warnings":[]}},"FlightAmenitiesResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is FlightAmenities[] (one per cabin), provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/FlightAmenities"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":[],"provenance":[],"warnings":[]}},"FlightTrackResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is FlightTrack[] (one per operated leg that has taken off), provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/FlightTrack"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":[],"provenance":[],"warnings":[]}},"FlightTrack":{"type":"object","description":"The flown track of one operated leg: the aircraft's observed positions in time order, oldest first, and the latest one while the leg is airborne. Coordinates are WGS 84 degrees; times are UTC. Never changes order state.\n","required":["flight","airborne","points","lastUpdatedAt"],"properties":{"flight":{"$ref":"#/components/schemas/FlightKey"},"airborne":{"type":"boolean","description":"True while the aircraft is between take-off and landing at the provider's data time"},"points":{"type":"array","description":"Observed positions, oldest first; at least one","minItems":1,"items":{"$ref":"#/components/schemas/FlightTrackPoint"}},"current":{"$ref":"#/components/schemas/FlightTrackPoint"},"lastUpdatedAt":{"type":"string","format":"date-time","description":"The provider's data timestamp (the newest observation), not the retrieval time"}}},"FlightTrackPoint":{"type":"object","description":"One observed position of the aircraft","required":["position","at"],"properties":{"position":{"$ref":"#/components/schemas/GeoPoint"},"at":{"type":"string","format":"date-time","description":"When the position was observed (UTC)"},"altitudeFeet":{"type":"integer","description":"Reported altitude in feet; near the ground it can be zero or slightly negative"},"groundSpeedKnots":{"type":"integer","minimum":0},"headingDegrees":{"type":"integer","minimum":0,"maximum":360},"source":{"type":"string","description":"ADSB | RADAR | MLAT | DATALINK | SPACE_BASED | OCEANIC | PROJECTED | SURFACE | OTHER; open string. PROJECTED marks an estimated point, not an observed one"}}},"RouteEstimatesResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is estimates[] for legs or matrix for origins × destinations, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"type":"object","description":"{estimates: RouteEstimate[]} for legs, {matrix: RouteMatrix} for origins × destinations","properties":{"estimates":{"type":"array","items":{"$ref":"#/components/schemas/RouteEstimate"}},"matrix":{"$ref":"#/components/schemas/RouteMatrix"}}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"estimates":[]},"provenance":[],"warnings":[]}},"WeatherResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is WeatherForecast, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/WeatherForecast"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"place":{"iata":"MIA"},"kind":"FORECAST","days":[]},"provenance":[],"warnings":[]}},"HolidaysResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is HolidayCalendar, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/HolidayCalendar"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"country":"BR","range":{"from":"2026-11-01","to":"2026-11-30"},"holidays":[]},"provenance":[],"warnings":[]}},"EntryRequirementsResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is EntryRequirements, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/EntryRequirements"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"query":{},"groups":[],"verdict":"NOT_PROVIDED","shownAt":"2026-11-01T10:05:00Z"},"provenance":[],"warnings":[]}},"OrderEnrichmentResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is OrderEnrichment (the trip-page aggregate), provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/OrderEnrichment"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"flightStatus":[],"weather":[]},"provenance":[],"warnings":[]}},"CarbonSummaryResponse":{"type":"object","description":"The flat wrapper of this /v1/enrichment read (API-0008 §11 — the lookup family's ApiResponse shape, not the {context, message} envelope): data is CarbonSummary, provenance lists every provider used, warnings carries ENRICHMENT_UNAVAILABLE or ENRICHMENT_STALE. Cache-Control and ETag headers carry the SPI §6 TTL ladder. Enrichment never fails a verb: an unavailable provider is an empty data plus a warning.\n","required":["requestId","data"],"properties":{"requestId":{"type":"string"},"data":{"$ref":"#/components/schemas/CarbonSummary"},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/EnrichmentProvenance"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}},"example":{"requestId":"req_01JA7J","data":{"period":{"from":"2026-10-01","to":"2026-10-31"},"flownSegments":0,"totalGrams":0},"provenance":[],"warnings":[]}},"FlightEmissionsRequest":{"type":"object","description":"Body of POST /v1/enrichment/flight-emissions — lists outgrow query strings","properties":{"flights":{"description":"The flights to estimate, at most 50.","type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/FlightKey"}},"markets":{"type":"array","maxItems":50,"description":"Origin-destination pairs for typical emissions","items":{"type":"object","required":["origin","destination"],"properties":{"origin":{"description":"The departure airport of a market, as its IATA code.","type":"string"},"destination":{"description":"The arrival airport of a market, as its IATA code.","type":"string"}}}},"cabin":{"description":"The cabin to estimate for, for example ECONOMY.","type":"string"}},"example":{"flights":[{"operatingCarrier":"LA","flightNumber":"8084","origin":"GRU","destination":"MIA","departureDate":"2026-11-10"}],"cabin":"ECONOMY"}},"RouteEstimatesRequest":{"type":"object","description":"Body of POST /v1/enrichment/route-estimates — legs, or origins × destinations for a matrix","properties":{"legs":{"description":"The legs to estimate, at most 25.","type":"array","maxItems":25,"items":{"type":"object","required":["origin","destination"],"properties":{"legRef":{"description":"Your own id for the leg, returned on its estimate.","type":"string"},"origin":{"$ref":"#/components/schemas/PlaceRef"},"destination":{"$ref":"#/components/schemas/PlaceRef"}}}},"origins":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/PlaceRef"}},"destinations":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/PlaceRef"}},"modes":{"description":"How the traveller moves, for example CAR.","type":"array","items":{"type":"string"}},"departAt":{"description":"When the traveller sets off, for travel times that depend on traffic.","type":"string","format":"date-time"},"traffic":{"type":"string","description":"LIVE | HISTORICAL | NONE"}},"example":{"legs":[{"legRef":"OI-2/0","origin":{"iata":"GRU"},"destination":{"placeId":"mondee:place:sp-hotel-unique"}}],"modes":["CAR"],"departAt":"2026-11-11T08:10:00-03:00","traffic":"HISTORICAL"}},"ConsoleAgentResumeRequest":{"type":"object","description":"Resume a PAUSED agent (the pause's inverse; FEAT-019 §6). Audited with the reason.","required":["reason"],"properties":{"reason":{"type":"string","minLength":4,"maxLength":500}},"example":{"reason":"Fare-family mapping fixed in the 09:40 deploy; proposals re-verified"}},"ConsoleJobDownload":{"type":"object","description":"A signed, short-lived download link for a SUCCEEDED job's artifact (ledger export). The link is a bearer: anyone holding it can fetch the file until expiresAt, so it is never logged and never longer-lived than 15 minutes; ask again for a fresh one.\n","required":["jobId","url","expiresAt"],"properties":{"jobId":{"type":"string"},"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"},"contentType":{"type":"string"},"sizeBytes":{"type":"integer"},"rows":{"type":"integer"}},"example":{"jobId":"job_01J8QP4","url":"https://exports.example/t/acme-travel/job_01J8QP4.csv?X-Amz-Signature=redacted","expiresAt":"2026-09-27T09:55:41Z","contentType":"text/csv","sizeBytes":184220,"rows":1612}},"SupplierDataFreshness":{"type":"object","description":"FEAT-023 §7 sync_profile.freshness — durations are ISO-8601 (PT6H)","properties":{"ttl":{"description":"How long synced data counts as fresh, as an ISO 8601 duration (for example PT6H).","type":"string"},"maxStaleness":{"description":"The oldest data may get before it is no longer served, as an ISO 8601 duration.","type":"string"},"staleWhileRevalidate":{"description":"How long stale data may still be served while a refresh runs, as an ISO 8601 duration.","type":"string"},"liveFallback":{"type":"string","description":"LIVE_READ | STALE_MARKER | DROP — what search does past maxStaleness"}},"example":{"ttl":"PT6H","maxStaleness":"P1D","staleWhileRevalidate":"PT1H","liveFallback":"STALE_MARKER"}},"SupplierDataMode":{"type":"object","description":"One scheduled mode of a profile (FEAT-023 §7 modes[])","required":["mode"],"properties":{"mode":{"type":"string","description":"FULL | INCREMENTAL | WINDOW | EVENT | ON_DEMAND; open string"},"cron":{"type":"string","description":"Five-field cron in the profile's time zone; absent for EVENT and ON_DEMAND"},"window":{"description":"For WINDOW mode, the days to sync, counted from today.","type":"object","properties":{"fromOffsetDays":{"description":"First day of the window, in days from today.","type":"integer"},"toOffsetDays":{"description":"Last day of the window, in days from today.","type":"integer"},"sliceDays":{"description":"How many days each request of the run covers.","type":"integer"}}},"scope":{"type":"object","additionalProperties":true,"description":"Dataset-specific filters (countries, categories, property ids)"}},"example":{"mode":"WINDOW","cron":"0 2 * * *","window":{"fromOffsetDays":0,"toOffsetDays":120,"sliceDays":7}}},"SupplierDataProfile":{"type":"object","description":"How one supplier × dataset is synced for one credential set (FEAT-023 §7 sync_profile). `version` is the optimistic-concurrency token: PUT sends If-Match with the ETag the GET answered; a stale one is 409 CONFLICT details.reason VERSION_MISMATCH.\n","required":["supplierId","dataset","credentialSet","enabled","version"],"properties":{"supplierId":{"type":"string","description":"Supplier registry id (SUP-…)"},"dataset":{"type":"string","description":"catalog_product | property_content | availability_calendar | reference_points | rate_table | calendar; open string"},"credentialSet":{"type":"string"},"modes":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDataMode"}},"limits":{"type":"object","properties":{"concurrency":{"type":"integer"},"quotaShare":{"type":"number","description":"Fraction (0–1) of the supplier's daily quota a run may use"},"quietHours":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"timeZone":{"type":"string"}}}}}},"freshness":{"$ref":"#/components/schemas/SupplierDataFreshness"},"language":{"type":"string"},"currency":{"type":"string"},"enabled":{"type":"boolean"},"version":{"type":"integer"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"estimate":{"type":"object","description":"Read-only validation preview from the descriptor and the last run (FEAT-023 §5 item 2)","readOnly":true,"properties":{"calls":{"type":"integer"},"durationSeconds":{"type":"integer"},"quotaShare":{"type":"number"}}}},"example":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","modes":[{"mode":"FULL","cron":"0 3 * * 0"},{"mode":"INCREMENTAL","cron":"15 * * * *"}],"limits":{"concurrency":4,"quotaShare":0.3,"quietHours":[{"from":"09:00","to":"18:00","timeZone":"Europe/London"}]},"freshness":{"ttl":"PT6H","maxStaleness":"P1D","staleWhileRevalidate":"PT1H","liveFallback":"STALE_MARKER"},"language":"en","currency":"USD","enabled":true,"version":7,"updatedAt":"2026-10-01T08:00:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","estimate":{"calls":1850,"durationSeconds":2400,"quotaShare":0.21}}},"SupplierDataProfileUpdate":{"type":"object","description":"PUT body — the editable part of a profile; version comes from If-Match","properties":{"modes":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDataMode"}},"limits":{"description":"Rate and volume limits for the sync, by name.","type":"object","additionalProperties":true},"freshness":{"description":"How long synced data stays fresh.","$ref":"#/components/schemas/SupplierDataFreshness"},"language":{"description":"The language content is synced in, as a BCP 47 tag.","type":"string"},"currency":{"description":"The currency prices are synced in, as an ISO 4217 code.","type":"string"},"enabled":{"description":"False stops scheduled runs of this profile.","type":"boolean"}},"example":{"modes":[{"mode":"FULL","cron":"0 3 * * 0"}],"enabled":true}},"SupplierDataRunStats":{"type":"object","properties":{"pages":{"type":"integer"},"records":{"type":"object","properties":{"added":{"type":"integer"},"updated":{"type":"integer"},"removed":{"type":"integer"}}},"quotaUsed":{"type":"integer"},"errors":{"type":"integer"},"etaSeconds":{"type":"integer"}},"example":{"pages":120,"records":{"added":310,"updated":4120,"removed":12},"quotaUsed":640,"errors":0,"etaSeconds":900}},"SupplierDataRun":{"type":"object","description":"One sync run (FEAT-023 §7 sync_run)","required":["runId","supplierId","dataset","credentialSet","mode","trigger","status"],"properties":{"runId":{"type":"string"},"supplierId":{"type":"string"},"dataset":{"type":"string"},"credentialSet":{"type":"string"},"mode":{"type":"string"},"trigger":{"type":"string","description":"SCHEDULE | USER | WEBHOOK | API; open string"},"triggeredBy":{"type":"string"},"status":{"type":"string","description":"QUEUED | RUNNING | PAUSED | SUCCEEDED | PARTIAL | FAILED | CANCELLED; open string"},"scope":{"type":"object","additionalProperties":true},"window":{"$ref":"#/components/schemas/DateRange"},"stats":{"$ref":"#/components/schemas/SupplierDataRunStats"},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"count":{"type":"integer"}}}},"startedAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"snapshotId":{"type":"string","description":"The snapshot a SUCCEEDED or PARTIAL run produced"}},"example":{"runId":"run_01JA7K3","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","mode":"INCREMENTAL","trigger":"USER","triggeredBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","status":"RUNNING","stats":{"pages":120,"records":{"added":310,"updated":4120,"removed":12},"quotaUsed":640,"errors":0,"etaSeconds":900},"startedAt":"2026-10-01T09:00:00Z"}},"SupplierDataRunRequest":{"type":"object","description":"Run now (FEAT-023 §6); answers 202 with the run","required":["supplierId","dataset","mode"],"properties":{"supplierId":{"description":"The supplier to sync.","type":"string"},"dataset":{"description":"The dataset to sync, for example catalog_product.","type":"string"},"credentialSet":{"type":"string","description":"Absent = MONDEE for an operator, the caller's own set for a tenant"},"mode":{"description":"How to sync, FULL, INCREMENTAL, WINDOW, EVENT or ON_DEMAND.","type":"string"},"scope":{"description":"Dataset-specific filters (countries, categories, property ids).","type":"object","additionalProperties":true},"window":{"description":"For WINDOW mode, the days to sync.","$ref":"#/components/schemas/DateRange"}},"example":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","mode":"INCREMENTAL"}},"SupplierDataRunList":{"type":"object","required":["runs","page"],"properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDataRun"}},"page":{"$ref":"#/components/schemas/Page"}},"example":{"runs":[],"page":{"total":0}}},"SupplierDataDatasetCell":{"type":"object","description":"One cell of the overview grid (FEAT-023 §5 item 1)","required":["supplierId","dataset","credentialSet","status"],"properties":{"supplierId":{"type":"string"},"dataset":{"type":"string"},"credentialSet":{"type":"string"},"status":{"type":"string","description":"FRESH | STALE | FAILING | PAUSED | NEVER_RUN | LIVE_ONLY; open string"},"lastRuns":{"type":"object","description":"Mode → the last run of that mode","additionalProperties":{"$ref":"#/components/schemas/SupplierDataRun"}},"dataAsOf":{"type":"string","format":"date-time"},"ageSeconds":{"type":"integer"},"maxStaleness":{"type":"string"},"recordCount":{"type":"integer"},"deltaSinceLastRun":{"type":"integer"},"errorsLast24h":{"type":"integer"},"nextRunAt":{"type":"string","format":"date-time"},"quotaUsedToday":{"type":"number","description":"Fraction (0–1) of the supplier's daily quota consumed"},"currentSnapshotId":{"type":"string"}},"example":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","status":"FRESH","dataAsOf":"2026-10-01T08:15:00Z","ageSeconds":2700,"maxStaleness":"P1D","recordCount":184210,"deltaSinceLastRun":298,"errorsLast24h":0,"nextRunAt":"2026-10-01T10:15:00Z","quotaUsedToday":0.21,"currentSnapshotId":"snap_01JA7K9"}},"SupplierDataDatasets":{"type":"object","required":["cells"],"properties":{"cells":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDataDatasetCell"}},"generatedAt":{"type":"string","format":"date-time"}},"example":{"cells":[{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","status":"FRESH","dataAsOf":"2026-10-01T08:15:00Z","recordCount":184210}],"generatedAt":"2026-10-01T09:00:00Z"}},"SupplierDataSnapshot":{"type":"object","description":"One immutable version of a dataset (FEAT-023 §7 sync_snapshot)","required":["snapshotId","supplierId","dataset","credentialSet","createdAt","recordCount"],"properties":{"snapshotId":{"type":"string"},"supplierId":{"type":"string"},"dataset":{"type":"string"},"credentialSet":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"recordCount":{"type":"integer"},"checksum":{"type":"string"},"mappingVersion":{"type":"string"},"parentSnapshotId":{"type":"string"},"runId":{"type":"string"},"current":{"type":"boolean"}},"example":{"snapshotId":"snap_01JA7K9","supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","credentialSet":"MONDEE","createdAt":"2026-10-01T08:15:00Z","recordCount":184210,"checksum":"sha256:9f2a1c","mappingVersion":"2026-09-28.2","parentSnapshotId":"snap_01JA6ZZ","runId":"run_01JA7K3","current":true}},"SupplierDataSnapshotList":{"type":"object","required":["snapshots","page"],"properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDataSnapshot"}},"page":{"$ref":"#/components/schemas/Page"}},"example":{"snapshots":[],"page":{"total":0}}},"SupplierDataDiff":{"type":"object","description":"The difference between two snapshots (FEAT-023 §5 item 5) — counts plus a page of changed keys","required":["from","to","added","updated","removed"],"properties":{"from":{"type":"string"},"to":{"type":"string"},"added":{"type":"integer"},"updated":{"type":"integer"},"removed":{"type":"integer"},"changes":{"type":"array","items":{"type":"object","properties":{"supplierKey":{"type":"string"},"change":{"type":"string","description":"ADDED | UPDATED | REMOVED"},"fields":{"type":"array","items":{"type":"string"}}}}},"page":{"$ref":"#/components/schemas/Page"}},"example":{"from":"snap_01JA6ZZ","to":"snap_01JA7K9","added":310,"updated":4120,"removed":12,"changes":[{"supplierKey":"5010SYDNEY","change":"UPDATED","fields":["price_from","images"]}],"page":{"total":4442}}},"SupplierDataRollbackRequest":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":4,"maxLength":500}},"example":{"reason":"08:15 run ingested a truncated dump (record count fell 31%)"}},"SupplierDataPurgeRequest":{"type":"object","description":"Purge from the cache layers (FEAT-023 §5 item 6); the dataset store is never purged by this call","required":["scope"],"properties":{"scope":{"type":"object","required":["supplierId"],"properties":{"supplierId":{"type":"string"},"dataset":{"type":"string"},"credentialSet":{"type":"string"},"supplierKeys":{"type":"array","maxItems":1000,"items":{"type":"string"}},"layers":{"type":"array","description":"L1 | L2 | CDN — absent = all cache layers","items":{"type":"string"}}}},"reason":{"type":"string","maxLength":500}},"example":{"scope":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","supplierKeys":["5010SYDNEY"]},"reason":"Stale image set reported by the supplier"}},"SupplierDataPurgeResult":{"type":"object","required":["purged"],"properties":{"purged":{"type":"object","additionalProperties":{"type":"integer"},"description":"Layer → entries purged"},"auditRef":{"type":"string"}},"example":{"purged":{"L1":1,"L2":1,"CDN":14},"auditRef":"aud_01JA7M1"}},"SupplierLoginRequest":{"type":"object","description":"Supplier-realm sign-in. Plain JSON, not an envelope. One login per supplier user; the supplier code is resolved from the user, never sent.","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string","format":"password"}},"example":{"username":"ops@villas.example","password":"correct-horse-battery-staple"}},"SupplierMfaChallenge":{"type":"object","description":"Answered 202 by login when the supplier user has MFA enrolled; complete it at POST /supplier/v1/auth/mfa/verify","required":["challengeId","methods","expiresAt"],"properties":{"challengeId":{"type":"string"},"methods":{"type":"array","items":{"type":"string","description":"TOTP | EMAIL_CODE; planned (IMP-P4-067) WEBAUTHN | RECOVERY"}},"expiresAt":{"type":"string","format":"date-time"},"webauthn":{"type":"object","additionalProperties":true,"x-status":"planned","description":"PublicKeyCredentialRequestOptions JSON (base64url) when WEBAUTHN is offered (planned)"}},"example":{"challengeId":"mfa_01JA7N2","methods":["TOTP"],"expiresAt":"2026-10-01T09:10:00Z"}},"SupplierMfaVerifyRequest":{"type":"object","description":"One proof: code (TOTP or EMAIL_CODE), or — planned (IMP-P4-067) — recoveryCode or webauthn. `code` stays required in this version (relaxing it is additive for requests, but the served backend reads only code).\n","required":["challengeId","code"],"properties":{"challengeId":{"type":"string"},"code":{"type":"string","minLength":6,"maxLength":8},"recoveryCode":{"type":"string","x-status":"planned","pattern":"^[A-Z0-9]{4,}(-[A-Z0-9]{4,})*$"},"webauthn":{"type":"object","additionalProperties":true,"x-status":"planned","description":"A WebAuthn assertion as JSON, base64url binaries (planned)"}},"example":{"challengeId":"mfa_01JA7N2","code":"000000"}},"SupplierAccessContext":{"type":"object","description":"The verified supplier-realm caller (the supplier principal, FEAT-020 extension): resolved from the credential, never the body. `supplierCode` is the wall every /supplier/v1 read and write is filtered by. `onBehalf` is present only on a platform-ops session acting for a supplier.\n","required":["userId","supplierCode","realm","role","permissions","actorKind"],"properties":{"userId":{"type":"string"},"supplierCode":{"type":"string","description":"Supplier registry code (08 §2)"},"supplierName":{"type":"string"},"realm":{"type":"string","enum":["SUPPLIER","PLATFORM_OPS"]},"role":{"type":"string","description":"SupplierOwner | SupplierOps | SupplierFinance | SupplierViewer; open string"},"permissions":{"type":"array","items":{"type":"string"},"description":"supplier_bookings:read, supplier_bookings:act, supplier_fulfilment:write, supplier_users:manage"},"actorKind":{"type":"string","enum":["human"]},"onBehalf":{"type":"object","properties":{"supplierCode":{"type":"string"},"reason":{"type":"string"}}}},"example":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act","supplier_fulfilment:write"],"actorKind":"human"}},"SupplierSessionResponse":{"type":"object","required":["tokenType","accessToken","expiresAt","expiresIn","refreshToken","refreshExpiresAt","context"],"properties":{"tokenType":{"type":"string","enum":["Bearer"]},"accessToken":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"expiresIn":{"type":"integer"},"refreshToken":{"type":"string"},"refreshExpiresAt":{"type":"string","format":"date-time"},"context":{"$ref":"#/components/schemas/SupplierAccessContext"}},"example":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-01T09:20:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-31T09:05:00Z","context":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","supplierName":"Example Villas","realm":"SUPPLIER","role":"SupplierOps","permissions":["supplier_bookings:read","supplier_bookings:act","supplier_fulfilment:write"],"actorKind":"human"}}},"SupplierUser":{"type":"object","required":["userId","email","role","active"],"properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"role":{"type":"string"},"active":{"type":"boolean"},"mfaEnrolled":{"type":"boolean"},"lastLoginAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"example":{"userId":"su_8a21f0","email":"ops@villas.example","name":"Priya N","role":"SupplierOps","active":true,"mfaEnrolled":true,"lastLoginAt":"2026-10-01T09:05:00Z","createdAt":"2026-09-20T10:00:00Z"}},"SupplierInviteAccepted":{"type":"object","description":"The supplier user an invite activated (v1.6.5). Not the tenant InviteAccepted, which names a username, tenant and sites.","required":["userId","supplierCode","email","role"],"properties":{"userId":{"type":"string"},"supplierCode":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","description":"SupplierOwner | SupplierOps | SupplierFinance | SupplierViewer"}},"example":{"userId":"su_8a21f0","supplierCode":"EXAMPLEVILLAS","email":"ops@villas.example","role":"SupplierOps"}},"SupplierUserList":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/SupplierUser"}}},"example":{"users":[{"userId":"su_8a21f0","email":"ops@villas.example","role":"SupplierOps","active":true}]}},"SupplierUserInvite":{"type":"object","required":["email","role"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string"},"name":{"type":"string"}},"example":{"email":"finance@villas.example","role":"SupplierFinance"}},"SupplierUserUpdate":{"type":"object","properties":{"role":{"type":"string"},"active":{"type":"boolean"}},"example":{"role":"SupplierViewer","active":true}},"SupplierOnBehalf":{"type":"object","description":"A platform-ops session acting for a supplier (ops-on-behalf) — supplierCode, reason and ticketRef are stamped into the audit row. Only operators send it. Since v1.6.7 (DR-53, IMP-P2-195) it carries the step-up proof, and the reason minimum is 8 characters, the same as every other operator reason (IMP-P2-197; was 4). Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.\n","required":["supplierCode","reason"],"properties":{"supplierCode":{"type":"string"},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100,"description":"The incident or change ticket this action answers (v1.6.7, optional)"},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Send this or stepUpAssertion, not both (v1.6.7)."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"supplierCode":"EXAMPLEVILLAS","reason":"Owner confirmed by phone at 09:12; portal access pending","ticketRef":"OPS-2310","stepUpCode":"000000"}},"SupplierBooking":{"type":"object","description":"One booking as the SUPPLIER sees it (spec/10 §11): the platform-minted bookingRef, the order lines it backs (one supplier booking may back several lines — supplierBookingGroup), the product summary, the party with the minimum traveller data the product needs, the supplier's own reference once set, its SLA, and the actions it still offers. Never the tenant's identity, markup or sell price: `supplierPrice` is the supplier's own rate.\n","required":["bookingRef","supplierCode","domain","state","requestedAt"],"properties":{"bookingRef":{"type":"string","description":"SB-… platform id; the key of every /supplier/v1/bookings route"},"supplierCode":{"type":"string"},"domain":{"type":"string"},"state":{"type":"string","description":"ON_REQUEST | CONFIRMED | DECLINED | CANCELLATION_REQUESTED | CANCELLED | COMPLETED | NO_SHOW | EXPIRED; open string"},"supplierReference":{"type":"string","description":"The supplier's own booking reference, set at confirm"},"supplierBookingGroup":{"type":"string","description":"The group key shared by the platform order lines this booking backs"},"lines":{"type":"array","items":{"type":"object","properties":{"orderItemRef":{"type":"string","description":"The order line (OrderItem.itemId) as the supplier console addresses it (10 §5)"},"domain":{"type":"string"},"state":{"type":"string"}}}},"product":{"type":"object","additionalProperties":true,"description":"Product summary — name, dates, unit, pax; shape per domain"},"travelers":{"type":"array","description":"Minimised — only what the product's requiredInfo demanded","items":{"$ref":"#/components/schemas/Traveler"}},"supplierPrice":{"$ref":"#/components/schemas/Money"},"requestedAt":{"type":"string","format":"date-time"},"slaDueAt":{"type":"string","format":"date-time","description":"When the on-request answer is due (conditions.confirmSlaSeconds)"},"decidedAt":{"type":"string","format":"date-time"},"decidedBy":{"type":"string"},"cancellation":{"type":"object","properties":{"requestedAt":{"type":"string","format":"date-time"},"penalty":{"$ref":"#/components/schemas/Money"},"kind":{"type":"string","x-status":"planned","description":"CANCEL | AMEND_QUOTE — the cancellation/amend inbox row kind (planned; web !29)"},"reason":{"type":"string","x-status":"planned","description":"The requester's reason or amend detail (planned; the web falls back to notes)"},"status":{"type":"string","x-status":"planned","description":"OPEN | RESPONDED (planned). Respond with :attest (CANCELLED accepts, CONFIRMED rejects)."}}},"actions":{"type":"array","description":"Verbs this booking offers now — confirm, decline, attest, fulfil, cancel-ack","items":{"type":"string"}},"attestations":{"type":"array","items":{"$ref":"#/components/schemas/SupplierAttestation"}},"notes":{"type":"string"}},"example":{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"ON_REQUEST","supplierBookingGroup":"sbg_01JA7P4","lines":[{"orderItemRef":"OI-1","domain":"VR","state":"PENDING_SUPPLIER"}],"product":{"name":"Casa Branca, Alibaug","checkIn":"2026-11-10","checkOut":"2026-11-14","unit":"WHOLE_PROPERTY","guests":6},"travelers":[{"firstName":"Ana","lastName":"Silva","lead":true}],"supplierPrice":{"amount":9600000,"currency":"INR"},"requestedAt":"2026-10-01T08:40:00Z","slaDueAt":"2026-10-02T08:40:00Z","actions":["confirm","decline"]}},"SupplierBookingList":{"type":"object","required":["bookings","page"],"properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/SupplierBooking"}},"page":{"$ref":"#/components/schemas/Page"},"asOf":{"type":"string","format":"date-time","x-status":"planned","description":"The server clock the list was read at (planned; web !29 uses the browser's clock today)"},"counts":{"type":"object","additionalProperties":{"type":"integer"},"description":"State → count across the supplier's bookings"}},"example":{"bookings":[{"bookingRef":"SB-7Q2-4KD-9ZT","supplierCode":"EXAMPLEVILLAS","domain":"VR","state":"ON_REQUEST","requestedAt":"2026-10-01T08:40:00Z","slaDueAt":"2026-10-02T08:40:00Z"}],"page":{"total":1},"counts":{"ON_REQUEST":1,"CONFIRMED":14}}},"SupplierConfirmRequest":{"type":"object","description":"Confirm an on-request booking; the order line moves PENDING_SUPPLIER → CONFIRMED and order.state_changed fires","required":["supplierReference"],"properties":{"supplierReference":{"type":"string","maxLength":128},"confirmedAt":{"type":"string","format":"date-time","description":"Absent = now"},"notes":{"type":"string","maxLength":1000},"onBehalf":{"$ref":"#/components/schemas/SupplierOnBehalf"}},"example":{"supplierReference":"SV-2026-118822","notes":"Early check-in agreed, 12:00"}},"SupplierDeclineRequest":{"type":"object","description":"Decline an on-request booking; the order line fails and the order compensates (order.item_failed)","required":["reasonCode"],"properties":{"reasonCode":{"type":"string","description":"UNAVAILABLE | PRICE_CHANGED | MIN_STAY | POLICY | OTHER; open string"},"reason":{"type":"string","maxLength":1000},"alternative":{"type":"object","additionalProperties":true,"description":"An alternative the supplier offers (dates, price); informational"},"onBehalf":{"$ref":"#/components/schemas/SupplierOnBehalf"}},"example":{"reasonCode":"UNAVAILABLE","reason":"Owner blocked the dates for maintenance"}},"SupplierAttestation":{"type":"object","description":"A supplier's signed statement of a booking's actual state (spec/10 §11; OD-534): what the supplier says happened, with evidence. Used where no supplier API can tell the platform (request-to-book, offline cancels): the attestation is what reconciliation adopts.\n","required":["attestedState","attestedAt","attestedBy"],"properties":{"attestationId":{"type":"string"},"attestedState":{"type":"string","description":"CONFIRMED | CANCELLED | COMPLETED | NO_SHOW | AMENDED; open string"},"attestedAt":{"type":"string","format":"date-time"},"attestedBy":{"type":"string"},"supplierReference":{"type":"string"},"evidence":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","description":"DOCUMENT | EMAIL | NOTE | URL"},"ref":{"type":"string","description":"An uploadId for DOCUMENT, a message id for EMAIL"},"label":{"type":"string"}}}},"note":{"type":"string","maxLength":1000},"onBehalf":{"$ref":"#/components/schemas/SupplierOnBehalf"}},"example":{"attestationId":"att_01JA7Q1","attestedState":"CANCELLED","attestedAt":"2026-10-03T07:00:00Z","attestedBy":"su_8a21f0","supplierReference":"SV-2026-118822","evidence":[{"kind":"EMAIL","ref":"msg_91ab","label":"Guest cancellation email"}],"note":"Cancelled inside the free window; no penalty"}},"SupplierAttestRequest":{"type":"object","required":["attestedState"],"properties":{"attestedState":{"type":"string"},"supplierReference":{"type":"string"},"evidence":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"ref":{"type":"string"},"label":{"type":"string"}}}},"note":{"type":"string","maxLength":1000},"onBehalf":{"$ref":"#/components/schemas/SupplierOnBehalf"}},"example":{"attestedState":"COMPLETED","note":"Guests checked out 14 Nov"}},"SupplierFulfilmentRequest":{"type":"object","description":"Push fulfilment facts for a booking: documents (as uploads), a dispatch update (ground), a redemption (vouchers, tickets), or a status note. Each kind maps to the matching order line field and event (order.document_ready, order.dispatch_updated, order.state_changed).\n","required":["kind"],"properties":{"kind":{"type":"string","description":"DOCUMENT | DISPATCH | REDEMPTION | STATUS; open string"},"documents":{"type":"array","items":{"type":"object","required":["uploadId","type"],"properties":{"uploadId":{"type":"string"},"type":{"type":"string","description":"OrderDocument.type"},"orderItemRef":{"type":"string"},"scope":{"type":"string","description":"PER_UNIT | PER_BOOKING"}}}},"dispatch":{"$ref":"#/components/schemas/GroundDispatch"},"redemption":{"type":"object","properties":{"state":{"type":"string"},"redeemedAt":{"type":"string","format":"date-time"},"orderItemRef":{"type":"string"}}},"status":{"type":"object","properties":{"state":{"type":"string"},"note":{"type":"string"}}},"onBehalf":{"$ref":"#/components/schemas/SupplierOnBehalf"}},"example":{"kind":"DOCUMENT","documents":[{"uploadId":"up_01JA7R7","type":"VOUCHER","orderItemRef":"OI-1","scope":"PER_BOOKING"}]}},"SupplierFulfilmentAccepted":{"type":"object","required":["bookingRef","accepted"],"properties":{"bookingRef":{"type":"string"},"accepted":{"type":"integer","description":"Fulfilment items accepted for processing"},"traceId":{"type":"string"}},"example":{"bookingRef":"SB-7Q2-4KD-9ZT","accepted":1,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}},"ConsoleCaseOpenRequest":{"type":"object","description":"The body of POST /admin/v1/cases (IMP-P3-014)","required":["type","action","orderRef","reason"],"properties":{"type":{"type":"string","description":"OFFLINE_SUPPLIER_ACTION | REQUEST_TO_BOOK | VOUCHER_REISSUE (case-insensitive)"},"action":{"type":"string","description":"CANCEL | AMEND | ENDORSE | VERIFY (OFFLINE_SUPPLIER_ACTION), CONFIRM (REQUEST_TO_BOOK), REISSUE (VOUCHER_REISSUE). VERIFY (v1.7.0): a person checks a supplier-side fact the platform could not classify (an uncatalogued supplier status on order.supplier_event) and closes the case with MARK_DONE or FAIL; the platform opens it itself, once per order while one is open.\n"},"orderRef":{"type":"string"},"reason":{"type":"string","minLength":8,"maxLength":500,"description":"The desk's own note; never shown to the tenant"},"slaHours":{"type":"integer","description":"Overrides the type's default SLA when > 0"},"publicMessage":{"type":"string","maxLength":500,"description":"The sentence the order's owner sees on serviceCases[].message"},"bookingRef":{"type":"string","description":"The supplier-realm booking (REQUEST_TO_BOOK)"}},"example":{"type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","reason":"Example Airport Services amends by email only"}},"ConsoleCaseCloseRequest":{"type":"object","description":"The body of :mark-done and :fail","required":["note"],"properties":{"note":{"type":"string","minLength":8,"maxLength":500},"publicMessage":{"type":"string","maxLength":500,"description":"Replaces the tenant-visible sentence when sent"}},"example":{"note":"Example Airport Services confirmed the amendment by email","publicMessage":"Your change is confirmed by the supplier."}},"ConsoleCaseEscalateRequest":{"type":"object","description":"The body of :escalate","required":["note"],"properties":{"note":{"description":"Why the case is escalated, for the team that picks it up.","type":"string","minLength":8,"maxLength":500},"slaHours":{"type":"integer","description":"New SLA from now; default 4"}},"example":{"note":"No reply from Example Airport Services after two emails; desk lead to phone","slaHours":2}},"ConsoleCase":{"type":"object","description":"An ops-desk case as the console reads it (IMP-P3-014, IMP-P3-060). Stored, unlike the four fulfilment queues; stateVersion guards every write; a closed case never reopens. `reason`, `note`, `openedBy` / `closedBy` and the SLA are desk-internal: the tenant sees only OrderServiceCase on its order. Absent fields are omitted, never null.\n","required":["caseId","type","action","orderRef","status","tenantStatus","openedAt","slaDueAt","updatedAt","escalations","stateVersion"],"properties":{"caseId":{"type":"string","description":"case_ + 16 hex characters"},"tenantId":{"type":"string"},"type":{"type":"string","enum":["OFFLINE_SUPPLIER_ACTION","REQUEST_TO_BOOK","VOUCHER_REISSUE"]},"action":{"type":"string","description":"CANCEL | AMEND | ENDORSE | CONFIRM | REISSUE"},"orderRef":{"type":"string"},"domain":{"type":"string"},"supplier":{"type":"string"},"credentialSet":{"type":"string"},"status":{"type":"string","enum":["OPEN","DONE","FAILED"]},"tenantStatus":{"type":"string","enum":["PENDING_SUPPLIER_ACTION","COMPLETED","FAILED"]},"tenantMessage":{"type":"string"},"reason":{"type":"string"},"note":{"type":"string","description":"The last escalate or close note"},"openedAt":{"type":"string","format":"date-time"},"slaDueAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"closedAt":{"type":"string","format":"date-time"},"openedBy":{"type":"string"},"closedBy":{"type":"string"},"bookingRef":{"type":"string"},"escalatedAt":{"type":"string","format":"date-time"},"escalations":{"type":"integer"},"stateVersion":{"type":"integer","format":"int64"}},"example":{"caseId":"case_9f2a1c33b0d4e871","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","orderRef":"MO-8KD-2FQ-9XT","status":"OPEN","tenantStatus":"PENDING_SUPPLIER_ACTION","openedAt":"2026-10-01T10:00:00Z","slaDueAt":"2026-10-02T10:00:00Z","updatedAt":"2026-10-01T10:00:00Z","escalations":0,"stateVersion":0}},"OrderServiceCase":{"type":"object","description":"What the order's owner sees of an ops-desk case on GET /v1/orders/{orderRef} (message.serviceCases[], newest first; IMP-P3-014). Never the desk's reason, owner or SLA.\n","required":["caseId","type","action","status","openedAt","updatedAt"],"properties":{"caseId":{"type":"string"},"type":{"type":"string","description":"OFFLINE_SUPPLIER_ACTION | REQUEST_TO_BOOK | VOUCHER_REISSUE; open string"},"action":{"type":"string","description":"CANCEL | AMEND | ENDORSE | VERIFY | CONFIRM | REISSUE; open string (VERIFY since v1.7.0)"},"status":{"type":"string","description":"PENDING_SUPPLIER_ACTION | COMPLETED | FAILED; open string"},"message":{"type":"string","description":"The sentence the desk wrote for the tenant; absent when none"},"openedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"example":{"caseId":"case_9f2a1c33b0d4e871","type":"OFFLINE_SUPPLIER_ACTION","action":"AMEND","status":"PENDING_SUPPLIER_ACTION","message":"Your change request is with the supplier; we will confirm within 24 hours.","openedAt":"2026-10-01T10:00:00Z","updatedAt":"2026-10-01T10:00:00Z"}},"PasswordChangeRequest":{"type":"object","description":"The body of POST /v1/auth/password/change (IMP-P2-002)","required":["currentPassword","newPassword"],"properties":{"currentPassword":{"type":"string","maxLength":1024,"writeOnly":true},"newPassword":{"type":"string","minLength":12,"maxLength":1024,"writeOnly":true}},"example":{"currentPassword":"correct-horse-battery-staple","newPassword":"a brand new passphrase 2026"}},"PasswordSetRequest":{"type":"object","description":"The body of POST /v1/auth/password/set (connect-v1 1.29.0, OD-ENR-15): the first password of an account that has none. Exactly one of stepUpCode and stepUpAssertion when the caller has an MFA factor enrolled (DR-54); neither without one. Both are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.\n","required":["newPassword"],"additionalProperties":false,"properties":{"newPassword":{"type":"string","minLength":12,"maxLength":1024,"writeOnly":true},"stepUpCode":{"type":"string","maxLength":16,"writeOnly":true,"description":"A current code from the caller's MFA factor; required when one is enrolled"},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"newPassword":"a brand new passphrase 2026"}},"FeatureFlags":{"type":"object","description":"The tenant's console feature flags for one environment (IMP-P2-148). An absent flag is false. version counts writes (0 = never written; updatedAt and updatedBy are then null).\n","required":["tenantId","environment","flags","version"],"properties":{"tenantId":{"type":"string"},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]},"flags":{"type":"object","additionalProperties":{"type":"boolean"}},"version":{"type":"integer","format":"int32"},"updatedAt":{"type":["string","null"],"format":"date-time"},"updatedBy":{"type":["string","null"],"description":"The writer's userId"}},"example":{"tenantId":"TEN-ACME","environment":"SANDBOX","flags":{"supplierData":true},"version":3,"updatedAt":"2026-10-01T09:12:00Z","updatedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"FeatureFlagsUpdate":{"type":"object","description":"The body of PUT /admin/v1/flags — a partial merge","required":["flags"],"properties":{"environment":{"type":"string","description":"SANDBOX | PRODUCTION (case-insensitive); defaults as on the GET"},"flags":{"type":"object","minProperties":1,"maxProperties":200,"propertyNames":{"pattern":"^[a-zA-Z][a-zA-Z0-9]{0,63}$"},"additionalProperties":{"type":"boolean"}}},"example":{"environment":"SANDBOX","flags":{"supplierData":true}}},"CopilotAutonomy":{"type":"object","description":"The tenant's effective Copilot autonomy as the AI plane answers it (aikonic-one-ai copilot/autonomy.py EffectiveAutonomy), passed through by the console proxy.\n","required":["tenant","level","enabled"],"properties":{"tenant":{"type":"string"},"level":{"type":"string","enum":["L1","L2","L3"]},"meaning":{"type":"string","description":"suggest | propose | propose+auto-file"},"ceiling":{"type":"string","enum":["L1","L2","L3"]},"enabled":{"type":"boolean"},"disabledBy":{"type":["string","null"],"description":"global | tenant-config | runtime"},"source":{"type":"string","description":"runtime | tenant-config | default"},"changedBy":{"type":["string","null"]},"reason":{"type":["string","null"]},"levels":{"type":"object","additionalProperties":{"type":"string"},"description":"Always {L1 suggest, L2 propose, L3 propose+auto-file}"}},"example":{"tenant":"TEN-ACME","level":"L1","meaning":"suggest","ceiling":"L2","enabled":true,"source":"default"}},"CopilotAutonomyUpdate":{"type":"object","description":"The body of PUT /admin/v1/copilot/autonomy — level and/or enabled","properties":{"level":{"type":"string","description":"L1 | L2 | L3 (case-insensitive)"},"enabled":{"type":"boolean"},"reason":{"type":"string"}},"example":{"level":"L2","reason":"pilot"}},"CopilotDraft":{"type":"object","description":"A proposal the Copilot drafted but did not file; POST …/drafts/{draftId}/file files it","required":["id","agentId","input"],"properties":{"id":{"type":"string","description":"CPD-<uuid>"},"agentId":{"type":"string","description":"AG-22 | AG-24"},"input":{"type":"object","additionalProperties":true,"description":"AG-22 {orderRef, domain, travelers[], searchIntent?}; AG-24 {}"},"summary":{"type":"string"},"turnId":{"type":"string"}},"example":{"id":"CPD-5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","agentId":"AG-22","input":{"orderRef":"MO-8KD-2FQ-9XT","domain":"FLT","travelers":[]},"summary":"Rebook onto LA8086 2026-11-11","turnId":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"}},"CopilotProposal":{"type":"object","additionalProperties":true,"description":"The AI plane's proposal object as the proxy serves it on draft filing, Turn.proposal and the SSE proposal event. AS SERVED it is the plane's own shape (desk/model.py Proposal.to_dict): `id`, not `proposalId`, and `state` in the plane's vocabulary (PROPOSED, APPROVED, EXECUTED, FAILED, REJECTED, EXPIRED) — the proxy's state rewrite keys on `proposalId` and so does not fire. Read it with GET /admin/v1/agent-proposals/{proposalId} for the console shape once BE-6 aligns the proxy.\n","required":["id","agentId","state"],"properties":{"id":{"type":"string"},"agentId":{"type":"string"},"tenant":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"expiresAt":{"type":"string"},"state":{"type":"string"},"autonomyLevel":{"type":"string"},"summary":{"type":"string"},"requires":{"type":"array","items":{"type":"string"}},"evidence":{"type":"array","items":{"type":"object","additionalProperties":true}},"auditRef":{"type":"string"},"actor":{"type":"object","additionalProperties":true},"input":{"type":"object","additionalProperties":true},"brief":{"type":"object","additionalProperties":true},"decision":{"type":["object","null"],"additionalProperties":true},"result":{"type":["object","null"],"additionalProperties":true},"error":{"type":["object","null"],"additionalProperties":true}},"example":{"id":"PRP-1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","agentId":"AG-22","state":"PROPOSED","summary":"Rebook onto LA8086 2026-11-11"}},"CopilotTurn":{"type":"object","description":"One question and its answer (the SSE done event's data; the JSON answer of …/messages)","required":["id","at","question","answer","intent"],"properties":{"id":{"type":"string","description":"CPT-<uuid>"},"at":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"intent":{"type":"string","description":"chat | order | rebook | adoption | proposals | capabilities"},"tools":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"code":{"type":"string"}}}},"suggestion":{"type":["object","null"],"description":"{agentId, input, summary, missing[]?}","additionalProperties":true},"draft":{"oneOf":[{"$ref":"#/components/schemas/CopilotDraft"},{"type":"null"}]},"proposal":{"oneOf":[{"$ref":"#/components/schemas/CopilotProposal"},{"type":"null"}]},"refusals":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"INJECTION_SUSPECTED | TAB_DISABLED | a desk code such as AGENT_PAUSED"},"message":{"type":"string"}}}},"model":{"type":"string"},"actor":{"type":"object","additionalProperties":true,"description":"{kind: ai, agentId: COPILOT}"}},"example":{"id":"CPT-0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d","at":"2026-10-01T09:01:10+00:00","question":"What can I sell in cruise?","answer":"Cruise is in BETA for your tenant.","intent":"capabilities","tools":[],"refusals":[]}},"CopilotSession":{"type":"object","description":"A Copilot conversation (aikonic-one-ai copilot/model.py Session)","required":["id","tenant","createdAt","level","turns","drafts"],"properties":{"id":{"type":"string","description":"CPS-<uuid>"},"tenant":{"type":"string"},"userId":{"type":["string","null"]},"createdAt":{"type":"string"},"level":{"type":"string","enum":["L1","L2","L3"]},"tools":{"type":"array","items":{"type":"string"},"description":"Subset of get_order, list_proposals, capabilities, propose_rebooking, adoption_advice"},"turns":{"type":"array","items":{"$ref":"#/components/schemas/CopilotTurn"}},"drafts":{"type":"array","items":{"$ref":"#/components/schemas/CopilotDraft"}}},"example":{"id":"CPS-6f1d2c9e-3b7a-4f0e-9a51-1c2b3d4e5f60","tenant":"TEN-ACME","createdAt":"2026-10-01T09:00:00+00:00","level":"L1","turns":[],"drafts":[]}},"CopilotMessageRequest":{"type":"object","description":"The body of POST /admin/v1/copilot/sessions/{sessionId}/messages","required":["text"],"properties":{"text":{"type":"string","minLength":1,"maxLength":4000},"context":{"type":"object","additionalProperties":true,"properties":{"orderRef":{"type":"string"},"domain":{"type":"string"},"travelers":{"type":"array","items":{"type":"object","additionalProperties":true}},"searchIntent":{"type":"object","additionalProperties":true}}}},"example":{"text":"Rebook MO-8KD-2FQ-9XT, the LA8084 was cancelled","context":{"orderRef":"MO-8KD-2FQ-9XT"}}},"HoldReleaseRequest":{"type":"object","description":"The (optional) body of POST /v1/hold/{holdRef}/release — planned","properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"type":"object","properties":{"intent":{"type":"object","properties":{"reason":{"type":"string","maxLength":500}}}}}},"example":{"context":{"domain":"EXP"},"message":{"intent":{"reason":"Traveller changed plans"}}}},"HoldReleaseResponse":{"type":"object","description":"The answer of POST /v1/hold/{holdRef}/release — planned","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","required":["holdRef","state"],"properties":{"holdRef":{"type":"string"},"state":{"type":"string","description":"RELEASED | EXPIRED; open string"},"releasedAt":{"type":"string","format":"date-time"},"releaseOutcome":{"type":"string","description":"RELEASED | ALREADY_RELEASED | RELEASE_REFUSED | RELEASE_UNKNOWN | NOT_TOLD_NO_RELEASE_VERB (the hold.expired data vocabulary)"}}}},"example":{"context":{"domain":"EXP","action":"on_hold_release"},"message":{"holdRef":"HD-E-9b1c22","state":"RELEASED","releaseOutcome":"RELEASED"}}},"AuthSession":{"type":"object","description":"One signed-in session (refresh family) of the caller, any realm (OD-IMP-P4-037)","required":["sessionId","current","createdAt"],"properties":{"sessionId":{"type":"string"},"current":{"type":"boolean","description":"True for the caller's own session"},"createdAt":{"type":"string","format":"date-time"},"lastSeenAt":{"type":["string","null"],"format":"date-time"},"device":{"type":["string","null"],"description":"A short label the platform derives; never the raw user agent"},"clientAddress":{"type":["string","null"],"description":"Already truncated by the platform"},"expiresAt":{"type":["string","null"],"format":"date-time"}},"example":{"sessionId":"ses_01JA9K2","current":true,"createdAt":"2026-10-01T08:00:00Z","lastSeenAt":"2026-10-02T07:55:00Z","device":"Chrome on macOS","clientAddress":"203.0.113.x","expiresAt":"2026-10-31T08:00:00Z"}},"AuthSessionList":{"type":"object","description":"The caller's sessions (current first)","required":["sessions"],"properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/AuthSession"}}},"example":{"sessions":[{"sessionId":"ses_01JA9K2","current":true,"createdAt":"2026-10-01T08:00:00Z"},{"sessionId":"ses_01JA2P7","current":false,"createdAt":"2026-09-28T19:10:00Z","device":"Safari on iOS"}]}},"MfaFactor":{"type":"object","description":"An enrolled second factor (IMP-P4-067)","required":["factorId","type","name","createdAt"],"properties":{"factorId":{"type":"string"},"type":{"type":"string","enum":["TOTP","WEBAUTHN"]},"name":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"}},"example":{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"}},"MfaStatus":{"type":"object","description":"The caller's MFA enrolment (GET {realm}/auth/mfa)","required":["enrolled","factors"],"properties":{"enrolled":{"type":"boolean"},"required":{"type":"boolean","description":"True when the realm or role makes MFA mandatory (every operator; console admins)"},"factors":{"type":"array","items":{"$ref":"#/components/schemas/MfaFactor"}},"recoveryCodesRemaining":{"type":["integer","null"]}},"example":{"enrolled":true,"required":true,"factors":[{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"}],"recoveryCodesRemaining":9}},"MfaTotpStartRequest":{"type":"object","description":"Start a TOTP enrolment","required":["name"],"properties":{"name":{"type":"string","maxLength":40}},"example":{"name":"Authenticator app"}},"MfaTotpEnrolment":{"type":"object","description":"A pending TOTP factor: show otpauthUri as a QR code, then verify one code","required":["factorId","otpauthUri"],"properties":{"factorId":{"type":"string"},"otpauthUri":{"type":"string","description":"otpauth://totp/Issuer:account?secret=BASE32&issuer=&digits=&period=&algorithm="},"expiresAt":{"type":["string","null"],"format":"date-time"}},"example":{"factorId":"fac_01JA9M1","otpauthUri":"otpauth://totp/Apiq:ops%40acme.example?secret=JBSWY3DPEHPK3PXPJBSWY3DP&issuer=Apiq&digits=6&period=30&algorithm=SHA1","expiresAt":"2026-10-01T08:15:00Z"}},"MfaCodeRequest":{"type":"object","description":"A one-time code","required":["code"],"properties":{"code":{"type":"string","minLength":6,"maxLength":8}},"example":{"code":"000000"}},"MfaFactorAdded":{"type":"object","description":"The enrolled factor; recoveryCodes only on the first factor (shown once)","required":["factor"],"properties":{"factor":{"$ref":"#/components/schemas/MfaFactor"},"recoveryCodes":{"type":"array","items":{"type":"string"}}},"example":{"factor":{"factorId":"fac_01JA9M1","type":"TOTP","name":"Authenticator app","createdAt":"2026-10-01T08:05:00Z"},"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA"]}},"MfaWebauthnOptions":{"type":"object","description":"WebAuthn registration options (PublicKeyCredentialCreationOptions JSON, base64url binaries)","required":["registrationId","publicKey"],"properties":{"registrationId":{"type":"string"},"publicKey":{"type":"object","additionalProperties":true,"description":"challenge, user.id (base64url), rp {name, id?}, pubKeyCredParams, timeout?, excludeCredentials?, authenticatorSelection?, attestation?"}},"example":{"registrationId":"reg_01JA9N4","publicKey":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rp":{"name":"Apiq"},"user":{"id":"dXNyXzhhMjFmMA","name":"ops@acme.example","displayName":"Ops"},"pubKeyCredParams":[{"type":"public-key","alg":-7}]}}},"WebAuthnAssertion":{"type":"object","description":"A WebAuthn assertion (the JSON of a PublicKeyCredential from navigator.credentials.get), every binary value base64url (v1.6.7, DR-54). The stepUpAssertion of every step-up-guarded body. Obtain the challenge from POST /ops/v1/auth/mfa/step-up/webauthn-options.","required":["id","rawId","type","response"],"properties":{"id":{"type":"string","description":"The credential id, base64url"},"rawId":{"type":"string","description":"The credential id, base64url"},"type":{"type":"string","enum":["public-key"]},"response":{"type":"object","required":["clientDataJSON","authenticatorData","signature"],"properties":{"clientDataJSON":{"type":"string","description":"base64url"},"authenticatorData":{"type":"string","description":"base64url"},"signature":{"type":"string","description":"base64url"},"userHandle":{"type":["string","null"],"description":"base64url, or null when the authenticator returns none"}}},"clientExtensionResults":{"type":"object","additionalProperties":true}},"example":{"id":"AaB3x9Zq1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","rawId":"AaB3x9Zq1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0","authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAQ","signature":"MEUCIQDq1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0","userHandle":null},"clientExtensionResults":{}}},"WebAuthnStepUpOptions":{"type":"object","description":"PublicKeyCredentialRequestOptions for a step-up assertion (v1.6.7, DR-54): pass it as {publicKey: options} to navigator.credentials.get, then send the result as stepUpAssertion. Binary values are base64url.","required":["challenge","userVerification"],"properties":{"challenge":{"type":"string","description":"base64url"},"rpId":{"type":"string"},"timeout":{"type":"integer","description":"Milliseconds"},"allowCredentials":{"type":"array","items":{"type":"object","required":["type","id"],"properties":{"type":{"type":"string","enum":["public-key"]},"id":{"type":"string","description":"base64url"},"transports":{"type":"array","items":{"type":"string"}}}}},"userVerification":{"type":"string","enum":["required","preferred","discouraged"],"description":"Always preferred on this route"}},"additionalProperties":true,"example":{"challenge":"q2v9X0cS1mZ7pL4dR8wYb","rpId":"ops.mondee.example","timeout":60000,"allowCredentials":[{"type":"public-key","id":"AaB3x9Zq1mK0rW9sE2uH7jL4cF5dA8gT3yU6iO0"}],"userVerification":"preferred"}},"MfaWebauthnRegisterRequest":{"type":"object","description":"Register a security key","required":["registrationId","name","credential"],"properties":{"registrationId":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":40},"credential":{"type":"object","additionalProperties":true,"description":"The attestation PublicKeyCredential as JSON, base64url binaries"}},"example":{"registrationId":"reg_01JA9N4","name":"YubiKey","credential":{"id":"AbC1","rawId":"AbC1","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZQ"}}}},"MfaRecoveryCodes":{"type":"object","description":"Freshly generated recovery codes (the old set is dead; shown once)","required":["recoveryCodes"],"properties":{"recoveryCodes":{"type":"array","items":{"type":"string"}}},"example":{"recoveryCodes":["7KQ2-9XTB","M4PD-2LZA","C8RW-5HNE"]}},"MfaChallenge":{"type":"object","description":"The 202 a sign-in answers when a second factor is due (customer and operator realms; IMP-P4-067)","required":["challengeId","methods","expiresAt"],"properties":{"challengeId":{"type":"string"},"methods":{"type":"array","items":{"type":"string","description":"TOTP | WEBAUTHN | RECOVERY"}},"expiresAt":{"type":"string","format":"date-time"},"webauthn":{"type":"object","additionalProperties":true,"description":"PublicKeyCredentialRequestOptions JSON (base64url) when WEBAUTHN is offered"}},"example":{"challengeId":"mfa_01JA9P0","methods":["TOTP","WEBAUTHN","RECOVERY"],"expiresAt":"2026-10-02T08:10:00Z"}},"MfaVerifyRequest":{"type":"object","description":"Complete a challenge with exactly one proof: code (TOTP), recoveryCode, or webauthn (an assertion)","required":["challengeId"],"properties":{"challengeId":{"type":"string"},"code":{"type":"string","minLength":6,"maxLength":8},"recoveryCode":{"type":"string","pattern":"^[A-Z0-9]{4,}(-[A-Z0-9]{4,})*$"},"webauthn":{"type":"object","additionalProperties":true,"description":"{id, rawId, type, response {clientDataJSON, authenticatorData, signature, userHandle?}} base64url"}},"example":{"challengeId":"mfa_01JA9P0","code":"000000"}},"SsoDiscoverRequest":{"type":"object","description":"Which identity provider an email belongs to","required":["email"],"properties":{"email":{"type":"string","format":"email"}},"example":{"email":"agent@acme-travel.example"}},"SsoDiscovery":{"type":"object","description":"PASSWORD (sign in here) or SSO (go to startUrl)","required":["mode"],"properties":{"mode":{"type":"string","enum":["PASSWORD","SSO"]},"protocol":{"type":"string","enum":["OIDC","SAML"]},"startUrl":{"type":"string","format":"uri","description":"https (loopback http on DEV only)"},"displayName":{"type":"string"}},"example":{"mode":"SSO","protocol":"OIDC","startUrl":"https://login.acme-travel.example/oauth2/authorize?client_id=apiq","displayName":"Acme Travel SSO"}},"InsightsEnvironment":{"type":"string","enum":["PRODUCTION","SANDBOX","ALL"],"description":"A side of the sandbox/production tenant pairing (07 §5); ALL is both."},"SupplierCallOutcome":{"type":"string","description":"How a supplier call attempt ended (adapter SDK SupplierCallEntry.Outcome: SUCCEEDED, REFUSED_UNBOUND, REFUSED_EGRESS, REFUSED_UNSENT, FAILED_PROTOCOL, FAILED_TRANSPORT, TIMED_OUT, ABANDONED_IN_FLIGHT). Open string.","example":"SUCCEEDED"},"InsightsCount":{"type":"object","description":"A label and how many.","required":["key","count"],"properties":{"key":{"type":"string"},"count":{"type":"integer","format":"int64"}}},"InsightsTotals":{"type":"object","description":"Window totals. `failed` reached the supplier and did not succeed; `refused` was stopped before anything was sent. Percentiles are over calls that reached the supplier, null when none did. `truncated`: the window held more rows than one summary reads, and the newest were summarised.","required":["calls","succeeded","failed","refused","orders","truncated"],"properties":{"calls":{"type":"integer","format":"int64"},"succeeded":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"refused":{"type":"integer","format":"int64"},"p50Ms":{"type":["integer","null"],"format":"int64"},"p95Ms":{"type":["integer","null"],"format":"int64"},"orders":{"type":"integer","format":"int64"},"ordersConfirmed":{"type":"integer","format":"int64"},"ordersCancelled":{"type":"integer","format":"int64"},"ordersPending":{"type":"integer","format":"int64"},"truncated":{"type":"boolean"}}},"InsightsSupplierRow":{"type":"object","required":["supplier","calls","failed","orders"],"properties":{"supplier":{"type":"string"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"p50Ms":{"type":["integer","null"],"format":"int64"},"p95Ms":{"type":["integer","null"],"format":"int64"},"orders":{"type":"integer","format":"int64"},"lastCallAt":{"type":["string","null"],"format":"date-time"}}},"InsightsOperationRow":{"type":"object","required":["supplier","operation","verb","calls","failed"],"properties":{"supplier":{"type":"string"},"operation":{"type":"string"},"verb":{"type":"string"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"p50Ms":{"type":["integer","null"],"format":"int64"},"p95Ms":{"type":["integer","null"],"format":"int64"},"lastCallAt":{"type":["string","null"],"format":"date-time"}}},"InsightsTenantRow":{"type":"object","required":["tenantId","calls","failed","orders"],"properties":{"tenantId":{"type":"string"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"orders":{"type":"integer","format":"int64"}}},"InsightsFaultRow":{"type":"object","required":["supplier","operation","outcome","count"],"properties":{"supplier":{"type":"string"},"operation":{"type":"string"},"outcome":{"$ref":"#/components/schemas/SupplierCallOutcome"},"faultCode":{"type":["string","null"]},"count":{"type":"integer","format":"int64"}}},"InsightsDayRow":{"type":"object","required":["day","calls","failed","searches","orders"],"properties":{"day":{"type":"string","format":"date"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"searches":{"type":"integer","format":"int64"},"orders":{"type":"integer","format":"int64"},"searchP50Ms":{"type":["integer","null"],"format":"int64"},"searchP95Ms":{"type":["integer","null"],"format":"int64"},"bookP50Ms":{"type":["integer","null"],"format":"int64"},"bookP95Ms":{"type":["integer","null"],"format":"int64"}}},"InsightsSummary":{"type":"object","description":"Supplier calls and orders for a window (connect-v1 1.9.0).","required":["asOf","from","to","totals"],"properties":{"asOf":{"type":"string","format":"date-time"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"totals":{"$ref":"#/components/schemas/InsightsTotals"},"bySupplier":{"type":"array","items":{"$ref":"#/components/schemas/InsightsSupplierRow"}},"byOperation":{"type":"array","items":{"$ref":"#/components/schemas/InsightsOperationRow"}},"byTenant":{"type":"array","items":{"$ref":"#/components/schemas/InsightsTenantRow"}},"byOutcome":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCount"}},"faults":{"type":"array","items":{"$ref":"#/components/schemas/InsightsFaultRow"}},"perDay":{"type":"array","items":{"$ref":"#/components/schemas/InsightsDayRow"}},"ordersByDomain":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCount"}},"ordersByState":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCount"}}}},"InsightsCall":{"type":"object","description":"One supplier call attempt. `parameters` are NAMES only — never a value.","required":["at","supplier","operation","verb","outcome","elapsedMs"],"properties":{"at":{"type":"string","format":"date-time"},"tenantId":{"type":["string","null"],"description":"Null: platform work (a background sync), made for no tenant."},"supplier":{"type":"string"},"operation":{"type":"string"},"verb":{"type":"string"},"credentialSetId":{"type":["string","null"]},"outcome":{"$ref":"#/components/schemas/SupplierCallOutcome"},"elapsedMs":{"type":"integer","format":"int64"},"faultCode":{"type":["string","null"]},"parameters":{"type":"array","items":{"type":"string"}}}},"InsightsOrder":{"type":"object","description":"Order metadata: no traveller, contact or payment instrument. Money in integer minor units of `currency` (the charged amount, else the quoted).","required":["orderRef","tenantId","state"],"properties":{"orderRef":{"type":"string"},"tenantId":{"type":"string"},"supplier":{"type":["string","null"]},"domain":{"type":["string","null"]},"state":{"type":"string"},"createdAt":{"type":["string","null"],"format":"date-time"},"amountMinor":{"type":["integer","null"],"format":"int64"},"currency":{"type":["string","null"]},"supplierReference":{"type":["string","null"]}}},"OpsInsightsEnvironmentTenant":{"type":"object","required":["tenantId","displayName","status"],"properties":{"tenantId":{"type":"string"},"displayName":{"type":"string"},"status":{"type":"string"},"pairedTenantId":{"type":["string","null"]},"dataRegion":{"type":["string","null"]},"features":{"type":"array","items":{"type":"string"}},"createdAt":{"type":["string","null"],"format":"date-time"}}},"OpsInsightsEnvironment":{"type":"object","required":["environment","tenants","byStatus","rows"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"tenants":{"type":"integer","format":"int64"},"byStatus":{"type":"object","additionalProperties":{"type":"integer"}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/OpsInsightsEnvironmentTenant"}}}},"OpsInsightsEnvironments":{"type":"object","required":["asOf","environments"],"properties":{"asOf":{"type":"string","format":"date-time"},"environments":{"type":"array","items":{"$ref":"#/components/schemas/OpsInsightsEnvironment"}}}},"OpsOperator":{"type":"object","description":"One operator (no secret, no password hash).","required":["operatorId","email","roles","status"],"properties":{"operatorId":{"type":"string"},"email":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"mfaEnrolled":{"type":"boolean"},"lastLoginAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"disabledAt":{"type":["string","null"],"format":"date-time"}}},"OpsOperatorList":{"type":"object","required":["operators"],"properties":{"operators":{"type":"array","items":{"$ref":"#/components/schemas/OpsOperator"}},"roles":{"type":"array","items":{"type":"string"}}}},"OpsOperatorRolesChange":{"type":"object","required":["roles","reason"],"properties":{"roles":{"type":"array","items":{"type":"string"}},"reason":{"type":"string"}}},"OpsReason":{"type":"object","description":"Why (8-500 characters).","required":["reason"],"properties":{"reason":{"type":"string"}}},"OpsSessionsRevoked":{"type":"object","required":["operatorId","revoked"],"properties":{"operatorId":{"type":"string"},"revoked":{"type":"integer","format":"int64"}}},"OpsCurrencyTotal":{"type":"object","required":["currency","orders","valueMinor"],"properties":{"currency":{"type":"string"},"orders":{"type":"integer","format":"int64"},"valueMinor":{"type":"integer","format":"int64"}}},"OpsCustomerValue":{"type":"object","required":["tenantId","currency","orders","confirmed","valueMinor"],"properties":{"tenantId":{"type":"string"},"currency":{"type":"string"},"orders":{"type":"integer","format":"int64"},"confirmed":{"type":"integer","format":"int64"},"valueMinor":{"type":"integer","format":"int64"}}},"SupplierContractRecord":{"type":"object","description":"A supplier contract as finance loaded it.","required":["supplierCode","contractRef","registryStatus"],"properties":{"supplierCode":{"type":"string"},"contractRef":{"type":"string"},"registryStatus":{"type":"string"},"moneyModels":{"type":"array","items":{"type":"string"}},"effectiveFrom":{"type":"string","format":"date"},"commissionSchedule":{"type":"array","items":{"type":"object","properties":{"domain":{"type":"string"},"commissionBps":{"type":"integer","format":"int64"}}}},"l2bCap":{"type":["integer","null"]},"sla":{"type":"object","properties":{"searchP95Ms":{"type":"integer","format":"int64"},"bookP95Ms":{"type":"integer","format":"int64"},"availabilityBps":{"type":"integer","format":"int64"},"confirmSlaHours":{"type":"integer","format":"int64"}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"OpsFinanceSummary":{"type":"object","required":["environment","totals","byCustomer","contracts"],"properties":{"environment":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"totals":{"type":"array","items":{"$ref":"#/components/schemas/OpsCurrencyTotal"}},"byCustomer":{"type":"array","items":{"$ref":"#/components/schemas/OpsCustomerValue"}},"truncated":{"type":"boolean"},"contracts":{"type":"array","items":{"$ref":"#/components/schemas/SupplierContractRecord"}}}},"OpsSupplierContractLoad":{"type":"object","required":["contractRef","registryStatus","moneyModels","effectiveFrom","reason"],"properties":{"contractRef":{"type":"string"},"registryStatus":{"type":"string"},"moneyModels":{"type":"array","items":{"type":"string"}},"effectiveFrom":{"type":"string","format":"date"},"commissionSchedule":{"type":"array","items":{"type":"object","properties":{"domain":{"type":"string"},"commissionBps":{"type":"integer","format":"int64"}}}},"l2bCap":{"type":["integer","null"]},"sla":{"type":"object","properties":{"searchP95Ms":{"type":"integer","format":"int64"},"bookP95Ms":{"type":"integer","format":"int64"},"availabilityBps":{"type":"integer","format":"int64"},"confirmSlaHours":{"type":"integer","format":"int64"}}},"reason":{"type":"string"}}},"OpsStatementLineIn":{"type":"object","description":"One line; amount in integer minor units of currency.","required":["supplierBookingRef","kind","amount","currency"],"properties":{"lineId":{"type":"string"},"supplierBookingRef":{"type":"string"},"kind":{"type":"string"},"amount":{"type":"integer","format":"int64"},"currency":{"type":"string"},"fxSnapshotId":{"type":"string"},"provisional":{"type":"boolean"}}},"OpsSupplierStatementPublish":{"type":"object","required":["period","moneyModel","direction","lines","reason"],"properties":{"statementId":{"type":"string"},"period":{"type":"string"},"moneyModel":{"type":"string"},"direction":{"type":"string"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/OpsStatementLineIn"}},"payoutHold":{"oneOf":[{"type":"object","properties":{"reasonCode":{"type":"string"},"documentId":{"type":"string"}}},{"type":"null"}]},"reason":{"type":"string"}}},"OpsSupplierCancellationsPage":{"type":"object","description":"connect-v1 1.16.0. One page of a supplier's cancellations (no personal data).","required":["supplierCode","credentialSet","cancellations"],"properties":{"supplierCode":{"type":"string"},"credentialSet":{"type":"string"},"cancellations":{"type":"array","items":{"$ref":"#/components/schemas/OpsSupplierCancellation"}},"nextOffset":{"type":"integer","description":"The offset of the next page; absent on the last"}}},"OpsSupplierCancellation":{"type":"object","required":["cancellationId"],"properties":{"cancellationId":{"type":"string"},"supplierLocator":{"type":"string","description":"The supplier booking it cancels (an order's primary locator)"},"state":{"type":"string","description":"The supplier's state, as stated (e.g. success)"},"method":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"refundAttemptedAt":{"type":"string","format":"date-time"},"refundConfirmedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"charged":{"$ref":"#/components/schemas/Money"},"orderRef":{"type":"string","description":"The Apiq order holding the booking; absent when none does"},"tenantId":{"type":"string"}}},"OpsSupplierStatementList":{"type":"object","required":["supplierCode","statements"],"properties":{"supplierCode":{"type":"string"},"statements":{"type":"array","items":{"$ref":"#/components/schemas/SupplierStatement"}}}},"OpsFeed":{"type":"object","required":["feedId","name"],"properties":{"feedId":{"type":"string"},"name":{"type":"string"},"scheduleWindowHours":{"type":"integer","format":"int64"},"lastRunAt":{"type":["string","null"],"format":"date-time"},"lastDeltaAt":{"type":["string","null"],"format":"date-time"},"rowsIngested":{"type":"integer","format":"int64"},"status":{"type":"string"},"lastError":{"type":["string","null"]}}},"OpsContentFeeds":{"type":"object","required":["asOf","suppliers"],"properties":{"asOf":{"type":"string","format":"date-time"},"suppliers":{"type":"array","items":{"type":"object","required":["supplierCode","feeds"],"properties":{"supplierCode":{"type":"string"},"feeds":{"type":"array","items":{"$ref":"#/components/schemas/OpsFeed"}}}}}}},"OpsDeveloperOverview":{"type":"object","required":["asOf","routes","credentialSets"],"properties":{"asOf":{"type":"string","format":"date-time"},"routes":{"type":"array","items":{"type":"object","required":["family","routes"],"properties":{"family":{"type":"string"},"routes":{"type":"integer","format":"int64"},"reads":{"type":"integer","format":"int64"},"writes":{"type":"integer","format":"int64"}}}},"credentialSets":{"type":"array","items":{"type":"object","required":["supplierCode","credentialSetId","status"],"properties":{"supplierCode":{"type":"string"},"credentialSetId":{"type":"string"},"environment":{"type":"string"},"status":{"type":"string"},"adapterVersion":{"type":"string"},"certState":{"type":"string"},"certifiedAt":{"type":["string","null"],"format":"date-time"}}}}}},"OpsInsightsSummary":{"type":"object","required":["environment","summary"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"summary":{"$ref":"#/components/schemas/InsightsSummary"}}},"OpsInsightsCalls":{"type":"object","required":["environment","calls"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"calls":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCall"}}}},"OpsInsightsOrders":{"type":"object","required":["environment","orders"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/InsightsOrder"}}}},"SupplierCallSelector":{"type":"object","description":"One selector of the supplier-call viewer (v1.25.0): its kind and its value. The list route takes it as exactly one query parameter; an unlock takes it as the grant's scope.\n","required":["kind","value"],"properties":{"kind":{"type":"string","enum":["traceId","searchRef","offerRef","repriceRef","holdRef","orderRef"]},"value":{"type":"string","maxLength":128}}},"SupplierCallCaptureState":{"type":"string","description":"What was stored for one call (v1.25.0). BODIES: the masked request and response bodies. METADATA_ONLY: the rung captures metadata only (PROD until OD-898 decides). NOT_SAMPLED: the rung captures bodies but this call was outside the sample. DROPPED: the body was refused by redaction or by the size cap and nothing was stored. UNDECODABLE: the response could not be decoded from its content encoding or charset. EXPIRED: past the 7-day retention, metadata kept, bodies gone. x-extensible-enum: a client renders the values it knows and tolerates the rest.\n","x-extensible-enum":["BODIES","METADATA_ONLY","NOT_SAMPLED","DROPPED","UNDECODABLE","EXPIRED"],"example":"BODIES"},"SupplierCallBodyFormat":{"type":"string","description":"How a stored body is rendered (v1.25.0); JSON | XML | TEXT, x-extensible-enum.","x-extensible-enum":["JSON","XML","TEXT"],"example":"JSON"},"SupplierCallRefs":{"type":"object","description":"The platform refs this call served (v1.25.0); each is absent when the call had none.","properties":{"searchRef":{"type":"string"},"offerRef":{"type":"string"},"repriceRef":{"type":"string"},"holdRef":{"type":"string"},"orderRef":{"type":"string"}}},"SupplierCallSummary":{"type":"object","description":"One supplier call attempt as the viewer lists it (v1.25.0): metadata only, never a body or a parameter value. path is the request path without its query string.\n","required":["callId","at","supplier","verb","outcome","elapsedMs","capture"],"properties":{"callId":{"type":"string"},"at":{"type":"string","format":"date-time"},"traceId":{"type":"string"},"tenantId":{"type":["string","null"],"description":"Null: platform work (a background sync), made for no tenant."},"environment":{"type":["string","null"],"description":"PRODUCTION | SANDBOX, the side of the tenant pairing the call was made for (07 §5); null for platform work."},"supplier":{"type":"string"},"credentialSetId":{"type":["string","null"]},"operation":{"type":"string"},"verb":{"type":"string"},"outcome":{"$ref":"#/components/schemas/SupplierCallOutcome"},"faultCode":{"type":["string","null"]},"httpStatus":{"type":["integer","null"],"description":"The supplier's HTTP status; null when no response arrived."},"elapsedMs":{"type":"integer","format":"int64"},"method":{"type":"string"},"path":{"type":"string"},"requestBytes":{"type":["integer","null"],"format":"int64"},"responseBytes":{"type":["integer","null"],"format":"int64"},"capture":{"$ref":"#/components/schemas/SupplierCallCaptureState"},"truncated":{"type":"boolean","description":"True when a stored body of this call was cut at the 2 MB inline limit (the download serves it whole)."},"refs":{"$ref":"#/components/schemas/SupplierCallRefs"}}},"SupplierCallList":{"type":"object","description":"The calls behind one selector (v1.25.0), newest first.","required":["query","resolved","capture","calls"],"properties":{"query":{"$ref":"#/components/schemas/SupplierCallSelector"},"resolved":{"type":"object","description":"What the selector resolved to; empty lists for an unknown or expired ref.","properties":{"traceIds":{"type":"array","items":{"type":"string"}},"searchRefs":{"type":"array","items":{"type":"string"}},"refs":{"type":"array","description":"Every other platform ref reached (offer, reprice, hold, order)","items":{"type":"string"}}}},"capture":{"type":"object","description":"How this rung captures.","properties":{"mode":{"type":"string","description":"BODIES | METADATA_ONLY, the rung's capture mode (OD-898); x-extensible-enum","x-extensible-enum":["BODIES","METADATA_ONLY"]},"rung":{"type":"string","description":"DEV | SANDBOX | UAT | PROD, the deployment rung answering; open string"},"retentionDays":{"type":"integer","minimum":1,"description":"How long a captured body is kept (7 on the debug tier, OD-898)"}}},"calls":{"type":"array","items":{"$ref":"#/components/schemas/SupplierCallSummary"}}}},"SupplierCallHeader":{"type":"object","description":"One HTTP header as stored (v1.25.0). A masked header keeps its name; its value is a placeholder.","required":["name"],"properties":{"name":{"type":"string"},"value":{"type":"string"},"masked":{"type":"boolean"}}},"SupplierCallRequestWire":{"type":"object","description":"The masked request of one call (v1.25.0).","properties":{"method":{"type":"string"},"url":{"type":"string","description":"The full URL with credential query values masked"},"headers":{"type":"array","items":{"$ref":"#/components/schemas/SupplierCallHeader"}},"contentType":{"type":"string"},"bodyFormat":{"$ref":"#/components/schemas/SupplierCallBodyFormat"},"body":{"type":"string","maxLength":2097152,"description":"The masked body as text, at most 2 MB inline; absent when the capture state is not BODIES"},"bodyBytes":{"type":"integer","format":"int64","description":"The body's size as sent, before masking"},"storedBytes":{"type":"integer","format":"int64","description":"The masked body's stored size"},"truncated":{"type":"boolean","description":"True when body is cut at 2 MB"}}},"SupplierCallResponseWire":{"type":"object","description":"The masked response of one call (v1.25.0).","properties":{"status":{"type":"integer"},"headers":{"type":"array","items":{"$ref":"#/components/schemas/SupplierCallHeader"}},"contentType":{"type":"string"},"contentEncoding":{"type":"string","description":"The Content-Encoding on the wire, for example gzip"},"bytesOnWire":{"type":"integer","format":"int64","description":"The response size as received, still encoded"},"decoded":{"type":"boolean","description":"True when body was decoded from contentEncoding"},"bodyFormat":{"$ref":"#/components/schemas/SupplierCallBodyFormat"},"body":{"type":"string","maxLength":2097152,"description":"The masked, decoded body as text, at most 2 MB inline; absent when the capture state is not BODIES"},"bodyBytes":{"type":"integer","format":"int64","description":"The decoded body's size, before masking"},"truncated":{"type":"boolean","description":"True when body is cut at 2 MB"}}},"SupplierCallDetail":{"description":"One supplier call with its masked wire (v1.25.0), the summary plus request, response, timing and redaction.","allOf":[{"$ref":"#/components/schemas/SupplierCallSummary"},{"type":"object","description":"Detail additions","properties":{"request":{"$ref":"#/components/schemas/SupplierCallRequestWire"},"response":{"$ref":"#/components/schemas/SupplierCallResponseWire"},"timing":{"type":"object","description":"Where the elapsed time went, in milliseconds","properties":{"connectMs":{"type":"integer","format":"int64"},"supplierMs":{"type":"integer","format":"int64","description":"From request sent to response received"},"mappingMs":{"type":"integer","format":"int64","description":"Mapping the response to the canonical model"},"platformMs":{"type":"integer","format":"int64","description":"Everything else on the platform side"}}},"redaction":{"type":"object","description":"The redaction stamp of the stored bodies (13 §2.2)","properties":{"policyVersion":{"type":"string"},"maskedCount":{"type":"integer","minimum":0,"description":"Values masked, pseudonymised or dropped"},"unknownDropped":{"type":"integer","minimum":0,"description":"Unknown fields dropped from known-sensitive containers (fail closed)"}}}}}]},"SupplierWireUnlockRequest":{"type":"object","description":"connect-v1 1.25.0 (OD-898). Exactly ONE of stepUpCode and stepUpAssertion; neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED (both optional in the schema, the backend enforces the rule).","required":["scope","reason"],"properties":{"scope":{"$ref":"#/components/schemas/SupplierCallSelector"},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"scope":{"kind":"orderRef","value":"MO-SYN-0000-0001"},"reason":"Customer reports a price change at book","ticketRef":"AIK-0000","stepUpCode":"000000"}},"SupplierWireGrant":{"type":"object","description":"A wire grant (v1.25.0), valid 15 minutes for the session that minted it and for its scope only.","required":["grantId","expiresAt","scope"],"properties":{"grantId":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"scope":{"$ref":"#/components/schemas/SupplierCallSelector"}}},"ConsoleInsightsSummary":{"type":"object","required":["environment","tenants","summary"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"tenants":{"type":"array","items":{"type":"string"}},"summary":{"$ref":"#/components/schemas/InsightsSummary"}}},"ConsoleInsightsCalls":{"type":"object","required":["environment","calls"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"calls":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCall"}}}},"ConsoleInsightsOrders":{"type":"object","required":["environment","orders"],"properties":{"environment":{"$ref":"#/components/schemas/InsightsEnvironment"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/InsightsOrder"}}}},"SupplierAnalyticsApiOperation":{"type":"object","required":["operation","verb","calls","failed"],"properties":{"operation":{"type":"string"},"verb":{"type":"string"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"p50Ms":{"type":["integer","null"],"format":"int64"},"p95Ms":{"type":["integer","null"],"format":"int64"},"lastCallAt":{"type":["string","null"],"format":"date-time"}}},"SupplierAnalyticsApiCall":{"type":"object","required":["at","operation","verb","outcome","elapsedMs"],"properties":{"at":{"type":"string","format":"date-time"},"operation":{"type":"string"},"verb":{"type":"string"},"outcome":{"$ref":"#/components/schemas/SupplierCallOutcome"},"elapsedMs":{"type":"integer","format":"int64"},"faultCode":{"type":["string","null"]},"parameters":{"type":"array","items":{"type":"string"}}}},"SupplierAnalyticsApiCalls":{"type":"object","description":"The platform's calls to this supplier's API (connect-v1 1.9.0).","required":["asOf","from","to","calls","failed","bookings","operations","outcomes","recent"],"properties":{"asOf":{"type":"string","format":"date-time"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"calls":{"type":"integer","format":"int64"},"failed":{"type":"integer","format":"int64"},"p50Ms":{"type":["integer","null"],"format":"int64"},"p95Ms":{"type":["integer","null"],"format":"int64"},"bookings":{"type":"integer","format":"int64"},"operations":{"type":"array","items":{"$ref":"#/components/schemas/SupplierAnalyticsApiOperation"}},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCount"}},"recent":{"type":"array","items":{"$ref":"#/components/schemas/SupplierAnalyticsApiCall"}}}},"SupplierAnalyticsVolume":{"type":"object","description":"Booking volume (IMP-P5 supplier analytics)","required":["asOf"],"properties":{"asOf":{"type":"string","format":"date-time"},"perDay":{"type":"array","items":{"type":"object","description":"","properties":{"date":{"type":"string","format":"date"},"bookings":{"type":"integer"}},"example":{"date":"2026-10-01","bookings":41}}},"byProduct":{"type":"array","items":{"type":"object","description":"","properties":{"domain":{"type":"string","description":"Canonical product code per the domain registry (02 §1); for supplier-portal suppliers this is typically HTL, ACT, TRF or CAR. Open string."},"bookings":{"type":"integer"}},"example":{"domain":"HTL","bookings":312}}},"byRegion":{"type":"array","items":{"type":"object","description":"","properties":{"region":{"type":"string"},"bookings":{"type":"integer"}},"example":{"region":"EU-SOUTH","bookings":120}}}},"example":{"asOf":"2026-10-02T06:00:00Z","perDay":[{"date":"2026-10-01","bookings":41}],"byProduct":[{"domain":"HTL","bookings":312}],"byRegion":[{"region":"EU-SOUTH","bookings":120}]}},"SupplierAnalyticsConversion":{"type":"object","description":"Offers shown → selected → booked","required":["asOf","stages"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerOpenWindows":{"type":"array","items":{"type":"object","description":"A window the supplier's circuit was open","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"example":{"from":"2026-09-12T10:00:00Z","to":"2026-09-12T10:20:00Z"}}},"stages":{"type":"array","items":{"type":"object","description":"","properties":{"key":{"type":"string","enum":["OFFERS_SHOWN","OFFERS_SELECTED","BOOKED"]},"count":{"type":"integer"}},"example":{"key":"BOOKED","count":100}}}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"stages":[{"key":"OFFERS_SHOWN","count":18420},{"key":"OFFERS_SELECTED","count":1310},{"key":"BOOKED","count":100}]}},"SupplierAnalyticsResponseTimes":{"type":"object","description":"Search and book latency per day","required":["asOf","points"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerOpenWindows":{"type":"array","items":{"type":"object","description":"A window the supplier's circuit was open","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"example":{"from":"2026-09-12T10:00:00Z","to":"2026-09-12T10:20:00Z"}}},"points":{"type":"array","items":{"type":"object","description":"","properties":{"day":{"type":"string","format":"date"},"searchP50Ms":{"type":"integer"},"searchP95Ms":{"type":"integer"},"bookP50Ms":{"type":"integer"},"bookP95Ms":{"type":"integer"},"breakerOpen":{"type":"boolean"}},"example":{"day":"2026-10-01","searchP50Ms":640,"searchP95Ms":1810,"bookP50Ms":2100,"bookP95Ms":5400,"breakerOpen":false}}}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"points":[{"day":"2026-10-01","searchP50Ms":640,"searchP95Ms":1810,"bookP50Ms":2100,"bookP95Ms":5400,"breakerOpen":false}]}},"SupplierAnalyticsErrors":{"type":"object","description":"Error classes and rates","required":["asOf","classes"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerOpenWindows":{"type":"array","items":{"type":"object","description":"A window the supplier's circuit was open","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"example":{"from":"2026-09-12T10:00:00Z","to":"2026-09-12T10:20:00Z"}}},"classes":{"type":"array","items":{"type":"object","description":"","properties":{"cls":{"type":"string","description":"TIMEOUT | RATE_LIMITED | UPSTREAM_UNAVAILABLE | CONTRACT_VIOLATION | AUTH_FAILED | SOLD_OUT_AT_COMMIT | AMBIGUOUS_RESULT | VALIDATION"},"count":{"type":"integer"},"ratePer10k":{"type":"number"}},"example":{"cls":"TIMEOUT","count":42,"ratePer10k":12.5}}}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"classes":[{"cls":"TIMEOUT","count":42,"ratePer10k":12.5}]}},"SupplierAnalyticsCancellations":{"type":"object","description":"Cancellation and decline rates","required":["asOf","cancellationRateBps","declineRateBps"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerOpenWindows":{"type":"array","items":{"type":"object","description":"A window the supplier's circuit was open","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"example":{"from":"2026-09-12T10:00:00Z","to":"2026-09-12T10:20:00Z"}}},"cancellationRateBps":{"type":"integer"},"declineRateBps":{"type":"integer"},"declineReasons":{"type":"array","items":{"type":"object","description":"","properties":{"code":{"type":"string","description":"SOLD_OUT | PRICE_ERROR | OPERATIONAL | OTHER"},"count":{"type":"integer"}},"example":{"code":"SOLD_OUT","count":7}}}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"cancellationRateBps":420,"declineRateBps":180,"declineReasons":[{"code":"SOLD_OUT","count":7}]}},"SupplierAnalyticsL2b":{"type":"object","description":"Look-to-book against the contracted cap","required":["asOf","l2bCap","points"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerOpenWindows":{"type":"array","items":{"type":"object","description":"A window the supplier's circuit was open","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"example":{"from":"2026-09-12T10:00:00Z","to":"2026-09-12T10:20:00Z"}}},"l2bCap":{"type":"integer"},"points":{"type":"array","items":{"type":"object","description":"","properties":{"day":{"type":"string","format":"date"},"l2b":{"type":"integer"},"shedding":{"type":"boolean"}},"example":{"day":"2026-10-01","l2b":410,"shedding":false}}},"sheddingSince":{"type":["string","null"],"format":"date-time"}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerOpenWindows":[],"l2bCap":500,"points":[{"day":"2026-10-01","l2b":410,"shedding":false}],"sheddingSince":null}},"SupplierFeeds":{"type":"object","description":"Content feed health and drift","required":["asOf","feeds"],"properties":{"asOf":{"type":"string","format":"date-time"},"feeds":{"type":"array","items":{"type":"object","description":"One feed","required":["feedId","name"],"properties":{"feedId":{"type":"string"},"name":{"type":"string"},"scheduleWindowHours":{"type":"integer"},"lastRunAt":{"type":"string","format":"date-time"},"lastDeltaAt":{"type":["string","null"],"format":"date-time"},"rowsIngested":{"type":"integer"},"rowsRejected":{"type":"integer"},"rejections":{"type":"array","items":{"type":"object","description":"","properties":{"reason":{"type":"string","description":"MALFORMED | CODE_TABLE_MAPPING_FAILED"},"count":{"type":"integer"},"sampleRowRef":{"type":"string"}},"example":{"reason":"MALFORMED","count":3,"sampleRowRef":"row 2210"}}}},"example":{"feedId":"feed-rates","name":"Rates","scheduleWindowHours":6,"lastRunAt":"2026-10-02T05:00:00Z","rowsIngested":18210,"rowsRejected":3}}},"drift":{"type":"array","items":{"type":"object","description":"","properties":{"feedId":{"type":"string"},"kind":{"type":"string","description":"FEED_STALE | IDENTITY_DRIFT"},"detectedAt":{"type":"string","format":"date-time"},"detail":{"type":"string"},"publicationPaused":{"type":"boolean"},"reviewItemRef":{"type":"string"}},"example":{"feedId":"feed-rates","kind":"FEED_STALE","detectedAt":"2026-10-02T05:30:00Z","detail":"No delta for 9 h","publicationPaused":false}}}},"example":{"asOf":"2026-10-02T06:00:00Z","feeds":[{"feedId":"feed-rates","name":"Rates","scheduleWindowHours":6,"lastRunAt":"2026-10-02T05:00:00Z","lastDeltaAt":"2026-10-02T05:00:00Z","rowsIngested":18210,"rowsRejected":3,"rejections":[{"reason":"MALFORMED","count":3,"sampleRowRef":"row 2210"}]}],"drift":[{"feedId":"feed-rates","kind":"IDENTITY_DRIFT","detectedAt":"2026-10-02T05:30:00Z","detail":"12 properties changed name","publicationPaused":true,"reviewItemRef":"REV-2291"}]}},"SupplierContract":{"type":"object","description":"The supplier's commercial contract with the platform","required":["contractRef","registryStatus"],"properties":{"contractRef":{"type":"string"},"registryStatus":{"type":"string","description":"ACTIVE | SUSPENDED"},"moneyModels":{"type":"array","items":{"type":"string","description":"MERCHANT | AGENCY"}},"effectiveFrom":{"type":"string","format":"date"},"commissionSchedule":{"type":"array","items":{"type":"object","description":"","properties":{"domain":{"type":"string","description":"Canonical product code per the domain registry (02 §1); for supplier-portal suppliers this is typically HTL, ACT, TRF or CAR. Open string."},"commissionBps":{"type":"integer"}},"example":{"domain":"HTL","commissionBps":1200}}},"l2bCap":{"type":"integer"},"sla":{"type":"object","description":"Contracted SLA","properties":{"searchP95Ms":{"type":"integer"},"bookP95Ms":{"type":"integer"},"availabilityBps":{"type":"integer"},"confirmSlaHours":{"type":"integer"}},"example":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24}}},"example":{"contractRef":"CTR-FIXTURE-2026","registryStatus":"ACTIVE","moneyModels":["MERCHANT"],"effectiveFrom":"2026-01-01","commissionSchedule":[{"domain":"HTL","commissionBps":1200}],"l2bCap":500,"sla":{"searchP95Ms":2000,"bookP95Ms":6000,"availabilityBps":9950,"confirmSlaHours":24}}},"SupplierSlaScorecard":{"type":"object","description":"Measured SLA against target per period","required":["asOf","periods"],"properties":{"asOf":{"type":"string","format":"date-time"},"periods":{"type":"array","items":{"type":"object","description":"","properties":{"period":{"type":"string"},"cells":{"type":"array","items":{"type":"object","description":"","properties":{"metric":{"type":"string","description":"searchP95Ms | bookP95Ms | availabilityBps | confirmSlaHours"},"target":{"type":"number"},"measured":{"type":"number"},"breach":{"type":"boolean"},"window":{"type":"string"},"eventRefs":{"type":"array","items":{"type":"string"}}},"example":{"metric":"bookP95Ms","target":6000,"measured":6400,"breach":true,"window":"2026-09-01/2026-09-28","eventRefs":["brk_01J9"]}}}},"example":{"period":"2026-09","cells":[]}}}},"example":{"asOf":"2026-10-02T06:00:00Z","periods":[{"period":"2026-09","cells":[{"metric":"bookP95Ms","target":6000,"measured":6400,"breach":true,"window":"2026-09-01/2026-09-28","eventRefs":["brk_01J9"]}]}]}},"SupplierBreakerEvents":{"type":"object","description":"Circuit-breaker openings charged to the supplier","required":["events"],"properties":{"events":{"type":"array","items":{"type":"object","description":"","required":["eventId","openedAt"],"properties":{"eventId":{"type":"string"},"openedAt":{"type":"string","format":"date-time"},"closedAt":{"type":["string","null"],"format":"date-time"},"trigger":{"type":"object","description":"","properties":{"errorClass":{"type":"string","description":"TIMEOUT | RATE_LIMITED | UPSTREAM_UNAVAILABLE | CONTRACT_VIOLATION | AUTH_FAILED | SOLD_OUT_AT_COMMIT | AMBIGUOUS_RESULT | VALIDATION"},"failures":{"type":"integer"},"windowSeconds":{"type":"integer"}},"example":{"errorClass":"TIMEOUT","failures":20,"windowSeconds":60}},"chargedMinutes":{"type":"integer"},"backoffMinutes":{"type":"integer"}},"example":{"eventId":"brk_01J9","openedAt":"2026-09-12T10:00:00Z"}}}},"example":{"events":[{"eventId":"brk_01J9","openedAt":"2026-09-12T10:00:00Z","closedAt":"2026-09-12T10:20:00Z","trigger":{"errorClass":"TIMEOUT","failures":20,"windowSeconds":60},"chargedMinutes":20,"backoffMinutes":5}]}},"SupplierIntegrationHealth":{"type":"object","description":"Integration health — breaker, per-verb errors, contract drift","required":["asOf","breakerState"],"properties":{"asOf":{"type":"string","format":"date-time"},"breakerState":{"type":"string","description":"CLOSED | OPEN"},"verbs":{"type":"array","items":{"type":"object","description":"","properties":{"verb":{"type":"string"},"errors":{"type":"array","items":{"type":"object","description":"","properties":{"cls":{"type":"string","description":"TIMEOUT | RATE_LIMITED | UPSTREAM_UNAVAILABLE | CONTRACT_VIOLATION | AUTH_FAILED | SOLD_OUT_AT_COMMIT | AMBIGUOUS_RESULT | VALIDATION"},"ratePer10k":{"type":"number"}},"example":{"cls":"TIMEOUT","ratePer10k":4.1}}}},"example":{"verb":"search","errors":[]}}},"driftAlerts":{"type":"array","items":{"type":"object","description":"","properties":{"alertId":{"type":"string"},"verb":{"type":"string"},"field":{"type":"string"},"firstSeenCaptureId":{"type":"string"},"detectedAt":{"type":"string","format":"date-time"}},"example":{"alertId":"dr_01JA","verb":"book","field":"rooms[].rateKey","firstSeenCaptureId":"cap_77","detectedAt":"2026-10-01T12:00:00Z"}}}},"example":{"asOf":"2026-10-02T06:00:00Z","breakerState":"CLOSED","verbs":[{"verb":"search","errors":[{"cls":"TIMEOUT","ratePer10k":4.1}]}],"driftAlerts":[]}},"SupplierCertStatus":{"type":"object","description":"Certification per supplier API version","required":["asOf","versions"],"properties":{"asOf":{"type":"string","format":"date-time"},"versions":{"type":"array","items":{"type":"object","description":"","properties":{"apiVersion":{"type":"string"},"certState":{"type":"string","description":"CERTIFIED | EXPIRING | EXPIRED | PENDING"},"certifiedAt":{"type":["string","null"],"format":"date-time"},"reCertDueAt":{"type":"string","format":"date-time"},"dueReason":{"type":"string","description":"VERSION_CHANGE | CERT_RELEVANT_DRIFT | NINETY_DAYS"}},"example":{"apiVersion":"v3","certState":"CERTIFIED","certifiedAt":"2026-07-01T00:00:00Z","reCertDueAt":"2026-09-29T00:00:00Z","dueReason":"NINETY_DAYS"}}}},"example":{"asOf":"2026-10-02T06:00:00Z","versions":[{"apiVersion":"v3","certState":"EXPIRING","certifiedAt":"2026-07-01T00:00:00Z","reCertDueAt":"2026-10-10T00:00:00Z","dueReason":"NINETY_DAYS"}]}},"SupplierSandboxInfo":{"type":"object","description":"The supplier's sandbox: endpoint, masked credentials, fixtures. Never a secret.","required":["endpoint"],"properties":{"endpoint":{"type":"string","format":"uri"},"credentials":{"type":"array","items":{"type":"object","description":"","properties":{"keyId":{"type":"string"},"kind":{"type":"string"},"last4":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"}},"example":{"keyId":"sk_01","kind":"API_KEY","last4":"9f2a","createdAt":"2026-09-01T00:00:00Z"}}},"fixtures":{"type":"array","items":{"type":"object","description":"","properties":{"fixtureId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}},"example":{"fixtureId":"fx-onrequest","name":"On-request booking","description":"A booking that waits for confirm"}}}},"example":{"endpoint":"https://supplier-sim.example/EXAMPLEVILLAS","credentials":[{"keyId":"sk_01","kind":"API_KEY","last4":"9f2a","createdAt":"2026-09-01T00:00:00Z","expiresAt":null}],"fixtures":[{"fixtureId":"fx-onrequest","name":"On-request booking","description":"A booking that waits for confirm"}]}},"SupplierSettlementLine":{"type":"object","description":"One statement line","required":["lineId","supplierBookingRef","kind","money","provisional"],"properties":{"lineId":{"type":"string"},"supplierBookingRef":{"type":"string"},"kind":{"type":"string","description":"NET_RATE | COMMISSION"},"money":{"$ref":"#/components/schemas/Money"},"provisional":{"type":"boolean"},"disputeId":{"type":"string"}},"example":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false}},"SupplierStatement":{"type":"object","description":"A settlement statement","required":["statementId","period","status","remittanceTotal","lines"],"properties":{"statementId":{"type":"string"},"period":{"type":"string"},"moneyModel":{"type":"string","description":"MERCHANT | AGENCY"},"direction":{"type":"string","description":"REMITTANCE_TO_SUPPLIER | COMMISSION_FROM_SUPPLIER"},"status":{"type":"string","description":"DRAFT | PUBLISHED"},"remittanceTotal":{"$ref":"#/components/schemas/Money"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/SupplierSettlementLine"}},"payoutHold":{"oneOf":[{"type":"object","description":"Why the payout is held","properties":{"reasonCode":{"type":"string","description":"BANKING_DOCUMENT_EXPIRED | TAX_DOCUMENT_EXPIRED"},"documentId":{"type":"string"}},"example":{"reasonCode":"BANKING_DOCUMENT_EXPIRED","documentId":"DOC-2001"}},{"type":"null"}]}},"example":{"statementId":"ST-2608-M","period":"2026-08","moneyModel":"MERCHANT","direction":"REMITTANCE_TO_SUPPLIER","status":"PUBLISHED","remittanceTotal":{"amount":48200,"currency":"EUR"},"lines":[{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false}],"payoutHold":null}},"SupplierSettlements":{"type":"object","description":"The supplier's statements","required":["asOf","statements"],"properties":{"asOf":{"type":"string","format":"date-time"},"statements":{"type":"array","items":{"$ref":"#/components/schemas/SupplierStatement"}}},"example":{"asOf":"2026-10-02T06:00:00Z","statements":[{"statementId":"ST-2608-M","period":"2026-08","moneyModel":"MERCHANT","direction":"REMITTANCE_TO_SUPPLIER","status":"PUBLISHED","remittanceTotal":{"amount":48200,"currency":"EUR"},"lines":[{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false}],"payoutHold":null}]}},"SupplierReconcileRequest":{"type":"object","description":"The supplier's own lines to match against a statement","required":["lines"],"properties":{"lines":{"description":"Your own statement lines to match.","type":"array","items":{"type":"object","description":"","required":["supplierBookingRef","currency","amount"],"properties":{"lineId":{"description":"Your own id for the line, matched with the booking reference when sent.","type":"string"},"supplierBookingRef":{"description":"The booking the line is for, by your booking reference.","type":"string"},"currency":{"description":"The line's currency, as an ISO 4217 code.","type":"string"},"amount":{"description":"The line's amount, in minor units of its currency.","type":"integer"}},"example":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}}}},"example":{"lines":[{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}]}},"SupplierReconcileResult":{"type":"object","description":"Match buckets","required":["matched","missingOurs","missingTheirs","amountMismatch","provisionalExcluded"],"properties":{"matched":{"type":"array","items":{"type":"object","description":"","properties":{"ours":{"$ref":"#/components/schemas/SupplierSettlementLine"},"theirs":{"type":"object","additionalProperties":true}},"example":{"ours":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false},"theirs":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}}}},"missingOurs":{"type":"array","items":{"type":"object","additionalProperties":true}},"missingTheirs":{"type":"array","items":{"$ref":"#/components/schemas/SupplierSettlementLine"}},"amountMismatch":{"type":"array","items":{"type":"object","description":"","properties":{"ours":{"$ref":"#/components/schemas/SupplierSettlementLine"},"theirs":{"type":"object","additionalProperties":true}},"example":{"ours":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false},"theirs":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}}}},"provisionalExcluded":{"type":"array","items":{"$ref":"#/components/schemas/SupplierSettlementLine"}}},"example":{"matched":[{"ours":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","kind":"NET_RATE","money":{"amount":48200,"currency":"EUR","fxSnapshotId":"fx-20260831-EUR-01"},"provisional":false},"theirs":{"lineId":"L-2608-003","supplierBookingRef":"SB-7Q2-4KD-9ZT","currency":"EUR","amount":48200}}],"missingOurs":[],"missingTheirs":[],"amountMismatch":[],"provisionalExcluded":[]}},"SupplierDisputeContext":{"type":"object","description":"What the dispute is about","required":["kind"],"properties":{"kind":{"type":"string","description":"SETTLEMENT_LINE | SLA_CELL | TOPIC | SANDBOX_MISMATCH"},"ref":{"type":"string","description":"A line id, or <period>/<metric> for an SLA cell"},"window":{"type":"string"},"eventRefs":{"type":"array","items":{"type":"string"}}},"example":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"}},"SupplierDispute":{"type":"object","description":"A dispute thread between the supplier and the platform","required":["disputeId","subject","status","openedAt","messages"],"properties":{"disputeId":{"type":"string"},"subject":{"type":"string"},"context":{"$ref":"#/components/schemas/SupplierDisputeContext"},"status":{"type":"string","description":"OPEN | UNDER_REVIEW | RESOLVED"},"openedAt":{"type":"string","format":"date-time"},"messages":{"type":"array","items":{"type":"object","description":"","required":["messageId","at","text"],"properties":{"messageId":{"type":"string"},"at":{"type":"string","format":"date-time"},"author":{"type":"object","description":"","properties":{"side":{"type":"string","description":"SUPPLIER | PLATFORM"},"displayName":{"type":"string"}},"example":{"side":"PLATFORM","displayName":"Apiq finance"}},"text":{"type":"string"},"auditRef":{"type":"string"}},"example":{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}}}},"example":{"disputeId":"DSP-0412","subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"status":"OPEN","openedAt":"2026-09-05T10:00:00Z","messages":[{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}]}},"SupplierDisputeList":{"type":"object","description":"The supplier's disputes","required":["disputes"],"properties":{"disputes":{"type":"array","items":{"$ref":"#/components/schemas/SupplierDispute"}}},"example":{"disputes":[{"disputeId":"DSP-0412","subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"status":"OPEN","openedAt":"2026-09-05T10:00:00Z","messages":[{"messageId":"msg_1","at":"2026-09-05T10:00:00Z","author":{"side":"SUPPLIER","displayName":"Example Villas finance"},"text":"We expected EUR 482.00 net.","auditRef":"aud_01JA"}]}]}},"SupplierDisputeCreate":{"type":"object","description":"Open a dispute","required":["subject","context","firstMessage"],"properties":{"subject":{"type":"string"},"context":{"$ref":"#/components/schemas/SupplierDisputeContext"},"firstMessage":{"type":"string"}},"example":{"subject":"Line L-2608-003 underpaid","context":{"kind":"SETTLEMENT_LINE","ref":"L-2608-003"},"firstMessage":"We expected EUR 482.00 net."}},"SupplierDisputeMessage":{"type":"object","description":"Add to a dispute","required":["text"],"properties":{"text":{"type":"string"}},"example":{"text":"Attached our remittance advice."}},"SupplierNotificationChannels":{"type":"object","description":"Delivery channels for one event","required":["email","webhook","console"],"properties":{"email":{"type":"boolean"},"webhook":{"type":"boolean"},"console":{"type":"boolean"}},"example":{"email":true,"webhook":false,"console":true}},"SupplierNotificationPrefs":{"type":"object","description":"Event → channels. Events: onrequest.sla_warning, feed.stale, cert.expiring, settlement.published, dispute.updated, breaker.opened, booking.attest_requested","additionalProperties":{"$ref":"#/components/schemas/SupplierNotificationChannels"},"example":{"onrequest.sla_warning":{"email":true,"webhook":false,"console":true},"settlement.published":{"email":true,"webhook":true,"console":true}}},"SupplierComplianceDocument":{"type":"object","description":"A compliance document on file","required":["documentId","type","fileName","reviewStatus"],"properties":{"documentId":{"type":"string"},"type":{"type":"string","description":"TAX_FORM | BANKING | INSURANCE_CERT | SANCTIONS_ATTESTATION | LICENSE"},"fileName":{"type":"string"},"expiresOn":{"type":["string","null"],"format":"date"},"reviewStatus":{"type":"string","description":"SUBMITTED | UNDER_REVIEW | APPROVED | REJECTED | EXPIRED"},"rejection":{"type":"object","description":"","properties":{"reasonCode":{"type":"string"},"detail":{"type":"string"}},"example":{"reasonCode":"ILLEGIBLE_SCAN","detail":"The account number is unreadable."}}},"example":{"documentId":"DOC-2003","type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31","reviewStatus":"REJECTED","rejection":{"reasonCode":"ILLEGIBLE_SCAN","detail":"The account number is unreadable."}}},"SupplierComplianceDocumentList":{"type":"object","description":"Documents on file","required":["documents"],"properties":{"documents":{"type":"array","items":{"$ref":"#/components/schemas/SupplierComplianceDocument"}}},"example":{"documents":[{"documentId":"DOC-2003","type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31","reviewStatus":"REJECTED","rejection":{"reasonCode":"ILLEGIBLE_SCAN","detail":"The account number is unreadable."}}]}},"SupplierComplianceDocumentCreate":{"type":"object","description":"Submit document metadata (the file itself goes through POST /v1/uploads)","required":["type","fileName"],"properties":{"type":{"type":"string"},"fileName":{"type":"string"},"expiresOn":{"type":["string","null"],"format":"date"},"uploadId":{"type":"string"}},"example":{"type":"BANKING","fileName":"bank-letter.pdf","expiresOn":"2027-03-31"}},"GoLiveSignoff":{"type":"object","description":"One owner's go-live sign-off","required":["userId","signedAt","reason"],"properties":{"userId":{"type":"string"},"displayName":{"type":"string"},"signedAt":{"type":"string","format":"date-time"},"reason":{"type":"string"}},"example":{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya (Owner)","signedAt":"2026-10-02T07:00:00Z","reason":"Live cert passed; canary clean"}},"GoLiveState":{"type":"object","description":"Go-live sign-offs (two distinct owners; one Owner for an INDIVIDUAL tenant since connect-v1 1.47.0) and the promotion\n","required":["signoffs"],"properties":{"signoffs":{"type":"array","items":{"$ref":"#/components/schemas/GoLiveSignoff"}},"requiredSignoffs":{"type":"integer","description":"2 for a BUSINESS tenant (two distinct Owners, OD-870); 1 for an INDIVIDUAL tenant (connect-v1 1.47.0, FEAT-033: one person, one Owner).\n"},"promotedAt":{"type":["string","null"],"format":"date-time"}},"example":{"signoffs":[{"userId":"1e9bb43d-5e35-42ba-90f1-152117834fb9","displayName":"Priya (Owner)","signedAt":"2026-10-02T07:00:00Z","reason":"Live cert passed; canary clean"}],"requiredSignoffs":2,"promotedAt":null}},"GoLiveReason":{"type":"object","description":"A reason","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500}},"example":{"reason":"Live cert passed; canary clean"}},"GoLivePromotion":{"type":"object","description":"The tenant after promotion","required":["tenantStatus","promotedAt"],"properties":{"tenantStatus":{"type":"string","description":"The tenant's lifecycle status; TRIAL until an operator approves the D-60 activation, then LIVE"},"promotedAt":{"type":"string","format":"date-time"},"activationApprovalId":{"type":"string","description":"The D-60 TENANT_ACTIVATE ops approval this promotion opened or found pending; absent when the tenant is already LIVE"}},"example":{"tenantStatus":"TRIAL","promotedAt":"2026-10-02T09:00:00Z","activationApprovalId":"apr_01JA9GL4QZ"}},"SupportTicket":{"type":"object","description":"A support ticket (OD-P5-21)","required":["ticketId","category","subject","state","openedBy","openedAt"],"properties":{"ticketId":{"type":"string"},"category":{"type":"string","description":"ORDER_ISSUE | SUPPLIER_OR_CREDENTIALS | PAYMENT_OR_FINANCE | API_INTEGRATION | ACCESS_AND_ROLES | OTHER"},"subject":{"type":"string"},"state":{"type":"string","description":"OPEN | IN_PROGRESS | WAITING_ON_YOU | RESOLVED | CLOSED"},"traceId":{"type":["string","null"]},"orderRef":{"type":["string","null"]},"openedBy":{"type":"string"},"openedAt":{"type":"string","format":"date-time"}},"example":{"ticketId":"TCK-1037","category":"ORDER_ISSUE","subject":"Voucher missing","state":"OPEN","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","openedAt":"2026-10-02T07:30:00Z"}},"SupportTicketList":{"type":"object","description":"The tenant's tickets","required":["tickets"],"properties":{"tickets":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicket"}}},"example":{"tickets":[{"ticketId":"TCK-1037","category":"ORDER_ISSUE","subject":"Voucher missing","state":"OPEN","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX","openedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9","openedAt":"2026-10-02T07:30:00Z"}]}},"SupportTicketCreate":{"type":"object","description":"Open a ticket; traceId is the ApiErrorResponse.traceId the user saw","required":["category","subject","description"],"properties":{"category":{"type":"string","description":"ORDER_ISSUE | SUPPLIER_OR_CREDENTIALS | PAYMENT_OR_FINANCE | API_INTEGRATION | ACCESS_AND_ROLES | OTHER"},"subject":{"type":"string","minLength":3},"description":{"type":"string","minLength":10},"traceId":{"type":"string","pattern":"^[A-Za-z0-9._:-]{4,128}$"},"orderRef":{"type":"string"}},"example":{"category":"ORDER_ISSUE","subject":"Voucher missing","description":"The voucher never arrived for this stay.","traceId":"tr_51b7e0aa19","orderRef":"MO-3HT-9PW-4CX"}},"ManualOrderRequest":{"type":"object","description":"An order booked outside the platform, entered by hand (OD-P5-20)","required":["channel","domain","supplier","supplierLocator","traveler","service","price","moneyModel","reason"],"properties":{"channel":{"type":"string","enum":["OFFLINE"]},"domain":{"type":"string"},"supplier":{"type":"string"},"supplierLocator":{"type":"string"},"traveler":{"type":"object","description":"","required":["name"],"properties":{"name":{"type":"string"}},"example":{"name":"Ana Souza"}},"service":{"type":"object","description":"","required":["start"],"properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}},"example":{"start":"2026-11-10","end":"2026-11-14"}},"price":{"$ref":"#/components/schemas/Money"},"moneyModel":{"type":"string","description":"PASS_THROUGH | AGENCY | MERCHANT | FEE_ONLY"},"reason":{"type":"string","minLength":8}},"example":{"channel":"OFFLINE","domain":"HTL","supplier":"EXAMPLEHOTELS","supplierLocator":"HB-118822","traveler":{"name":"Ana Souza"},"service":{"start":"2026-11-10","end":"2026-11-14"},"price":{"amount":184000,"currency":"USD"},"moneyModel":"AGENCY","reason":"Booked by phone during the outage"}},"ManualOrderResult":{"type":"object","description":"The recorded order","required":["orderRef","channel","supplierLocator","price","moneyModel"],"properties":{"orderRef":{"type":"string"},"channel":{"type":"string","enum":["OFFLINE"]},"supplierLocator":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"},"moneyModel":{"type":"string"}},"example":{"orderRef":"MO-7RT-2KQ-8ZD","channel":"OFFLINE","supplierLocator":"HB-118822","price":{"amount":184000,"currency":"USD"},"moneyModel":"AGENCY"}},"SupplierDataAlertSettings":{"type":"object","description":"Alert thresholds for one supplier dataset (OD-IMP-P4-074)","required":["stalenessOverMax","failureStreak","quotaAbovePct","recordDropPct","mappingErrors"],"properties":{"stalenessOverMax":{"type":"boolean"},"failureStreak":{"type":"integer"},"quotaAbovePct":{"type":"integer"},"recordDropPct":{"type":"integer"},"mappingErrors":{"type":"boolean"}},"example":{"stalenessOverMax":true,"failureStreak":3,"quotaAbovePct":80,"recordDropPct":20,"mappingErrors":true}},"OperatorAccessContext":{"type":"object","description":"The verified operator (FEAT-024 §3 goal 1): platform scope, never a tenant user, never an API key. permissions carries the OPS_* roles. permissions is an open string array: a client must tolerate a value it does not know. Among the values are the OPS_ADMIN permissions added with operator invites (v1.6.7 backend, DR-55): ops:operators:invite (request an operator invite, POST /ops/v1/operators:invite) and ops:approvals:operator (approve or reject an operator invite another OPS_ADMIN requested).","required":["operatorId","realm","roles","permissions"],"properties":{"mfaRequired":{"type":"boolean","description":"connect-v1 1.12.0 (OD-879): whether this deployment requires the operator second factor at sign-in and the step-up proof (stepUpCode / stepUpAssertion) on approvals, kill switches, capability writes and on-behalf acts. False: the proof fields are not needed and are ignored if sent."},"userId":{"type":"string"},"operatorId":{"type":"string"},"email":{"type":"string","format":"email"},"realm":{"type":"string","enum":["OPERATOR"]},"roles":{"type":"array","items":{"type":"string","description":"OPS_VIEWER | OPS_ENGINEER | OPS_ADMIN | OPS_SECURITY | OPS_FINANCE"}},"permissions":{"type":"array","items":{"type":"string"}},"mfaEnrolled":{"type":"boolean"},"status":{"type":"string","description":"INVITED | ACTIVE | DISABLED"},"actorKind":{"type":"string"}},"example":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}},"OperatorSessionResponse":{"type":"object","description":"An operator session (15-minute access token, rotating refresh; FEAT-024 §3 goal 2)","required":["tokenType","accessToken","expiresAt","refreshToken","context"],"properties":{"tokenType":{"type":"string"},"accessToken":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"expiresIn":{"type":"integer"},"refreshToken":{"type":"string"},"refreshExpiresAt":{"type":"string","format":"date-time"},"context":{"$ref":"#/components/schemas/OperatorAccessContext"}},"example":{"tokenType":"Bearer","accessToken":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8tMjAyNi0wOS0yMyJ9.eyJzdWIiOiIxZTliYjQzZCJ9.c2lnbmF0dXJl","expiresAt":"2026-10-02T08:25:00Z","expiresIn":899,"refreshToken":"rt_2kq9vX0cS1mZ7pL4dR8wYb","refreshExpiresAt":"2026-10-03T08:10:00Z","context":{"userId":"op_3c9a71","operatorId":"op_3c9a71","email":"priya@mondee.example","realm":"OPERATOR","roles":["OPS_ADMIN"],"permissions":["OPS_ADMIN"],"mfaEnrolled":true,"actorKind":"human"}}},"OperatorLoginRequest":{"type":"object","description":"Operator sign-in","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string","writeOnly":true}},"example":{"username":"priya@mondee.example","password":"correct-horse-battery-staple"}},"OpsTenant":{"type":"object","description":"A tenant as the operator sees it — platform metadata only (FEAT-024 §3 goal 5)","required":["tenantId","displayName","status"],"properties":{"tenantId":{"type":"string"},"displayName":{"type":"string"},"status":{"type":"string","description":"PROVISIONING | TRIAL | LIVE | SUSPENDED | OFFBOARDING | OFFBOARDED"},"tier":{"type":["string","null"]},"entitlementVersion":{"type":["integer","null"]},"createdAt":{"type":["string","null"],"format":"date-time"}},"example":{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","tier":"GROWTH","entitlementVersion":7,"createdAt":"2026-03-02T10:00:00Z"}},"OpsTenantList":{"type":"object","description":"Tenants","required":["tenants"],"properties":{"tenants":{"type":"array","items":{"$ref":"#/components/schemas/OpsTenant"}},"page":{"$ref":"#/components/schemas/Page"}},"example":{"tenants":[{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","tier":"GROWTH","entitlementVersion":7,"createdAt":"2026-03-02T10:00:00Z"}]}},"OpsTenantDetail":{"allOf":[{"$ref":"#/components/schemas/OpsTenant"},{"type":"object","description":"Detail additions","properties":{"standingReason":{"type":["string","null"]},"approvalRef":{"type":["string","null"]},"usage":{"type":"object","description":"Platform-aggregated counts, never tenant records","properties":{"period":{"type":"string"},"searches":{"type":"integer"},"orders":{"type":"integer"},"apiCalls":{"type":"integer"}},"example":{"period":"2026-09","searches":182000,"orders":912,"apiCalls":410000}},"breakGlassActive":{"type":"boolean","description":"True only under an ACTIVE FEAT-017 grant for this operator"}},"example":{"breakGlassActive":false}}],"description":"One tenant — still platform metadata; tenant data needs a break-glass grant","example":{"tenantId":"TEN-ACME","displayName":"Acme Travel","status":"LIVE","tier":"GROWTH","entitlementVersion":7,"createdAt":"2026-03-02T10:00:00Z","standingReason":null,"approvalRef":"APR-0091","usage":{"period":"2026-09","searches":182000,"orders":912,"apiCalls":410000},"breakGlassActive":false}},"EntitlementSet":{"type":"object","description":"A tenant's entitlements (spec 07 §4's four kinds), one immutable version (FEAT-024 §7)","required":["version","products","endpoints","volumeTier","flags"],"properties":{"tenantId":{"type":"string"},"version":{"type":"integer"},"products":{"type":"object","description":"product code → verb → entitled","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"endpoints":{"type":"array","items":{"type":"string"}},"volumeTier":{"type":"string"},"flags":{"type":"object","additionalProperties":{"type":"boolean"}},"publishedBy":{"type":["string","null"]},"publishedAt":{"type":["string","null"],"format":"date-time"},"reason":{"type":["string","null"]},"previousVersion":{"type":["integer","null"]}},"example":{"tenantId":"TEN-ACME","version":7,"products":{"HTL":{"search":true,"reprice":true,"orders":true,"cancel":true},"FLT":{"search":true,"orders":false}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot","previousVersion":6}},"EntitlementPublishRequest":{"type":"object","description":"Publish a new entitlement version; baseVersion is the version edited (a stale base is 409). Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["baseVersion","products","endpoints","volumeTier","flags","reason"],"properties":{"baseVersion":{"type":"integer"},"products":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"endpoints":{"type":"array","items":{"type":"string"}},"volumeTier":{"type":"string"},"flags":{"type":"object","additionalProperties":{"type":"boolean"}},"reason":{"type":"string","minLength":8},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Optional since v1.6.7; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"baseVersion":7,"products":{"HTL":{"search":true,"orders":true}},"endpoints":["/v1/search","/v1/orders"],"volumeTier":"GROWTH","flags":{"supplierData":true},"reason":"Flights search for the pilot","stepUpCode":"000000"}},"EntitlementVersion":{"type":"object","description":"One row of the version history","required":["version","publishedBy","publishedAt","reason"],"properties":{"version":{"type":"integer"},"publishedBy":{"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"reason":{"type":"string"}},"example":{"version":7,"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot"}},"EntitlementVersionList":{"type":"object","description":"Versions, newest first","required":["versions"],"properties":{"versions":{"type":"array","items":{"$ref":"#/components/schemas/EntitlementVersion"}}},"example":{"versions":[{"version":7,"publishedBy":"op_3c9a71","publishedAt":"2026-09-30T12:00:00Z","reason":"Flights search for the pilot"}]}},"OpsReasonRequest":{"type":"object","description":"A reason (8+ characters)","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100,"description":"The incident or change ticket this decision answers (v1.6.4, optional)"}},"example":{"reason":"Version 7 broke the pilot's hotel flow","ticketRef":"OPS-2210"}},"OpsRollbackRequest":{"type":"object","description":"Roll back an entitlement version: a reason, an optional ticket and the step-up proof (v1.6.4). FEAT-024 §3 goal 2: step-up re-verification guards every capability write, and rollback is one. Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100,"description":"The incident or change ticket this rollback answers"},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Optional since v1.6.7; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"reason":"Version 7 broke the pilot's hotel flow","ticketRef":"OPS-2210","stepUpCode":"000000"}},"OpsRejectRequest":{"type":"object","description":"Reject an approval: a reason, an optional ticket and the step-up proof (v1.6.7, DR-53, IMP-P2-195). The body of POST /ops/v1/approvals/{id}:reject; :activate keeps OpsReasonRequest. Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100,"description":"The incident or change ticket this decision answers"},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"reason":"No live cert report attached","ticketRef":"OPS-2210","stepUpCode":"000000"}},"OperatorRole":{"type":"string","description":"An operator role (FEAT-024 §3 goal 1 and §9; the five OperatorRoles of the backend, published in v1.6.7). OperatorMe.roles items stay plain strings and carry these values.","enum":["OPS_VIEWER","OPS_ENGINEER","OPS_ADMIN","OPS_SECURITY","OPS_FINANCE"]},"OpsOperatorInviteRequest":{"type":"object","description":"Invite an operator (v1.6.7, IMP-P2-160 (DR-55)): the e-mail, the roles to grant, a reason, an optional ticket and the step-up proof. It opens an OPERATOR_INVITE approval; a second OPS_ADMIN approves it with step-up and the approval issues the invite. Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["email","roles","reason"],"properties":{"email":{"type":"string","format":"email"},"roles":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/OperatorRole"}},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100,"description":"The incident or change ticket this invite answers"},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"email":"new.engineer@mondee.example","roles":["OPS_ENGINEER"],"reason":"New platform engineer joins the on-call rota","ticketRef":"OPS-2330","stepUpCode":"000000"}},"OperatorInviteAccepted":{"type":"object","description":"The operator an invite activated (v1.6.7). No session is issued; the first sign-in answers 202 MfaChallenge carrying enrolment.","required":["operatorId","email","roles"],"properties":{"operatorId":{"type":"string"},"email":{"type":"string","format":"email"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/OperatorRole"}}},"example":{"operatorId":"op_8d3f21","email":"new.engineer@mondee.example","roles":["OPS_ENGINEER"]}},"OpsApproval":{"type":"object","description":"A pending two-person action (FEAT-024 §7 ops_approval). PROD_PURGE and PROD_ROLLBACK are opened by /ops/v1/supplier-data purge and rollback on PROD (v1.13.0); their effect carries action (supplier_data.purge or supplier_data.snapshot.rollback), supplierId, dataset, credentialSet, reason, and the snapshotId or the purge scope.","required":["id","kind","requesterId","scope","reason","state","requestedAt","expiresAt","effect"],"properties":{"id":{"type":"string"},"kind":{"type":"string","description":"TENANT_ACTIVATE | TENANT_OFFBOARD | BREAK_GLASS | PROD_PURGE | PROD_ROLLBACK | SWITCH_MONEY_PATH | OPERATOR_INVITE | PLATFORM_SUPPLIER_ACTIVATE | MCP_OPERATOR_GRANT. OPERATOR_INVITE since v1.6.7; PLATFORM_SUPPLIER_ACTIVATE since v1.17.0 (OD-883); MCP_OPERATOR_GRANT since v1.26.0 (ADR-C10 Phase 4, DR-68), the second approval of an operator MCP grant request, scope.key the requestId, effect the operations and cross-tenant reads to grant; OPS_SECURITY approves, never the requester. TIER_LIMIT_RAISE since v1.51.0 (PROPOSAL, FEAT-030 §18b, OD-ENR-27), opened by a tenant Owner's POST /v1/activation/limit-requests, requesterId the Owner's user id, scope.tenantId the tenant, scope.key the limit request id, effect {action tenant.daily_cap.raise, requestedCap, currentCap}; approving raises the tenant's daily cap to requestedCap [assumption, the approver holds the TENANT_ACTIVATE approver permission]. AGENT_KEY_REVIEW since v1.57.0 (PROPOSAL, FEAT-034 §5.4, phase 2, built behind aikonic.keys.velocity.enabled), opened by the platform when a tenant's live keys are paused three times in 30 days, requesterId system, scope.tenantId the tenant, scope.key the third paused keyPrefix, effect {action tenant.agent_keys.review, pausedKeys, windowDays 30}; a review, never a suspension, approving records the review and changes nothing on the tenant [assumption, the effect members and the approver permission, as TIER_LIMIT_RAISE]. MONEY_MODEL_REVIEW since v1.63.0 (PROPOSAL, FEAT-030 §18b round 2, OD-ENR-34), opened by a tenant Owner's POST /v1/activation/money-model with OWN_ACCREDITATION or BYOC on the CAPPED flow, requesterId the Owner's user id, scope.tenantId the tenant, effect {action tenant.money_model.review, model, accreditationNumber or contractReference}; approving completes the rail step money_model and rejecting returns it to needs_you. GO_LIVE_REVIEW since v1.63.0 (PROPOSAL, FEAT-030 §18d go-live review, OD-ENR-39; amended 2026-10-29, same version), opened by a tenant Owner's POST /v1/activation:go-live on the CAPPED flow, requesterId the Owner's user id, scope.tenantId the tenant, scope.key the go-live review id (GoLiveReview.id), effect {action tenant.go_live.capped, reviewId, products (the requested product codes), tier LIVE_CAPPED, dailyCap}; the approver holds the TENANT_ACTIVATE approver permission; approving (with its comment) approves every requested product (OD-ENR-40 option (a), all or nothing) and turns the tenant LIVE, tier LIVE_CAPPED, after re-checking the go-live prerequisites (409 CONFLICT PREREQUISITES_OPEN to the operator and nothing changes when one no longer holds) and the test calls (409 CONFLICT GO_LIVE_TEST_CALLS_MISSING with details.missingTestCalls until test_calls is passed for every requested product, OD-ENR-41); rejecting (with its reason) sets the review to changes_requested and the Owner may ask again; the comment or reason is shown to the customer as GoLiveReview.note; an approval that expires undecided is filed again by the system with the same scope.key [assumption]. AGENT_TIER_T3 since v1.64.0 (PROPOSAL, FEAT-044 §5.4, DR-167), the FEAT-024 approval that lets an operator set an agent to tier T3 (POST /ops/v1/agent-registry/{agentId}:set-tier sends its id as approvalId), requesterId the operator who asked, scope.key the agentId, effect {action agent.tier.set, agentId, from, to}; a second operator approves and the requester never does [assumption, the approver permission as MCP_OPERATOR_GRANT]"},"requesterId":{"type":"string"},"scope":{"type":"object","description":"","properties":{"tenantId":{"type":["string","null"]},"supplierCode":{"type":["string","null"]},"key":{"type":["string","null"]}},"example":{"tenantId":"TEN-ACME"}},"reason":{"type":"string"},"ticketRef":{"type":["string","null"]},"state":{"type":"string","description":"PENDING | APPROVED | REJECTED | EXPIRED | EXECUTED"},"approverId":{"type":["string","null"]},"decidedAt":{"type":["string","null"],"format":"date-time"},"requestedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"effect":{"type":"object","additionalProperties":true,"description":"The exact effect approving executes, shown verbatim"}},"example":{"id":"APR-0091","kind":"TENANT_ACTIVATE","requesterId":"op_51d2e0","scope":{"tenantId":"TEN-ACME"},"reason":"Live cert passed; D-60 activation","ticketRef":"OPS-2210","state":"PENDING","approverId":null,"decidedAt":null,"requestedAt":"2026-10-01T15:00:00Z","expiresAt":"2026-10-03T15:00:00Z","effect":{"tenantStatus":"LIVE"}}},"OpsApprovalList":{"type":"object","description":"Approvals","required":["approvals"],"properties":{"approvals":{"type":"array","items":{"$ref":"#/components/schemas/OpsApproval"}}},"example":{"approvals":[{"id":"APR-0091","kind":"TENANT_ACTIVATE","requesterId":"op_51d2e0","scope":{"tenantId":"TEN-ACME"},"reason":"Live cert passed; D-60 activation","ticketRef":"OPS-2210","state":"PENDING","approverId":null,"decidedAt":null,"requestedAt":"2026-10-01T15:00:00Z","expiresAt":"2026-10-03T15:00:00Z","effect":{"tenantStatus":"LIVE"}}]}},"OpsApproveRequest":{"type":"object","description":"Approve: a comment and the step-up proof. Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["comment"],"properties":{"comment":{"type":"string","minLength":8},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Optional since v1.6.7; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"comment":"Checked the cert report","stepUpCode":"000000"}},"PlatformSwitch":{"type":"object","description":"A runtime kill switch (FEAT-024 §7). ON means the kill is engaged — the thing is switched off. Keys ai.*, agent.*, supplier.*, product.*, job.*, sandbox.*","required":["key","state","scope","requiresApproval"],"properties":{"key":{"type":"string"},"state":{"type":"string","enum":["ON","OFF"]},"scope":{"type":"string","description":"GLOBAL or a tenantId"},"reason":{"type":["string","null"]},"changedBy":{"type":["string","null"]},"changedAt":{"type":["string","null"],"format":"date-time"},"requiresApproval":{"type":"boolean","description":"A money-path switch; a flip parks for a second approver"}},"example":{"key":"supplier.example-hotels","state":"OFF","scope":"GLOBAL","reason":null,"changedBy":null,"changedAt":null,"requiresApproval":false}},"PlatformSwitchList":{"type":"object","description":"Switches","required":["switches"],"properties":{"switches":{"type":"array","items":{"$ref":"#/components/schemas/PlatformSwitch"}}},"example":{"switches":[{"key":"supplier.example-hotels","state":"OFF","scope":"GLOBAL","reason":null,"changedBy":null,"changedAt":null,"requiresApproval":false}]}},"PlatformSwitchFlip":{"type":"object","description":"Flip a switch. Exactly ONE of stepUpCode and stepUpAssertion is required (v1.6.7, DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED, and a locked principal is STEP_UP_LOCKED. Both fields are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule.","required":["state","reason"],"properties":{"state":{"type":"string","enum":["ON","OFF"]},"scope":{"type":"string"},"reason":{"type":"string","minLength":8},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code. Optional since v1.6.7; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"state":"ON","scope":"GLOBAL","reason":"Example Hotels book timeouts; take it out of fan-out","stepUpCode":"000000"}},"PlatformSwitchPending":{"type":"object","description":"A money-path flip parked for a second approver","required":["approvalId"],"properties":{"approvalId":{"type":"string"}},"example":{"approvalId":"APR-0094"}},"PlatformSupplierList":{"type":"object","description":"connect-v1 1.17.0 (OD-883). Every deployed supplier adapter, sorted by supplierCode.","required":["suppliers"],"properties":{"suppliers":{"type":"array","items":{"$ref":"#/components/schemas/PlatformSupplier"}}}},"PlatformSupplier":{"type":"object","description":"connect-v1 1.17.0 (OD-883). A deployed supplier adapter and the platform's own (MONDEE) credential set for it on this rung. Never carries a secret value.","required":["supplierCode","domains","materialFields","status","serving","source","endpointHosts"],"properties":{"supplierCode":{"type":"string","example":"SUP-BUS-EXAMPLE"},"domains":{"type":"array","items":{"type":"string"},"description":"Canonical domains the adapter serves"},"materialFields":{"type":"array","description":"What a landing must carry. endpoint.host first (required, not secret), then the adapter's own fields.","items":{"$ref":"#/components/schemas/PlatformMaterialField"}},"status":{"type":"string","enum":["NOT_CONFIGURED","PENDING_VALIDATION","CERTIFYING","ACTIVE","RATE_LIMITED","EXPIRING","REVOKED","FAILED_HEALTH"],"description":"Spec 08 §3's lifecycle, plus NOT_CONFIGURED when no platform set exists"},"serving":{"type":"boolean","description":"The platform set serves traffic now (ACTIVE or EXPIRING)"},"source":{"type":"string","enum":["CONFIG","CONSOLE","NONE"],"description":"Where the platform set was declared — the deployment's platform-sets properties, this console, or nowhere"},"material":{"$ref":"#/components/schemas/PlatformMaterialState"},"endpointHosts":{"type":"array","items":{"type":"string"},"description":"Hosts the platform set may bind to on this rung — the deployment allowlist and the console-written hosts"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}},"example":{"supplierCode":"SUP-BUS-EXAMPLE","domains":["BUS"],"materialFields":[{"name":"endpoint.host","required":true,"secret":false},{"name":"public_token","required":true,"secret":true},{"name":"private_token","required":true,"secret":true}],"status":"ACTIVE","serving":true,"source":"CONSOLE","material":{"fields":["private_token","public_token"],"version":1,"landedAt":"2026-10-09T10:15:00Z","landedBy":"op_7vhjysun"},"endpointHosts":["api.bus-supplier.example"],"updatedAt":"2026-10-09T10:16:00Z","updatedBy":"op_7vhjysun"}},"PlatformMaterialField":{"type":"object","required":["name","required","secret"],"properties":{"name":{"type":"string"},"required":{"type":"boolean"},"secret":{"type":"boolean","description":"A secret field is entered masked and never shown again"}}},"PlatformMaterialState":{"type":"object","description":"The landed material — field NAMES and version only","required":["fields","version"],"properties":{"fields":{"type":"array","items":{"type":"string"}},"version":{"type":"integer"},"landedAt":{"type":"string","format":"date-time"},"landedBy":{"type":"string"}}},"PlatformCredentialsLanding":{"type":"object","description":"connect-v1 1.17.0 (OD-883). material holds the adapter fields only (no endpoint.* key). endpointHost is a bare DNS hostname: lower-case, no scheme, port, path, wildcard or trailing dot, not an IP literal, not localhost / *.localhost / *.local / *.internal, at least one dot. Exactly ONE of stepUpCode and stepUpAssertion.","required":["material","endpointHost","reason"],"properties":{"material":{"type":"object","writeOnly":true,"additionalProperties":{"type":"string","maxLength":4096}},"endpointHost":{"type":"string","maxLength":253},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"material":{"public_token":"PARTNER_…","private_token":"PARTNER_…"},"endpointHost":"api.bus-supplier.example","reason":"Example Bus preview keys for the sandbox rung","ticketRef":"AIK-412","stepUpCode":"000000"}},"PlatformSupplierStatusChange":{"type":"object","description":"connect-v1 1.17.0 (OD-883). Exactly ONE of stepUpCode and stepUpAssertion.","required":["status","reason"],"properties":{"status":{"type":"string","enum":["ACTIVE","CERTIFYING"]},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"status":"ACTIVE","reason":"Preview certified 2026-10-09, see cert-history","stepUpCode":"000000"}},"PlatformSupplierPending":{"type":"object","description":"A PLATFORM_SUPPLIER_ACTIVATE approval parked for a second operator (connect-v1 1.17.0)","required":["approvalId"],"properties":{"approvalId":{"type":"string"}},"example":{"approvalId":"APR-0131"}},"PlatformSupplierAccountProfile":{"type":"object","description":"connect-v1 1.20.0. A supplier's account profile: the non-secret JSON block (customer and user objects) by which an upstream supplier identifies the agency in each upstream request body. Stored per supplier in MongoDB collection platform_supplier_account_profile, readable with ops:read, never logged (contact PII). When set it overrides any account profile carried in the credential material.\n","required":["supplierCode","profile"],"properties":{"supplierCode":{"type":"string","example":"SUP-AIR-EXAMPLE"},"profile":{"type":"object","additionalProperties":true,"description":"The account profile as the supplier reads it; its shape is the supplier's (a hotel supplier's customer, user and UserInfo; a flight supplier's Customer, UserInfo, SearchUserInfo and OtherInfo)"},"version":{"type":"integer","minimum":1,"description":"Incremented on every set"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}},"example":{"supplierCode":"SUP-AIR-EXAMPLE","profile":{"Customer":{"customerId":"00000000-0000-4000-8000-000000000001","dkNumber":"T000000001","consolidatorId":1000,"siteId":"00000000-0000-4000-8000-000000000002","currency":"USD","userType":"Agent"},"UserInfo":{"userId":"00000000-0000-4000-8000-000000000003","userType":"Agent","platform":"Example Air"},"SearchUserInfo":{"LoggedInUserId":"00000000-0000-4000-8000-000000000003"},"OtherInfo":{"DkNumber":"T000000001","ConsolidatorId":1000,"AgentLocation":"US"}},"version":2,"updatedAt":"2026-10-16T09:30:00Z","updatedBy":"op_00000001"}},"PlatformSupplierAccountProfileUpdate":{"type":"object","description":"connect-v1 1.20.0. The whole account profile (it replaces the stored one), at most 16384 bytes serialised. SUP-HTL-EXAMPLE2 needs a customer object; a flight supplier needs a Customer object with a non-blank customerId. Exactly ONE of stepUpCode and stepUpAssertion.\n","required":["profile","reason"],"properties":{"profile":{"type":"object","additionalProperties":true},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"profile":{"customer":{"customerId":"00000000-0000-4000-8000-000000000011","dkNumber":"T000000002","name":"Example Travel Agency","email":"agency@example.com","phone":"+10000000000","currency":"USD"},"user":{"userId":"00000000-0000-4000-8000-000000000012","firstName":"Test","lastName":"Agent","email":"agent@example.com"},"UserInfo":{"userId":"00000000-0000-4000-8000-000000000012","userType":"Agent","platform":"ExamplePlatform"}},"reason":"Agency account profile for the sandbox rung","ticketRef":"AIK-000","stepUpCode":"000000"}},"OpsApprovalRequested":{"type":"object","description":"The approval a tenant-activation request (v1.6.5), an operator invite (v1.6.7) or a PROD supplier-data purge or rollback (v1.13.0, PROD_PURGE / PROD_ROLLBACK) opened","required":["approvalId"],"properties":{"approvalId":{"type":"string"}},"example":{"approvalId":"APR-0095"}},"OpsSupplierDataRollbackRequest":{"type":"object","description":"connect-v1 1.13.0 (OD-881): roll a PLATFORM credential set's dataset back to a retained snapshot. Exactly ONE of stepUpCode and stepUpAssertion is required (DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED. Both are optional in the schema (not oneOf, which generators handle badly); the backend enforces the rule. Neither is kept in a parked approval.","required":["reason"],"properties":{"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"reason":"08:15 run ingested a truncated dump (record count fell 31%)","stepUpCode":"000000"}},"OpsSupplierDataPurgeRequest":{"type":"object","description":"connect-v1 1.13.0 (OD-881): purge the cache layers of a PLATFORM credential set (the dataset store is never purged). scope is SupplierDataPurgeRequest's; reason is REQUIRED here. Exactly ONE of stepUpCode and stepUpAssertion is required (DR-54); neither or both is 403 PERMISSION_DENIED details.reason STEP_UP_FAILED.","required":["scope","reason"],"properties":{"scope":{"type":"object","required":["supplierId"],"properties":{"supplierId":{"type":"string"},"dataset":{"type":"string"},"credentialSet":{"type":"string"},"supplierKeys":{"type":"array","maxItems":1000,"items":{"type":"string"}},"layers":{"type":"array","description":"L1 | L2 | CDN — absent = all cache layers","items":{"type":"string"}}}},"reason":{"type":"string","minLength":8,"maxLength":500},"ticketRef":{"type":"string","maxLength":100},"stepUpCode":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"description":"The 6-digit TOTP code; send this or stepUpAssertion, not both."},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"}},"example":{"scope":{"supplierId":"SUP-EXP-EXAMPLE","dataset":"catalog_product","supplierKeys":["5010SYDNEY"]},"reason":"Stale image set reported by the supplier","stepUpCode":"000000"}},"HolidayCalendarCell":{"type":"object","x-status":"planned","description":"One day of a holiday calendar as a date-picker renders it (planned)","required":["date"],"properties":{"date":{"type":"string","format":"date"},"weekend":{"type":"boolean"},"holidays":{"type":"array","items":{"type":"string"},"description":"Holiday names on this day"},"publicHoliday":{"type":"boolean"},"longWeekend":{"type":"boolean"},"bridgeDay":{"type":"boolean"}},"example":{"date":"2026-11-20","weekend":false,"holidays":["Dia da Consciência Negra"],"publicHoliday":true,"longWeekend":true,"bridgeDay":false}},"EntryRequirementDocument":{"type":"object","x-status":"planned","description":"One document of an entry-requirement profile (SPI §3.7; planned typing)","required":["name","type","requirementLevel"],"properties":{"name":{"type":"string"},"type":{"type":"string","description":"VISA | PASSPORT | ETA | HEALTH | FORM | OTHER; open string"},"requirementLevel":{"type":"string","description":"REQUIRED | MAY_BE_REQUIRED | RECOMMENDED"},"description":{"type":"string"},"fee":{"$ref":"#/components/schemas/Money"},"validity":{"type":"object","properties":{"minExpiryDate":{"type":"string","format":"date"},"validityPeriod":{"type":"string","description":"ISO-8601 duration"}}}},"example":{"name":"Visa B1/B2","type":"VISA","requirementLevel":"REQUIRED","fee":{"amount":18500,"currency":"USD"}}},"DiningSearchIntent":{"type":"object","description":"A restaurant search. Where (a destination, or restaurantIds to ask named restaurants directly), when (a date plus a restaurant-local time, with an optional window around it), the party, seating preferences, and whether the restaurants' experiences (set menus, tasting events) are offered beside plain table slots. Send destination or restaurantIds. Send `date`; ExperienceSearchIntent's `dates.from` is also read when `date` is absent, so a client that already sends `dates` keeps working. Times are restaurant-local without an offset, because the supplier states none.\n","required":["time","party"],"properties":{"resultShape":{"type":"string","enum":["OFFERS","CALENDAR"],"default":"OFFERS"},"destination":{"$ref":"#/components/schemas/PlaceRef"},"restaurantIds":{"type":"array","description":"Public restaurant ids to search directly: `prov:dining:<rid>` until the restaurant cross-walk exists (FEAT-014), or a canonical id once it does. At most 50.\n","maxItems":50,"items":{"type":"string","maxLength":128}},"date":{"type":"string","format":"date","description":"The dining date (restaurant-local)"},"dates":{"$ref":"#/components/schemas/DateRange","description":"As-built fallback read when `date` is absent (dates.from is the dining date)"},"time":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","description":"HH:mm, restaurant-local"},"windowMinutes":{"type":"integer","minimum":0,"maximum":300,"description":"Forward and backward window around `time` (Example Dining forward_minutes / backward_minutes); absent = the supplier default"},"party":{"$ref":"#/components/schemas/DiningParty"},"seating":{"type":"array","description":"Required seating attributes (Example Dining require_attributes), e.g. default, outdoor, bar, highTop, counter; open strings","items":{"type":"string","maxLength":32}},"includeExperiences":{"type":"boolean","default":false,"description":"When true, experience slots are offered beside plain table slots (Example Dining include_experiences)"},"experienceSelection":{"$ref":"#/components/schemas/DiningExperienceSelection"}},"example":{"destination":{"text":"San Francisco","countryCode":"US"},"date":"2026-11-12","time":"19:00","windowMinutes":90,"party":{"adults":2}}},"DiningParty":{"type":"object","description":"The dining party (dining proposal §3). Example Dining books a party size, not ages, so children are a count; childAges is accepted for clients that send PartySpec and is counted, never priced.\n","required":["adults"],"properties":{"adults":{"type":"integer","minimum":1,"maximum":50},"children":{"type":"integer","minimum":0,"maximum":50},"childAges":{"type":"array","items":{"type":"integer","minimum":0,"maximum":17}}},"example":{"adults":2,"children":1}},"DiningExtension":{"type":"object","description":"FNB block `dining` of ItemExtension (dining proposal §2 and §5.3; v1.7.0): the restaurant, the slot (restaurant-local, no offset), the policies, the experience on this slot and the restaurant's experiences, menus, reviews and payment facts. Carried on Offer.extension and OrderItem.extension. Until v1.7.0 the backend carried the same data in the undeclared `supplierExtensions.dining`; that key stays as a deprecated echo for one release. The diner's answers (phoneCountry, phone, specialRequest, restaurantEmailOptIn, opentableEmailOptIn, smsOptIn) are sent in the order's message.intent.requiredInfoAnswers, as the offer's requiredInfo rows name them. `poweredBy` is Example Dining's branding requirement (the dining supplier's terms): a client that renders the offer shows it. A venue-paid experience price is a money line on Offer.pricing.payAtProperty (collectedBy PROPERTY, payableAt PROPERTY), never inside Offer.price, which stays zero for a FREE table.\n","required":["restaurant","slot","poweredBy"],"properties":{"restaurant":{"$ref":"#/components/schemas/DiningRestaurant"},"slot":{"$ref":"#/components/schemas/DiningSlot"},"creditCardRequired":{"type":"boolean","description":"True when the slot is card-secured; the order then needs intent.payment.paymentMethod (a Spreedly token)"},"cancellationPolicy":{"$ref":"#/components/schemas/DiningCancellationPolicy"},"bookingPolicies":{"type":"array","description":"The restaurant's booking policy messages for this slot, shown before booking","items":{"$ref":"#/components/schemas/DiningBookingPolicy"}},"experience":{"$ref":"#/components/schemas/DiningExperience"},"experiences":{"type":"array","description":"The restaurant's active experiences (detail answers; search carries `experience` only)","items":{"$ref":"#/components/schemas/DiningExperience"}},"menus":{"type":"array","description":"The restaurant's menus, structured, or a link to its web menu (detail answers)","items":{"$ref":"#/components/schemas/DiningMenu"}},"reviews":{"$ref":"#/components/schemas/DiningReviews"},"payment":{"$ref":"#/components/schemas/DiningPayment"},"confirmationNumber":{"type":"string","description":"Example Dining's confirmation number, as the diner and the restaurant know it. Order lines only (OrderItem.extension.dining), populated once the reservation is made; never on an offer.\n"},"guarantee":{"$ref":"#/components/schemas/DiningGuarantee"},"poweredBy":{"type":"string","description":"The branding line to show, e.g. Example Dining"}},"example":{"restaurant":{"rid":"100123","name":"Harbour Table","city":"San Francisco","country":"US"},"slot":{"dateTime":"2026-11-12T19:30","partySize":2,"diningAreaId":"1","area":"Main dining room","environment":"INDOOR","tableAttributes":["default"]},"creditCardRequired":false,"cancellationPolicy":{"type":"FREE"},"bookingPolicies":[{"type":"STANDARD","message":"Tables are held for 15 minutes past the reservation time."}],"poweredBy":"Example Dining"}},"DiningRestaurant":{"type":"object","description":"The restaurant of a dining offer (dining proposal §2)","required":["rid"],"properties":{"rid":{"type":"string","description":"The supplier's restaurant id, as text"},"restaurantId":{"type":"string","description":"The public id, prov:dining:<rid> until the cross-walk exists"},"name":{"type":"string"},"city":{"type":"string"},"country":{"type":"string","pattern":"^[A-Z]{2}$"}}},"DiningSlot":{"type":"object","description":"One bookable table slot (dining proposal §2). dateTime is restaurant-local with NO offset (yyyy-MM-ddTHH:mm), because Example Dining states none; once the supplier confirms the time zone an offset may be added (additive).\n","required":["dateTime","partySize"],"properties":{"dateTime":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?$"},"partySize":{"type":"integer","minimum":1},"diningAreaId":{"type":"string"},"area":{"type":"string"},"environment":{"type":"string","description":"INDOOR | OUTDOOR and others; open string as Example Dining states it"},"tableAttributes":{"type":"array","items":{"type":"string"}}}},"DiningCancellationPolicy":{"type":"object","description":"The slot's cancellation policy (dining proposal §2). type FREE when Example Dining states none; otherwise Example Dining's policy type (e.g. CREDIT_CARD_DEPOSIT, NO_SHOW_FEE). `deposit` is the amount the restaurant may charge to the card on file under that policy, never collected by the platform. `denominator` is Example Dining's minor-unit factor, kept so a client can show the supplier's own figure; `deposit` is already canonical minor units.\n","required":["type"],"properties":{"type":{"type":"string"},"deposit":{"$ref":"#/components/schemas/Money"},"denominator":{"type":"integer","minimum":1},"cutoffType":{"type":"string","description":"Example Dining's cutoff type (e.g. HOURS_BEFORE, DAYS_BEFORE); open string"},"daysBeforeCutoff":{"type":"integer","minimum":0}}},"DiningBookingPolicy":{"type":"object","description":"One booking-policy message (dining proposal §2)","required":["message"],"properties":{"type":{"type":"string","description":"The policy kind as Example Dining states it; STANDARD when it states none"},"message":{"type":"string"}}},"DiningExperience":{"type":"object","description":"A restaurant experience (dining proposal §5.3). A priced experience that is not prepaid is PAID AT THE RESTAURANT (payableAt RESTAURANT): its prices are shown here and as Offer.pricing.payAtProperty lines, never in Offer.price. A prepaid experience needs a card token at the order (D3, OD-875).\n","required":["experienceId"],"properties":{"experienceId":{"type":"integer","format":"int64"},"version":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","description":"Example Dining's experience type (e.g. EXPERIENCE, EVENT); open string"},"priceType":{"type":"string","description":"PER_PERSON | PER_RESERVATION and others; open string"},"prepaymentRequired":{"type":"boolean"},"payableAt":{"type":"string","description":"RESTAURANT | BOOKING; open string"},"prices":{"type":"array","items":{"type":"object","required":["priceId"],"properties":{"priceId":{"type":"integer","format":"int64"},"title":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"},"allInclusive":{"type":"boolean"}}}},"gratuity":{"type":"object","properties":{"basisPoints":{"type":"integer","minimum":0,"description":"2000 = 20%"},"mandatory":{"type":"boolean"}}}},"example":{"experienceId":900101,"version":3,"name":"Chef's counter tasting","type":"EXPERIENCE","priceType":"PER_PERSON","prepaymentRequired":false,"payableAt":"RESTAURANT","prices":[{"priceId":1,"title":"Tasting menu","price":{"amount":12500,"currency":"USD"},"allInclusive":false}],"gratuity":{"basisPoints":2000,"mandatory":false}}},"DiningMenu":{"type":"object","description":"One menu (dining proposal §5.3): structured sections and items, or only a webMenuUrl when the restaurant publishes no structured menu.\n","properties":{"menuId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"currency":{"type":"string"},"sections":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"}}}}}}},"webMenuUrl":{"type":"string","format":"uri"}}},"DiningReviews":{"type":"object","description":"Ratings and recent public review text (dining proposal §5.3), never the reviewer. Ratings are strings (one decimal, 1-5) so no float reaches the response.\n","properties":{"summary":{"type":"object","properties":{"count":{"type":"integer"},"ratings":{"type":"object","description":"overall, food, service, ambience, value, noise; each a string rating","additionalProperties":{"type":"string"}},"recommendations":{"type":"integer"},"recommendedYes":{"type":"integer"}}},"recent":{"type":"array","items":{"type":"object","properties":{"overall":{"type":"integer"},"ratings":{"type":"object","additionalProperties":{"type":"integer"}},"title":{"type":"string"},"text":{"type":"string"},"dinedOn":{"type":"string","format":"date"},"submittedAt":{"type":"string","format":"date-time"}}}}}},"DiningPayment":{"type":"object","description":"Payment facts of the slot (dining proposal §5.3; D3, OD-875). cardCapture says how a card-secured slot is booked: PAYMENT_METHOD_TOKEN (Online Booking API: the client tokenises the card with Spreedly and sends intent.payment.paymentMethod; the platform never sees a card number), OPENTABLE_SMS_LINK (In-House API: Example Dining texts the diner a link to enter the card with Example Dining; the order sends no token and needs requiredInfoAnswers.smsOptIn true, since the link is a text message) or NOT_SUPPORTED (a card-secured slot of this restaurant cannot be booked through the API). processor names the restaurant's payment processor from its payment profile. cardProvider, cardBrand and cardLast4 appear only on an order line (OrderItem.extension.dining), read from the order's supplier locator: the vault, brand and last four digits of the token the order was booked with; never a card number.\n","properties":{"creditCardRequired":{"type":"boolean"},"processor":{"type":"string","description":"e.g. SPREEDLY; open string"},"cardCapture":{"type":"string","description":"PAYMENT_METHOD_TOKEN | OPENTABLE_SMS_LINK | NOT_SUPPORTED; open string"},"cardProvider":{"type":"string","description":"Order line only; the vault of the token booked with (SPREEDLY)"},"cardBrand":{"type":"string","description":"Order line only; the card brand the vault returned"},"cardLast4":{"type":"string","pattern":"^[0-9]{4}$","description":"Order line only; the last four digits the vault returned"}},"example":{"creditCardRequired":true,"processor":"SPREEDLY","cardCapture":"OPENTABLE_SMS_LINK"}},"AmendChanges":{"type":"object","description":"The values an amend QUOTE asks for beside its amendType (v1.7.0; dining proposal §5.1). A flat object of scalars: anything else is 400 VALIDATION details.field message.intent.changes. FNB (DATE_SHIFT): `dateTime` (restaurant-local yyyy-MM-ddTHH:mm) or `date` + `time`, and/or `partySize`; at least one must differ from the booking, or the amend is 400 VALIDATION. A COMMIT sends no changes: what it executes travels in the amendRef. Other domains add keys additively.\n","properties":{"dateTime":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$"},"date":{"type":"string","format":"date"},"time":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$"},"partySize":{"type":"integer","minimum":1,"maximum":50}},"example":{"dateTime":"2026-11-12T20:30","partySize":4}},"EncryptedCardEnvelope":{"type":"object","additionalProperties":false,"description":"v1.36.0 (PLAT-CARDENV §4.3; SANDBOX/UAT only, OD-904). A card sealed by the caller to the key of GET /v1/payments/card-key — byte-compatible with the upstream platform's contract A3. jwe is a compact JWE (five base64url segments, no padding): protected header exactly {\"alg\":\"RSA-OAEP-256\",\"enc\":\"A256GCM\",\"kid\":\"<kid>\"}, a random 32-byte CEK wrapped with RSA-OAEP (SHA-256 hash and MGF1), AES-256-GCM content with a 96-bit IV and 128-bit tag, the protected-header segment's ASCII as AAD. The plaintext is UTF-8 JSON, all strings: {\"number\":\"<13-19 digits>\",\"cvv\":\"<3-4 digits>\",\"expMonth\":\"MM\",\"expYear\":\"YYYY\",\"holderName\":\"<2-60>\"}. kid here and in the header must match each other and a live key.\n","required":["kid","jwe"],"properties":{"kid":{"type":"string","maxLength":64},"jwe":{"type":"string","maxLength":4096,"pattern":"^[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]*){4}$"}}},"CardEnvelopeKey":{"type":"object","description":"v1.36.0 (PLAT-CARDENV §4.1). The public key a caller seals a card to — the shape of the upstream platform's /v1/payments/card-key, plus publicKeyJwk.alg. Cached an hour; after a rotation the previous kid still opens for 24 hours.\n","required":["kid","alg","enc","publicKeyJwk"],"properties":{"kid":{"type":"string"},"alg":{"type":"string","enum":["RSA-OAEP-256"]},"enc":{"type":"string","enum":["A256GCM"]},"publicKeyJwk":{"type":"object","required":["kty","n","e","kid","use"],"properties":{"kty":{"type":"string","enum":["RSA"]},"n":{"type":"string","description":"base64url modulus"},"e":{"type":"string","description":"base64url public exponent"},"kid":{"type":"string"},"use":{"type":"string","enum":["enc"]},"alg":{"type":"string","enum":["RSA-OAEP-256"]}}}}},"PaymentMethodToken":{"type":"object","description":"A card the CLIENT tokenised with the payment-method vault (D3, OD-875; v1.7.0). TOKEN ONLY: this object has no field for a card number, an expiry or a security code, and the platform never receives, stores or logs one. The client collects the card in the vault's own iframe (Spreedly iFrame or Express), receives a payment-method token and sends it here; the platform passes the token to the supplier (Example Dining's credit_card object) and keeps only provider, brand and last4 on the order. A digit run that looks like a card number in ANY field of the request is 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED (PanGuard), and the token itself is never echoed or logged. threeDs (v1.7.0, before release) is sent only on the RETRY of an order the supplier refused with details.reason SCA_REQUIRED: the same token plus the result of the 3-D Secure challenge the client ran with the vault.\n","required":["provider","token"],"additionalProperties":false,"properties":{"provider":{"type":"string","enum":["SPREEDLY"],"description":"The vault that issued the token. Closed on purpose; a new vault is a contract change"},"token":{"type":"string","minLength":8,"maxLength":128,"pattern":"^[A-Za-z0-9_-]+$","description":"The vault's payment-method token. Treated like a credential; never returned"},"type":{"type":"string","enum":["credit_card"],"description":"The payment method kind (Example Dining paymentMethod); absent = credit_card"},"last4":{"type":"string","pattern":"^[0-9]{4}$","description":"The last four digits the vault returned, for display and for the supplier"},"brand":{"type":"string","maxLength":32,"description":"Card brand the vault returned (visa, master, american_express and others)"},"threeDs":{"$ref":"#/components/schemas/PaymentThreeDs"}},"example":{"provider":"SPREEDLY","token":"syn-spreedly-pm-token-0001","type":"credit_card","last4":"1111","brand":"visa"}},"EnquiryCreateRequest":{"type":"object","description":"POST /v1/enquiries (v1.7.0; dining proposal §5.5): a lead to a supplier that takes enquiries, not bookings (Example Dining private dining; any other lead-only supplier). Envelope in: context.domain names the product, message.intent carries the lead.\n","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeContext"},"message":{"description":"The request: `message.intent` is the lead.","type":"object","required":["intent"],"properties":{"intent":{"$ref":"#/components/schemas/EnquiryIntent"}}}}},"EnquiryIntent":{"type":"object","description":"The lead (dining proposal §5.5). productId names the venue (for FNB the restaurant id). contact is personal data: it travels to the supplier's lead call and is never echoed; the enquiry record keeps it encrypted for the tenant's retention period (spec 15 §3.3). eventType is the supplier's documented vocabulary; for Example Dining: Anniversary, BabyShower, BarMitzvah, Birthday, BridalWeddingShower, CharityEvent, Class, CocktailParty, Conference, CorporateEvent, DinnerParty, Engagement, FilmingLocation, Fundraiser, Graduation, HolidayEvent, LaunchParty, Meeting, Other, PrivateDining, RehearsalDinner, SocialEvent, SpecialOccasion, WeddingReception. An unknown type, a date that is not in the future or a missing phone is 400 VALIDATION before anything is sent (details.reason <SUPPLIER>_LEAD_INVALID, details.invalid lists the fields).\n","required":["productId","contact","eventDate","eventTime","partySize","eventType"],"properties":{"productId":{"description":"The venue the lead is for (for dining, the restaurant id).","type":"string","maxLength":128},"contact":{"description":"Who the venue should contact; personal data, sent to the venue and never echoed back.","type":"object","required":["firstName","lastName","phoneNumber"],"properties":{"firstName":{"description":"First name of the contact.","type":"string","maxLength":100},"lastName":{"description":"Last name of the contact.","type":"string","maxLength":100},"email":{"description":"Email address of the contact.","type":"string","format":"email"},"phoneNumber":{"type":"string","maxLength":32,"description":"E.164"}}},"eventDate":{"description":"Date of the event, in the future.","type":"string","format":"date"},"eventTime":{"description":"Start time of the event, 24-hour HH:MM, local to the venue.","type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$"},"partySize":{"description":"How many guests.","type":"integer","minimum":1,"maximum":5000},"eventType":{"description":"The kind of event, from the venue's list (for example Birthday or CorporateEvent).","type":"string"},"flexibleDate":{"description":"True when another date near eventDate would also work.","type":"boolean"},"clientReference":{"description":"Your own reference for the lead, returned on the enquiry.","type":"string","maxLength":128}},"example":{"productId":"prov:dining:100123","contact":{"firstName":"Ada","lastName":"Example","email":"ada@example.test","phoneNumber":"+14155550100"},"eventDate":"2026-12-04","eventTime":"19:00","partySize":24,"eventType":"CorporateEvent","flexibleDate":false}},"EnquiryResponse":{"type":"object","description":"The enquiry as POST /v1/enquiries and GET /v1/enquiries/{enquiryRef} answer it (envelope out)","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"$ref":"#/components/schemas/Enquiry"}}},"Enquiry":{"type":"object","description":"One lead (v1.7.0). state SUBMITTED (the supplier acknowledged it and gave a reference) | UNCONFIRMED (the call was not answered; the lead may have reached the venue and is never re-sent; context.warnings[] carries <SUPPLIER>_LEAD_UNCONFIRMED) | FAILED (the supplier refused it, e.g. the restaurant has no private-dining subscription). Open string. The contact is never echoed; the supplier's lead id is published as supplierReference.\n","required":["enquiryRef","state","domain","productId","createdAt"],"properties":{"enquiryRef":{"type":"string","description":"EN-XXX-XXX-XXX, unguessable"},"state":{"type":"string"},"domain":{"type":"string"},"productId":{"type":"string"},"supplier":{"type":"string"},"supplierReference":{"type":"string"},"eventDate":{"type":"string","format":"date"},"eventTime":{"type":"string"},"partySize":{"type":"integer"},"eventType":{"type":"string"},"clientReference":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningPartnerPage":{"type":"object","description":"Offset paging of the dining partner lists (Example Dining Sync and Directory APIs page by limit and offset)","required":["limit","offset","hasMore"],"properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"},"nextOffset":{"type":"integer","description":"The offset of the next page; absent on the last page"}}},"DiningPartnerAck":{"type":"object","description":"The answer of a partner write the supplier acknowledges without a body of its own","required":["status"],"properties":{"status":{"type":"string","description":"ACCEPTED | APPLIED | DELETED; open string"},"supplierReference":{"type":"string"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}},"DiningOrderNoteRequest":{"type":"object","description":"A partner note for the restaurant reservation behind a dining order (v1.30.0)","additionalProperties":false,"required":["note"],"properties":{"note":{"type":"string","minLength":1,"maxLength":5000,"description":"Shown to the restaurant on the reservation; Unicode (emoji included) allowed"}}},"DiningOrderNoteUpdate":{"type":"object","description":"The new text of a partner note; an empty string deletes the note (v1.30.0)","additionalProperties":false,"required":["note"],"properties":{"note":{"type":"string","maxLength":5000}}},"DiningOrderNote":{"type":"object","description":"A partner note on the restaurant reservation behind a dining order (v1.30.0)","required":["orderRef","noteId","note"],"properties":{"orderRef":{"type":"string"},"noteId":{"type":"string","description":"The supplier's id for the note; send it to PATCH .../notes/{noteId}"},"note":{"type":"string","description":"The text as the supplier stores it; empty after a delete"}}},"DiningOrderConfirmation":{"type":"object","description":"The supplier recorded the diner's confirmation of the reservation (v1.30.0)","required":["orderRef","confirmed"],"properties":{"orderRef":{"type":"string"},"confirmed":{"type":"boolean","const":true},"restaurantId":{"type":"string","description":"The supplier's restaurant id, as on the order's dining extension"},"confirmationNumber":{"type":"string","description":"The supplier's confirmation number, as on the order's dining extension"}}},"DiningGuest":{"type":"object","description":"A restaurant's guest profile, as the CRM and Sync APIs carry it (personal data, partner audience only)","properties":{"guestId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phoneNumber":{"type":"string"},"emailOptIn":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"updatedAt":{"type":"string","format":"date-time"}}},"DiningGuestCreateRequest":{"type":"object","description":"POST .../guests (CRM v3 Guest Creation); answers the guest id Example Dining assigns or matches","required":["firstName","lastName"],"properties":{"firstName":{"type":"string","maxLength":100},"lastName":{"type":"string","maxLength":100},"email":{"type":"string","format":"email"},"phoneNumber":{"type":"string","maxLength":32},"partnerGuestId":{"type":"string","maxLength":128,"description":"The partner's own id for the guest"}}},"DiningLoyaltyRequest":{"type":"object","description":"PUT .../guests/{guestId}/loyalty (CRM v3 Loyalties)","required":["programName"],"properties":{"programName":{"type":"string"},"memberId":{"type":"string"},"tier":{"type":"string"},"points":{"type":"integer"},"pointsToNextTier":{"type":"integer"},"expiresOn":{"type":"string","format":"date"}}},"DiningTag":{"type":"object","description":"A restaurant guest tag (CRM v3 Tags)","required":["tagId","name"],"properties":{"tagId":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"icon":{"type":"string"}}},"DiningTagList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningTag"}}}},"DiningTagUpsertRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":60},"category":{"type":"string"},"icon":{"type":"string"}}},"DiningGuestTagsRequest":{"type":"object","description":"PUT .../guests/{guestId}/tags (CRM v3 Tag Assign); the guest's full tag set","required":["tagIds"],"properties":{"tagIds":{"type":"array","items":{"type":"string"}}}},"DiningGuestInsightsRequest":{"type":"object","description":"PUT .../guests/{guestId}/insights (CRM v3 Insights); free-form insights shown to the host","required":["insights"],"properties":{"insights":{"type":"array","items":{"type":"object","required":["label","value"],"properties":{"label":{"type":"string"},"value":{"type":"string"}}}}}},"DiningGuestPropertiesRequest":{"type":"object","description":"PUT .../guests/{guestId}/properties (CRM v3 Properties; hotel stay details for a hotel restaurant)","required":["properties"],"properties":{"properties":{"type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"roomNumber":{"type":"string"},"checkIn":{"type":"string","format":"date"},"checkOut":{"type":"string","format":"date"},"vip":{"type":"boolean"}}}}}},"DiningGuestPhotoRequest":{"type":"object","description":"PUT .../guests/{guestId}/photo (CRM v3 Upload Photo); the image is an uploadId from POST /v1/uploads (a scanned image/jpeg or image/png). The platform hands Example Dining a signed URL for it on GET /ingest/dining/opentable/media/{token}, valid 30 days; the client never sees or calls that URL.\n","required":["uploadId"],"properties":{"uploadId":{"type":"string"}}},"DiningGuestPhotoStatus":{"type":"object","required":["status"],"properties":{"status":{"type":"string","description":"NONE | PROCESSING | ACTIVE | REJECTED; open string"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningPosTicket":{"type":"object","description":"One check from the partner's POS (POS Tickets API), matched to a reservation by Example Dining. CREATE: every Money on the check (total, and subtotal, tax, tip and item prices when sent) must carry its ISO-4217 currency, one currency per check; a missing or mixed currency is 400 VALIDATION details.field naming the member. READ (GET .../pos/tickets): when Example Dining states no currency for a check, its money members are ABSENT (never guessed, never defaulted to USD) and the row carries currencyStated false; amounts are only published with the currency they are in.\n","required":["ticketId","openedAt","total"],"properties":{"ticketId":{"type":"string"},"reservationId":{"type":"string"},"tableNumber":{"type":"string"},"covers":{"type":"integer"},"openedAt":{"type":"string","format":"date-time"},"closedAt":{"type":"string","format":"date-time"},"status":{"type":"string","description":"OPEN | CLOSED | VOID; open string"},"currencyStated":{"type":"boolean","readOnly":true,"description":"Read only; false when Example Dining stated no currency, so the money members are absent"},"subtotal":{"$ref":"#/components/schemas/Money"},"tax":{"$ref":"#/components/schemas/Money"},"tip":{"$ref":"#/components/schemas/Money"},"total":{"$ref":"#/components/schemas/Money"},"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer"},"price":{"$ref":"#/components/schemas/Money"}}}}}},"DiningPosTicketList":{"type":"object","required":["items","page"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningPosTicketRead"}},"page":{"$ref":"#/components/schemas/DiningPartnerPage"}}},"DiningPosRestaurant":{"type":"object","properties":{"rid":{"type":"string"},"name":{"type":"string"},"partnerLocationId":{"type":"string"},"status":{"type":"string","description":"ACTIVE | INACTIVE | PENDING; open string"}}},"DiningPosRestaurantList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningPosRestaurant"}}}},"DiningPosStatusRequest":{"type":"object","description":"PATCH .../pos/status (POS Status API); also how a delayed-email POS onboarding is activated","required":["status"],"properties":{"status":{"type":"string","description":"ACTIVE | INACTIVE; open string"},"partnerLocationId":{"type":"string"}}},"DiningSyncReservation":{"type":"object","description":"One reservation as the Sync API pulls it (partner audience; personal data minimised to the guest id)","properties":{"reservationId":{"type":"string"},"rid":{"type":"string"},"guestId":{"type":"string"},"scheduledTime":{"type":"string","description":"Restaurant-local yyyy-MM-ddTHH:mm"},"partySize":{"type":"integer"},"state":{"type":"string","description":"The canonical lifecycle token (BOOKED, SEATED, DONE, NO_SHOW, CANCELLED and others)"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningSyncReservationList":{"type":"object","required":["items","page"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningSyncReservation"}},"page":{"$ref":"#/components/schemas/DiningPartnerPage"}}},"DiningSyncGuestList":{"type":"object","required":["items","page"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningGuest"}},"page":{"$ref":"#/components/schemas/DiningPartnerPage"}}},"DiningSyncPosData":{"type":"object","properties":{"reservationId":{"type":"string"},"rid":{"type":"string"},"ticketId":{"type":"string"},"total":{"$ref":"#/components/schemas/Money"},"closedAt":{"type":"string","format":"date-time"}}},"DiningSyncPosDataList":{"type":"object","required":["items","page"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningSyncPosData"}},"page":{"$ref":"#/components/schemas/DiningPartnerPage"}}},"DiningMenusPublishRequest":{"type":"object","description":"POST .../menus (Menus API publish); the restaurant's full menu set, replacing what was published","required":["menus"],"properties":{"menus":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/DiningMenu"}}}},"DiningProfileContentRequest":{"type":"object","description":"PUT .../profile (Profile Content API); the restaurant's profile text and links","properties":{"description":{"type":"string","maxLength":5000},"cuisines":{"type":"array","items":{"type":"string"}},"dressCode":{"type":"string"},"parking":{"type":"string","description":"Not published to Example Dining (Profile Content has no such field); accepted and answered with warning <SUPPLIER>_FIELD_NOT_PUBLISHED"},"websiteUrl":{"type":"string","format":"uri"},"menuUrl":{"type":"string","format":"uri","description":"Not published to Example Dining (no such field; publish menus with POST .../menus); accepted and answered with warning <SUPPLIER>_FIELD_NOT_PUBLISHED"},"photos":{"type":"array","description":"uploadIds from POST /v1/uploads (scanned image/jpeg or image/png). Example Dining fetches each by a signed URL on GET /ingest/dining/opentable/media/{token}, valid 30 days; an upload that is not a scanned image is 400 VALIDATION.\n","items":{"type":"string"}}}},"DiningPartnerReview":{"type":"object","properties":{"reviewId":{"type":"string"},"rid":{"type":"string"},"overall":{"type":"integer"},"ratings":{"type":"object","additionalProperties":{"type":"integer"}},"title":{"type":"string"},"text":{"type":"string"},"dinedOn":{"type":"string","format":"date"},"submittedAt":{"type":"string","format":"date-time"},"reply":{"type":"string"}}},"DiningPartnerReviewList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningPartnerReview"}}}},"DiningReviewSummaryList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object","required":["rid"],"properties":{"rid":{"type":"string"},"count":{"type":"integer"},"ratings":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"DiningReviewReplyRequest":{"type":"object","required":["text"],"properties":{"text":{"type":"string","minLength":1,"maxLength":2000}}},"DiningDirectoryEntry":{"type":"object","properties":{"rid":{"type":"string"},"restaurantId":{"type":"string"},"name":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"privateDining":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningDirectoryPage":{"type":"object","required":["items","page"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningDirectoryEntry"}},"page":{"$ref":"#/components/schemas/DiningPartnerPage"},"warnings":{"type":"array","description":"Non-fatal events; <SUPPLIER>_DIRECTORY_TRUNCATED when Example Dining stopped before the page was full","items":{"$ref":"#/components/schemas/Warning"}}}},"DiningPartnerLeadRequest":{"type":"object","description":"POST .../private-dining/leads; a lead to the restaurant's private-dining team (the EnquiryIntent fields, without productId)","required":["contact","eventDate","eventTime","partySize","eventType"],"properties":{"contact":{"type":"object","required":["firstName","lastName","phoneNumber"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phoneNumber":{"type":"string"}}},"eventDate":{"type":"string","format":"date"},"eventTime":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$"},"partySize":{"type":"integer","minimum":1},"eventType":{"type":"string"},"flexibleDate":{"type":"boolean"}}},"DiningOAuthTokenRequest":{"type":"object","description":"The code exchange Example Dining makes after a restaurant connects (Integrations Marketplace OAuth flow). RFC 6749 §4.1.3: grant_type authorization_code with the code and redirect_uri, or refresh_token. The client authenticates with client_id + client_secret in the body or HTTP Basic, or with code_verifier (PKCE).\n","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string"},"client_secret":{"type":"string","writeOnly":true},"code_verifier":{"type":"string"},"refresh_token":{"type":"string","writeOnly":true}}},"DiningOAuthTokenResponse":{"type":"object","description":"RFC 6749 §5.1 plus the `location` Example Dining maps the restaurant to (unique per rid)","required":["access_token","token_type"],"properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"integer"},"refresh_token":{"type":"string"},"location":{"type":"object","properties":{"location_id":{"type":"string"},"location_name":{"type":"string"}}}}},"DiningOnboardingLocation":{"type":"object","description":"One partner location a restaurant can be linked to (Example Dining Location API and Validation API shape)","required":["partner_identifier","name"],"properties":{"partner_identifier":{"type":"string"},"name":{"type":"string"},"state":{"type":"string","description":"active | inactive"},"address":{"$ref":"#/components/schemas/PostalAddress"}}},"DiningOnboardingLocationList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningOnboardingLocation"}}}},"DiningOnboardingLinkRequest":{"type":"object","description":"Example Dining's Onboarding and Offboarding API body; link (or unlink) a restaurant to a partner location","required":["rid","partner_identifier"],"properties":{"rid":{"type":"string"},"partner_identifier":{"type":"string"}}},"DiningOnboardingLinkResult":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"DiningOnboardingStatusRequest":{"type":"object","description":"The delayed-email onboarding status the partner reports for one restaurant (Example Dining integration-status API): PENDING with a step, ACTIVE with the partner location id, or INACTIVE to offboard.\n","required":["status"],"properties":{"status":{"type":"string","enum":["PENDING","ACTIVE","INACTIVE"]},"locationId":{"type":"string","description":"Required with ACTIVE and INACTIVE"},"step":{"type":"string","description":"With PENDING, the in-progress step Example Dining configured (e.g. user_request_sent)"}}},"DiningOnboardingStatus":{"type":"object","required":["rid","status"],"properties":{"rid":{"type":"string"},"status":{"type":"string","description":"PENDING | ACTIVE | INACTIVE | NONE; open string"},"locationId":{"type":"string"},"step":{"type":"string"},"channel":{"type":"string","description":"OAUTH | UNIQUE_IDENTIFIER | DELAYED_EMAIL"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningOnboardingRedirect":{"type":"object","description":"The redirect target of an onboarding browser step, answered as JSON when the caller sends Accept application/json","required":["location"],"properties":{"location":{"type":"string","format":"uri"},"state":{"type":"string","description":"authorize only: the platform's own server-generated state for this onboarding (opaque, single-use, 10 minutes), the value the callback verifies. Not Example Dining's state, which the platform stores and returns to Example Dining unchanged.\n"}}},"PaymentThreeDs":{"type":"object","description":"The 3-D Secure / SCA result a client sends when it retries an order that was refused with details.reason SCA_REQUIRED (v1.7.0; CARDS lane). The client runs the challenge with the vault (Spreedly) using details.intent from the refusal, then retries POST /v1/orders with a NEW Idempotency-Key, the same payment-method token and this object: redirectUrl plus setupIntent when details.retryWith is setupIntent (card-vault SCA), or paymentIntent when it is paymentIntent (payment SCA); mpiData when the client's own MPI authenticated the card. The platform passes these to the supplier and never stores them.\n","additionalProperties":false,"properties":{"redirectUrl":{"type":"string","format":"uri","maxLength":2048,"description":"Where the vault sent the diner back after the challenge"},"mpiData":{"type":"object","additionalProperties":false,"description":"Authentication data from a merchant plug-in (MPI)","required":["threeDSVersion","eci","cavv"],"properties":{"threeDSVersion":{"type":"string","maxLength":16,"description":"e.g. 2.2.0"},"eci":{"type":"string","pattern":"^[0-9]{2}$","description":"Electronic Commerce Indicator"},"cavv":{"type":"string","maxLength":128,"description":"SENSITIVE authentication data: the cardholder authentication verification value. Passed to the supplier for this one order and never stored, logged, echoed or returned; treated like a credential (spec 15 §2).\n"},"dsTransId":{"type":"string","maxLength":64,"description":"The 3-D Secure directory-server transaction id"}}},"setupIntent":{"type":"string","maxLength":128,"pattern":"^[A-Za-z0-9_-]+$","description":"The vault's setup-intent id (card-vault SCA, details.retryWith setupIntent)"},"paymentIntent":{"type":"string","maxLength":128,"pattern":"^[A-Za-z0-9_-]+$","description":"The vault's payment-intent id (payment SCA, details.retryWith paymentIntent)"}},"example":{"redirectUrl":"https://app.acme-travel.example/checkout/3ds-return","setupIntent":"syn-spreedly-setup-intent-0001"}},"DiningGuarantee":{"type":"object","description":"The card guarantee of a card-secured table (v1.7.0; CARDS lane): what the restaurant may charge (a no-show or late-cancel fee, or a deposit), how the card is captured and who charges it. Always charged by the restaurant, never by the platform (platformCharge false). On an offer it states the slot's terms; on an order line (OrderItem.extension.dining) it is the guarantee recorded on the order's supplier locator when it booked.\n","required":["type"],"properties":{"type":{"type":"string","description":"The guarantee type as Example Dining states it (e.g. CREDIT_CARD_DEPOSIT, NO_SHOW_FEE, CARD_HOLD); open string"},"amount":{"$ref":"#/components/schemas/Money"},"capture":{"type":"string","description":"PAYMENT_METHOD_TOKEN | OPENTABLE_SMS_LINK (as DiningPayment.cardCapture); open string"},"chargedBy":{"type":"string","description":"RESTAURANT"},"platformCharge":{"type":"boolean","description":"Always false; the platform collects nothing"},"refundableUntilDaysBefore":{"type":"integer","minimum":0},"policyId":{"type":"string","description":"Example Dining's cancellation-policy id the guarantee was booked under (order line)"}},"example":{"type":"CREDIT_CARD_DEPOSIT","amount":{"amount":15000,"currency":"USD"},"capture":"PAYMENT_METHOD_TOKEN","chargedBy":"RESTAURANT","platformCharge":false,"refundableUntilDaysBefore":1}},"DiningExperienceSelection":{"type":"object","description":"Which experience and how many diners take each of its price options (v1.7.0; WEB lane). An experience with several prices (e.g. a tasting menu and a wine pairing, or adult and child) is priced per diner: the counts must add up to the party size. On search it narrows the offers to that experience; on reprice, hold and order it fixes the price split the offer is firmed, locked and booked with (Example Dining experience total and reservation `experience` block). A selection whose version is not the experience's current one is 409 CONFLICT details.reason EXPERIENCE_VERSION_CHANGED (re-read the detail); counts that do not add up to the party are 400 VALIDATION details.field message.intent.experienceSelection.\n","required":["experienceId","prices"],"properties":{"experienceId":{"type":"integer","format":"int64"},"version":{"type":"integer","description":"The experience version the client showed (DiningExperience.version)"},"prices":{"type":"array","minItems":1,"items":{"type":"object","required":["priceId","count"],"properties":{"priceId":{"type":"integer","format":"int64"},"count":{"type":"integer","minimum":1,"maximum":50}}}}},"example":{"experienceId":900101,"version":3,"prices":[{"priceId":1,"count":2},{"priceId":2,"count":1}]}},"EnquiryListResponse":{"type":"object","description":"One page of GET /v1/enquiries (envelope out), newest first","required":["context","message"],"properties":{"context":{"$ref":"#/components/schemas/MondeeResponseContext"},"message":{"type":"object","required":["enquiries"],"properties":{"enquiries":{"type":"array","items":{"$ref":"#/components/schemas/Enquiry"}},"nextCursor":{"type":"string","description":"Present when more enquiries match; pass it back as ?cursor= with the same filters"}}}}},"DiningPartnerLocation":{"type":"object","description":"One partner location of the tenant, keyed by the Example Dining restaurant id it serves (v1.7.0)","required":["restaurantId","partner_identifier","name"],"properties":{"restaurantId":{"type":"string"},"partner_identifier":{"type":"string"},"name":{"type":"string"},"state":{"type":"string","description":"active | inactive"},"address":{"$ref":"#/components/schemas/PostalAddress"},"updatedAt":{"type":"string","format":"date-time"}}},"DiningPartnerLocationUpsert":{"type":"object","description":"PUT /v1/dining/partner/onboarding/locations/{restaurantId}; the whole location (replace)","required":["partner_identifier","name"],"properties":{"partner_identifier":{"type":"string","maxLength":128},"name":{"type":"string","maxLength":200},"state":{"type":"string","description":"active | inactive; absent = active"},"address":{"$ref":"#/components/schemas/PostalAddress"}}},"DiningPartnerLocationList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DiningPartnerLocation"}}}},"EnquiryEventData":{"type":"object","description":"The data of the enquiry.submitted, enquiry.unconfirmed and enquiry.failed webhooks (v1.7.0). No contact and no other personal data, by design.\n","required":["enquiryRef","domain","productId","status","createdAt"],"properties":{"enquiryRef":{"type":"string"},"domain":{"type":"string"},"productId":{"type":"string"},"status":{"type":"string","description":"SUBMITTED | UNCONFIRMED | FAILED; open string"},"supplierReference":{"type":"string","description":"The supplier's lead id, when it gave one (SUBMITTED)"},"createdAt":{"type":"string","format":"date-time"}},"example":{"enquiryRef":"EN-4KD-8QT-2ZP","domain":"FNB","productId":"prov:dining:100123","status":"SUBMITTED","supplierReference":"lead-7731","createdAt":"2026-10-05T17:04:11Z"}},"DiningPosTicketRead":{"type":"object","description":"A check as GET .../pos/tickets reads it (v1.7.0): the DiningPosTicket fields, except that total and every other Money member are ABSENT when Example Dining stated no currency for the check (currencyStated false). Amounts are never published without their currency and never defaulted to USD.\n","required":["ticketId","openedAt"],"properties":{"ticketId":{"type":"string"},"reservationId":{"type":"string"},"tableNumber":{"type":"string"},"covers":{"type":"integer"},"openedAt":{"type":"string","format":"date-time"},"closedAt":{"type":"string","format":"date-time"},"status":{"type":"string","description":"OPEN | CLOSED | VOID; open string"},"currencyStated":{"type":"boolean","readOnly":true,"description":"Read only; false when Example Dining stated no currency, so the money members are absent"},"subtotal":{"$ref":"#/components/schemas/Money"},"tax":{"$ref":"#/components/schemas/Money"},"tip":{"$ref":"#/components/schemas/Money"},"total":{"$ref":"#/components/schemas/Money"},"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer"},"price":{"$ref":"#/components/schemas/Money"}}}}}},"AmendTargetSlot":{"type":"object","description":"The slot an amend QUOTE would move the booking to (v1.7.0; FNB DATE_SHIFT). Present on a QUOTE answer for dining. creditCardRequired true means the target is card-secured: the client captures the card (Spreedly token) and the COMMIT MUST carry it as message.intent.payment.paymentMethod. The token is never stored (D3), so a token sent on the QUOTE is only validated, not kept (warning <SUPPLIER>_CARD_TOKEN_UNUSED: send it again on the COMMIT). guarantee states what the restaurant may charge. A COMMIT answer does not carry targetSlot.\n","required":["creditCardRequired"],"properties":{"dateTime":{"type":"string","description":"Restaurant-local yyyy-MM-ddTHH:mm, no offset"},"partySize":{"type":"integer","minimum":1},"creditCardRequired":{"type":"boolean"},"guarantee":{"$ref":"#/components/schemas/DiningGuarantee"}},"example":{"dateTime":"2026-11-13T20:00","partySize":6,"creditCardRequired":true,"guarantee":{"type":"CREDIT_CARD_DEPOSIT","amount":{"amount":15000,"currency":"USD"},"capture":"PAYMENT_METHOD_TOKEN","chargedBy":"RESTAURANT","platformCharge":false}}},"ActivationState":{"type":"object","description":"The activation rail of one tenant (FEAT-030 §5.3, §7; GET /v1/activation). Steps are derived from facts, never set by hand. tier is null while TRIAL. moneyModel and terms are what the rail needs to render steps 5, 6 and 8 [assumption: beyond the §7 field list, so the client needs no second read].\n","required":["tenantId","steps","tier","firstSandboxCallAt","firstSandboxOrderAt","liveAt"],"properties":{"tenantId":{"type":"string"},"steps":{"type":"array","description":"The rail steps, in the suggested order. The rail is per account type (connect-v1 1.47.0): a BUSINESS tenant has the nine steps verify_email, sandbox_booking, business_profile, business_verification, money_model, fund_wallet, certification, terms, go_live; an INDIVIDUAL tenant has the five steps verify_email, sandbox_booking, identity_verification, terms, go_live. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): a BUSINESS tenant on flow CAPPED has the seven steps verify_email, business_profile, business_verification, terms, money_model, sandbox_booking, go_live (no fund_wallet, no certification).\n","items":{"$ref":"#/components/schemas/ActivationStep"}},"accountType":{"$ref":"#/components/schemas/AccountType","description":"connect-v1 1.47.0 (FEAT-033): the tenant's account type, which decides the rail (see steps). Absent means BUSINESS.\n"},"identityCheck":{"description":"connect-v1 1.47.0 (FEAT-033): the latest identity check (POST /v1/activation/identity-checks), null until one ran. Carries the masked value only, never the full identity number.\n","oneOf":[{"$ref":"#/components/schemas/IdentityCheck"},{"type":"null"}]},"buildingIntent":{"type":["string","null"],"description":"The signup answer to \"What are you building?\" (FEAT-030 §5.1), null when skipped. API_INTEGRATION | AI_AGENT | BOOKING_SITE; an x-extensible-enum. Written from SignupVerifyRequest.buildingIntent (schema BuildingIntent).\n","x-extensible-enum":["API_INTEGRATION","AI_AGENT","BOOKING_SITE"]},"tier":{"type":["string","null"],"description":"The LIVE tier (FEAT-030 §5.7), null while TRIAL. LIVE_CAPPED | LIVE_STANDARD | LIVE_ENTERPRISE; an x-extensible-enum. A tenant LIVE before FEAT-030 reads LIVE_ENTERPRISE.\n","x-extensible-enum":["LIVE_CAPPED","LIVE_STANDARD","LIVE_ENTERPRISE"]},"moneyModel":{"type":["string","null"],"description":"The elected money model (POST /v1/activation/money-model), null until elected. CARD (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b): each booking is paid with a saved card or UPI method.\n","x-extensible-enum":["WALLET","OWN_ACCREDITATION","BYOC","CARD"]},"firstSandboxCallAt":{"type":["string","null"],"format":"date-time"},"firstSandboxOrderAt":{"type":["string","null"],"format":"date-time","description":"Set when the four quick-start cards are complete (FEAT-030 §5.2); ticks step sandbox_booking"},"liveAt":{"type":["string","null"],"format":"date-time"},"terms":{"$ref":"#/components/schemas/ActivationTerms"},"flow":{"type":"string","description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): which rail this tenant is on. NINE_STEP is the rail of §5.3 (two-Owner signoff and the OD-870 ops approval to LIVE); CAPPED is the proposed rail, served while aikonic.activation.capped-flow.enabled is on, whose required steps are business_verification, money_model and terms (plus fund_wallet when the money model is WALLET) and whose last required step turns the tenant LIVE with tier LIVE_CAPPED by itself (no signoff, no ops approval). Absent from an older server, read as NINE_STEP. x-extensible-enum. connect-v1 1.63.0 (PROPOSAL, round 2): on CAPPED going live is the Owner's explicit POST /v1/activation:go-live, which replaces the automatic promotion above.\n","x-extensible-enum":["NINE_STEP","CAPPED"]},"limits":{"description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): the daily limit of a LIVE_CAPPED tenant, as GET /v1/activation/limits answers it; null until the tenant is LIVE_CAPPED, and on the NINE_STEP flow.\n","oneOf":[{"$ref":"#/components/schemas/TierLimits"},{"type":"null"}]},"country":{"type":["string","null"],"description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): the business's country (ISO 3166-1 alpha-2) from sign-up (SignupVerifyRequest.country, for a BUSINESS tenant) or the business profile; null when never given.\n"},"registrationGstin":{"type":["string","null"],"description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): the GSTIN confirmed against the GST register (POST /v1/activation/business-registration:confirm); null while none is confirmed.\n"},"registrationConfirmedAt":{"type":["string","null"],"format":"date-time","description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): when that GSTIN was confirmed (BusinessProfile.gstinConfirmedAt); null while none is confirmed. Lets a client tell from GET /v1/activation alone whether the registration is confirmed and the signatory check is open. connect-v1 1.63.0 (PROPOSAL, round 2): also set when a registration is confirmed by scheme and number (BusinessProfile.registrationScheme), any country; registrationGstin stays the India GSTIN only.\n"},"workEmail":{"description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): the work-e-mail guardrail of the CAPPED flow; absent on NINE_STEP and from an older server (read as not required).\n","oneOf":[{"$ref":"#/components/schemas/ActivationWorkEmail"},{"type":"null"}]},"liveAvailable":{"type":"boolean","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): whether live bookings are open for the tenant's country (ActivationState.country) on the CAPPED flow. The platform opens a country for live once a real verification provider is connected there (deployment setting aikonic.activation.live-countries, a list of ISO 3166-1 alpha-2 codes; an empty list opens every country). Every country can sign up and use test mode whatever this reads; when false, POST /v1/activation:go-live is refused with 409 CONFLICT COUNTRY_NOT_LIVE. Absent on NINE_STEP and from an older server; absent reads true.\n"},"businessVerification":{"description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): the business verification case with the provider routed for the tenant's country, on the CAPPED flow only; absent on NINE_STEP and from an older server. business_verification is done only when its status is verified.\n","oneOf":[{"$ref":"#/components/schemas/ActivationBusinessVerification"},{"type":"null"}]},"goLiveReview":{"description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): the latest go-live review on the CAPPED flow (GoLiveReview), null until the Owner first calls POST /v1/activation:go-live; absent on NINE_STEP and from an older server. While it is in_review the tenant stays TRIAL (tier null) and step go_live reads in_review; the tenant turns LIVE, tier LIVE_CAPPED, only when an operator approves it.\n","oneOf":[{"$ref":"#/components/schemas/GoLiveReview"},{"type":"null"}]}},"example":{"tenantId":"TEN-ACME","buildingIntent":"AI_AGENT","registrationGstin":null,"registrationConfirmedAt":null,"tier":null,"moneyModel":null,"firstSandboxCallAt":"2026-10-09T09:02:11Z","firstSandboxOrderAt":null,"liveAt":null,"terms":{"currentVersion":"2026-10","acceptedVersion":null,"acceptedAt":null},"steps":[{"key":"verify_email","state":"done","updatedAt":"2026-10-09T09:00:30Z","needs":[]},{"key":"sandbox_booking","state":"in_progress","updatedAt":"2026-10-09T09:02:11Z","needs":[]}]}},"ActivationStep":{"type":"object","description":"One rail step. key is verify_email | sandbox_booking | business_profile | business_verification | money_model | fund_wallet | certification | terms | go_live | identity_verification (connect-v1 1.47.0); state is todo | in_progress | needs_you | in_review | done | blocked. Both are x-extensible-enum. Which keys a rail carries depends on the account type (ActivationState.steps): identity_verification is on the INDIVIDUAL rail only; business_profile, business_verification, money_model, fund_wallet and certification are on the BUSINESS rail only. needs[] names the exact field, document or check still missing (never empty on needs_you, AC-030-04); on blocked it names the steps or the condition in the way. fund_wallet is done without a top-up when the money model is not WALLET. identity_verification is done when the latest identity check is VERIFIED; until then its needs[] names what is missing (taxId). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): on the CAPPED flow business_verification follows the verification provider's case (ActivationState.businessVerification): in_progress while the case is open and unanswered, in_review while the provider reviews, needs_you with needs[] the outstanding requirement codes (for example representative.id_number) on needs_more, blocked on rejected (needs[] verification.rejected), done only on verified. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): on the CAPPED flow go_live follows the go-live review (ActivationState.goLiveReview): todo (needs[] go_live.confirm) or blocked (needs[] the open steps) before the Owner asks, in_review while the review is in_review, needs_you with needs[] go_live.changes_requested when the operator asked for changes, blocked with needs[] go_live.rejected when it was rejected, done once it is approved and the tenant is LIVE. sandbox_booking (\"Try the sandbox\") is required on CAPPED (owner direction 2026-10-29, OD-ENR-41): before a go-live review exists it reads as before (firstSandboxOrderAt); once one exists it is done only when the test_calls check is passed for every requested product, in_progress otherwise, with needs[] the missing product verbs as <product>.<verb> (for example HTL.cancel). The go-live request does not wait on it; the operator's approval does.\n","required":["key","state","updatedAt","needs"],"properties":{"key":{"type":"string","x-extensible-enum":["verify_email","sandbox_booking","business_profile","business_verification","money_model","fund_wallet","certification","terms","go_live","identity_verification"]},"state":{"type":"string","x-extensible-enum":["todo","in_progress","needs_you","in_review","done","blocked"]},"updatedAt":{"type":["string","null"],"format":"date-time","description":"When the state last changed; null while the step was never touched"},"needs":{"type":"array","items":{"type":"string"}},"required":{"type":"boolean","description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): whether go-live waits on this step. On the CAPPED flow exactly business_verification, money_model and terms are required:true, plus fund_wallet only when the money model is WALLET; verify_email is also required:true and is always done at sign-up. Every other step is required:false: sandbox_booking and certification (recommended), business_profile (completed automatically by POST /v1/activation/business-registration:confirm on the India path; elsewhere the hosted KYB collects the company details) and go_live (it reads done when the tenant turns LIVE_CAPPED, it is not a prerequisite). When the money model is not WALLET, fund_wallet reads state done with needs[] empty and required false (not needed). On the NINE_STEP flow every step is required:true. Absent from an older server: read it as true. connect-v1 1.63.0 (PROPOSAL, round 2): on the CAPPED rail of 1.63.0 every listed step is required:true except sandbox_booking (business_profile and go_live included; go_live is done by POST /v1/activation:go-live); fund_wallet and certification are not listed. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): on CAPPED sandbox_booking is required:true too, so every listed step is required (owner direction, OD-ENR-41). It is the one required step that POST /v1/activation:go-live does not check (it never appears in details.open): the go-live review may open first, and the operator's approval waits on it (409 GO_LIVE_TEST_CALLS_MISSING).\n"}},"example":{"key":"business_profile","state":"needs_you","updatedAt":"2026-10-09T09:20:00Z","needs":["website"]}},"ActivationTerms":{"type":"object","description":"The terms version an Owner must accept (rail step terms) and what was accepted","required":["currentVersion","acceptedVersion","acceptedAt"],"properties":{"currentVersion":{"type":"string"},"acceptedVersion":{"type":["string","null"]},"acceptedAt":{"type":["string","null"],"format":"date-time"}},"example":{"currentVersion":"2026-10","acceptedVersion":"2026-10","acceptedAt":"2026-10-09T10:30:00Z"}},"BusinessProfileUpdate":{"type":"object","description":"PATCH body of /v1/activation/profile (FEAT-030 §7 business_profile): every member optional, absent = unchanged. businessType and volumeBand are closed request enums [assumption: the spec names the fields, not the values]; volumeBand is the expected monthly booking value in USD equivalent. gstin and pan only with country IN. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): businessStructure, industry, industryOther, noWebsite, productDescription, phone and monthlyVolume, all optional; the CAPPED rail's business_profile reads them instead of businessType and volumeBand (PATCH /v1/activation/profile). industry is x-extensible-enum: a value the server does not know is 400 VALIDATION details.field industry.\n","additionalProperties":false,"properties":{"businessType":{"type":"string","enum":["TRAVEL_AGENCY","ONLINE_TRAVEL_AGENCY","TRAVEL_MANAGEMENT_COMPANY","TOUR_OPERATOR","SOFTWARE_PLATFORM","OTHER"]},"country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2"},"website":{"type":"string","format":"uri","maxLength":2048},"volumeBand":{"type":"string","enum":["UNDER_10K","FROM_10K_TO_100K","FROM_100K_TO_1M","OVER_1M"]},"gstin":{"type":"string","pattern":"^[0-9]{2}[A-Z0-9]{13}$","description":"India GSTIN (15 characters); verified against the government source by the KYB provider"},"pan":{"type":"string","pattern":"^[A-Z]{5}[0-9]{4}[A-Z]$","description":"India PAN (10 characters)"},"buildingIntent":{"$ref":"#/components/schemas/BuildingIntent","description":"connect-v1 1.51.0 (PROPOSAL, FEAT-030 §18b): the answer to \"What are you building?\" given after sign-up (on the Welcome screen) instead of at it; reads back as ActivationState.buildingIntent. Setting it changes no rail step.\n"},"businessStructure":{"type":"string","minLength":1,"maxLength":80,"description":"connect-v1 1.63.0 (PROPOSAL): the country's legal form label as the person picks it, for example \"Private limited company\", \"Limited liability partnership\", \"Sole proprietorship\", \"Single-member LLC\" or \"Free zone establishment\"; required on CAPPED for business_profile\n"},"industry":{"type":"string","x-extensible-enum":["TRAVEL_AGENCY","ONLINE_TRAVEL_AGENCY","INDEPENDENT_ADVISOR","HOST_AGENCY","CONSOLIDATOR","TOUR_OPERATOR","DESTINATION_MANAGEMENT","TRAVEL_MANAGEMENT_COMPANY","IN_HOUSE_TRAVEL","MICE","GROUP_TRAVEL","BOOKING_SOFTWARE","EXPENSE_PLATFORM","FINTECH","LOYALTY","MARKETPLACE","TRAVEL_SUPPLIER","VISA_SERVICES","TRAVEL_MEDIA","OTHER"],"description":"connect-v1 1.63.0 (PROPOSAL) — what the business does; OTHER with industryOther"},"industryOther":{"type":"string","minLength":1,"maxLength":120,"description":"connect-v1 1.63.0 (PROPOSAL) — the industry in the person's words, with industry OTHER only"},"noWebsite":{"type":"boolean","description":"connect-v1 1.63.0 (PROPOSAL): true when the business has no website; productDescription is then required and website must be absent\n"},"productDescription":{"type":"string","minLength":10,"maxLength":500,"description":"connect-v1 1.63.0 (PROPOSAL): what the business sells and to whom, in a sentence or two; required when noWebsite is true\n"},"phone":{"type":"string","pattern":"^\\+[1-9][0-9]{6,14}$","description":"connect-v1 1.63.0 (PROPOSAL) — the business phone, E.164"},"monthlyVolume":{"$ref":"#/components/schemas/MonthlyVolume","description":"connect-v1 1.63.0 (PROPOSAL) — the expected monthly booking value, a band in a currency"}},"example":{"website":"https://acme-travel.example"}},"BusinessProfile":{"type":"object","description":"The tenant's business profile as stored (FEAT-030 §7). complete is true when businessType, country, website and volumeBand are all set (rail step business_profile done). Response-side values are x-extensible-enum. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): on the CAPPED flow complete is true when country, businessStructure, industry, website or noWebsite with productDescription, phone and monthlyVolume are set; businessType and volumeBand stay for NINE_STEP. registrationScheme and registrationNumber name a registration confirmed by scheme and number.\n","required":["complete","updatedAt"],"properties":{"businessType":{"type":["string","null"],"x-extensible-enum":["TRAVEL_AGENCY","ONLINE_TRAVEL_AGENCY","TRAVEL_MANAGEMENT_COMPANY","TOUR_OPERATOR","SOFTWARE_PLATFORM","OTHER"]},"country":{"type":["string","null"]},"website":{"type":["string","null"]},"volumeBand":{"type":["string","null"],"x-extensible-enum":["UNDER_10K","FROM_10K_TO_100K","FROM_100K_TO_1M","OVER_1M"]},"gstin":{"type":["string","null"]},"pan":{"type":["string","null"]},"legalName":{"type":["string","null"],"description":"connect-v1 1.51.0 (PROPOSAL): the legal name from the confirmed GST registration (POST /v1/activation/business-registration:confirm); null until one is confirmed.\n"},"address":{"description":"connect-v1 1.51.0 (PROPOSAL) — the principal place of business from the confirmed registration","oneOf":[{"$ref":"#/components/schemas/BusinessAddress"},{"type":"null"}]},"gstinConfirmedAt":{"type":["string","null"],"format":"date-time","description":"connect-v1 1.51.0 (PROPOSAL): when the GSTIN was confirmed against the GST register; null for a GSTIN only typed on the profile. Under the CAPPED flow complete is true once this is set.\n"},"businessStructure":{"type":["string","null"]},"industry":{"type":["string","null"],"x-extensible-enum":["TRAVEL_AGENCY","ONLINE_TRAVEL_AGENCY","INDEPENDENT_ADVISOR","HOST_AGENCY","CONSOLIDATOR","TOUR_OPERATOR","DESTINATION_MANAGEMENT","TRAVEL_MANAGEMENT_COMPANY","IN_HOUSE_TRAVEL","MICE","GROUP_TRAVEL","BOOKING_SOFTWARE","EXPENSE_PLATFORM","FINTECH","LOYALTY","MARKETPLACE","TRAVEL_SUPPLIER","VISA_SERVICES","TRAVEL_MEDIA","OTHER"]},"industryOther":{"type":["string","null"]},"noWebsite":{"type":"boolean"},"productDescription":{"type":["string","null"]},"phone":{"type":["string","null"]},"monthlyVolume":{"oneOf":[{"$ref":"#/components/schemas/MonthlyVolume"},{"type":"null"}]},"registrationScheme":{"type":["string","null"],"x-extensible-enum":["IN_GSTIN","GB_COMPANY_NUMBER","AE_TRADE_LICENCE","US_EIN","CA_BUSINESS_NUMBER"],"description":"connect-v1 1.63.0 (PROPOSAL) — the scheme of the confirmed registration; null until one is confirmed"},"registrationNumber":{"type":["string","null"],"description":"connect-v1 1.63.0 (PROPOSAL) — the number of the confirmed registration; null until one is confirmed"},"complete":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}},"example":{"businessType":"SOFTWARE_PLATFORM","country":"US","website":"https://example.com","volumeBand":"UNDER_10K","gstin":null,"pan":null,"complete":true,"updatedAt":"2026-10-09T09:31:00Z"}},"VerificationSessionRequest":{"type":"object","description":"Optional body of POST /v1/activation/verification-sessions","properties":{"returnUrl":{"type":"string","format":"uri","maxLength":2048,"description":"Where the provider's hosted flow sends the user back (a console URL)"}},"example":{"returnUrl":"https://console.aikonic.example/console/activate"}},"VerificationSession":{"type":"object","description":"The KYB provider session (FEAT-030 §5.4, §7 verification_case): exactly one of hostedUrl or embedToken is set while the session is open. status is not_started | in_progress | needs_more | in_review | verified | rejected (x-extensible-enum); outstanding[] lists the provider's missing items on needs_more. riskTier and decidedAt are set on a decision. No document is ever carried or stored.\n","required":["provider","providerRef","status","outstanding"],"properties":{"provider":{"type":"string","description":"The configured provider id (aikonic.kyb.provider), for example harness"},"providerRef":{"type":"string"},"status":{"type":"string","x-extensible-enum":["not_started","in_progress","needs_more","in_review","verified","rejected"]},"hostedUrl":{"type":["string","null"],"format":"uri"},"embedToken":{"type":["string","null"]},"expiresAt":{"type":["string","null"],"format":"date-time"},"outstanding":{"type":"array","items":{"type":"string"}},"riskTier":{"type":["string","null"],"x-extensible-enum":["low","medium","high"]},"decidedAt":{"type":["string","null"],"format":"date-time"}},"example":{"provider":"harness","providerRef":"kyb_hrn_01JA9Q7","status":"needs_more","hostedUrl":"https://harness.sandbox.aikonic.example/kyb/kyb_hrn_01JA9Q7","embedToken":null,"expiresAt":"2026-10-09T11:00:00Z","outstanding":["proof_of_address"],"riskTier":null,"decidedAt":null}},"IdentityCheckRequest":{"type":"object","description":"Body of POST /v1/activation/identity-checks (connect-v1 1.47.0, FEAT-033): the identity number and the name exactly as on the document that carries it. Never stored as sent: the platform keeps the masked value and a keyed hash only.\n","required":["taxId","fullName"],"additionalProperties":false,"properties":{"taxId":{"$ref":"#/components/schemas/TaxIdentifier"},"fullName":{"type":"string","minLength":1,"maxLength":200,"description":"The person's name exactly as on the PAN card; the provider compares it with the holder's name"}},"example":{"taxId":{"scheme":"IN_PAN","value":"ABCPE1234F"},"fullName":"Priya Sharma"}},"TaxIdentifier":{"type":"object","description":"A tax identity number and its scheme (connect-v1 1.47.0). IN_PAN is India's Permanent Account Number: ten characters ^[A-Z]{5}[0-9]{4}[A-Z]$, checked by the server (400 VALIDATION details.reason TAX_ID_FORMAT) rather than by a schema pattern, so other schemes can be added; for an INDIVIDUAL account the fourth character must be P, a personal PAN (400 VALIDATION details.reason PERSONAL_PAN_REQUIRED). A scheme the server does not take is 400 VALIDATION details.reason UNSUPPORTED_SCHEME. value is write-only: no response carries it.\n","required":["scheme","value"],"additionalProperties":false,"properties":{"scheme":{"type":"string","x-extensible-enum":["IN_PAN"]},"value":{"type":"string","writeOnly":true,"maxLength":32,"description":"The identity number as issued, upper case, no spaces; never returned, logged or stored in full"}},"example":{"scheme":"IN_PAN","value":"ABCPE1234F"}},"IdentityCheck":{"type":"object","description":"One identity check (connect-v1 1.47.0, FEAT-033): the answer of POST /v1/activation/identity-checks and ActivationState.identityCheck. status is VERIFIED | FAILED | PENDING; failureReason (on FAILED) is NAME_MISMATCH | NOT_FOUND | INACTIVE; scheme is IN_PAN; all x-extensible-enum. maskedValue shows the first five and the last character of the number; the full number is never stored or returned. The platform keeps this record plus a keyed hash of the number (to recognise a repeat) — no document, no image.\n","required":["checkId","status","scheme","maskedValue","nameMatched","provider","checkedAt"],"properties":{"checkId":{"type":"string","description":"The check's id, idc_ followed by an opaque suffix"},"status":{"type":"string","x-extensible-enum":["VERIFIED","FAILED","PENDING"]},"scheme":{"type":"string","x-extensible-enum":["IN_PAN"]},"maskedValue":{"type":"string","description":"The number masked, first five and last character shown (ABCPE••••F)"},"nameMatched":{"type":"boolean","description":"Whether the provider found fullName to match the holder's name"},"failureReason":{"type":["string","null"],"x-extensible-enum":["NAME_MISMATCH","NOT_FOUND","INACTIVE"],"description":"Why a FAILED check failed; null otherwise"},"provider":{"type":"string","description":"The configured provider id (aikonic.identity-check.provider), for example harness"},"checkedAt":{"type":"string","format":"date-time"}},"example":{"checkId":"idc_01JB2K7Q9M4X","status":"VERIFIED","scheme":"IN_PAN","maskedValue":"ABCPE••••F","nameMatched":true,"failureReason":null,"provider":"harness","checkedAt":"2026-10-24T10:12:03Z"}},"MoneyModelElectionRequest":{"type":"object","description":"Body of POST /v1/activation/money-model (FEAT-030 §5.3 step 5, 08 §7). currency (ISO-4217) is required when model is WALLET (400 VALIDATION details.field currency otherwise) and ignored for the others. CARD (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b; served when aikonic.payment-methods.enabled is on): pay each booking with a saved card or UPI method (/v1/payments/methods); step money_model is done once one is saved.\n","required":["model"],"additionalProperties":false,"properties":{"model":{"type":"string","enum":["WALLET","OWN_ACCREDITATION","BYOC","CARD"]},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"accreditationNumber":{"type":"string","minLength":1,"maxLength":20,"description":"connect-v1 1.63.0 (PROPOSAL): the IATA, ARC or TIDS number, with OWN_ACCREDITATION (required for it on CAPPED); opens the MONEY_MODEL_REVIEW operator approval\n"},"contractReference":{"type":"string","minLength":1,"maxLength":200,"description":"connect-v1 1.63.0 (PROPOSAL): the supplier contract or account reference, with BYOC (required for it on CAPPED); opens the MONEY_MODEL_REVIEW operator approval\n"}},"example":{"model":"WALLET","currency":"USD"}},"MoneyModelElection":{"type":"object","description":"The recorded money-model election. CARD (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b) pays each booking with a saved card or UPI method.\n","required":["model","electedAt","electedBy"],"properties":{"model":{"type":"string","x-extensible-enum":["WALLET","OWN_ACCREDITATION","BYOC","CARD"]},"currency":{"type":["string","null"]},"electedAt":{"type":"string","format":"date-time"},"electedBy":{"type":"string","description":"The electing user's id"},"reviewState":{"type":["string","null"],"x-extensible-enum":["PENDING","APPROVED","REJECTED"],"description":"connect-v1 1.63.0 (PROPOSAL): the operator review (MONEY_MODEL_REVIEW) of an OWN_ACCREDITATION or BYOC election on CAPPED; null or absent when no review applies. Step money_model reads in_review while PENDING.\n"}},"example":{"model":"BYOC","currency":null,"electedAt":"2026-10-09T09:25:00Z","electedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"TermsAcceptRequest":{"type":"object","description":"Body of POST /v1/activation/terms:accept. termsVersion is the version being accepted; exactly one of stepUpCode or stepUpAssertion is required (DR-53 / DR-54 step-up pair; the backend enforces it, no oneOf). connect-v1 1.52.0 (PROPOSAL, FEAT-030 §18b): or emailCode, on the CAPPED flow only — exactly one of stepUpCode, stepUpAssertion or emailCode (the backend enforces it, no oneOf). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): on the CAPPED flow no proof is needed — termsVersion alone is a complete body (click-wrap; the Owner's human session is the proof); a proof sent anyway is judged as before. On the NINE_STEP flow exactly one of stepUpCode or stepUpAssertion stays required.\n","required":["termsVersion"],"additionalProperties":false,"properties":{"termsVersion":{"type":"string","maxLength":32},"stepUpCode":{"type":"string","maxLength":16,"description":"A current code from the caller's MFA factor"},"stepUpAssertion":{"$ref":"#/components/schemas/WebAuthnAssertion"},"emailCode":{"type":"string","pattern":"^[0-9]{6}$","description":"connect-v1 1.52.0 (PROPOSAL; CAPPED flow only): the 6-digit code POST /v1/activation/terms:send-code e-mailed to the signed-in Owner (15 minutes, at most 5 attempts). On the NINE_STEP flow it is 400 VALIDATION details.field emailCode, details.reason FLOW_NOT_CAPPED. connect-v1 1.63.0: optional on CAPPED too (the acceptance needs no proof there); kept for compatibility.\n"}},"example":{"termsVersion":"2026-10","stepUpCode":"000000"}},"TermsCodeSent":{"type":"object","description":"connect-v1 1.52.0 (PROPOSAL, FEAT-030 §18b): a terms code was e-mailed to the signed-in Owner (POST /v1/activation/terms:send-code). The code itself is never returned.\n","required":["sentTo","expiresAt","resendAfter"],"properties":{"sentTo":{"type":"string","maxLength":254,"description":"The Owner's e-mail address, masked: the first two characters of the local part (the first one when it is shorter than three), then *** and the domain\n"},"expiresAt":{"type":"string","format":"date-time","description":"When the code stops working (15 minutes after it is sent)"},"resendAfter":{"type":"string","format":"date-time","description":"The earliest time another code may be sent; earlier is 429 RATE_LIMITED"}},"example":{"sentTo":"ar***@sampletravels.in","expiresAt":"2026-10-26T09:45:00Z","resendAfter":"2026-10-26T09:31:00Z"}},"TermsAcceptance":{"type":"object","description":"A recorded terms acceptance","required":["termsVersion","acceptedAt","acceptedBy"],"properties":{"termsVersion":{"type":"string"},"acceptedAt":{"type":"string","format":"date-time"},"acceptedBy":{"type":"string","description":"The accepting Owner's user id"}},"example":{"termsVersion":"2026-10","acceptedAt":"2026-10-09T10:30:00Z","acceptedBy":"1e9bb43d-5e35-42ba-90f1-152117834fb9"}},"CertificationState":{"type":"object","description":"Passive certification of the tenant (FEAT-030 §5.6; GET /v1/certification)","required":["checks","passed","total"],"properties":{"checks":{"type":"array","items":{"$ref":"#/components/schemas/CertificationCheck"}},"passed":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"advisory":{"type":"array","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034; reported when aikonic.keys.velocity.enabled is on, else absent). Checks reported but never counted in passed or total in phase 1: velocity_retry_honoured (after a 429 VELOCITY_LIMIT the next call waited retryAfterMs) and stopped_on_pause (no call with the key within 60 s after a 423 KEY_PAUSED). They gate in phase 2 (FEAT-034 §15).\n","items":{"$ref":"#/components/schemas/CertificationCheck"}}},"example":{"passed":0,"total":1,"checks":[{"key":"webhook_verified","state":"not_observed","observedAt":null,"requestIds":[]}]}},"CertificationCheck":{"type":"object","description":"One passive check. key is idempotent_retry | price_change_handled | async_respected | webhook_verified | ambiguity_rule | deprecation_logged; state is not_observed | passed | failed. Both x-extensible-enum. requestIds are the traceIds of the requests that proved (passed) or broke (failed) the rule; a passed check always carries at least one (AC-030-10).\n","required":["key","state","observedAt","requestIds"],"properties":{"key":{"type":"string","x-extensible-enum":["idempotent_retry","price_change_handled","async_respected","webhook_verified","ambiguity_rule","deprecation_logged","velocity_retry_honoured","stopped_on_pause"]},"state":{"type":"string","x-extensible-enum":["not_observed","passed","failed"]},"observedAt":{"type":["string","null"],"format":"date-time"},"requestIds":{"type":"array","items":{"type":"string"}},"evidence":{"type":"array","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7; when aikonic.keys.agent-mint.enabled is on, else absent). One row per request in requestIds: the request id and its Attribution (principal, agent, key).\n","items":{"allOf":[{"$ref":"#/components/schemas/Attribution"},{"type":"object","required":["requestId"],"properties":{"requestId":{"type":"string"}}}]}}},"example":{"key":"idempotent_retry","state":"passed","observedAt":"2026-10-09T09:12:03Z","requestIds":["4bf92f3577b34da6a3ce929d0e0e4736"]}},"CertificationRun":{"type":"object","description":"A scheduled run of the missing checks (POST /v1/certification:run-missing)","required":["runId","scheduled","alreadyPassed","startedAt"],"properties":{"runId":{"type":"string"},"scheduled":{"type":"array","description":"Check keys the run executes","items":{"type":"string"}},"alreadyPassed":{"type":"array","description":"Check keys already passed, not re-run","items":{"type":"string"}},"startedAt":{"type":"string","format":"date-time"}},"example":{"runId":"CRUN-01JA9R2","scheduled":["webhook_verified"],"alreadyPassed":[],"startedAt":"2026-10-09T10:00:00Z"}},"WalletEnvironment":{"type":"string","enum":["PRODUCTION","SANDBOX"],"description":"A side of the sandbox/production tenant pair (07 §5) for the wallet routes (v1.18.0)"},"Wallet":{"type":"object","description":"One prepaid wallet (FEAT-030 §5.5, §7). balance, held and available are Money in the wallet's currency and are sums of ledger legs (F-08), never stored: available = balance - held. environment is the side of the tenant pair the wallet belongs to (x-extensible-enum PRODUCTION | SANDBOX). lowBalanceThreshold is null when no alert is set.\n","required":["currency","environment","balance","held","available","fundingInstructions","lowBalanceThreshold","updatedAt"],"properties":{"currency":{"type":"string","description":"ISO-4217"},"environment":{"type":"string","x-extensible-enum":["PRODUCTION","SANDBOX"]},"balance":{"$ref":"#/components/schemas/Money"},"held":{"$ref":"#/components/schemas/Money"},"available":{"$ref":"#/components/schemas/Money"},"fundingInstructions":{"$ref":"#/components/schemas/WalletFundingInstructions"},"lowBalanceThreshold":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"updatedAt":{"type":"string","format":"date-time","description":"When the last leg was posted"}},"example":{"currency":"INR","environment":"SANDBOX","balance":{"amount":1000000,"currency":"INR"},"held":{"amount":0,"currency":"INR"},"available":{"amount":1000000,"currency":"INR"},"fundingInstructions":{"virtualAccount":"SIM-0000-0000-4417","reference":"AIK-TEN-ACME","note":"Simulated in sandbox; no money moves."},"lowBalanceThreshold":null,"updatedAt":"2026-10-09T09:00:31Z"}},"WalletFundingInstructions":{"type":"object","description":"How to fund the wallet by bank transfer (FEAT-030 §5.5): a virtual account number, or an account plus a unique reference where virtual accounts are not offered (OD-ENR-4 picks the bank). In sandbox the values are simulated. Credited when the bank notice arrives, usually within one business day.\n","properties":{"virtualAccount":{"type":["string","null"]},"reference":{"type":["string","null"]},"bankName":{"type":["string","null"]},"note":{"type":["string","null"]}},"example":{"virtualAccount":"SIM-0000-0000-4417","reference":"AIK-TEN-ACME","bankName":null,"note":"Simulated in sandbox; no money moves."}},"WalletList":{"type":"object","required":["wallets"],"properties":{"wallets":{"type":"array","items":{"$ref":"#/components/schemas/Wallet"}}},"example":{"wallets":[]}},"WalletTransaction":{"type":"object","description":"One ledger leg on the wallet (F-08), never edited. type is WALLET_TOPUP | WALLET_HOLD | WALLET_SETTLE | WALLET_RELEASE | WALLET_FEE (x-extensible-enum); direction CREDIT | DEBIT says which way it moved the balance (a WALLET_HOLD or WALLET_RELEASE moves held, and reads DEBIT and CREDIT of available). amount is positive Money.\n","required":["transactionId","type","direction","amount","createdAt"],"properties":{"transactionId":{"type":"string","description":"The ledger entry id"},"type":{"type":"string","x-extensible-enum":["WALLET_TOPUP","WALLET_HOLD","WALLET_SETTLE","WALLET_RELEASE","WALLET_FEE"]},"direction":{"type":"string","x-extensible-enum":["CREDIT","DEBIT"]},"amount":{"$ref":"#/components/schemas/Money"},"orderRef":{"type":["string","null"],"description":"The order a hold, settle or release belongs to"},"topUpRef":{"type":["string","null"],"description":"The top-up a WALLET_TOPUP or WALLET_FEE belongs to"},"attribution":{"$ref":"#/components/schemas/Attribution","description":"connect-v1 1.57.0 (PROPOSAL, FEAT-034 §7, AC-034-11): the principal, agent and key of the request that moved the money (an order's hold, settle or release); absent for a top-up, from an older server, and while aikonic.keys.agent-mint.enabled was off.\n"},"createdAt":{"type":"string","format":"date-time"}},"example":{"transactionId":"led_01JA9K7I","type":"WALLET_RELEASE","direction":"CREDIT","amount":{"amount":45000,"currency":"USD"},"orderRef":"MO-8KD-2FQ-9XT","topUpRef":null,"createdAt":"2026-10-09T09:15:02Z"}},"WalletTransactionPage":{"type":"object","description":"A page of wallet movements, newest first; nextCursor is null on the last page","required":["transactions"],"properties":{"transactions":{"type":"array","items":{"$ref":"#/components/schemas/WalletTransaction"}},"nextCursor":{"type":["string","null"]}},"example":{"transactions":[],"nextCursor":null}},"WalletAlertsRequest":{"type":"object","description":"Body of PUT /v1/wallets/{currency}/alerts; null clears the threshold","required":["lowBalanceThreshold"],"additionalProperties":false,"properties":{"lowBalanceThreshold":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]}},"example":{"lowBalanceThreshold":null}},"WalletTopUpRequest":{"type":"object","description":"Body of POST /v1/wallets/{currency}/top-ups. amount is positive Money in the wallet's currency. paymentMethod (a Spreedly payment-method token, OD-875) is not read by the sandbox simulation; it is what a real card top-up will take.\n","required":["amount"],"additionalProperties":false,"properties":{"amount":{"$ref":"#/components/schemas/Money"},"paymentMethod":{"$ref":"#/components/schemas/PaymentMethodToken"}},"example":{"amount":{"amount":50000,"currency":"USD"}}},"WalletTopUp":{"type":"object","description":"A top-up. state is SUCCEEDED | PENDING | FAILED (x-extensible-enum); simulated is true for every top-up in this phase. fee is shown before paying and posted as its own WALLET_FEE leg when non-zero. transactionIds are the ledger legs it posted.\n","required":["topUpRef","state","simulated","amount","fee","transactionIds","createdAt"],"properties":{"topUpRef":{"type":"string"},"state":{"type":"string","x-extensible-enum":["SUCCEEDED","PENDING","FAILED"]},"simulated":{"type":"boolean"},"amount":{"$ref":"#/components/schemas/Money"},"fee":{"$ref":"#/components/schemas/Money"},"transactionIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"}},"example":{"topUpRef":"TU-SIM-01JA9S4","state":"SUCCEEDED","simulated":true,"amount":{"amount":50000,"currency":"USD"},"fee":{"amount":0,"currency":"USD"},"transactionIds":["led_01JA9S4A"],"createdAt":"2026-10-09T10:10:00Z"}},"BusinessAddress":{"type":"object","description":"A business address as the GST register states it (connect-v1 1.51.0, PROPOSAL)","required":["line1","city","region","postalCode","country"],"properties":{"line1":{"type":"string"},"city":{"type":"string"},"region":{"type":"string","description":"State or region"},"postalCode":{"type":"string"},"country":{"type":"string","description":"ISO 3166-1 alpha-2"}},"example":{"line1":"4th Floor, 12 Residency Road","city":"Bengaluru","region":"Karnataka","postalCode":"560025","country":"IN"}},"BusinessRegistration":{"type":"object","description":"A GST registration as the register states it (GET /v1/activation/business-registrations/{gstin}; connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b). pan is the company's PAN (characters 3 to 12 of the GSTIN). constitution is x-extensible-enum (PROPRIETORSHIP | PARTNERSHIP | LLP | PRIVATE_LIMITED_COMPANY | PUBLIC_LIMITED_COMPANY | OTHER); status is x-extensible-enum (ACTIVE | CANCELLED | SUSPENDED) and only ACTIVE can be confirmed. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): also the answer of POST /v1/activation/business-registrations:look-up for IN, GB, AE, US and CA, with scheme, number and jurisdiction. Only legalName and status are schema-required now: gstin and pan are set on the India path only, constitution, registeredOn and address only where the register states them (a US EIN match states none). On the GSTIN route every 1.51.0 member is always present, as before. status adds DISSOLVED, EXPIRED and NOT_MATCHED (the US name and EIN do not match); only ACTIVE can be confirmed.\n","required":["legalName","status"],"properties":{"scheme":{"type":"string","x-extensible-enum":["IN_GSTIN","GB_COMPANY_NUMBER","AE_TRADE_LICENCE","US_EIN","CA_BUSINESS_NUMBER"],"description":"connect-v1 1.63.0 (PROPOSAL) — the register scheme; IN_GSTIN on the India path"},"number":{"type":"string","description":"connect-v1 1.63.0 (PROPOSAL) — the registration number as the register states it"},"jurisdiction":{"type":["string","null"],"description":"connect-v1 1.63.0 (PROPOSAL): where the registration is held when the register says (for example England and Wales, Dubai, a free zone, a province); null otherwise\n"},"gstin":{"type":"string"},"legalName":{"type":"string"},"tradeName":{"type":["string","null"]},"constitution":{"type":"string","x-extensible-enum":["PROPRIETORSHIP","PARTNERSHIP","LLP","PRIVATE_LIMITED_COMPANY","PUBLIC_LIMITED_COMPANY","OTHER"]},"status":{"type":"string","x-extensible-enum":["ACTIVE","CANCELLED","SUSPENDED","DISSOLVED","EXPIRED","NOT_MATCHED"]},"registeredOn":{"type":"string","format":"date"},"pan":{"type":"string"},"address":{"$ref":"#/components/schemas/BusinessAddress"}},"example":{"gstin":"29AABCS1234F1Z5","legalName":"SAMPLE TRAVELS PRIVATE LIMITED","tradeName":"Sample Travels","constitution":"PRIVATE_LIMITED_COMPANY","status":"ACTIVE","registeredOn":"2019-03-14","pan":"AABCS1234F","address":{"line1":"4th Floor, 12 Residency Road","city":"Bengaluru","region":"Karnataka","postalCode":"560025","country":"IN"}}},"BusinessRegistrationConfirmRequest":{"type":"object","description":"Body of POST /v1/activation/business-registration:confirm (connect-v1 1.51.0, PROPOSAL). connect-v1 1.63.0 (PROPOSAL, round 2): exactly one of gstin, or scheme with number (and legalName for US_EIN), is sent; gstin is no longer schema-required (the backend enforces it, no oneOf; 400 VALIDATION details.field gstin).\n","additionalProperties":false,"properties":{"gstin":{"type":"string","pattern":"^[0-9]{2}[A-Z0-9]{13}$"},"scheme":{"type":"string","x-extensible-enum":["IN_GSTIN","GB_COMPANY_NUMBER","AE_TRADE_LICENCE","US_EIN","CA_BUSINESS_NUMBER"],"description":"connect-v1 1.63.0 (PROPOSAL) — the scheme the look-up by number answered"},"number":{"type":"string","minLength":1,"maxLength":32,"description":"connect-v1 1.63.0 (PROPOSAL) — the number the look-up by number answered"},"legalName":{"type":"string","minLength":1,"maxLength":200,"description":"connect-v1 1.63.0 (PROPOSAL) — with US_EIN, the legal name the IRS matches (as the look-up)"}},"example":{"gstin":"29AABCS1234F1Z5"}},"SignatoryCheckRequest":{"type":"object","description":"Body of POST /v1/activation/signatory-checks (connect-v1 1.51.0, PROPOSAL): the signing Owner's own PAN and the name printed on it. Neither is stored. connect-v1 1.63.0 (PROPOSAL, round 2): method PAN (the default) sends pan and nameOnPan; method NAME_DOB sends dateOfBirth only (the name is the signed-in Owner's). Exactly one method's fields; pan and nameOnPan are no longer schema-required (the backend enforces it, no oneOf; 400 VALIDATION details.field naming the member).\n","additionalProperties":false,"properties":{"pan":{"type":"string","pattern":"^[A-Z]{5}[0-9]{4}[A-Z]$"},"nameOnPan":{"type":"string","minLength":1,"maxLength":120},"method":{"type":"string","x-extensible-enum":["PAN","NAME_DOB"],"description":"connect-v1 1.63.0 (PROPOSAL): PAN (absent means PAN; India) or NAME_DOB (the Owner's name and date of birth against the register's directors, officers or managers; GB, AE, US, CA)\n"},"dateOfBirth":{"type":"string","format":"date","description":"connect-v1 1.63.0 (PROPOSAL) — the signed-in Owner's date of birth, with NAME_DOB; not kept by the check"}},"example":{"pan":"ABCPM1234K","nameOnPan":"Arjun Mehta"}},"SignatoryCheck":{"type":"object","description":"The result of a signatory check (connect-v1 1.51.0, PROPOSAL). result is x-extensible-enum: DIRECTOR_MATCH (the PAN belongs to a director or signatory of the confirmed registration) or NOT_A_DIRECTOR. panLast4 is the last four characters of the PAN checked; the PAN itself is never answered. connect-v1 1.63.0 (PROPOSAL, round 2): result also OFFICER_MATCH (US, an officer or member on the register); method echoes the check made; panLast4 is set on the PAN method only, so it is no longer schema-required (always set when method is PAN).\n","required":["result","checkedAt"],"properties":{"result":{"type":"string","x-extensible-enum":["DIRECTOR_MATCH","NOT_A_DIRECTOR","OFFICER_MATCH"]},"method":{"type":"string","x-extensible-enum":["PAN","NAME_DOB"]},"panLast4":{"type":"string"},"checkedAt":{"type":"string","format":"date-time"}},"example":{"result":"DIRECTOR_MATCH","panLast4":"234K","checkedAt":"2026-10-26T09:14:00Z"}},"TierLimits":{"type":"object","description":"The daily spend limit of a capped tier (GET /v1/activation/limits, ActivationState.limits; connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b). It applies to the orders of a LIVE_CAPPED tenant that is not a sandbox twin, on any rung. dailyCap's currency is INR when the activation country is IN, else USD. usedToday counts the tenant's non-cancelled orders since 00:00 UTC, in dailyCap's currency (each order converted at its FX snapshot); resetAt is the next 00:00 UTC. tier is x-extensible-enum. The cap is checked per order against today's confirmed and in-flight spend, and it is SOFT under concurrency: orders placed at the same time, each under the cap on its own, may together exceed it by at most the orders in flight; a reservation-based hard cap is an option for the OD-ENR-26 decision.\n","required":["tier","dailyCap","usedToday","resetAt"],"properties":{"tier":{"type":"string","x-extensible-enum":["LIVE_CAPPED","LIVE_STANDARD","LIVE_ENTERPRISE"]},"dailyCap":{"$ref":"#/components/schemas/Money"},"usedToday":{"$ref":"#/components/schemas/Money"},"resetAt":{"type":"string","format":"date-time"}},"example":{"tier":"LIVE_CAPPED","dailyCap":{"amount":500000,"currency":"USD"},"usedToday":{"amount":0,"currency":"USD"},"resetAt":"2026-10-27T00:00:00Z"}},"LimitRequestCreate":{"type":"object","description":"Body of POST /v1/activation/limit-requests (connect-v1 1.51.0, PROPOSAL): the daily cap asked for, in the current cap's currency and above it, and why.\n","required":["dailyCap","reason"],"additionalProperties":false,"properties":{"dailyCap":{"$ref":"#/components/schemas/Money"},"reason":{"type":"string","minLength":1,"maxLength":500}},"example":{"dailyCap":{"amount":100000000,"currency":"INR"},"reason":"Corporate travel season"}},"LimitRequest":{"type":"object","description":"A request for a higher daily limit (connect-v1 1.51.0, PROPOSAL). status is x-extensible-enum PENDING | APPROVED | REJECTED; decidedAt is set with the decision. currentCap is the cap when the request was filed.\n","required":["id","status","requestedCap","currentCap","reason","createdAt"],"properties":{"id":{"type":"string"},"status":{"type":"string","x-extensible-enum":["PENDING","APPROVED","REJECTED"]},"requestedCap":{"$ref":"#/components/schemas/Money"},"currentCap":{"$ref":"#/components/schemas/Money"},"reason":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"decidedAt":{"type":["string","null"],"format":"date-time"}},"example":{"id":"LRQ-01JB2K7Q","status":"PENDING","requestedCap":{"amount":100000000,"currency":"INR"},"currentCap":{"amount":40000000,"currency":"INR"},"reason":"Corporate travel season","createdAt":"2026-10-26T11:00:00Z","decidedAt":null}},"LimitRequestPage":{"type":"object","description":"A page of limit requests, newest first; nextCursor is null on the last page (connect-v1 1.51.0, PROPOSAL)","required":["items","nextCursor"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/LimitRequest"}},"nextCursor":{"type":["string","null"]}},"example":{"items":[],"nextCursor":null}},"SavedPaymentMethod":{"type":"object","description":"A saved payment method (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b): a PSP token held server-side and its display facts. kind is x-extensible-enum CARD | UPI. A card carries brand and last4; a UPI method carries vpaMasked. label is a display string the platform composes. No card number, security code, full VPA or token is ever answered.\n","required":["id","kind","label","isDefault","createdAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","x-extensible-enum":["CARD","UPI"]},"label":{"type":"string"},"brand":{"type":["string","null"],"description":"The card network as the PSP reports it (VISA, MASTERCARD, RUPAY, AMEX); open string"},"last4":{"type":["string","null"]},"vpaMasked":{"type":["string","null"]},"isDefault":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"example":{"id":"PM-01JB2C4V","kind":"CARD","label":"Visa ending 1111","brand":"VISA","last4":"1111","vpaMasked":null,"isDefault":true,"createdAt":"2026-10-26T09:20:00Z"}},"PaymentMethodCreate":{"type":"object","description":"Body of POST /v1/payments/methods (connect-v1 1.51.0, PROPOSAL). kind CARD needs token (a PSP payment-method token from the hosted card fields, never a card number); the server derives brand and last4 from the PSP token (SavedPaymentMethod carries them). kind UPI needs vpa. A member of the other kind is 400 VALIDATION details.field naming it. One flat object rather than a oneOf, so every generated client can build it; the backend enforces the per-kind rule.\n","required":["kind"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["CARD","UPI"]},"token":{"type":"string","maxLength":256,"description":"kind CARD — the PSP payment-method token (OD-875); the harness PSP's test tokens are tok_harness_success and tok_harness_decline. connect-v1 1.63.0 round 3 (amended 2026-10-29): when the setup provider is STRIPE (POST /v1/payments/methods:setup), the card provider's payment-method id (pm_ followed by the id) that its browser library confirmed on the setup; never card data.\n"},"vpa":{"type":"string","pattern":"^[\\w.\\-]{2,256}@[A-Za-z]{2,64}$","description":"kind UPI — the virtual payment address; stored for the PSP, answered only masked. connect-v1 1.63.0 round 3 (amended 2026-10-29): with the UPI provider razorpay it is validated with the provider before it is saved; an address the provider refuses is 400 VALIDATION details.reason VPA_INVALID.\n"}},"example":{"kind":"CARD","token":"tok_harness_success"}},"SavedPaymentMethodList":{"type":"object","description":"The tenant's saved payment methods, default first (connect-v1 1.51.0, PROPOSAL)","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SavedPaymentMethod"}}},"example":{"items":[]}},"Invoice":{"type":"object","description":"A GST invoice for one confirmed order (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b). amount is the taxable value (the order total) and tax the GST on it at taxRateBps (basis points; 500 = 5%); both Money in the order's currency. status is x-extensible-enum ISSUED | VOID; a void invoice keeps its number.\n","required":["id","number","orderRef","issuedAt","billedTo","amount","tax","taxRateBps","status"],"properties":{"id":{"type":"string"},"number":{"type":"string","maxLength":16,"pattern":"^AIK/[0-9]{4}/[0-9]{7}$","description":"The invoice number, AIK/yyyy/nnnnnnn (16 characters, the GST limit). The platform is the issuer, so there is ONE platform-wide sequence per Indian financial year: yyyy is the financial year's start and end two-digit years (April to March, in UTC; 2026-10-26 is in 2627) and nnnnnnn a seven-digit sequence starting at 0000001, never reused (a void invoice keeps its number).\n"},"orderRef":{"type":"string"},"issuedAt":{"type":"string","format":"date-time"},"billedTo":{"type":"object","required":["legalName","gstin"],"properties":{"legalName":{"type":"string"},"gstin":{"type":"string"}}},"amount":{"$ref":"#/components/schemas/Money"},"tax":{"$ref":"#/components/schemas/Money"},"taxRateBps":{"type":"integer","minimum":0},"status":{"type":"string","x-extensible-enum":["ISSUED","VOID"]}},"example":{"id":"INV-01JB3A1F","number":"AIK/2627/0000001","orderRef":"MO-8KD-2FQ-9XT","issuedAt":"2026-10-26T12:05:00Z","billedTo":{"legalName":"SAMPLE TRAVELS PRIVATE LIMITED","gstin":"29AABCS1234F1Z5"},"amount":{"amount":2500000,"currency":"INR"},"tax":{"amount":125000,"currency":"INR"},"taxRateBps":500,"status":"ISSUED"}},"InvoicePage":{"type":"object","description":"A page of invoices, newest first; nextCursor is null on the last page (connect-v1 1.51.0, PROPOSAL)","required":["items","nextCursor"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"nextCursor":{"type":["string","null"]}},"example":{"items":[],"nextCursor":null}},"Notification":{"type":"object","description":"One item of the console notifications feed (connect-v1 1.51.0, PROPOSAL, FEAT-030 §18b). kind is x-extensible-enum; params holds the kind's display values as strings, never personal data beyond what the console already shows; link is a console path. readAt is when the caller marked it read, null while unread. refund.issued is derived from payment.refunded and its params carry paymentRef (orderRef only when known); webhook.test_delivered is emitted when the signed test event is queued for delivery to the endpoint. connect-v1 1.57.0 (PROPOSAL, FEAT-034 §8): key.paused (params keyPrefix, agent, summary; link the key's console page) and key.resumed (params keyPrefix, category), signup.device.approved (params agent, maskedEmail) on the tenant an agent sign-up created, and signup.tenant_exists (params agent, maskedEmail; link /console/team) to an existing tenant's Owner once the e-mail of a device sign-up for its domain was proven and the request refused 409 TENANT_EXISTS (never on the anonymous start; OD-AGT-21, AC-034-03); key.revoked (params keyPrefix, reason) when the platform revokes a paused key that kept calling (FEAT-034 §12). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): the business verification verdicts, each linked to /console/activate — verification.needs_more (params count, the number of outstanding requirements), verification.verified (no params; going live is now open) and verification.rejected (params reason, ActivationBusinessVerification.rejectionReason). connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): the go-live review, each linked to /console?setup=go_live (the go-live step on the console's home) — golive.in_review (params products, the requested product codes comma-separated, and expectedBy), golive.approved (params products; tenant.live is emitted beside it), golive.changes_requested (params products; the note is read from GET /v1/activation) and golive.rejected (no params). One per review state change; each decision is also e-mailed to the Owner and to the tenant's operations contact e-mail (ContactEmails.operations; the Owner's when unset).\n","required":["id","kind","params","createdAt","readAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","x-extensible-enum":["activation.step_done","tenant.live","order.confirmed","order.cancelled","refund.issued","team.invite_sent","team.member_joined","limit.request_decided","webhook.test_delivered","key.paused","key.resumed","key.revoked","signup.device.approved","signup.tenant_exists","verification.needs_more","verification.verified","verification.rejected","golive.in_review","golive.approved","golive.changes_requested","golive.rejected"]},"params":{"type":"object","additionalProperties":{"type":"string"}},"link":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"readAt":{"type":["string","null"],"format":"date-time"}},"example":{"id":"NTF-01JB3A2C","kind":"tenant.live","params":{"tier":"LIVE_CAPPED"},"link":"/console/activate","createdAt":"2026-10-26T10:31:00Z","readAt":null}},"NotificationPage":{"type":"object","description":"A page of the notifications feed, newest first (connect-v1 1.51.0, PROPOSAL). unread counts the caller's unread items in the whole feed; nextCursor is null on the last page.\n","required":["items","nextCursor","unread"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"nextCursor":{"type":["string","null"]},"unread":{"type":"integer","minimum":0}},"example":{"items":[],"nextCursor":null,"unread":0}},"NotificationsMarkRead":{"type":"object","description":"Body of POST /admin/v1/notifications:mark-read (connect-v1 1.51.0, PROPOSAL): exactly one of ids or all true (the backend enforces it, no oneOf).\n","additionalProperties":false,"properties":{"ids":{"type":"array","maxItems":100,"items":{"type":"string"}},"all":{"type":"boolean"}},"example":{"all":true}},"NotificationsMarkReadResult":{"type":"object","description":"How many notifications the call marked read, and the caller's unread count after (connect-v1 1.51.0, PROPOSAL)","required":["marked","unread"],"properties":{"marked":{"type":"integer","minimum":0},"unread":{"type":"integer","minimum":0}},"example":{"marked":2,"unread":0}},"MonthlyVolume":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b): the expected monthly booking value as a band in a currency (ISO 4217). The bands are per currency. INR BAND_1 under ₹5 lakh, BAND_2 ₹5 lakh to ₹25 lakh, BAND_3 ₹25 lakh to ₹1 crore, BAND_4 ₹1 crore to ₹5 crore, BAND_5 over ₹5 crore. GBP, USD and CAD BAND_1 under 5,000, BAND_2 5,000 to 25,000, BAND_3 25,000 to 100,000, BAND_4 100,000 to 500,000, BAND_5 over 500,000. AED BAND_1 under 25,000, BAND_2 25,000 to 100,000, BAND_3 100,000 to 500,000, BAND_4 500,000 to 2,500,000, BAND_5 over 2,500,000. Another currency reads the USD bands [assumption]. NOT_SURE is a valid answer. band is x-extensible-enum.\n","required":["band","currency"],"additionalProperties":false,"properties":{"band":{"type":"string","x-extensible-enum":["BAND_1","BAND_2","BAND_3","BAND_4","BAND_5","NOT_SURE"]},"currency":{"type":"string","pattern":"^[A-Z]{3}$"}},"example":{"band":"BAND_2","currency":"INR"}},"BusinessRegistrationLookup":{"type":"object","description":"Body of POST /v1/activation/business-registrations:look-up (connect-v1 1.63.0, PROPOSAL, FEAT-030 §18b): the country (which picks the register and the scheme), the number as issued and, for US, the legal name the IRS matches against the EIN. Nothing is stored by the look-up.\n","required":["country","number"],"additionalProperties":false,"properties":{"country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2, one of IN (IN_GSTIN), GB (GB_COMPANY_NUMBER), AE (AE_TRADE_LICENCE), US (US_EIN) or CA (CA_BUSINESS_NUMBER); the tenant's own country\n"},"number":{"type":"string","minLength":1,"maxLength":32,"description":"The number as issued. IN a 15-character GSTIN; GB an 8-character company number (it may start with two letters, as SC or NI); AE the trade licence number; US the nine-digit EIN, with or without the hyphen after the second digit; CA the nine-digit business number (the root BN)\n"},"legalName":{"type":"string","minLength":1,"maxLength":200,"description":"Required for US (the IRS name and EIN match); not read for the other countries"}},"example":{"country":"GB","number":"09876543"}},"BusinessPeople":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): who acts for and who owns the business (GET and PUT /v1/activation/business-people). PUT replaces the whole record. representative is the signed-in Owner acting for the business; owners lists every natural person who holds 25 percent or more (at most 10, sharePercent 25 to 100, together at most 100); noOwnerAt25Percent true says no one does, and owners is then empty. The record counts for business_verification when representative is set and owners is non-empty or noOwnerAt25Percent is true. submittedAt is read-only (ignored on PUT), null until the first PUT.\n","required":["representative","owners","noOwnerAt25Percent"],"additionalProperties":false,"properties":{"representative":{"oneOf":[{"$ref":"#/components/schemas/BusinessRepresentative"},{"type":"null"}]},"owners":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/BusinessOwner"}},"noOwnerAt25Percent":{"type":"boolean","description":"True when no natural person owns 25 percent or more; owners must then be empty"},"submittedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the record was last submitted; null until then"}},"example":{"representative":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Lane","city":"London","region":"Greater London","postalCode":"N1 1AA","country":"GB"},"idNumberLast4":"456C"},"owners":[{"fullName":"Arjun Mehta","sharePercent":60},{"fullName":"Kavya Rao","sharePercent":40}],"noOwnerAt25Percent":false,"submittedAt":"2026-10-28T09:44:00Z"}},"BusinessRepresentative":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL): the person acting for the business, the signed-in Owner. idNumberLast4 is the last four characters of a national identity number where asked (UK National Insurance, US SSN, UAE Emirates ID, Canadian SIN), never the full number; India leaves it absent (the PAN already checked stands for it). Personal data: kept for the KYB record only and never logged. connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): idNumber, write-only, carries the full identity number when the verification provider asks for it (requirement representative.id_number; for example the full US SSN where the last four did not verify).\n","required":["jobTitle","dateOfBirth","homeAddress"],"additionalProperties":false,"properties":{"jobTitle":{"type":"string","minLength":1,"maxLength":80},"dateOfBirth":{"type":"string","format":"date"},"homeAddress":{"$ref":"#/components/schemas/BusinessAddress"},"idNumberLast4":{"type":"string","pattern":"^[0-9A-Za-z]{4}$","description":"The last four characters only; never the full number"},"idNumber":{"type":"string","writeOnly":true,"minLength":5,"maxLength":32,"pattern":"^[0-9A-Za-z -]+$","description":"connect-v1 1.63.0 round 3 (amended 2026-10-29): the full identity number (US SSN, UK National Insurance number, Canadian SIN, UAE Emirates ID), sent only when the verification provider asks for it (requirement representative.id_number). Write-only: forwarded to the provider with this submission and never stored, logged, audited or answered; idNumberLast4 is set from its last four characters (an idNumberLast4 sent beside it is ignored). India leaves it absent.\n"}},"example":{"jobTitle":"Director","dateOfBirth":"1984-05-12","homeAddress":{"line1":"1 Sample Lane","city":"London","region":"Greater London","postalCode":"N1 1AA","country":"GB"},"idNumberLast4":"456C"}},"BusinessOwner":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL) — a natural person who owns 25 percent or more of the business. connect-v1 1.63.0 round 3 (FEAT-030 §18c; amended 2026-10-29, same version): optional dateOfBirth, address (BusinessAddress, the owner's home address, as the representative's homeAddress), idNumber (write-only) and idNumberLast4 (read-only). None is needed at the first submission (the harness and India do not ask for them); the verification provider may ask for them on needs_more (requirement fields owners[i].dateOfBirth, owners[i].address, owners[i].idNumber), and the Owner fills them in place and submits again. idNumber is forwarded to the provider with that submission and never stored, logged, audited or answered; idNumberLast4 is set from it by the server (ignored on PUT). Personal data: kept for the KYB record only and never logged.\n","required":["fullName","sharePercent"],"additionalProperties":false,"properties":{"fullName":{"type":"string","minLength":1,"maxLength":120},"sharePercent":{"type":"integer","minimum":25,"maximum":100},"dateOfBirth":{"type":"string","format":"date"},"address":{"$ref":"#/components/schemas/BusinessAddress"},"idNumber":{"type":"string","writeOnly":true,"minLength":5,"maxLength":32,"pattern":"^[0-9A-Za-z -]+$","description":"The owner's full identity number (US SSN, UK National Insurance number, Canadian SIN, UAE Emirates ID), sent only when the provider asks for it (requirement field owners[i].idNumber). Write-only: forwarded to the provider and never stored, logged, audited or answered.\n"},"idNumberLast4":{"type":"string","readOnly":true,"pattern":"^[0-9A-Za-z]{4}$","description":"The last four characters of the idNumber last sent, set by the server; ignored on PUT"}},"example":{"fullName":"Arjun Mehta","sharePercent":60}},"WorkEmailSendCodeRequest":{"type":"object","description":"Body of POST /v1/activation/work-email:send-code (connect-v1 1.63.0, PROPOSAL) — the work address to prove","required":["email"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":254}},"example":{"email":"arjun@sampletravels.co.uk"}},"WorkEmailCodeSent":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b): a code was e-mailed to the work address (POST /v1/activation/work-email:send-code). The code itself is never returned.\n","required":["sentTo","expiresAt","resendAfter"],"properties":{"sentTo":{"type":"string","maxLength":254,"description":"The work address, masked as TermsCodeSent.sentTo: the first two characters of the local part (the first one when it is shorter than three), then *** and the domain\n"},"expiresAt":{"type":"string","format":"date-time","description":"When the code stops working (15 minutes after it is sent)"},"resendAfter":{"type":"string","format":"date-time","description":"The earliest time another code may be sent"}},"example":{"sentTo":"ar***@sampletravels.co.uk","expiresAt":"2026-10-28T09:16:00Z","resendAfter":"2026-10-28T09:02:00Z"}},"WorkEmailConfirmRequest":{"type":"object","description":"Body of POST /v1/activation/work-email:confirm (connect-v1 1.63.0, PROPOSAL)","required":["email","code"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":254,"description":"The address the latest code was sent to"},"code":{"type":"string","pattern":"^[0-9]{6}$","description":"The 6-digit code from the e-mail (15 minutes, at most 5 attempts)"}},"example":{"email":"arjun@sampletravels.co.uk","code":"482913"}},"WorkEmail":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL) — the tenant's verified work e-mail (POST /v1/activation/work-email:confirm)","required":["email","verifiedAt"],"properties":{"email":{"type":"string","format":"email"},"verifiedAt":{"type":"string","format":"date-time"}},"example":{"email":"arjun@sampletravels.co.uk","verifiedAt":"2026-10-28T09:02:00Z"}},"ActivationWorkEmail":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): the work-e-mail guardrail of the CAPPED flow (ActivationState.workEmail), for every CAPPED BUSINESS tenant with no exception. required is true when the sign-up e-mail is on a free-mail domain, or not on the domain (or a subdomain) of the profile's website when one is set; POST /v1/activation:go-live is then refused (409 WORK_EMAIL_REQUIRED) until verifiedAt is set. email and verifiedAt are the verified work address, null until one is proved. Test mode is unaffected.\n","required":["required"],"properties":{"email":{"type":["string","null"]},"verifiedAt":{"type":["string","null"],"format":"date-time"},"required":{"type":"boolean"}},"example":{"email":null,"verifiedAt":null,"required":true}},"ContactEmails":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18b round 2): the tenant's contact addresses (GET and PUT /admin/v1/contact-emails). billing gets invoices and payment notices, technical API deprecations, incidents and webhook failures, operations booking and supplier issues. Each defaults to the Owner's e-mail until set. updatedAt is read-only (ignored on PUT), null until the first PUT.\n","required":["billing","technical","operations"],"additionalProperties":false,"properties":{"billing":{"type":"string","format":"email","maxLength":254},"technical":{"type":"string","format":"email","maxLength":254},"operations":{"type":"string","format":"email","maxLength":254},"updatedAt":{"type":["string","null"],"format":"date-time","readOnly":true}},"example":{"billing":"accounts@sampletravels.co.uk","technical":"dev@sampletravels.co.uk","operations":"desk@sampletravels.co.uk","updatedAt":"2026-10-28T11:00:00Z"}},"VerificationRequirement":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): one item the business verification provider still needs. code is the provider-neutral requirement, a dotted path in the provider's vocabulary mapped to ours (for example representative.id_number, representative.dob, company.tax_id, owners[0].dob); field is the member of our own request bodies that fixes it, so the console highlights exactly that field and the Owner fixes it in place and submits again (for example representative.idNumber, representative.dateOfBirth, owners[0].dateOfBirth, owners[0].address, owners[0].idNumber, the business profile's phone). field is absent only on a document_required item: the API-only flow cannot collect documents yet, so the console sends the Owner to support for it (a document-upload step is a follow-up tied to OD-ENR-35). reason is x-extensible-enum missing (never given), invalid (given but not well-formed or refused), unverified (given but the provider could not verify it) or document_required (the provider needs a document; never uploaded to Apiq). dueBy is when the provider needs it by, absent when the provider states none.\n","required":["code","reason"],"properties":{"code":{"type":"string","maxLength":120},"field":{"type":"string","maxLength":120},"reason":{"type":"string","x-extensible-enum":["missing","invalid","unverified","document_required"]},"dueBy":{"type":"string","format":"date-time"}},"example":{"code":"representative.id_number","field":"representative.idNumber","reason":"missing","dueBy":"2026-11-12T00:00:00Z"}},"ActivationBusinessVerification":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): the business verification case of a CAPPED tenant (ActivationState.businessVerification). status is x-extensible-enum: not_submitted (the business people were not submitted yet, PUT /v1/activation/business-people), in_progress (submitted; the provider has not answered), needs_more (the provider needs the outstanding items; fix them and submit again), in_review (the provider is reviewing), verified (business_verification done; going live is open) or rejected (the provider refused; contact support). provider is the provider routed for the tenant's country (harness, stripe_connect or a registered India KYC adapter id; deployment settings aikonic.kyb.provider.<country> and aikonic.kyb.provider), null when none is routed (submitting is then 501 KYB_UNAVAILABLE). submittedAt is the last submission, decidedAt the provider's last verdict (verified or rejected). outstanding is empty unless status is needs_more. rejectionReason (x-extensible-enum) is set only when rejected: documents_unverifiable, ownership_unverifiable, prohibited_business or other. No document and no full identity number is ever carried.\n","required":["status","provider","outstanding"],"properties":{"status":{"type":"string","x-extensible-enum":["not_submitted","in_progress","needs_more","in_review","verified","rejected"]},"provider":{"type":["string","null"],"maxLength":64},"submittedAt":{"type":["string","null"],"format":"date-time"},"decidedAt":{"type":["string","null"],"format":"date-time"},"outstanding":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/VerificationRequirement"}},"rejectionReason":{"type":["string","null"],"x-extensible-enum":["documents_unverifiable","ownership_unverifiable","prohibited_business","other"]}},"example":{"status":"in_review","provider":"stripe_connect","submittedAt":"2026-10-29T09:44:00Z","decidedAt":null,"outstanding":[],"rejectionReason":null}},"GoLiveRequest":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): the optional body of POST /v1/activation:go-live. products names the products the Owner asks to go live for, as canonical product codes (02 §1; the codes of CapabilityProduct.domain, for example FLT, HTL, CAR; an alias is resolved to its canonical code); each must be a product of the tenant's sandbox capabilities (GET /v1/capabilities with a test key), else 400 VALIDATION details.field products, details.reason PRODUCT_NOT_AVAILABLE. Absent (or no body, or an empty object) means every product of the tenant's sandbox capabilities. Duplicates are refused (400 VALIDATION details.field products).\n","additionalProperties":false,"properties":{"products":{"type":"array","minItems":1,"maxItems":50,"uniqueItems":true,"items":{"type":"string","pattern":"^[A-Z][A-Z0-9_]{1,31}$"}}},"example":{"products":["FLT","HTL"]}},"GoLiveCheck":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d; amended 2026-10-29, same version): one check of one product in a go-live review (GoLiveProductReview.checks). key is x-extensible-enum: business_details (an operator reviews the business's details; by operator; in_progress while the review is open, passed on approval, failed on changes requested or rejected) and test_calls (the tenant's own sandbox calls for the product, by system; the four verbs, named as in CapabilityProduct.verbs, are search, reprice, orders (an order created) and cancel, each counted when it answered 2xx; todo when none was seen, in_progress when some were, passed when all four were; verbsSeen lists them). test_calls is REQUIRED (OD-ENR-41, owner direction 2026-10-29): an operator cannot approve the review until test_calls is passed for every requested product (POST /ops/v1/approvals/{id}:approve is 409 CONFLICT GO_LIVE_TEST_CALLS_MISSING, details.missingTestCalls); the Owner may still ask to go live before, and the console shows what is missing. More keys come without a contract break: an automated certification agent (OD-ENR-41) adds one row per verb it proves end to end in the sandbox, for example search, reprice, orders and cancel, by agent. status is x-extensible-enum todo | in_progress | passed | failed; by is x-extensible-enum operator | agent | system. at is when status last changed, null while todo. detail is a short customer-facing sentence, null when none; verbsSeen is set on test_calls only. A client renders keys, statuses and actors it does not know as a plain row with their detail.\n","required":["key","status","by"],"properties":{"key":{"type":"string","x-extensible-enum":["business_details","test_calls"]},"status":{"type":"string","x-extensible-enum":["todo","in_progress","passed","failed"]},"by":{"type":"string","x-extensible-enum":["operator","agent","system"]},"at":{"type":["string","null"],"format":"date-time"},"detail":{"type":["string","null"],"maxLength":500},"verbsSeen":{"type":"array","maxItems":10,"uniqueItems":true,"items":{"type":"string","x-extensible-enum":["search","reprice","orders","cancel"]}}},"example":{"key":"test_calls","status":"in_progress","by":"system","at":"2026-10-29T11:02:00Z","detail":"Search and reprice seen in the sandbox; order and cancel not yet.","verbsSeen":["search","reprice"]}},"GoLiveProductReview":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d; amended 2026-10-29, same version): one product of a go-live review. product is the canonical product code (02 §1, as CapabilityProduct.domain). status is x-extensible-enum: queued (waiting for automated checks before an operator looks at it; not produced until an automated agent's checks exist, OD-ENR-41), in_review (an operator reviews it), approved (live), changes_requested (the operator asked for changes; the Owner fixes them and asks again) or rejected (refused; contact support). Today one decision covers every product of the review (OD-ENR-40 option (a), all or nothing), so every product reads the review's decision; per-product decisions come with OD-ENR-40 option (b). decidedAt is when this product was decided, null while open. note is the operator's customer-facing note for this product, null today (the decision's note is GoLiveReview.note) until per-product decisions exist. checks lists the product's checks (GoLiveCheck), business_details and test_calls today, in that order.\n","required":["product","status","checks"],"properties":{"product":{"type":"string","pattern":"^[A-Z][A-Z0-9_]{1,31}$"},"status":{"type":"string","x-extensible-enum":["queued","in_review","approved","rejected","changes_requested"]},"decidedAt":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"],"maxLength":500},"checks":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/GoLiveCheck"}}},"example":{"product":"HTL","status":"in_review","decidedAt":null,"note":null,"checks":[{"key":"business_details","status":"in_progress","by":"operator","at":"2026-10-29T11:00:00Z","detail":null},{"key":"test_calls","status":"passed","by":"system","at":"2026-10-29T10:40:00Z","detail":"Search, reprice, order and cancel seen in the sandbox.","verbsSeen":["search","reprice","orders","cancel"]}]}},"GoLiveMissingTestCalls":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): one requested product whose test_calls check is not passed (ApiErrorResponse.details.missingTestCalls on 409 CONFLICT GO_LIVE_TEST_CALLS_MISSING) and the sandbox verbs not yet seen — search, reprice, orders, cancel, named as CapabilityProduct.verbs; an x-extensible-enum.\n","required":["product","verbs"],"properties":{"product":{"type":"string","pattern":"^[A-Z][A-Z0-9_]{1,31}$"},"verbs":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","x-extensible-enum":["search","reprice","orders","cancel"]}}},"example":{"product":"FLT","verbs":["orders","cancel"]}},"GoLiveReview":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18d go-live review; amended 2026-10-29, same version): the latest go-live review of a CAPPED tenant (ActivationState.goLiveReview). POST /v1/activation:go-live opens it; operators decide it through the operator approvals queue (OpsApproval kind GO_LIVE_REVIEW, scope.key the review id); the tenant turns LIVE, tier LIVE_CAPPED, only when it is approved. status is x-extensible-enum: in_review (open), approved (every requested product approved; the tenant is LIVE), partly_approved (some products approved and some not; not produced while OD-ENR-40 is option (a), all or nothing), changes_requested (the operator asked for changes; the Owner fixes them and calls go-live again, which opens a new review) or rejected (refused; today only when the tenant is offboarded or its business verification is rejected while the review is open; contact support). submittedAt is when the Owner asked; decidedAt when it was decided, null while open; expectedBy the answer the platform aims for (\"usually within one business day\": submittedAt plus one business day, Monday to Friday, UTC [assumption, OD-ENR-42]), an estimate and never a deadline, null when none is promised. note is the operator's customer-facing note with the decision (the approval's comment or rejection reason), null while open or when none. products lists every product asked for, in the order asked. The sandbox keeps working whatever this reads.\n","required":["id","status","submittedAt","products"],"properties":{"id":{"type":"string","maxLength":64},"status":{"type":"string","x-extensible-enum":["in_review","approved","partly_approved","changes_requested","rejected"]},"submittedAt":{"type":"string","format":"date-time"},"decidedAt":{"type":["string","null"],"format":"date-time"},"expectedBy":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"],"maxLength":500},"products":{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/GoLiveProductReview"}}},"example":{"id":"GLR-01JB7Q2M4X","status":"in_review","submittedAt":"2026-10-29T11:00:00Z","decidedAt":null,"expectedBy":"2026-10-30T11:00:00Z","note":null,"products":[{"product":"FLT","status":"in_review","decidedAt":null,"note":null,"checks":[{"key":"business_details","status":"in_progress","by":"operator","at":"2026-10-29T11:00:00Z","detail":null},{"key":"test_calls","status":"in_progress","by":"system","at":"2026-10-29T10:20:00Z","detail":"Search and reprice seen in the sandbox; order and cancel not yet.","verbsSeen":["search","reprice"]}]}]}},"PaymentMethodSetupRequest":{"type":"object","description":"Body of POST /v1/payments/methods:setup (connect-v1 1.63.0, PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): what to set up. kind CARD only; a UPI method needs no setup.\n","required":["kind"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["CARD"]}},"example":{"kind":"CARD"}},"PaymentMethodSetup":{"type":"object","description":"connect-v1 1.63.0 (PROPOSAL, FEAT-030 §18c round 3; amended 2026-10-29, same version): a card setup at the card provider (POST /v1/payments/methods:setup). provider is x-extensible-enum STRIPE (a card setup for off-session use on the tenant's customer at the card provider) or HARNESS (DEV and SANDBOX; the console uses the harness test tokens). setupRef is the provider's setup id. clientSecret is handed to the provider's browser library only, never logged, audited or stored by the client. publishableKey is the card provider's publishable key for the browser library, null for HARNESS. expiresAt is when the setup stops working, null when the provider states none.\n","required":["provider","setupRef","clientSecret","publishableKey"],"properties":{"provider":{"type":"string","x-extensible-enum":["STRIPE","HARNESS"]},"setupRef":{"type":"string","maxLength":255},"clientSecret":{"type":"string","maxLength":512},"publishableKey":{"type":["string","null"],"maxLength":255},"expiresAt":{"type":["string","null"],"format":"date-time"}},"example":{"provider":"HARNESS","setupRef":"setup_hrn_0000000001","clientSecret":"setup_hrn_0000000001_client","publishableKey":null,"expiresAt":"2026-10-29T11:00:00Z"}}}}}
