# info.description
Tenant-facing HTTP contract for MeshHub. Every endpoint lives under `/api/v1/`; admin, supplier-portal, and staff routes are intentionally excluded.

**Authentication.** Send your API key in the `X-MH-Key` header on every request. Keys carry scopes: `search` for read/discovery operations, `execute` for state-changing ones; each operation's description names its scope.

**Async model.** Search, book, cancel, tracking, and extras operations are asynchronous: they return **202** with a `TaskStatus` envelope. Poll `GET /api/v1/tasks/{task_id}/` (immediately, then with backoff — polls count against your rate limit) until `status` is `completed`, `failed`, or `dead`, then read `result` or `error` — or subscribe to the `task_completed`/`task_failed` webhooks instead. `X-Execution-Mode: sync` runs inline (development only).

**Errors.** Every failure uses one flat envelope (`GatewayError`): `{error, code, message, ...}`. Branch on the `error` slug, not the HTTP status — supplier and payment failures arrive as HTTP 400 with `error: supplier_error` / `payment_error`.

**Webhooks.** MeshHub pushes order, task, payment, and tracking events to your webhook URL in a single signed envelope (`WebhookPayload`, HMAC-SHA256 in `X-MeshHub-Signature`).

The human guide with end-to-end flows lives in `docs/api/tenant-integration.md`.

# paths./api/v1/auth/api-keys/.get.description
List the tenant's API keys (metadata only — the raw key is never returned after creation). Requires the `search` scope.

# paths./api/v1/auth/api-keys/.post.description
Mint a new API key for the tenant with the given scopes. Requires the `search` scope (you must already hold a valid key). The response is the only time the raw key is returned — store it securely; MeshHub keeps only a hash.

# paths./api/v1/auth/api-keys/{id}/.delete.description
Deactivate one API key by id; revoked keys are rejected on the next request. Requires the `search` scope. Returns 204 on success.

# paths./api/v1/auth/trial-keys/.post.description
Mint an isolated trial tenant and a 7-day API key without any prior credentials — no request body, no auth. The trial key carries all scopes so the full search/book/pay flow works, but trial traffic is capped at the trial rate tier and calls hit live suppliers. Minting is limited to 3 keys per rolling hour per client IP plus a small global daily cap, and the IP cap is shared by everyone behind the same egress (cloud IDEs, NAT) — store the key and reuse it for its 7-day lifetime instead of minting per run. On 429 retry after the Retry-After header (may be most of an hour). Trial access may be revoked at any time — contact MeshHub for a production key.

# paths./api/v1/book/.post.description
Create a booking from a search offer. Requires the `execute` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` until terminal (or use the `task_completed` webhook); on success `result` holds the created order, including the `supplier_order_id` used everywhere else (detail, pay, cancel, tracking, extras).

Pass `end_user_external_id` (your own customer id) so the order is linked to the customer, stored personal details fill required fields, and saved payment methods can be charged later. The request body is polymorphic on `category`. Booking does not take payment — call `POST /api/v1/payments/pay-order/` next.

# paths./api/v1/car-rentals/cities/.get.description
Returns the cities that have at least one active rental vehicle matching the optional filters. Use this before /search to narrow a query to a region where the requested vehicle class is offered.

# paths./api/v1/car-rentals/cities/.get.summary
List cities with car-rental availability

# paths./api/v1/car-rentals/cities/.get.parameters[].description
* `manual` - manual
* `automatic` - automatic

# paths./api/v1/car-rentals/pickup-locations/.get.description
Returns the pickup locations served by at least one active rental vehicle. Useful for populating drop-down selectors and for /book payloads that need a concrete pickup_point.

# paths./api/v1/car-rentals/pickup-locations/.get.summary
List pickup points with active inventory

# paths./api/v1/car-rentals/vehicle-classes/.get.description
Returns the distinct vehicle-class names available across active rental suppliers. Optionally restrict to classes available in a given city.

# paths./api/v1/car-rentals/vehicle-classes/.get.summary
List vehicle classes with active inventory

# paths./api/v1/end-users/.get.description
List the tenant's end users, newest first, with limit/offset pagination. Pass `external_id` to look up one customer by your own id — the match is exact and unique, so the page contains at most one result; read its `id` for subsequent update/delete calls. Requires the `search` scope.

# paths./api/v1/end-users/.get.parameters[].description
Optional. Your own id for the customer (the one you send as end_user_external_id when booking). Exact match; returns zero or one result. Example: user-42

# paths./api/v1/end-users/.get.parameters[].description
The initial index from which to return the results.

# paths./api/v1/end-users/.post.description
Tenant-facing CRUD for the EndUser record.

Authenticated via the tenant API key (`X-MH-Key`); the request's
tenant is resolved by the auth backend and used to scope every
query. Reads require the `search` scope, writes require `execute`.

# paths./api/v1/end-users/{id}/.get.description
Tenant-facing CRUD for the EndUser record.

Authenticated via the tenant API key (`X-MH-Key`); the request's
tenant is resolved by the auth backend and used to scope every
query. Reads require the `search` scope, writes require `execute`.

# paths./api/v1/end-users/{id}/.put.description
Tenant-facing CRUD for the EndUser record.

Authenticated via the tenant API key (`X-MH-Key`); the request's
tenant is resolved by the auth backend and used to scope every
query. Reads require the `search` scope, writes require `execute`.

# paths./api/v1/end-users/{id}/.patch.description
Tenant-facing CRUD for the EndUser record.

Authenticated via the tenant API key (`X-MH-Key`); the request's
tenant is resolved by the auth backend and used to scope every
query. Reads require the `search` scope, writes require `execute`.

# paths./api/v1/end-users/{id}/.delete.description
Tenant-facing CRUD for the EndUser record.

Authenticated via the tenant API key (`X-MH-Key`); the request's
tenant is resolved by the auth backend and used to scope every
query. Reads require the `search` scope, writes require `execute`.

# paths./api/v1/health/.get.description
Unauthenticated liveness probe; returns `{"status": "ok"}`. Useful as the first call when wiring up an integration.

# paths./api/v1/offers/details/.post.description
Return full, on-demand detail for one search offer. Requires the `search` scope; **synchronous** — the detail comes back in this response.

Universal across categories: offers with no richer detail return an empty `detail` object. For **hotels**, `detail` carries the hotel `description`, `amenities`, `images`, `address`/`city`/`postal_code` and `web`/`email`/`phone`; pass the offer's `hotel_code` (from the search offer's `extras`) in `extra` so it resolves without re-pricing. Best-effort: if the supplier's content service is briefly unavailable, `detail` comes back empty with a 200.

# paths./api/v1/orders/.get.description
List the requesting tenant's orders, newest first, with limit/offset pagination. Requires the `search` scope; synchronous. Filter by status, category, supplier, customer (your own end-user id), or creation-time range — use this instead of keeping your own order store.

# paths./api/v1/orders/.get.parameters[].description
Optional. Return only orders in this supplier category (exact match), e.g. car_rental, salon, hotel. Example: hotel

# paths./api/v1/orders/.get.parameters[].description
Optional. Lower bound (inclusive) on order creation time, as an ISO 8601 timestamp. Example: 2026-01-01T00:00:00Z

# paths./api/v1/orders/.get.parameters[].description
Optional. Upper bound (inclusive) on order creation time, as an ISO 8601 timestamp. Example: 2026-02-01T00:00:00Z

# paths./api/v1/orders/.get.parameters[].description
Optional. Return only orders belonging to this customer, identified by your own end-user id (the external_id you assigned when creating the end-user) — not MeshHub's UUID. Example: user-42

# paths./api/v1/orders/.get.parameters[].description
Maximum number of orders to return (default 50, max 200).

# paths./api/v1/orders/.get.parameters[].description
Number of orders to skip before the returned page.

# paths./api/v1/orders/.get.parameters[].description
Optional. Return only orders in this lifecycle state. One of: pending, paid, confirmed, cancelled, failed, unknown. Example: confirmed

* `pending` - pending
* `paid` - paid
* `confirmed` - confirmed
* `cancelled` - cancelled
* `failed` - failed
* `unknown` - unknown

# paths./api/v1/orders/.get.parameters[].description
Optional. Return only orders fulfilled by this supplier (exact match on the supplier identifier). Example: hotelbeds

# paths./api/v1/orders/{supplier_order_id}/.get.description
Return the full current state of one order by its `supplier_order_id`. Requires the `search` scope; synchronous. Scoped to the requesting tenant — an unknown or foreign id yields 404.

# paths./api/v1/orders/{supplier_order_id}/cancel/.post.description
Ask the supplier to cancel an order. Requires the `execute` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` until terminal (or use the `task_completed` webhook). A successful task moves the order to `cancelled` and fires the `order_status_changed` webhook; paid orders are refunded where the supplier allows it.

# paths./api/v1/orders/{supplier_order_id}/cancel/.post.requestBody.content.application/json.examples.EmptyExtra(noPer-categoryFieldsToday).summary
Empty extra (no per-category fields today)

# paths./api/v1/orders/{supplier_order_id}/extras/.post.description
Fetch what can still be added to an existing order (seats, bags, PUDO points, ...). Requires the `search` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` until terminal; `result` holds the extras catalogue (see `ExtrasInfo`). Apply chosen extras with `POST .../extras/apply/`.

# paths./api/v1/orders/{supplier_order_id}/extras/.post.requestBody.content.application/json.examples.EmptyExtra(noPer-categoryFieldsToday).summary
Empty extra (no per-category fields today)

# paths./api/v1/orders/{supplier_order_id}/extras/apply/.post.description
Attach chosen catalogue items to an existing order by id — send `extra.items` entries whose `id` comes verbatim from the get-extras `items` list (or a seat's `item_id` in `seat_map`). Requires the `execute` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` until terminal; `result` holds the updated totals (see `ExtrasResult`). The body shape depends on the order's category — see the examples.

# paths./api/v1/orders/{supplier_order_id}/extras/apply/.post.requestBody.content.application/json.examples.Travel—Seats+Bag,ByCatalogueItemId.summary
Travel — seats + bag, by catalogue item id

# paths./api/v1/orders/{supplier_order_id}/timeline/.get.description
Return the immutable status-transition log of one order, oldest first. Requires the `search` scope; synchronous. Useful for rendering an order-history view; live changes are also pushed via the `order_status_changed` webhook.

# paths./api/v1/orders/{supplier_order_id}/tracking/.post.description
Fetch the latest fulfilment/tracking state from the supplier (e.g. parcel movement for shipping). Requires the `search` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` until terminal; `result` holds the tracking payload. Supplier-initiated updates are also pushed via the `tracking_updated` webhook.

# paths./api/v1/orders/{supplier_order_id}/tracking/.post.requestBody.content.application/json.examples.EmptyExtra(noPer-categoryFieldsToday).summary
Empty extra (no per-category fields today)

# paths./api/v1/payments/methods/.get.description
Return the active payment methods saved for one end user, with limit/offset pagination. Requires the `search` scope; synchronous. A method appears here after the customer completes a first-time checkout.

# paths./api/v1/payments/methods/.get.parameters[].description
Required. Your own id for the customer (the external_id from /api/v1/end-users/). Example: user-42

# paths./api/v1/payments/methods/.get.parameters[].description
Maximum number of items to return (default 50, max 200).

# paths./api/v1/payments/methods/.get.parameters[].description
Number of items to skip before the returned page.

# paths./api/v1/payments/methods/{method_id}/.delete.description
Deactivate one saved payment method by its id so it can no longer be charged. Requires the `execute` scope; synchronous. Returns 204 on success.

# paths./api/v1/payments/methods/{method_id}/default/.post.description
Make one saved payment method the customer's default; any previous default is cleared automatically. Future server-side charges for the customer use the default method. Requires the `execute` scope; synchronous. Returns the updated method.

# paths./api/v1/payments/pay-order/.post.description
Take payment for a booked order. Requires the `execute` scope; synchronous.

Branch on `requires_action` in the response. `true` (first payment for this customer): open the Revolut checkout with `order_token` (JS widget, preferred) or redirect to `checkout_url`; the card is saved for next time. `false`: the customer's saved payment method was charged server-side and no UI step is needed. Either way the definitive outcome is pushed asynchronously via the `payment_succeeded` / `payment_failed` webhook (and `order_status_changed` to `paid`).

The order must have an end user attached (book with `end_user_external_id`). Payment failures return HTTP 400 with the standard error envelope (`error: payment_error`).

# paths./api/v1/payments/transactions/.get.description
Return the tenant's payment transactions (charges, refunds, setups), optionally narrowed to one order or one customer, with limit/offset pagination. Requires the `search` scope; synchronous.

# paths./api/v1/payments/transactions/.get.parameters[].description
Optional. Only transactions for this customer, by your own end-user id. Example: user-42

# paths./api/v1/payments/transactions/.get.parameters[].description
Maximum number of items to return (default 50, max 200).

# paths./api/v1/payments/transactions/.get.parameters[].description
Number of items to skip before the returned page.

# paths./api/v1/payments/transactions/.get.parameters[].description
Optional. Only transactions for this order. Example: VMT2

# paths./api/v1/payments/webhooks/revolut/.post.description
Inbound webhook delivered by Revolut, not called by tenants. Documented here so the path is part of the published schema; the tenant API key is not required and is not accepted.

# paths./api/v1/salons/cities/.get.description
Returns the cities where at least one active salon-vertical supplier offers a service. Use this before /search to confirm coverage in a target city.

# paths./api/v1/salons/service-categories/.get.description
Returns the distinct service-category labels that active salon suppliers have published. ``service_category`` is free-text, so counts help spot variants.

# paths./api/v1/salons/service-categories/.get.summary
List service categories offered by active salons

# paths./api/v1/salons/staff/.get.description
Returns active salon staff, optionally restricted to a supplier or to staff linked to services in a given category.

# paths./api/v1/salons/staff/.get.summary
List active staff across salon suppliers

# paths./api/v1/search/.post.description
Search for bookable offers in one category, optionally pinned to one supplier. Requires the `search` scope.

Asynchronous by default: returns **202** with a `TaskStatus` envelope — poll `GET /api/v1/tasks/{task_id}/` every ~2s until `status` is terminal, then read the offers from `result.results`, or subscribe to the `task_completed` webhook instead of polling. Sending `X-Execution-Mode: sync` blocks and returns the finished envelope inline (development only).

The request body is polymorphic on `category`; fetch `GET /api/v1/search/schema/?category=...` for the live per-category field contract.

# paths./api/v1/search/schema/.get.description
Returns the fields a tenant should send in the body of ``POST /search/`` for the given ``category``. Combines the envelope-level fields (``supplier_id``, ``currency``, ``meta``) with the per-category ``extra.*`` fields. Each entry includes a ``type``, optional validation hints (``format``, ``choices``, ``minimum``/``maximum``, ...), and a ``required`` flag. The vocabulary is closed: any ``extra`` key not listed here is rejected with a field-level 400. Supplier-specific parameters outside the schema go in the declared ``extra.supplier_data`` object, which is forwarded to the supplier verbatim.

# paths./api/v1/search/schema/.get.parameters[].description
* `beauty_salon` - beauty_salon
* `car_rental` - car_rental
* `car_repair` - car_repair
* `child_care` - child_care
* `dental_clinic` - dental_clinic
* `fitness` - fitness
* `hair_salon` - hair_salon
* `health_clinic` - health_clinic
* `hotel` - hotel
* `housekeeping` - housekeeping
* `massage_center` - massage_center
* `nail_salon` - nail_salon
* `nursing` - nursing
* `pet_care` - pet_care
* `pet_service` - pet_service
* `refurbishment` - refurbishment
* `restaurant` - restaurant
* `shipping` - shipping
* `spa` - spa
* `tennis_court` - tennis_court
* `training` - training
* `travel` - travel

# paths./api/v1/tasks/{task_id}/.get.description
Return the current `TaskStatus` envelope for a task started by an async endpoint (search, book, cancel, tracking, extras). Requires the `search` scope; synchronous.

Poll every ~2 seconds until `status` is terminal (`completed`, `failed`, or `dead`), then read `result` or `error`. Tasks are kept for 30 days. Prefer the `task_completed`/`task_failed` webhooks over long polling in production.

# components.schemas.APIKeyCreate.properties.name.description
Required. Human-readable label for the key, e.g. the app or environment it belongs to. Example: my-shop-production

# components.schemas.APIKeyCreate.properties.scopes.description
Scopes the key is granted. search: read/discovery endpoints (search, orders, tasks, payment methods, transactions). execute: state-changing endpoints (book, cancel, apply extras, pay, revoke). confirm: reserved for future use. Defaults to an empty list, which grants nothing — pass e.g. ["search", "execute"].

# components.schemas.APIKeyCreated.description
Response of key creation — the only time the raw key is shown.

# components.schemas.APIKeyCreated.properties.key.description
The raw API key (mh_ + 80 hex chars). Shown only in this response and stored hashed — save it now. Send it on every request in the X-MH-Key header.

# components.schemas.APIKeyCreated.properties.prefix.description
First characters of the key, for identifying it in lists.

# components.schemas.APIKeyResponse.description
One API key as returned by the list endpoint (never the raw key).

# components.schemas.APIKeyResponse.properties.prefix.description
First characters of the raw key, for identification.

# components.schemas.APIKeyResponse.properties.scopes.description
Scopes granted to the key (search, execute).

# components.schemas.APIKeyResponse.properties.is_active.description
false once revoked; inactive keys are rejected.

# components.schemas.APIKeyResponse.properties.last_used_at.description
When the key last authenticated a request; null if never.

# components.schemas.BookerAddress.description
Booker (primary contact) postal address. Kyte rejects OTA
bookings without it — every field below is required by the supplier
schema, so we enforce them at the gateway boundary.

# components.schemas.BookerAddress.properties.addressLines.description
Required. Booker street address as a list of one or more non-empty lines (e.g. ['10 Downing St']). Required by the supplier for OTA bookings.

# components.schemas.BookerAddress.properties.postalCode.description
Required. Booker postal / ZIP code (e.g. 'SW1A 2AA').

# components.schemas.BookerAddress.properties.countryCode.description
Required. Booker country as an ISO 3166-1 alpha-2 code (e.g. 'GB').

# components.schemas.CarRentalBookEnvelope.properties.supplier_id.description
Required. Supplier that produced the offer — take it from the search result. Example: kyte

# components.schemas.CarRentalBookEnvelope.properties.offer_id.description
Required. Offer identifier from the search result. Offers expire; re-search and use a fresh offer_id if booking fails with an expired-offer error.

# components.schemas.CarRentalBookEnvelope.properties.end_user_external_id.description
Optional but recommended. Your own id for the customer. A new id auto-creates the customer record from the end_user details on first booking; a known id links the order to the existing record, lets stored personal details satisfy required fields, prefills saved per-category booking details, and is needed later to pay with a saved payment method. Example: user-42

# components.schemas.CarRentalBookEnvelope.properties.end_user.description
Optional. Personal details for the customer; merged onto the stored end-user record (created or updated). Fields already stored on the record do not need to be resent.

# components.schemas.CarRentalBookEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, order_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.CarRentalBookEnvelope.properties.quoted_price.description
Optional. The price you displayed to the customer (from the search offer), as a decimal string. MeshHub audit-logs any drift between it and the actual booked price. Example: 129.99

# components.schemas.CarRentalBookExtra.description
``extra`` body for car-rental /book. Strict — unknown keys → 400.

``date_from`` / ``date_to`` (both required) define the rental
period; ``contact`` is an optional free-form block.

# components.schemas.CarRentalBookExtra.properties.date_from.description
Required. Date the customer wants to pick up the car, as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-06-10'). Must be on or before date_to. No time component.

# components.schemas.CarRentalBookExtra.properties.date_to.description
Required. Date the customer will return the car, as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-06-14'). Must be on or after date_from. No time component.

# components.schemas.CarRentalBookExtra.properties.contact.description
Optional free-form contact block forwarded to the supplier and stored on the order for audit.

# components.schemas.CarRentalBookExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Stored verbatim on the order's raw payload for audit.

# components.schemas.CarRentalSearchEnvelope.description
Unified search request.

Required: ``category``. Optional: ``supplier_id`` (omit to fan out
across every supplier in the category), ``currency`` (default EUR),
and ``extra`` (per-category parameters validated by the matching
serializer in :mod:`meshhub.gateway.extras`).

# components.schemas.CarRentalSearchEnvelope.properties.supplier_id.description
Optional. Restrict the search to one supplier (e.g. kyte, ecoparcel). Omit to fan out across every supplier in the category and get merged results.

# components.schemas.CarRentalSearchEnvelope.properties.currency.description
Optional. Preferred ISO 4217 currency for offer prices. Defaults to EUR. This is a preference, not a guarantee: MeshHub does not convert currencies and some suppliers always quote in their own settlement currency (flights are priced in the departure airport's local currency). Always read each offer's currency field. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.CarRentalSearchEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, user_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.CarRentalSearchExtra.description
``extra`` body for car-rental /search. Strict — unknown keys → 400.

# components.schemas.CarRentalSearchExtra.properties.query.description
Free-text end-user intent (e.g. 'cheap automatic for the airport with 3 kids'). When the catalogue is embedded, results are ranked by semantic similarity then price; otherwise falls back to case-insensitive substring matching.

# components.schemas.CarRentalSearchExtra.properties.vehicle_class.description
Supplier-defined vehicle class (e.g. 'Compact').

# components.schemas.CarRentalSearchExtra.properties.transmission.description
Required transmission.

* `manual` - Manual
* `automatic` - Automatic

# components.schemas.CarRentalSearchExtra.properties.city.description
Pickup city; matches the vehicle's locations.

# components.schemas.CarRentalSearchExtra.properties.pickup_point.description
Specific pickup point name within a city.

# components.schemas.CarRentalSearchExtra.properties.currency.description
Restrict to vehicles priced in this currency.

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.CarRentalSearchExtra.properties.unlimited_mileage.description
Restrict to vehicles with (or without) unlimited mileage.

# components.schemas.CarRentalSearchExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Catalogue-based suppliers ignore it when matching; it is kept for audit.

# components.schemas.ContactInformation.description
Travel passenger contact info. Clients send `phone` as a single
E.164 string (e.g. `+447700000000`); validate_phone parses it into
the airline-friendly `[{countryCode, number, type: "Mobile"}]`
list shape that GDSes expect.

# components.schemas.ContactInformation.properties.email.description
Required. Passenger contact email address (e.g. 'ada@example.com').

# components.schemas.ContactInformation.properties.phone.description
Required. Passenger contact phone in E.164 format, including the country code (e.g. '+447700900123').

# components.schemas.CurrencyEnum.description
* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.EndUser.properties.external_id.description
Your own id for the customer, from your auth system. Unique per tenant; referenced everywhere else as end_user_external_id. Example: user-42

# components.schemas.EndUser.properties.date_of_birth.description
Date of birth as YYYY-MM-DD; required by some suppliers (e.g. flights). Example: 1990-04-21

# components.schemas.EndUser.properties.gender.description
Gender, for suppliers that require it.

* `male` - Male
* `female` - Female
* `other` - Other
* `unspecified` - Unspecified

# components.schemas.EndUser.properties.title.description
Honorific title (mr, ms, ...).

* `mr` - Mr
* `mrs` - Mrs
* `ms` - Ms
* `miss` - Miss
* `mstr` - Master

# components.schemas.EndUser.properties.address_lines.description
Street address as a list of lines. Example: ["Gedimino pr. 1", "Apt 2"]

# components.schemas.EndUser.properties.extra_info.description
Free-form structured field for supplier-specific extras

# components.schemas.EndUser.properties.booking_profile.description
Per-category saved booking details, keyed by category (e.g. travel). Merged under extra on subsequent bookings for this customer so known fields need not be resent; keys sent at book time always win.

# components.schemas.EndUser.properties.saved_payment_method_ref.description
PSP reference of the customer's saved payment method; managed by MeshHub after a first checkout. Leave empty.

# components.schemas.EndUserDetail.description
Compact customer block nested in order responses.

# components.schemas.EndUserDetail.properties.external_id.description
Your own id for the customer. Example: user-42

# components.schemas.EndUserGender.description
* `male` - Male
* `female` - Female
* `other` - Other
* `unspecified` - Unspecified

# components.schemas.EndUserPayload.description
Personal-info block accepted by the gateway book endpoint.

Merged onto the EndUser record (create or update). Minimum-required
fields are enforced at the request level — see BookRequestSerializer.

# components.schemas.EndUserPayload.properties.first_name.description
Customer's given name. Letters, spaces, hyphens and apostrophes only. Example: Ada

# components.schemas.EndUserPayload.properties.last_name.description
Customer's family name. Letters, spaces, hyphens and apostrophes only. Example: Lovelace

# components.schemas.EndUserPayload.properties.email.description
Contact email; suppliers may send confirmations to it.

# components.schemas.EndUserPayload.properties.phone.description
Contact phone in E.164 format. Example: +37060000000

# components.schemas.EndUserPayload.properties.date_of_birth.description
Date of birth as YYYY-MM-DD. Some suppliers (e.g. flight booking) require it. Example: 1990-04-21

# components.schemas.EndUserPayload.properties.gender.description
Gender as expected by suppliers that require it.

* `male` - Male
* `female` - Female
* `other` - Other
* `unspecified` - Unspecified

# components.schemas.EndUserPayload.properties.title.description
Honorific title (mr, ms, ...); some suppliers need it.

* `mr` - Mr
* `mrs` - Mrs
* `ms` - Ms
* `miss` - Miss
* `mstr` - Master

# components.schemas.EndUserPayload.properties.address_lines.description
Street address as a list of lines. Example: ["Gedimino pr. 1", "Apt 2"]

# components.schemas.EndUserPayload.properties.address_country.description
ISO 3166-1 alpha-2 country code. Example: LT

# components.schemas.EndUserPayload.properties.extra_info.description
Free-form structured blob for supplier-specific personal details not covered by the named fields.

# components.schemas.EndUserTitle.description
* `mr` - Mr
* `mrs` - Mrs
* `ms` - Ms
* `miss` - Miss
* `mstr` - Master

# components.schemas.ErrorEnum.description
* `validation_error` - validation_error
* `supplier_error` - supplier_error
* `supplier_timeout` - supplier_timeout
* `payment_error` - payment_error
* `authentication_failed` - authentication_failed
* `permission_denied` - permission_denied
* `not_found` - not_found
* `rate_limit_exceeded` - rate_limit_exceeded
* `internal_server_error` - internal_server_error

# components.schemas.FlexibilityEnum.description
* `lowest` - lowest
* `low` - low
* `flexible` - flexible

# components.schemas.GatewayError.description
The single, flat error envelope for every tenant-facing failure
(validation, auth, business, upstream, throttle, internal). Always
carries an ``error`` slug and a ``code``; agents key off the slug
(not the HTTP status) to choose a retry strategy. See the "Errors"
section of tenant-integration.md for the full contract.

# components.schemas.GatewayError.properties.error.description
Family slug — the primary discriminator.

* `validation_error` - validation_error
* `supplier_error` - supplier_error
* `supplier_timeout` - supplier_timeout
* `payment_error` - payment_error
* `authentication_failed` - authentication_failed
* `permission_denied` - permission_denied
* `not_found` - not_found
* `rate_limit_exceeded` - rate_limit_exceeded
* `internal_server_error` - internal_server_error

# components.schemas.GatewayError.properties.code.description
Stable machine code (e.g. VALIDATION_ERROR, BOOK_FAILED, TIMEOUT, CHARGE_DECLINED, NOT_FOUND). See the catalogue in tenant-integration.md.

# components.schemas.GatewayError.properties.fields.description
Field-level detail, present chiefly on validation_error. A flat map of dotted JSON path (e.g. 'extra.origin') to a list of messages; the key is the request-body path to correct. Top-level non-field messages appear under 'non_field_errors'.

# components.schemas.GatewayError.properties.supplier_id.description
Present on supplier_error / supplier_timeout responses.

# components.schemas.HotelBookEnvelope.properties.supplier_id.description
Required. Supplier that produced the offer — take it from the search result. Example: kyte

# components.schemas.HotelBookEnvelope.properties.offer_id.description
Required. Offer identifier from the search result. Offers expire; re-search and use a fresh offer_id if booking fails with an expired-offer error.

# components.schemas.HotelBookEnvelope.properties.end_user_external_id.description
Optional but recommended. Your own id for the customer. A new id auto-creates the customer record from the end_user details on first booking; a known id links the order to the existing record, lets stored personal details satisfy required fields, prefills saved per-category booking details, and is needed later to pay with a saved payment method. Example: user-42

# components.schemas.HotelBookEnvelope.properties.end_user.description
Optional. Personal details for the customer; merged onto the stored end-user record (created or updated). Fields already stored on the record do not need to be resent.

# components.schemas.HotelBookEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, order_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.HotelBookEnvelope.properties.quoted_price.description
Optional. The price you displayed to the customer (from the search offer), as a decimal string. MeshHub audit-logs any drift between it and the actual booked price. Example: 129.99

# components.schemas.HotelBookExtra.description
``extra`` body for hotel /book. Strict — unknown keys → 400.

All fields are optional: when ``guests``/``holder`` are omitted the
connector derives a single adult guest and the holder from the end_user
record. Supplier booking params beyond these go through
``supplier_data``.

# components.schemas.HotelBookExtra.properties.guests.description
One object per room guest. Omit for a single-guest stay taken from the end_user record.

# components.schemas.HotelBookExtra.properties.holder.description
Optional booking holder override ({name, surname}); defaults to the end_user's name.

# components.schemas.HotelBookExtra.properties.remark.description
Optional free-text remark forwarded to the hotel.

# components.schemas.HotelBookExtra.properties.client_reference.description
Optional caller reference stored on the HotelBeds booking (short string).

# components.schemas.HotelBookExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Keys are merged into the top level of the supplier booking payload.

# components.schemas.HotelGuest.properties.roomId.description
1-based room index this guest occupies. Defaults to 1.

# components.schemas.HotelGuest.properties.type.description
Guest type: 'AD' (adult) or 'CH' (child). Defaults to 'AD'.

* `AD` - Adult
* `CH` - Child

# components.schemas.HotelGuest.properties.name.description
Required. Guest given name. Letters, spaces, hyphens and apostrophes only.

# components.schemas.HotelGuest.properties.surname.description
Required. Guest family name. Letters, spaces, hyphens and apostrophes only.

# components.schemas.HotelSearchEnvelope.description
Unified search request.

Required: ``category``. Optional: ``supplier_id`` (omit to fan out
across every supplier in the category), ``currency`` (default EUR),
and ``extra`` (per-category parameters validated by the matching
serializer in :mod:`meshhub.gateway.extras`).

# components.schemas.HotelSearchEnvelope.properties.supplier_id.description
Optional. Restrict the search to one supplier (e.g. kyte, ecoparcel). Omit to fan out across every supplier in the category and get merged results.

# components.schemas.HotelSearchEnvelope.properties.currency.description
Optional. Preferred ISO 4217 currency for offer prices. Defaults to EUR. This is a preference, not a guarantee: MeshHub does not convert currencies and some suppliers always quote in their own settlement currency (flights are priced in the departure airport's local currency). Always read each offer's currency field. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.HotelSearchEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, user_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.HotelSearchExtra.description
``extra`` body for hotel /search. Strict — unknown keys → 400.

Either ``destination`` (a HotelBeds destination code) or ``hotel_codes``
(specific properties) selects what to price for the ``check_in`` /
``check_out`` stay and room occupancy. Forward-compatible HotelBeds
availability params go through ``supplier_data``.

# components.schemas.HotelSearchExtra.properties.destination.description
HotelBeds destination code to search (e.g. 'PMI' for Palma). Provide either 'destination' or 'hotel_codes'.

# components.schemas.HotelSearchExtra.properties.hotel_codes.description
List of specific HotelBeds numeric hotel codes to price. Use instead of 'destination' to target known properties.

# components.schemas.HotelSearchExtra.properties.check_in.description
Required. Check-in date as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-07-10'). Must not be in the past.

# components.schemas.HotelSearchExtra.properties.check_out.description
Required. Check-out date as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-07-14'). Must be after check_in.

# components.schemas.HotelSearchExtra.properties.adults.description
Number of adults in the room. Defaults to 2 when omitted.

# components.schemas.HotelSearchExtra.properties.children.description
Number of children in the room. Defaults to 0; when sending children, also provide 'child_ages'.

# components.schemas.HotelSearchExtra.properties.rooms.description
Number of rooms required. Defaults to 1 when omitted.

# components.schemas.HotelSearchExtra.properties.child_ages.description
Ages of each child at check-in (HotelBeds prices children by age), e.g. [4, 9].

# components.schemas.HotelSearchExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Keys are merged into the top level of the supplier availability payload.

# components.schemas.OfferDetailsRequest.description
Request full on-demand detail for one search offer.

Identifies the offer by ``supplier_id`` + ``category`` + ``offer_id``
(all taken straight from the search result). ``extra`` carries any
category-specific hint the connector needs — for hotels, pass the
``hotel_code`` from the search offer's ``extras``.

# components.schemas.OfferDetailsRequest.properties.supplier_id.description
Required. The offer's supplier, from the search result (e.g. hotelbeds).

# components.schemas.OfferDetailsRequest.properties.category.description
Required. The offer's category, from the search result.

* `shipping` - shipping
* `travel` - travel
* `car_rental` - car_rental
* `hair_salon` - hair_salon
* `massage_center` - massage_center
* `health_clinic` - health_clinic
* `beauty_salon` - beauty_salon
* `spa` - spa
* `fitness` - fitness
* `dental_clinic` - dental_clinic
* `pet_service` - pet_service
* `nail_salon` - nail_salon
* `tennis_court` - tennis_court
* `child_care` - child_care
* `training` - training
* `housekeeping` - housekeeping
* `pet_care` - pet_care
* `nursing` - nursing
* `refurbishment` - refurbishment
* `hotel` - hotel
* `restaurant` - restaurant
* `car_repair` - car_repair

# components.schemas.OfferDetailsRequest.properties.offer_id.description
Required. The offer identifier from the search result.

# components.schemas.OfferDetailsRequest.properties.extra.description
Optional category-specific hints, validated against the category's typed offer-details schema (unknown keys → 400). For hotels, pass {"hotel_code": <code>} from the search offer's extras so the detail can be resolved without re-pricing. For flights, pass {"include_extras": true} to embed the full pre-booking extras catalogue (flat items + seat map, indicative prices) in detail.extras — expect up to ~30s while the airline assembles seat maps, so send it only for the one offer under consideration.

# components.schemas.OfferDetailsResponse.description
Response for the offer-details endpoint.

``detail`` is a free-form, category-specific object (empty for categories
with no detail view). The per-category shape is documented in
docs/api/tenant-integration.md, not enumerated here.

# components.schemas.OfferDetailsResponse.properties.detail.description
Free-form, category-specific detail; empty when the supplier has nothing to show. For flights requested with {"include_extras": true}, contains an "extras" block with the catalogue items in the same flat shape as the post-booking extras endpoint, a "seat_map", and "prices_indicative": true (prices may change until the offer is booked).

# components.schemas.OrderDetail.properties.supplier_id.description
Supplier fulfilling the order. Example: kyte

# components.schemas.OrderDetail.properties.supplier_order_id.description
Order reference used across the whole API (detail, cancel, tracking, extras, pay, webhooks). For API-integrated suppliers it is the id issued by the third party; for self-serviced suppliers it is a 4-char MeshHub short code.

# components.schemas.OrderDetail.properties.status.description
Order lifecycle state: pending, confirmed, paid, cancelled, failed, or unknown. Status changes are also pushed via the order_status_changed webhook.

# components.schemas.OrderDetail.properties.summary.description
One-line human-readable description of what was booked.

# components.schemas.OrderDetail.properties.price.description
Order total as a decimal string. Example: 129.99

# components.schemas.OrderDetail.properties.currency.description
ISO 4217 currency of price. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.OrderDetail.properties.selected_extras.description
Extras attached to the order (empty list when none).

# components.schemas.OrderDetail.properties.expires_at.description
When an unpaid order stops being payable (e.g. a fare hold lapses); null when the order does not expire.

# components.schemas.OrderDetail.properties.end_user_external_id.description
Your own id of the customer the order belongs to; null when the order was booked without one.

# components.schemas.OrderEvent.description
One entry in an order's status timeline (immutable event log).

# components.schemas.OrderEvent.properties.from_status.description
Status before the transition; empty for the first event.

# components.schemas.OrderEvent.properties.to_status.description
Status after the transition. Example: paid

# components.schemas.OrderEvent.properties.detail.description
Structured context for the transition (e.g. failure reason); empty object when there is none.

# components.schemas.OrderEvent.properties.created_at.description
When the transition happened (ISO 8601).

# components.schemas.OrderExtraEnvelope.description
Shared body shape for order-scoped ops.

The order's stored category decides which schema validates
``extra``; that branching is runtime-only, so OpenAPI shows the
union of valid shapes as :class:`OpenApiExample` blocks.

# components.schemas.OrderSummary.description
Lightweight order row for the list endpoint. The per-order detail
endpoint returns the full ``OrderDetail`` (nested end-user, extras).

# components.schemas.OrderSummary.properties.supplier_id.description
Supplier fulfilling the order. Example: kyte

# components.schemas.OrderSummary.properties.supplier_order_id.description
Order reference; use it with the order detail, timeline, cancel, tracking, extras, and pay endpoints.

# components.schemas.OrderSummary.properties.status.description
Order lifecycle state: pending, confirmed, paid, cancelled, failed, or unknown.

# components.schemas.OrderSummary.properties.category.description
Supplier category of the order. Example: travel

# components.schemas.OrderSummary.properties.summary.description
One-line human-readable description of what was booked.

# components.schemas.OrderSummary.properties.price.description
Order total as a decimal string. Example: 129.99

# components.schemas.OrderSummary.properties.currency.description
ISO 4217 currency of price. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.OrderSummary.properties.end_user_external_id.description
Your own id of the customer the order belongs to; null when the order was booked without one.

# components.schemas.Passenger.description
One passenger object on a travel book request.

``id`` is auto-hydrated by the connector from its search-offer cache —
clients must not send it, so it is intentionally not declared.

# components.schemas.Passenger.properties.firstName.description
Required. Passenger given name as on their travel document. Letters, spaces, hyphens and apostrophes only.

# components.schemas.Passenger.properties.lastName.description
Required. Passenger family name as on their travel document. Letters, spaces, hyphens and apostrophes only.

# components.schemas.Passenger.properties.gender.description
Required. Passenger gender; one of: m, f.

* `male` - male
* `female` - female

# components.schemas.Passenger.properties.title.description
Required. Passenger title; one of: mr, mrs, ms, miss, mstr.

* `mr` - mr
* `mrs` - mrs
* `ms` - ms
* `miss` - miss
* `mstr` - mstr

# components.schemas.Passenger.properties.dateOfBirth.description
Required for flight bookings. Passenger date of birth as an ISO-8601 calendar date YYYY-MM-DD (e.g. '1990-05-21'). The airline rejects a booking without it.

# components.schemas.Passenger.properties.contactInformation.description
Required. Passenger contact email and phone.

# components.schemas.PassengerTitleEnum.description
* `mr` - mr
* `mrs` - mrs
* `ms` - ms
* `miss` - miss
* `mstr` - mstr

# components.schemas.PatchedEndUser.properties.external_id.description
Your own id for the customer, from your auth system. Unique per tenant; referenced everywhere else as end_user_external_id. Example: user-42

# components.schemas.PatchedEndUser.properties.date_of_birth.description
Date of birth as YYYY-MM-DD; required by some suppliers (e.g. flights). Example: 1990-04-21

# components.schemas.PatchedEndUser.properties.gender.description
Gender, for suppliers that require it.

* `male` - Male
* `female` - Female
* `other` - Other
* `unspecified` - Unspecified

# components.schemas.PatchedEndUser.properties.title.description
Honorific title (mr, ms, ...).

* `mr` - Mr
* `mrs` - Mrs
* `ms` - Ms
* `miss` - Miss
* `mstr` - Master

# components.schemas.PatchedEndUser.properties.address_lines.description
Street address as a list of lines. Example: ["Gedimino pr. 1", "Apt 2"]

# components.schemas.PatchedEndUser.properties.extra_info.description
Free-form structured field for supplier-specific extras

# components.schemas.PatchedEndUser.properties.booking_profile.description
Per-category saved booking details, keyed by category (e.g. travel). Merged under extra on subsequent bookings for this customer so known fields need not be resent; keys sent at book time always win.

# components.schemas.PatchedEndUser.properties.saved_payment_method_ref.description
PSP reference of the customer's saved payment method; managed by MeshHub after a first checkout. Leave empty.

# components.schemas.PayOrder.properties.supplier_order_id.description
Required. The order to pay, by the supplier_order_id returned from booking. The order must have an end user attached. Example: VMT2

# components.schemas.PayOrderResponse.description
Two-shape response of pay-order; branch on ``requires_action``.

``requires_action=true``: first-time payment — drive the Revolut
checkout with ``order_token`` (preferred, JS widget) or redirect to
``checkout_url``. ``requires_action=false``: a saved payment method
was charged server-side; nothing to render.

# components.schemas.PayOrderResponse.properties.requires_action.description
true: customer interaction needed — open the Revolut checkout using order_token (JS widget) or checkout_url (redirect); the card is saved for future payments. false: the saved payment method was charged server-side, no UI step required. In both cases the final outcome arrives via the payment_succeeded/payment_failed webhook.

# components.schemas.PayOrderResponse.properties.order_token.description
Revolut order token for the JS Checkout widget; present only when requires_action is true.

# components.schemas.PayOrderResponse.properties.checkout_url.description
Hosted Revolut checkout page URL (redirect alternative to the widget); present only when requires_action is true.

# components.schemas.PaymentMethod.description
A payment method saved for an end user after a completed
first-time checkout.

# components.schemas.PaymentMethod.properties.id.description
Payment-method id; use it to revoke the method.

# components.schemas.PaymentMethod.properties.last4.description
Last four digits of the card number. Example: 4242

# components.schemas.PaymentMethod.properties.exp_month.description
Card expiry month (1-12); null when unknown.

# components.schemas.PaymentMethod.properties.exp_year.description
Card expiry year (4 digits); null when unknown.

# components.schemas.PaymentMethod.properties.is_default.description
Whether this method is charged for saved-method payments.

# components.schemas.PaymentMethod.properties.status.description
Method state; only active methods are listed/chargeable.

# components.schemas.SalonBookEnvelope.properties.category.description
Required. Supplier category of the offer being booked; must match the category used in the search. Example: travel

* `shipping` - shipping
* `travel` - travel
* `car_rental` - car_rental
* `hair_salon` - hair_salon
* `massage_center` - massage_center
* `health_clinic` - health_clinic
* `beauty_salon` - beauty_salon
* `spa` - spa
* `fitness` - fitness
* `dental_clinic` - dental_clinic
* `pet_service` - pet_service
* `nail_salon` - nail_salon
* `tennis_court` - tennis_court
* `child_care` - child_care
* `training` - training
* `housekeeping` - housekeeping
* `pet_care` - pet_care
* `nursing` - nursing
* `refurbishment` - refurbishment
* `hotel` - hotel
* `restaurant` - restaurant
* `car_repair` - car_repair

# components.schemas.SalonBookEnvelope.properties.supplier_id.description
Required. Supplier that produced the offer — take it from the search result. Example: kyte

# components.schemas.SalonBookEnvelope.properties.offer_id.description
Required. Offer identifier from the search result. Offers expire; re-search and use a fresh offer_id if booking fails with an expired-offer error.

# components.schemas.SalonBookEnvelope.properties.end_user_external_id.description
Optional but recommended. Your own id for the customer. A new id auto-creates the customer record from the end_user details on first booking; a known id links the order to the existing record, lets stored personal details satisfy required fields, prefills saved per-category booking details, and is needed later to pay with a saved payment method. Example: user-42

# components.schemas.SalonBookEnvelope.properties.end_user.description
Optional. Personal details for the customer; merged onto the stored end-user record (created or updated). Fields already stored on the record do not need to be resent.

# components.schemas.SalonBookEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, order_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.SalonBookEnvelope.properties.quoted_price.description
Optional. The price you displayed to the customer (from the search offer), as a decimal string. MeshHub audit-logs any drift between it and the actual booked price. Example: 129.99

# components.schemas.SalonBookExtra.description
``extra`` body for salon /book. Strict — unknown keys → 400.

``appointment_at`` (required) tells the supplier when the customer
wants the service; ``contact`` is an optional free-form block.

# components.schemas.SalonBookExtra.properties.appointment_at.description
Required. Date and time the customer wants the service (e.g. haircut, spa, massage), as an ISO-8601 date-time 'YYYY-MM-DDTHH:MM:SS' (e.g. '2026-06-10T14:30:00'; seconds optional, so '2026-06-10T14:30' from an HTML datetime-local input is accepted). Use the salon's local time; include a UTC offset only if known.

# components.schemas.SalonBookExtra.properties.contact.description
Optional free-form contact block forwarded to the supplier and stored on the order for audit.

# components.schemas.SalonBookExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Stored verbatim on the order's raw payload for audit.

# components.schemas.SalonSearchEnvelope.description
Unified search request.

Required: ``category``. Optional: ``supplier_id`` (omit to fan out
across every supplier in the category), ``currency`` (default EUR),
and ``extra`` (per-category parameters validated by the matching
serializer in :mod:`meshhub.gateway.extras`).

# components.schemas.SalonSearchEnvelope.properties.category.description
Required. Supplier category to search, e.g. travel, shipping, car_rental, hotel, hair_salon. Drives which fields extra accepts — fetch GET /api/v1/search/schema/?category=<value> for the live per-category contract. Example: travel

* `shipping` - shipping
* `travel` - travel
* `car_rental` - car_rental
* `hair_salon` - hair_salon
* `massage_center` - massage_center
* `health_clinic` - health_clinic
* `beauty_salon` - beauty_salon
* `spa` - spa
* `fitness` - fitness
* `dental_clinic` - dental_clinic
* `pet_service` - pet_service
* `nail_salon` - nail_salon
* `tennis_court` - tennis_court
* `child_care` - child_care
* `training` - training
* `housekeeping` - housekeeping
* `pet_care` - pet_care
* `nursing` - nursing
* `refurbishment` - refurbishment
* `hotel` - hotel
* `restaurant` - restaurant
* `car_repair` - car_repair

# components.schemas.SalonSearchEnvelope.properties.supplier_id.description
Optional. Restrict the search to one supplier (e.g. kyte, ecoparcel). Omit to fan out across every supplier in the category and get merged results.

# components.schemas.SalonSearchEnvelope.properties.currency.description
Optional. Preferred ISO 4217 currency for offer prices. Defaults to EUR. This is a preference, not a guarantee: MeshHub does not convert currencies and some suppliers always quote in their own settlement currency (flights are priced in the departure airport's local currency). Always read each offer's currency field. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.SalonSearchEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, user_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.SalonSearchExtra.description
``extra`` body for salon /search. Strict — unknown keys → 400.

# components.schemas.SalonSearchExtra.properties.query.description
Free-text end-user intent (e.g. 'balayage for thin hair'). When the catalogue is embedded, results are ranked by semantic similarity then price; otherwise falls back to case-insensitive substring matching.

# components.schemas.SalonSearchExtra.properties.city.description
Salon city; exact match (case-insensitive).

# components.schemas.SalonSearchExtra.properties.service_category.description
Service-category tag (free text on the supplier side, e.g. 'haircut', 'colour'). Substring match.

# components.schemas.SalonSearchExtra.properties.staff_name.description
Filter to services performed by the named stylist.

# components.schemas.SalonSearchExtra.properties.max_duration_minutes.description
Only services lasting at most this many minutes.

# components.schemas.SalonSearchExtra.properties.max_price.description
Only services priced at or below this amount.

# components.schemas.SalonSearchExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Catalogue-based suppliers ignore it when matching; it is kept for audit.

# components.schemas.ScopesEnum.description
* `confirm` - confirm
* `execute` - execute
* `search` - search

# components.schemas.SearchPassenger.description
One traveller in a flight *search* — pricing input only.

Search needs the traveller count and ages to price a trip; passenger
identities are collected later at /book. Send ``type``
(adult/child/infant) or an explicit ``age``; the supplier derives the
fare class from age, so a ``type`` maps to a representative age
(adult -> 30, child -> 8, infant -> 1).

# components.schemas.SearchPassenger.properties.type.description
Optional. Traveller type — one of adult, child, infant. When 'age' is omitted it sets a representative age for pricing (adult -> 30, child -> 8, infant -> 1). Defaults to adult if neither 'type' nor 'age' is given.

* `adult` - adult
* `child` - child
* `infant` - infant

# components.schemas.SearchPassenger.properties.age.description
Optional. Traveller age in whole years (0-120). Takes precedence over 'type'; the airline derives the fare class (adult/child/infant) from it. Use it for accurate child / infant pricing.

# components.schemas.SearchPassengerTypeEnum.description
* `adult` - adult
* `child` - child
* `infant` - infant

# components.schemas.ShippingApplyExtra.description
Reject any input key the serializer does not declare.

DRF normally drops unknown input fields silently. For typed
``extra`` schemas we want a 400 instead so tenants notice typos
and AI agents discover the vocabulary via ``/search/schema/``.
Since MA-419 this is the uniform policy for every category and op;
supplier-specific forward-compat params go through the declared
``supplier_data`` field instead of undeclared passthrough keys.

# components.schemas.ShippingApplyItem.description
One entry inside ``extra.items`` — a catalogue item the caller is
applying to an existing order (MA-414). Items are addressed purely
by the ``id`` from the get-extras catalogue (or a seat's ``item_id``
in ``seat_map``); the id's embedded key is validated against the
category's whitelist.

# components.schemas.ShippingApplyItem.properties.id.description
Required. The item id copied verbatim from the get-extras catalogue's items[].id — or, for an exact seat, the seat's item_id from seat_map. Do not construct ids yourself.

# components.schemas.ShippingApplyItem.properties.passenger.description
Optional. 0-based index into the order's passenger list identifying which traveller the item is for (items with per_passenger=true). Defaults to 0 (the lead passenger).

# components.schemas.ShippingApplyItem.properties.quantity.description
Optional. Units of this item to add. Defaults to 1. Note quantity *tiers* (e.g. '2 checked bags') are separate items — pick the tier item instead of raising quantity.

# components.schemas.ShippingBookEnvelope.properties.supplier_id.description
Required. Supplier that produced the offer — take it from the search result. Example: kyte

# components.schemas.ShippingBookEnvelope.properties.offer_id.description
Required. Offer identifier from the search result. Offers expire; re-search and use a fresh offer_id if booking fails with an expired-offer error.

# components.schemas.ShippingBookEnvelope.properties.end_user_external_id.description
Optional but recommended. Your own id for the customer. A new id auto-creates the customer record from the end_user details on first booking; a known id links the order to the existing record, lets stored personal details satisfy required fields, prefills saved per-category booking details, and is needed later to pay with a saved payment method. Example: user-42

# components.schemas.ShippingBookEnvelope.properties.end_user.description
Optional. Personal details for the customer; merged onto the stored end-user record (created or updated). Fields already stored on the record do not need to be resent.

# components.schemas.ShippingBookEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, order_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.ShippingBookEnvelope.properties.quoted_price.description
Optional. The price you displayed to the customer (from the search offer), as a decimal string. MeshHub audit-logs any drift between it and the actual booked price. Example: 129.99

# components.schemas.ShippingBookExtra.description
`extra` body for shipping /book. Strict — unknown keys → 400.

Declares the shipper/consignee blocks, the parcel list, and the
shipment options connectors read; supplier params beyond these go
through ``supplier_data``.

# components.schemas.ShippingBookExtra.properties.shipper_details.description
Sender contact and address block. Required by shipping suppliers to create the shipment.

# components.schemas.ShippingBookExtra.properties.consignee_details.description
Recipient contact and address block. Required by shipping suppliers to create the shipment.

# components.schemas.ShippingBookExtra.properties.parcels.description
Parcels in the shipment, one object per physical parcel. Optional: when omitted, a single parcel is built from the offer's quoted weight with minimal dimensions and a nominal declared value — supply the real parcels for accurate pricing, insurance, and customs.

# components.schemas.ShippingBookExtra.properties.package_type.description
Optional. Supplier package type (e.g. 'parcel', 'pallet'). Defaults to 'parcel' when omitted.

# components.schemas.ShippingBookExtra.properties.transportation_type.description
Optional. Supplier transportation type (e.g. 'door_to_door', 'terminal_to_terminal'). Defaults to 'door_to_door' when omitted.

# components.schemas.ShippingBookExtra.properties.confirm_and_pay.description
Optional. Whether the supplier should confirm and charge the shipment immediately (defaults to true). Send false to create the order without triggering supplier-side payment. One-off per booking — never saved to the customer's booking profile.

# components.schemas.ShippingBookExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Keys are merged into the supplier's order entry for this shipment.

# components.schemas.ShippingDimensions.description
Parcel dimensions on a shipping search (``extra.dimensions``).

# components.schemas.ShippingDimensions.properties.w.description
Required. Parcel width in centimetres; must be > 0.

# components.schemas.ShippingDimensions.properties.h.description
Required. Parcel height in centimetres; must be > 0.

# components.schemas.ShippingDimensions.properties.l.description
Required. Parcel length in centimetres; must be > 0.

# components.schemas.ShippingParcel.description
One physical parcel in a shipping booking (``extra.parcels[]``).

Field names mirror what shipping connectors forward verbatim to the
supplier's order payload.

# components.schemas.ShippingParcel.properties.description.description
Required. What the parcel contains (customs/courier description, e.g. 'Books').

# components.schemas.ShippingParcel.properties.weight.description
Required. Parcel weight in kilograms; must be > 0.

# components.schemas.ShippingParcel.properties.value.description
Required. Declared value of the contents (decimal, offer currency), used for insurance/customs. Example: 25.00

# components.schemas.ShippingPartyDetails.description
Shipper / consignee contact + address block for shipping bookings.

Same shape on both ends of the parcel, so one serializer powers both
``extra.shipper_details`` and ``extra.consignee_details``. Field names
mirror what shipping connectors read off ``params.extra`` verbatim.

# components.schemas.ShippingPartyDetails.properties.contact_name.description
Required. Full name of the contact at this end of the parcel. A person's name — letters, spaces, hyphens and apostrophes only (not a company name).

# components.schemas.ShippingPartyDetails.properties.phone.description
Required. Contact phone in E.164 format, including the country code (e.g. '+447700900123').

# components.schemas.ShippingPartyDetails.properties.email_address.description
Required. Contact email address (e.g. 'ops@example.com').

# components.schemas.ShippingPartyDetails.properties.address.description
Required. Street address line for pickup/delivery.

# components.schemas.ShippingPartyDetails.properties.country.description
Required. Country as an ISO 3166-1 alpha-2 code (e.g. 'LT').

# components.schemas.ShippingSearchEnvelope.description
Unified search request.

Required: ``category``. Optional: ``supplier_id`` (omit to fan out
across every supplier in the category), ``currency`` (default EUR),
and ``extra`` (per-category parameters validated by the matching
serializer in :mod:`meshhub.gateway.extras`).

# components.schemas.ShippingSearchEnvelope.properties.supplier_id.description
Optional. Restrict the search to one supplier (e.g. kyte, ecoparcel). Omit to fan out across every supplier in the category and get merged results.

# components.schemas.ShippingSearchEnvelope.properties.currency.description
Optional. Preferred ISO 4217 currency for offer prices. Defaults to EUR. This is a preference, not a guarantee: MeshHub does not convert currencies and some suppliers always quote in their own settlement currency (flights are priced in the departure airport's local currency). Always read each offer's currency field. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.ShippingSearchEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, user_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.ShippingSearchExtra.description
``extra`` body for shipping /search. Strict — unknown keys → 400.

# components.schemas.ShippingSearchExtra.properties.origin.description
Required. Origin country as an ISO 3166-1 alpha-2 code (e.g. 'LT').

# components.schemas.ShippingSearchExtra.properties.destination.description
Required. Destination country as an ISO 3166-1 alpha-2 code (e.g. 'DE').

# components.schemas.ShippingSearchExtra.properties.weight_kg.description
Required. Total parcel weight in kilograms; must be > 0.

# components.schemas.ShippingSearchExtra.properties.dimensions.description
Optional. Parcel dimensions in centimetres as an object {"w": <width>, "h": <height>, "l": <length>} (e.g. {"w": 30, "h": 20, "l": 40}). When omitted, suppliers quote for a minimal 1x1x1 cm parcel — send the real dimensions for accurate pricing.

# components.schemas.ShippingSearchExtra.properties.package_type.description
Optional. Supplier package type (e.g. 'parcel', 'pallet'). Defaults to 'parcel' when omitted.

# components.schemas.ShippingSearchExtra.properties.transportation_type.description
Optional. Supplier transportation type (e.g. 'door_to_door', 'terminal_to_terminal'). Defaults to 'door_to_door' when omitted.

# components.schemas.ShippingSearchExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings.

# components.schemas.StatusEnum.description
* `pending` - Pending
* `running` - Running
* `completed` - Completed
* `failed` - Failed
* `dead` - Dead

# components.schemas.SupplierCategory.description
* `shipping` - shipping
* `travel` - travel
* `car_rental` - car_rental
* `hair_salon` - hair_salon
* `massage_center` - massage_center
* `health_clinic` - health_clinic
* `beauty_salon` - beauty_salon
* `spa` - spa
* `fitness` - fitness
* `dental_clinic` - dental_clinic
* `pet_service` - pet_service
* `nail_salon` - nail_salon
* `tennis_court` - tennis_court
* `child_care` - child_care
* `training` - training
* `housekeeping` - housekeeping
* `pet_care` - pet_care
* `nursing` - nursing
* `refurbishment` - refurbishment
* `hotel` - hotel
* `restaurant` - restaurant
* `car_repair` - car_repair

# components.schemas.TaskStatus.description
Envelope returned by every async endpoint (HTTP 202) and by
``GET /api/v1/tasks/{task_id}/``. Poll the task endpoint every ~2
seconds until ``status`` is terminal (completed, failed, or dead),
then read ``result`` on success or ``error`` on failure.

# components.schemas.TaskStatus.properties.task_id.description
Task identifier. Poll GET /api/v1/tasks/{task_id}/ with this value until the task reaches a terminal status. Example: 018f2a9c-3f6e-7d21-b7c4-9a1e5c2d8b30

# components.schemas.TaskStatus.properties.operation.description
Which gateway operation the task runs, e.g. search, book, cancel, track, get_extras, apply_extras.

# components.schemas.TaskStatus.properties.task_name.description
Internal worker task name; for observability only.

# components.schemas.TaskStatus.properties.status.description
Task lifecycle state. pending: queued, not started. running: executing. completed: finished OK — read result. failed: finished with an error — read error. dead: gave up after exhausting retries — read error. Terminal states are completed, failed, and dead; stop polling once reached.

* `pending` - Pending
* `running` - Running
* `completed` - Completed
* `failed` - Failed
* `dead` - Dead

# components.schemas.TaskStatus.properties.result.description
Operation result, present only when status is completed. The shape depends on operation: search returns {offers: [offer, ...], field_schema: {...}}; book returns the created order; get_extras returns the available extras catalogue; apply_extras returns the updated totals. Null until then.

# components.schemas.TaskStatus.properties.error.description
Failure detail in the standard GatewayError envelope ({error, code, message, ...}), present only when status is failed or dead. Null otherwise.

# components.schemas.TaskStatus.properties.attempts.description
How many execution attempts have been made so far.

# components.schemas.TaskStatus.properties.started_at.description
When execution began; null while still pending.

# components.schemas.TaskStatus.properties.completed_at.description
When the task reached a terminal state; null until then.

# components.schemas.Transaction.description
One payment transaction (charge, refund, or setup) recorded
against the tenant's orders.

# components.schemas.Transaction.properties.supplier_order_id.description
Order the transaction belongs to; null for setup.

# components.schemas.Transaction.properties.amount.description
Amount as a decimal string. Example: 129.99

# components.schemas.Transaction.properties.currency.description
ISO 4217 currency of amount. Example: EUR

# components.schemas.Transaction.properties.status.description
One of pending, succeeded, failed, refunded.

# components.schemas.Transaction.properties.failure_code.description
Machine failure code when status is failed; else empty.

# components.schemas.Transaction.properties.failure_message.description
Human-readable failure detail; empty unless failed.

# components.schemas.Transaction.properties.created_at.description
When the transaction was created (ISO 8601).

# components.schemas.TransmissionEnum.description
* `manual` - Manual
* `automatic` - Automatic

# components.schemas.TravelApplyItem.description
One entry inside ``extra.items`` — a catalogue item the caller is
applying to an existing order (MA-414). Items are addressed purely
by the ``id`` from the get-extras catalogue (or a seat's ``item_id``
in ``seat_map``); the id's embedded key is validated against the
category's whitelist.

# components.schemas.TravelApplyItem.properties.id.description
Required. The item id copied verbatim from the get-extras catalogue's items[].id — or, for an exact seat, the seat's item_id from seat_map. Do not construct ids yourself.

# components.schemas.TravelApplyItem.properties.passenger.description
Optional. 0-based index into the order's passenger list identifying which traveller the item is for (items with per_passenger=true). Defaults to 0 (the lead passenger).

# components.schemas.TravelApplyItem.properties.quantity.description
Optional. Units of this item to add. Defaults to 1. Note quantity *tiers* (e.g. '2 checked bags') are separate items — pick the tier item instead of raising quantity.

# components.schemas.TravelBookEnvelope.properties.supplier_id.description
Required. Supplier that produced the offer — take it from the search result. Example: kyte

# components.schemas.TravelBookEnvelope.properties.offer_id.description
Required. Offer identifier from the search result. Offers expire; re-search and use a fresh offer_id if booking fails with an expired-offer error.

# components.schemas.TravelBookEnvelope.properties.end_user_external_id.description
Optional but recommended. Your own id for the customer. A new id auto-creates the customer record from the end_user details on first booking; a known id links the order to the existing record, lets stored personal details satisfy required fields, prefills saved per-category booking details, and is needed later to pay with a saved payment method. Example: user-42

# components.schemas.TravelBookEnvelope.properties.end_user.description
Optional. Personal details for the customer; merged onto the stored end-user record (created or updated). Fields already stored on the record do not need to be resent.

# components.schemas.TravelBookEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, order_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.TravelBookEnvelope.properties.quoted_price.description
Optional. The price you displayed to the customer (from the search offer), as a decimal string. MeshHub audit-logs any drift between it and the actual booked price. Example: 129.99

# components.schemas.TravelBookExtra.description
`extra` body for travel /book. Strict — unknown keys → 400. The
optional passenger list mirrors the airline-API field names
(firstName, lastName, ...). The ``contact`` block is the booker's
primary postal address and is forwarded to the supplier (Kyte calls
it ``address``). Airline ancillary params go through
``supplier_data``.

# components.schemas.TravelBookExtra.properties.passengers.description
Optional. One object per traveller. Omit for a single-passenger booking taken from the end_user record; send the full list for multi-passenger bookings.

# components.schemas.TravelBookExtra.properties.contact.description
Booker's primary postal address, forwarded to the supplier. Required by some suppliers (e.g. Kyte) — send it for those.

# components.schemas.TravelBookExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings. Keys are merged into the top level of the supplier booking payload.

# components.schemas.TravelSearchEnvelope.description
Unified search request.

Required: ``category``. Optional: ``supplier_id`` (omit to fan out
across every supplier in the category), ``currency`` (default EUR),
and ``extra`` (per-category parameters validated by the matching
serializer in :mod:`meshhub.gateway.extras`).

# components.schemas.TravelSearchEnvelope.properties.supplier_id.description
Optional. Restrict the search to one supplier (e.g. kyte, ecoparcel). Omit to fan out across every supplier in the category and get merged results.

# components.schemas.TravelSearchEnvelope.properties.currency.description
Optional. Preferred ISO 4217 currency for offer prices. Defaults to EUR. This is a preference, not a guarantee: MeshHub does not convert currencies and some suppliers always quote in their own settlement currency (flights are priced in the departure airport's local currency). Always read each offer's currency field. Example: EUR

* `USD` - USD
* `EUR` - EUR
* `GBP` - GBP

# components.schemas.TravelSearchEnvelope.properties.meta.description
Optional. Free-form tenant observability identifiers (e.g. session_id, user_id). Echoed into MeshHub telemetry; never forwarded to suppliers.

# components.schemas.TravelSearchExtra.description
`extra` body for travel /search. Strict — unknown keys → 400;
airline-specific params the schema does not declare go through
``supplier_data``.

# components.schemas.TravelSearchExtra.properties.origin.description
Required. Departure airport as a 3-letter IATA code (e.g. 'LHR').

# components.schemas.TravelSearchExtra.properties.destination.description
Required. Arrival airport as a 3-letter IATA code (e.g. 'JFK').

# components.schemas.TravelSearchExtra.properties.date_from.description
Required. Outbound departure date as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-06-10'). For a return trip, also send 'date_to' (return date) and 'trip_type': 'return'.

# components.schemas.TravelSearchExtra.properties.date_to.description
Optional. Return departure date as an ISO-8601 calendar date YYYY-MM-DD (e.g. '2026-06-17'). Required for a return trip; omit for one-way. Must not be in the past.

# components.schemas.TravelSearchExtra.properties.trip_type.description
Optional. One of 'one_way' or 'return'. Defaults to 'return' when 'date_to' is present and 'one_way' otherwise, so you usually only need 'date_to'.

* `one_way` - one_way
* `return` - return

# components.schemas.TravelSearchExtra.properties.cabin.description
Optional. Cabin class to shop, using the supplier's own vocabulary (e.g. 'economy', 'business'). Defaults to 'economy' when omitted.

# components.schemas.TravelSearchExtra.properties.airlines.description
Optional. Comma-separated IATA airline codes to restrict the search to (e.g. 'FR' or 'FR,W6'). Omit to search all carriers.

# components.schemas.TravelSearchExtra.properties.flexibility.description
Optional. Fare flexibility tier to shop. One of 'lowest', 'low', 'flexible'. **Omit this to shop all three tiers and get the merged offers back** — that is the recommended default, because it surfaces the cheapest fare alongside the flexible one. Pass a single tier only when you deliberately want to restrict the search to it. 'lowest' returns the cheapest Basic fares (which avoid bundles that force a mandatory seat reservation on some carriers, e.g. Ryanair); 'flexible' returns the most changeable fares. Higher tiers cost more but include more inclusive baggage/seat allowances. Each returned offer is labelled with the tier it came from in its 'extras.flexibility'.

* `lowest` - lowest
* `low` - low
* `flexible` - flexible

# components.schemas.TravelSearchExtra.properties.passengers.description
Optional. Travellers to price — one object per passenger; this is how you search for more than one passenger. Each object takes a 'type' (adult/child/infant) or an explicit 'age'. A bare integer (e.g. 3) is shorthand for that many adults. Defaults to a single adult. Send the same number of passengers, in the same order, at /book.

# components.schemas.TravelSearchExtra.properties.supplier_data.description
Optional. Free-form object of supplier-specific parameters forwarded to the supplier verbatim — MeshHub does not validate or transform its keys, and the supplier may reject ones it does not understand. Use it only for parameters the supplier's own API documents and this schema does not declare (e.g. {"fareBrand": "FLEXI"}). Never reused across bookings.

# components.schemas.TrialKeyCreated.description
Response of trial-key creation — the only time the raw key is shown.

# components.schemas.TrialKeyCreated.properties.key.description
The raw trial API key (mh_ + 80 hex chars). Shown only in this response and stored hashed — save it now. Send it on every request in the X-MH-Key header.

# components.schemas.TrialKeyCreated.properties.tenant_id.description
Id of the isolated trial tenant the key belongs to. Trial tenants are rate-limited to the trial tier and may be deactivated by MeshHub at any time.

# components.schemas.TrialKeyCreated.properties.expires_at.description
When the key stops working (ISO 8601; 7 days after creation). Contact MeshHub for a production key before this date.

# components.schemas.TrialKeyCreated.properties.scopes.description
Scopes granted to the trial key: search, execute, confirm — the full search/book/pay flow works, but calls hit live suppliers.

# components.schemas.TripTypeEnum.description
* `one_way` - one_way
* `return` - return

# components.schemas.WebhookPayload.description
Body MeshHub POSTs to the tenant's webhook URL. Every event shares this one envelope, signed with `X-MeshHub-Signature: HMAC_SHA256(webhook_secret, raw_body)` (hex digest). Switch on `event_type`; event-specific fields live in `details`. See §7 of the integration guide for the full event catalogue and per-event `details` shapes.

# components.schemas.WebhookPayload.properties.supplier_order_id.description
Order reference when the event concerns an order; empty for events with no order (e.g. a search task). For API-integrated suppliers (Kyte, EcoParcel) it is the id issued by the third party; for self-serviced suppliers it is a 4-char MeshHub-generated short code.

# components.schemas.WebhookPayload.properties.details.description
Event-specific fields; treat unknown keys as forward-compatible additions. `order_status_changed`: empty. `task_*`: `task_id`, `task_kind` (plus `error` on failure). `payment_*`: `transaction_id`, `amount`, `currency` (plus `original_transaction_id` on refund; `failure_code`/`failure_message` on failure). `tracking_updated`: a `tracking` object with `status`, `parcels`, and `estimated_delivery_date`.

# components.schemas.WebhookPayload.properties.timestamp.description
ISO-8601 timestamp the event was generated.

# components.schemas._SupplierUpstreamError.description
Optional parsed body of an upstream supplier HTTP error.

# components.schemas._SupplierUpstreamError.properties.body.description
Set when the upstream body is not in MeshHub's canonical {error: {code, message, details}} shape — the raw body is preserved verbatim.