{"openapi":"3.1.0","info":{"title":"Plexe API","version":"1.8.45","description":"## Introduction\n\nMap a hotel's supplier offers into room categories. Send one hotel's complete inventory; receive room groups, their original offers and any unresolved rooms.\n\nUse your existing API key as a bearer token. Set `PLEXE_KEY` in your environment, then try:\n\n```bash\ncurl --fail-with-body -X POST https://model.plexe.ai/v1/room-mappings \\\n  -H \"Authorization: Bearer $PLEXE_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"hotelName\": \"Taj Lands End, Mumbai\",\n    \"city\": \"Mumbai\",\n    \"country\": \"IN\",\n    \"mode\": \"standard\",\n    \"roomRates\": [\n      {\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"supplier\": \"supplier-a\"},\n      {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\", \"supplier\": \"supplier-b\"}\n    ]\n  }'\n```\n\nSend `hotelId` when you know the Plexe catalogue ID. Otherwise, include the hotel name, city and country. Keep supplier room names unchanged and give each offer a unique `index`. Include room-specific `beds`, `view` and `description` when available; send `smokingAllowed` only when explicitly known.\n\n## The room view\n\nBuild your display from `rooms[]`. Each room contains its original supplier offers in `rates[]`.\n\n| Read | Use it for |\n| --- | --- |\n| `rooms[].roomId` and `name` | Identify and display each room. |\n| `rooms[].rates[]` | Retain and select individual supplier offers. |\n| `rooms[].rates[].placement` | Read each offer's mapping status, reason and evidence. |\n| `hotel.resolution` and `notices[]` | Check property matching and conditions that need attention. |\n| `requestId` | Find the saved request in Logs. |\n\nKeep rooms with `tier: \"unresolved\"` visible too. A similarity score is a matching signal, not an accuracy percentage. Preserve your rate IDs when refreshing mappings because a corrected category can change its room ID.\n\nExisting integrations can continue using `groups[]` and `roomRates[]`. Both request shapes are supported on the same endpoint. See [mapping behavior and compatibility](https://model.plexe.ai/docs/guides/mapping) for policies, evidence checks and field details.\n\n## Guides\n\n| I want to… | Read |\n| --- | --- |\n| Choose a policy or understand a mapping | [Mapping behavior](https://model.plexe.ai/docs/guides/mapping) |\n| Map many hotels asynchronously | [Batch processing](https://model.plexe.ai/docs/guides/batches) |\n| Group similar rooms under parent classes | [Room class hierarchy](https://model.plexe.ai/docs/guides/hierarchy) |\n| Recheck low-confidence results and export labels | [Review queue](https://model.plexe.ai/docs/guides/reviews) |\n\nThe endpoint reference below contains the complete request fields, response schemas, examples and error codes.\n\n## Limits\n\n| Limit | Value |\n| --- | --- |\n| Offers per hotel | 2,000 with `hotelId` in standard mode; otherwise 600 distinct room names. Submit the complete inventory together. |\n| Batch upload | Up to 1,000 hotels and 32 MiB; standard mode. |\n| Batch result retention | 7 days; download before `expiresAt`. |\n| Daily requests | Your account quota, shared by single-hotel and batch submissions; resets at 00:00 UTC. |\n\nSee the [batch guide](https://model.plexe.ai/docs/guides/batches#limits) for per-line limits, retries and quota accounting.\n","contact":{"name":"Plexe","url":"https://plexe.ai"}},"servers":[{"url":"https://model.plexe.ai","description":"Sandbox"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Room mapping","description":"Group one hotel's supplier room names into its rooms."},{"name":"Hotel mapping","description":"Match a supplier's hotel record to one Plexe hotel ID, and look hotels up by supplier ID."},{"name":"Batches","description":"Run standard room mapping asynchronously for multiple hotels; seven-day result retention."},{"name":"Feedback","description":"Tell us when a mapping is wrong; the correction is applied to every later call for that hotel."},{"name":"System","description":"Service status."}],"paths":{"/v1/room-mappings/{requestId}/variants":{"get":{"tags":["Room mapping"],"operationId":"getRoomVariants","summary":"Get attribute-aware offer variants","description":"Poll roomVariants.statusUrl using the same key. Refreshes only this inventory from existing attribute jobs; no new model calls, mapping or quota. Replace the complete roomVariants view. Original assignments and prices remain unchanged. Available while the request log is retained. Unfinished expired extraction becomes unavailable and offers stay isolated. See /docs/guides/attributes#variants.","parameters":[{"in":"path","name":"requestId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Variant view; pending responses include Retry-After: 2.","content":{"application/json":{"schema":{"type":"object","required":["requestId","roomVariants"],"properties":{"requestId":{"type":"string"},"roomVariants":{"$ref":"#/components/schemas/RoomVariants"}}}}}},"401":{"description":"Missing or invalid key."},"404":{"description":"Unknown, removed or other-tenant inventory, or variants were not requested."},"409":{"description":"The snapshot uses a no-longer-supported variant version; retain its original response."}}}},"/v1/room-mapping-batches":{"post":{"tags":["Batches"],"operationId":"createRoomMappingBatch","summary":"Submit a JSONL batch","description":"Upload raw UTF-8 JSONL, not multipart form data or a JSON array. Each line follows BatchInputRecord. Up to 1,000 hotels, 32 MiB per file, 512 KiB per line. Standard mode only; supports optional attributes and hierarchy. Malformed JSON/envelopes and duplicate customIds reject the file. Invalid mapping requests fail individually. Every accepted hotel counts once in usage, including cache hits and invalid requests. The shared daily quota applies unless your account is configured as unlimited. Same key and identical bytes replay the retained job; changed bytes return 409. Five active jobs per tenant; deployment storage caps also apply. Examples are illustrative. Read the Batch room mapping section for retention and cache behavior.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"},"description":"Keep this key and exact file bytes when retrying an uncertain upload."},{"name":"Webhook-Url","in":"header","required":false,"schema":{"type":"string","format":"uri"},"description":"Optional https URL to notify when this batch finishes, instead of the key's default webhook. Must resolve to public addresses; signed with your key's webhook secret."}],"requestBody":{"required":true,"content":{"application/x-ndjson":{"schema":{"type":"string","description":"Newline-delimited BatchInputRecord objects; at most 33,554,432 UTF-8 bytes."},"example":"{\"customId\": \"hotel-001\", \"request\": {\"hotelName\": \"Taj Lands End, Mumbai\", \"city\": \"Mumbai\", \"country\": \"IN\", \"roomRates\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"beds\": [{\"type\": \"King\", \"count\": 1}], \"boardBasis\": \"BB\"}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\"}, {\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\"}, {\"index\": 3, \"roomName\": \"Luxury Suite Sea View\"}, {\"index\": 4, \"roomName\": \"Extra Bed Adult\"}], \"mode\": \"standard\"}}\n{\"customId\": \"hotel-002\", \"request\": {\"hotelName\": \"Taj Lands End, Mumbai\", \"city\": \"Mumbai\", \"country\": \"IN\", \"roomRates\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"beds\": [{\"type\": \"King\", \"count\": 1}], \"boardBasis\": \"BB\"}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\"}, {\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\"}, {\"index\": 3, \"roomName\": \"Luxury Suite Sea View\"}, {\"index\": 4, \"roomName\": \"Extra Bed Adult\"}], \"mode\": \"standard\"}}\n"}}},"responses":{"202":{"description":"New batch accepted; processing continues asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchJob"},"example":{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}}}},"200":{"description":"Idempotent replay of an existing retained job; no additional quota charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchJob"},"example":{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}}}},"400":{"description":"Invalid JSONL, envelope, duplicate customId or idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"invalid batch file"}}}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"408":{"description":"Upload timed out. Retry with the same key and exact file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"upload timed out"}}}}}},"409":{"description":"This idempotency key was used with different file bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"idempotency key already used with different content"}}}}}},"413":{"description":"File, line or record-count limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"batch limit exceeded"}}}}}},"415":{"description":"Content-Type must be application/x-ndjson.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"expected application/x-ndjson"}}}}}},"429":{"description":"Insufficient daily quota, too many active jobs or deployment storage limit reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"batch capacity or quota reached"}}}}}}}},"get":{"tags":["Batches"],"operationId":"listRoomMappingBatches","summary":"List your retained batch jobs","responses":{"200":{"description":"Up to 25 newest retained jobs owned by this key. Keep job IDs to retrieve earlier jobs directly.","content":{"application/json":{"schema":{"type":"object","required":["jobs"],"properties":{"jobs":{"type":"array","maxItems":25,"items":{"$ref":"#/components/schemas/BatchJob"}}}},"example":{"jobs":[{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}]}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}}}}},"/v1/room-mapping-batches/{jobId}":{"get":{"tags":["Batches"],"operationId":"getRoomMappingBatch","summary":"Get batch status and counts","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"description":"Poll about every 5 seconds. Stop when status is completed or completed_with_errors. Polling is free of additional quota.","responses":{"200":{"description":"Current job status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchJob"},"example":{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"404":{"description":"Unknown or expired job, or a job owned by another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no such batch"}}}}}}}}},"/v1/webhooks/room-mapping-batches":{"get":{"tags":["Batches"],"operationId":"getBatchWebhook","summary":"Get the batch webhook","responses":{"200":{"description":"This key's webhook URL and a prefix of its secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchWebhookConfig"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}}}},"put":{"tags":["Batches"],"operationId":"setBatchWebhook","summary":"Set the batch webhook","description":"Every batch this key submits then POSTs a signed batch.completed event (BatchWebhookEvent) to the URL when it finishes. The response shows the signing secret in full; store it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}},"example":{"url":"https://example.com/plexe/batches"}}}},"responses":{"200":{"description":"Saved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchWebhookConfig"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"422":{"description":"Not an https URL resolving to public addresses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"invalid webhook URL"}}}}}}}},"delete":{"tags":["Batches"],"operationId":"deleteBatchWebhook","summary":"Remove the default batch webhook","responses":{"200":{"description":"Removed; the secret is kept for per-batch Webhook-Url deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchWebhookConfig"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}}}}},"/v1/webhooks/room-mapping-batches/rotate-secret":{"post":{"tags":["Batches"],"operationId":"rotateBatchWebhookSecret","summary":"Rotate the webhook signing secret","responses":{"200":{"description":"The new secret, in full. Deliveries from now on are signed with it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchWebhookConfig"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}}}}},"/v1/room-mapping-batches/{jobId}/webhook/resend":{"post":{"tags":["Batches"],"operationId":"resendRoomMappingBatchWebhook","summary":"Resend a batch's webhook","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"description":"Queues the job's last batch.completed event again under a new deliveryId, with redelivery: true.","responses":{"202":{"description":"Queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchWebhookDelivery"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"404":{"description":"Unknown or expired job, or a job owned by another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no such batch"}}}}}},"409":{"description":"The job has no webhook event yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no webhook event to resend"}}}}}}}}},"/v1/room-mapping-batches/{jobId}/retry":{"post":{"tags":["Batches"],"operationId":"retryRoomMappingBatch","summary":"Retry transient failures","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"description":"No body. Requeues only failed records marked retryable, preserves successful records and charges no new quota. Repeated calls while active do not duplicate work. Each hotel gets up to three automatic attempts per cycle and nine total attempts including manual retries; exhaustion produces error.code=retry_budget_exhausted with retryable=false. Correct permanent validation errors in a new batch.","responses":{"202":{"description":"Retry queued or already active.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchJob"},"example":{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}}}},"200":{"description":"No transient failures to retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchJob"},"example":{"jobId":"batch_example","status":"queued","counts":{"total":2,"pending":2,"running":0,"succeeded":0,"failed":0,"cacheHits":0},"createdAt":"2026-10-07T10:00:00Z","updatedAt":"2026-10-07T10:00:00Z","expiresAt":"2026-10-14T10:00:00Z","mappingRevision":"example-model-catalogue-revision"}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"404":{"description":"Unknown or expired job, or a job owned by another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no such batch"}}}}}},"429":{"description":"Active-job capacity reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"batch capacity reached"}}}}}}}}},"/v1/room-mapping-batches/{jobId}/results":{"get":{"tags":["Batches"],"operationId":"downloadRoomMappingBatchResults","summary":"Download all records","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"description":"Available only for terminal jobs and before expiresAt. Each JSONL line follows BatchResult. Join using customId; ordering is not a contract. Downloads do not consume quota. Examples are illustrative.","responses":{"200":{"description":"UTF-8 JSONL stream of all records.","content":{"application/x-ndjson":{"schema":{"type":"string","description":"One BatchResult object per line."},"example":"{\"customId\": \"hotel-001\", \"status\": \"succeeded\", \"attempts\": 1, \"cacheHit\": false, \"response\": {\"requestId\": \"req_40932e6d3b6f4ead\", \"mode\": \"standard\", \"policyVersion\": \"2026-10-07.2.standard\", \"hotel\": {\"id\": \"39614731\", \"name\": \"Taj Lands End\", \"city\": \"Mumbai\", \"country\": \"IN\", \"resolvedBy\": \"name_search\", \"nameSimilarity\": 0.7744, \"roomFit\": 0.7339, \"confident\": true, \"catalogueRooms\": 22, \"candidates\": [{\"id\": \"39614731\", \"name\": \"Taj Lands End\", \"city\": \"Mumbai\", \"country\": \"IN\", \"nameSimilarity\": 0.7744, \"roomFit\": 0.7339}, {\"id\": \"39612769\", \"name\": \"West End Hotel\", \"city\": \"Mumbai\", \"country\": \"IN\", \"nameSimilarity\": 0.6191, \"roomFit\": 0.1203}, {\"id\": \"39658767\", \"name\": \"Taj West End\", \"city\": \"Bengaluru\", \"country\": \"IN\", \"nameSimilarity\": 0.6167, \"roomFit\": 0.2752}, {\"id\": \"51117724\", \"name\": \"Taj The Trees, Mumbai\", \"city\": \"Mumbai\", \"country\": \"IN\", \"nameSimilarity\": 0.6113, \"roomFit\": 0.3026}, {\"id\": \"32923128\", \"name\": \"Taj Palace\", \"city\": \"Mumbai\", \"country\": \"IN\", \"nameSimilarity\": 0.6074, \"roomFit\": 0.0566}]}, \"groups\": [{\"roomId\": \"39614731:room-1-king-bed-bay-view\", \"label\": \"Room, 1 King Bed, Bay View\", \"labelSource\": \"catalogue\", \"catalogued\": true, \"matchedRoom\": \"Room, 1 King Bed, Bay View\", \"rooms\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"boardBasis\": \"BB\", \"supplier\": \"hotelbeds\", \"beds\": [{\"type\": \"King\", \"count\": 1}]}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\"}]}, {\"roomId\": \"39614731:room-1-twin-bed-bay-view\", \"label\": \"Room, 1 Twin Bed, Bay View\", \"labelSource\": \"catalogue\", \"catalogued\": true, \"matchedRoom\": \"Room, 1 Twin Bed, Bay View\", \"rooms\": [{\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\"}]}, {\"roomId\": \"39614731:luxury-suite-1-king-bed-sea-view\", \"label\": \"Luxury Suite, 1 King Bed, Sea View\", \"labelSource\": \"catalogue\", \"catalogued\": true, \"matchedRoom\": \"Luxury Suite, 1 King Bed, Sea View\", \"rooms\": [{\"index\": 3, \"roomName\": \"Luxury Suite Sea View\"}]}, {\"roomId\": \"39614731:unplaced:623cd491\", \"label\": \"Extra Bed Adult\", \"labelSource\": \"supplier\", \"catalogued\": false, \"matchedRoom\": null, \"rooms\": [{\"index\": 4, \"roomName\": \"Extra Bed Adult\"}]}], \"roomRates\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"matchedRoom\": \"Room, 1 King Bed, Bay View\", \"encoderScore\": 0.92, \"decidedBy\": \"encoder\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"boardBasis\": \"BB\", \"supplier\": \"hotelbeds\", \"beds\": [{\"type\": \"King\", \"count\": 1}]}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\", \"matchedRoom\": \"Room, 1 King Bed, Bay View\", \"encoderScore\": 0.8091, \"decidedBy\": \"encoder\"}, {\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\", \"matchedRoom\": \"Room, 1 Twin Bed, Bay View\", \"encoderScore\": 0.8947, \"decidedBy\": \"encoder\"}, {\"index\": 3, \"roomName\": \"Luxury Suite Sea View\", \"matchedRoom\": \"Luxury Suite, 1 King Bed, Sea View\", \"encoderScore\": 0.8516, \"decidedBy\": \"encoder\"}, {\"index\": 4, \"roomName\": \"Extra Bed Adult\", \"matchedRoom\": null, \"encoderScore\": 0.1942, \"decidedBy\": \"picker_none_cached\"}], \"summary\": {\"inputRooms\": 5, \"uniqueNames\": 5, \"groups\": 4, \"reduction\": 0.2, \"closeCalls\": 1, \"pickerCalls\": 0, \"uncatalogued\": 1, \"attributedRooms\": 1, \"feedbackApplied\": 0}, \"warnings\": [], \"timingsMs\": {\"total_ms\": 119}, \"usage\": {}, \"latencyMs\": 472}}\n{\"customId\": \"hotel-002\", \"status\": \"failed\", \"attempts\": 1, \"cacheHit\": false, \"error\": {\"status\": 504, \"message\": \"upstream timed out\", \"retryable\": true}, \"request\": {\"hotelName\": \"Taj Lands End, Mumbai\", \"city\": \"Mumbai\", \"country\": \"IN\", \"roomRates\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"beds\": [{\"type\": \"King\", \"count\": 1}], \"boardBasis\": \"BB\"}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\"}, {\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\"}, {\"index\": 3, \"roomName\": \"Luxury Suite Sea View\"}, {\"index\": 4, \"roomName\": \"Extra Bed Adult\"}], \"mode\": \"standard\"}}\n"}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"404":{"description":"Unknown or expired job, or a job owned by another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no such batch"}}}}}},"409":{"description":"The job is not terminal yet. Keep polling before downloading.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"batch is not complete"}}}}}}}}},"/v1/room-mapping-batches/{jobId}/errors":{"get":{"tags":["Batches"],"operationId":"downloadRoomMappingBatchErrors","summary":"Download failed records only","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"description":"Available only for terminal jobs and before expiresAt. Each JSONL line follows BatchResult. Join using customId; ordering is not a contract. Downloads do not consume quota. Examples are illustrative.","responses":{"200":{"description":"UTF-8 JSONL stream of failed records only.","content":{"application/x-ndjson":{"schema":{"type":"string","description":"One BatchResult object per line."},"example":"{\"customId\": \"hotel-002\", \"status\": \"failed\", \"attempts\": 1, \"cacheHit\": false, \"error\": {\"status\": 504, \"message\": \"upstream timed out\", \"retryable\": true}, \"request\": {\"hotelName\": \"Taj Lands End, Mumbai\", \"city\": \"Mumbai\", \"country\": \"IN\", \"roomRates\": [{\"index\": 0, \"roomName\": \"Bay View Room, 1 King Bed\", \"code\": \"BVK\", \"provider\": \"hotelbeds\", \"view\": \"Bay View\", \"beds\": [{\"type\": \"King\", \"count\": 1}], \"boardBasis\": \"BB\"}, {\"index\": 1, \"roomName\": \"BAY VIEW, KING BED\"}, {\"index\": 2, \"roomName\": \" [1]Bay View Room Twin Bed(Room_Only)\"}, {\"index\": 3, \"roomName\": \"Luxury Suite Sea View\"}, {\"index\": 4, \"roomName\": \"Extra Bed Adult\"}], \"mode\": \"standard\"}}\n"}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"unauthorized"}}}}}},"404":{"description":"Unknown or expired job, or a job owned by another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"no such batch"}}}}}},"409":{"description":"The job is not terminal yet. Keep polling before downloading.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"error":{"summary":"Illustrative error","value":{"error":"batch is not complete"}}}}}}}}},"/v1/hotels/find-match":{"post":{"tags":["Hotel mapping"],"operationId":"findHotelMatch","summary":"Match a supplier hotel record","description":"Ranks Plexe hotels for one supplier hotel record against a read-only index of 155,396 supplier records clustered into 64,193 hotels. No request changes the index. Counts toward the daily quota.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelRecord"},"example":{"name":"Hadleys Orient Hotel","address1":"34 Murray Street","city":"Hobart","postalCode":"7000","countryCode":"AU","lat":-42.884,"lon":147.3279}}}},"responses":{"200":{"description":"The decision and ranked candidates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelMatch"}}}},"422":{"description":"Invalid record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"429":{"description":"Daily quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"503":{"description":"The hotel index is loading; retry in a few seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}}}}},"/v1/hotels/{hotelId}":{"get":{"tags":["Hotel mapping"],"operationId":"getHotel","summary":"Get a Plexe hotel","parameters":[{"name":"hotelId","in":"path","required":true,"schema":{"type":"string"},"example":"H00000001"}],"responses":{"200":{"description":"The hotel and every supplier hotel ID mapped to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Hotel"}}}},"404":{"description":"No such hotel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"429":{"description":"Daily quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"503":{"description":"The hotel index is loading; retry in a few seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}}}}},"/v1/hotel-mappings/{source}/{sourceId}":{"get":{"tags":["Hotel mapping"],"operationId":"getHotelMapping","summary":"Find the Plexe hotel for a supplier hotel ID","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"example":"HotelBeds","description":"DOTW, GRNConnect, HotelBeds, RateHawk, Restel or HyperGuestDirect."},{"name":"sourceId","in":"path","required":true,"schema":{"type":"string"},"example":"196260"}],"responses":{"200":{"description":"The Plexe hotel this supplier ID belongs to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelMapping"}}}},"404":{"description":"Not in the index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"429":{"description":"Daily quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"503":{"description":"The hotel index is loading; retry in a few seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}}}}},"/v1/hotels/validate":{"post":{"tags":["Hotel mapping"],"operationId":"validateHotelRecord","summary":"Check a hotel record for data problems","description":"No index needed and no quota used.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelRecord"}}}},"responses":{"200":{"description":"Issues found.","content":{"application/json":{"schema":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}}}}},"/v1/hotels/status":{"get":{"tags":["System"],"operationId":"hotelIndexStatus","summary":"Hotel index status","security":[],"responses":{"200":{"description":"Whether the hotel index is loaded.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ready","loading","failed","disabled"]},"hotels":{"type":"integer"},"supplierRecords":{"type":"integer"},"loadSeconds":{"type":"number"}}}}}}}}},"/v1/room-attributes/{attributeId}":{"get":{"tags":["Room mapping"],"operationId":"getRoomAttributes","summary":"Get extracted room attributes","description":"Poll roomAttributes.statusUrl with the same API key. Returns source-supported model extractions, never booking verification. Polling does not rerun mapping or extraction, or consume mapping quota. Results expire after 30 days. See /docs/guides/attributes.","security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"attributeId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Attribute state and evidence; pending responses include Retry-After: 2.","content":{"application/json":{"schema":{"type":"object","required":["roomAttributes"],"properties":{"roomAttributes":{"$ref":"#/components/schemas/RoomAttributes"}}}}}},"401":{"description":"Missing or invalid key."},"404":{"description":"Unknown, expired or other-tenant attribute ID."}}}},"/v1/room-hierarchy-reviews/{reviewId}":{"get":{"tags":["Room mapping"],"operationId":"getHierarchyReview","summary":"Get reviewed browsing classes","description":"Poll roomHierarchy.review.statusUrl with the same API key. This does not rerun mapping or consume mapping quota. Apply only to the inventory that issued this reviewId; replace its hierarchy as a whole. Reviews expire after 30 days.","parameters":[{"name":"reviewId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current hierarchy and review status. Pending responses suggest Retry-After: 2.","content":{"application/json":{"schema":{"type":"object","required":["roomHierarchy"],"properties":{"roomHierarchy":{"$ref":"#/components/schemas/RoomHierarchy"}}}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}},"404":{"description":"Unknown, expired, or another tenant's review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{}}}}}}},"/v1/room-mappings":{"post":{"tags":["Room mapping"],"operationId":"createRoomMapping","summary":"Create a room mapping","description":"One call per hotel. Every call is logged under its `requestId` on the Logs page of the portal.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/RoomMappingRequest"},{"$ref":"#/components/schemas/RoomsRequest"}],"description":"Either the original shape (a body with `roomRates`) or the newer shape (a body with `rates`)."},"examples":{"names":{"summary":"Supplier names only","value":{"hotelName":"Taj Lands End, Mumbai","city":"Mumbai","country":"IN","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed"},{"index":1,"roomName":"BAY VIEW, KING BED"},{"index":2,"roomName":" [1]Bay View Room Twin Bed(Room_Only)"},{"index":3,"roomName":"Luxury Suite Sea View"},{"index":4,"roomName":"Extra Bed Adult"}]}},"attributes":{"summary":"With room details","value":{"hotelName":"Taj Lands End, Mumbai","city":"Mumbai","country":"IN","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed","code":"BVK","provider":"hotelbeds","view":"Bay View","beds":[{"type":"King","count":1}],"boardBasis":"BB"},{"index":1,"roomName":"BAY VIEW, KING BED"},{"index":2,"roomName":" [1]Bay View Room Twin Bed(Room_Only)"},{"index":3,"roomName":"Luxury Suite Sea View"},{"index":4,"roomName":"Extra Bed Adult"}]}},"rooms":{"summary":"Newer shape","value":{"hotel":{"id":"39614731"},"placementPolicy":"guarded","rates":[{"rateId":"a-101","roomName":"Bay View Room, 1 King Bed","supplier":"supplier-a","nightlyPrice":210.5,"currency":"INR","boardBasis":"BB"},{"rateId":"b-201","roomName":"BAY VIEW, KING BED","supplier":"supplier-b","images":["https://cdn.example/bay-king.jpg"]}]}},"hotelId":{"summary":"Catalogue id","value":{"hotelId":"39614731","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed"},{"index":1,"roomName":"Luxury Suite Sea View"}]}},"extractedAttributes":{"summary":"Optional room attributes with evidence","value":{"hotelId":"39614731","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed"},{"index":1,"roomName":"Luxury Suite Sea View"}],"include":["attributes"]}},"hierarchy":{"summary":"Optional room class hierarchy","value":{"hotelId":"39614731","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed"},{"index":1,"roomName":"Luxury Suite Sea View"}],"include":["hierarchy"]}},"consolidated":{"summary":"Consolidated mode","value":{"hotelName":"Taj Lands End, Mumbai","city":"Mumbai","country":"IN","roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed","code":"BVK","provider":"hotelbeds","view":"Bay View","beds":[{"type":"King","count":1}],"boardBasis":"BB"},{"index":1,"roomName":"BAY VIEW, KING BED"},{"index":2,"roomName":" [1]Bay View Room Twin Bed(Room_Only)"},{"index":3,"roomName":"Luxury Suite Sea View"},{"index":4,"roomName":"Extra Bed Adult"}],"mode":"consolidated"}}}}}},"responses":{"200":{"description":"The rooms, best matches first. Read `warnings`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomMapping"},"examples":{"grouped":{"summary":"Grouped","value":{"requestId":"req_40932e6d3b6f4ead","mode":"standard","policyVersion":"2026-10-07.2.standard","hotel":{"id":"39614731","name":"Taj Lands End","city":"Mumbai","country":"IN","resolvedBy":"name_search","nameSimilarity":0.7744,"roomFit":0.7339,"confident":true,"catalogueRooms":22,"candidates":[{"id":"39614731","name":"Taj Lands End","city":"Mumbai","country":"IN","nameSimilarity":0.7744,"roomFit":0.7339},{"id":"39612769","name":"West End Hotel","city":"Mumbai","country":"IN","nameSimilarity":0.6191,"roomFit":0.1203},{"id":"39658767","name":"Taj West End","city":"Bengaluru","country":"IN","nameSimilarity":0.6167,"roomFit":0.2752},{"id":"51117724","name":"Taj The Trees, Mumbai","city":"Mumbai","country":"IN","nameSimilarity":0.6113,"roomFit":0.3026},{"id":"32923128","name":"Taj Palace","city":"Mumbai","country":"IN","nameSimilarity":0.6074,"roomFit":0.0566}]},"groups":[{"roomId":"39614731:room-1-king-bed-bay-view","label":"Room, 1 King Bed, Bay View","labelSource":"catalogue","catalogued":true,"matchedRoom":"Room, 1 King Bed, Bay View","rooms":[{"index":0,"roomName":"Bay View Room, 1 King Bed","code":"BVK","provider":"hotelbeds","view":"Bay View","boardBasis":"BB","supplier":"hotelbeds","beds":[{"type":"King","count":1}]},{"index":1,"roomName":"BAY VIEW, KING BED"}]},{"roomId":"39614731:room-1-twin-bed-bay-view","label":"Room, 1 Twin Bed, Bay View","labelSource":"catalogue","catalogued":true,"matchedRoom":"Room, 1 Twin Bed, Bay View","rooms":[{"index":2,"roomName":" [1]Bay View Room Twin Bed(Room_Only)"}]},{"roomId":"39614731:luxury-suite-1-king-bed-sea-view","label":"Luxury Suite, 1 King Bed, Sea View","labelSource":"catalogue","catalogued":true,"matchedRoom":"Luxury Suite, 1 King Bed, Sea View","rooms":[{"index":3,"roomName":"Luxury Suite Sea View"}]},{"roomId":"39614731:unplaced:623cd491","label":"Extra Bed Adult","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":4,"roomName":"Extra Bed Adult"}]}],"roomRates":[{"index":0,"roomName":"Bay View Room, 1 King Bed","matchedRoom":"Room, 1 King Bed, Bay View","encoderScore":0.92,"decidedBy":"encoder","code":"BVK","provider":"hotelbeds","view":"Bay View","boardBasis":"BB","supplier":"hotelbeds","beds":[{"type":"King","count":1}]},{"index":1,"roomName":"BAY VIEW, KING BED","matchedRoom":"Room, 1 King Bed, Bay View","encoderScore":0.8091,"decidedBy":"encoder"},{"index":2,"roomName":" [1]Bay View Room Twin Bed(Room_Only)","matchedRoom":"Room, 1 Twin Bed, Bay View","encoderScore":0.8947,"decidedBy":"encoder"},{"index":3,"roomName":"Luxury Suite Sea View","matchedRoom":"Luxury Suite, 1 King Bed, Sea View","encoderScore":0.8516,"decidedBy":"encoder"},{"index":4,"roomName":"Extra Bed Adult","matchedRoom":null,"encoderScore":0.1942,"decidedBy":"picker_none_cached"}],"summary":{"inputRooms":5,"uniqueNames":5,"groups":4,"reduction":0.2,"closeCalls":1,"pickerCalls":0,"uncatalogued":1,"attributedRooms":1,"feedbackApplied":0},"warnings":[],"timingsMs":{"total_ms":119},"usage":{},"latencyMs":472}},"lowConfidence":{"summary":"Doubtful hotel match, flagged","value":{"requestId":"req_8fb538b1d7e34091","mode":"standard","policyVersion":"2026-10-07.2.standard","hotel":{"id":"39962128","name":"Jumeirah Al Qasr Dubai","city":"Dubai","country":"AE","resolvedBy":"name_search","nameSimilarity":0.646,"roomFit":0.4675,"confident":false,"catalogueRooms":12,"candidates":[{"id":"38393020","name":"Jumeirah Burj Al Arab Dubai","city":"Dubai","country":"AE","nameSimilarity":0.7129,"roomFit":0.3281},{"id":"16340396","name":"Luxury apartment Burj al Arab View","city":"Dubai","country":"AE","nameSimilarity":0.6533,"roomFit":0.23},{"id":"39962128","name":"Jumeirah Al Qasr Dubai","city":"Dubai","country":"AE","nameSimilarity":0.646,"roomFit":0.4675},{"id":"72460478","name":"Stylish 1BR with Burj Al Arab View","city":"Dubai","country":"AE","nameSimilarity":0.6455,"roomFit":0.0996},{"id":"47146656","name":"Jumeirah Marsa Al Arab Dubai","city":"Dubai","country":"AE","nameSimilarity":0.6382,"roomFit":0.2782}]},"groups":[{"roomId":"39962128:unplaced:c4c4dfee","label":"Deluxe One Bedroom Suite","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":0,"roomName":"Deluxe One Bedroom Suite"}]},{"roomId":"39962128:suite-1-bedroom-ocean","label":"Suite, 1 Bedroom (Ocean)","labelSource":"catalogue","catalogued":true,"matchedRoom":"Suite, 1 Bedroom (Ocean)","rooms":[{"index":1,"roomName":"Panoramic One Bedroom Suite"}]},{"roomId":"39962128:family-suite-2-bedrooms","label":"Family Suite, 2 Bedrooms","labelSource":"catalogue","catalogued":true,"matchedRoom":"Family Suite, 2 Bedrooms","rooms":[{"index":2,"roomName":"Two Bedroom Delux Suite - Breakfast"}]},{"roomId":"39962128:unplaced:0388c889","label":"Club One Bedroom Suite","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":3,"roomName":"Club One Bedroom Suite"}]}],"roomRates":[{"index":0,"roomName":"Deluxe One Bedroom Suite","matchedRoom":null,"encoderScore":0.4648,"decidedBy":"picker_none"},{"index":1,"roomName":"Panoramic One Bedroom Suite","matchedRoom":"Suite, 1 Bedroom (Ocean)","encoderScore":0.4701,"decidedBy":"picker"},{"index":2,"roomName":"Two Bedroom Delux Suite - Breakfast","matchedRoom":"Family Suite, 2 Bedrooms","encoderScore":0.4591,"decidedBy":"picker"},{"index":3,"roomName":"Club One Bedroom Suite","matchedRoom":null,"encoderScore":0.4761,"decidedBy":"picker_none"}],"summary":{"inputRooms":4,"uniqueNames":4,"groups":4,"reduction":0.0,"closeCalls":4,"pickerCalls":1,"uncatalogued":2,"attributedRooms":0,"feedbackApplied":0},"warnings":["hotel matched by name with low room fit (0.47 < 0.5): the property may be wrong. Check hotel.candidates, or send hotelId / city and country."],"timingsMs":{"total_ms":2520},"usage":{},"latencyMs":3157}},"ungrouped":{"summary":"Doubtful hotel match, ungrouped","value":{"requestId":"req_665a24fb9a9d46f2","mode":"standard","policyVersion":"2026-10-07.2.standard","hotel":{"id":"39962128","name":"Jumeirah Al Qasr Dubai","city":"Dubai","country":"AE","resolvedBy":"name_search","nameSimilarity":0.646,"roomFit":0.4675,"confident":false,"catalogueRooms":12,"candidates":[{"id":"38393020","name":"Jumeirah Burj Al Arab Dubai","city":"Dubai","country":"AE","nameSimilarity":0.7129,"roomFit":0.3281},{"id":"16340396","name":"Luxury apartment Burj al Arab View","city":"Dubai","country":"AE","nameSimilarity":0.6533,"roomFit":0.23},{"id":"39962128","name":"Jumeirah Al Qasr Dubai","city":"Dubai","country":"AE","nameSimilarity":0.646,"roomFit":0.4675},{"id":"72460478","name":"Stylish 1BR with Burj Al Arab View","city":"Dubai","country":"AE","nameSimilarity":0.6455,"roomFit":0.0996},{"id":"47146656","name":"Jumeirah Marsa Al Arab Dubai","city":"Dubai","country":"AE","nameSimilarity":0.6382,"roomFit":0.2782}]},"groups":[{"roomId":"39962128:unplaced:c4c4dfee","label":"Deluxe One Bedroom Suite","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":0,"roomName":"Deluxe One Bedroom Suite"}]},{"roomId":"39962128:unplaced:372a92b9","label":"Panoramic One Bedroom Suite","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":1,"roomName":"Panoramic One Bedroom Suite"}]},{"roomId":"39962128:unplaced:c3e21991","label":"Two Bedroom Delux Suite - Breakfast","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":2,"roomName":"Two Bedroom Delux Suite - Breakfast"}]},{"roomId":"39962128:unplaced:0388c889","label":"Club One Bedroom Suite","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":3,"roomName":"Club One Bedroom Suite"}]}],"roomRates":[{"index":0,"roomName":"Deluxe One Bedroom Suite","matchedRoom":null,"encoderScore":null,"decidedBy":"ungrouped_low_confidence"},{"index":1,"roomName":"Panoramic One Bedroom Suite","matchedRoom":null,"encoderScore":null,"decidedBy":"ungrouped_low_confidence"},{"index":2,"roomName":"Two Bedroom Delux Suite - Breakfast","matchedRoom":null,"encoderScore":null,"decidedBy":"ungrouped_low_confidence"},{"index":3,"roomName":"Club One Bedroom Suite","matchedRoom":null,"encoderScore":null,"decidedBy":"ungrouped_low_confidence"}],"summary":{"inputRooms":4,"uniqueNames":4,"groups":4,"reduction":0.0,"closeCalls":0,"pickerCalls":0,"uncatalogued":4,"attributedRooms":0,"feedbackApplied":0},"warnings":["hotel matched by name with low room fit (0.47 < 0.5): the property may be wrong. Check hotel.candidates, or send hotelId / city and country. Names returned ungrouped as requested."],"timingsMs":{"total_ms":99},"usage":{},"latencyMs":463}},"notInCatalogue":{"summary":"hotelId not in the catalogue","value":{"requestId":"req_2f0c61a8d4e94b17","mode":"standard","policyVersion":"2026-10-07.2.standard","hotel":{"id":"39681218","name":null,"city":null,"country":null,"resolvedBy":"hotelId","nameSimilarity":null,"roomFit":null,"confident":false,"catalogueRooms":0,"candidates":[]},"groups":[{"roomId":"39681218:unplaced:40457521","label":"Double Room","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":0,"roomName":"Double Room"},{"index":1,"roomName":"DOUBLE ROOM"},{"index":3,"roomName":"Double Room"}]},{"roomId":"39681218:unplaced:dec8d853","label":"Double Room with Sea View","labelSource":"supplier","catalogued":false,"matchedRoom":null,"rooms":[{"index":2,"roomName":"Double Room with Sea View"}]}],"roomRates":[{"index":0,"roomName":"Double Room","matchedRoom":null,"encoderScore":null,"decidedBy":"no_catalogue_rooms"},{"index":1,"roomName":"DOUBLE ROOM","matchedRoom":null,"encoderScore":null,"decidedBy":"no_catalogue_rooms"},{"index":2,"roomName":"Double Room with Sea View","matchedRoom":null,"encoderScore":null,"decidedBy":"no_catalogue_rooms"},{"index":3,"roomName":"Double Room","matchedRoom":null,"encoderScore":null,"decidedBy":"no_catalogue_rooms"}],"summary":{"closeCalls":0,"pickerCalls":0,"inputRooms":4,"uniqueNames":3,"groups":2,"reduction":0.5,"uncatalogued":2,"attributedRooms":0,"feedbackApplied":0},"warnings":["hotelId 39681218 is not in the Plexe catalogue yet, so there is no room list to map to: each distinct room name comes back as its own group (names that differ only in case or punctuation share one). Nothing was merged on a guess."],"timingsMs":{},"usage":{},"latencyMs":21}}}}}},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"badJson":{"summary":"Not JSON","value":{"error":"body must be valid JSON"}}}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"summary":"No key","value":{"error":"missing or invalid API key; send it as 'Authorization: Bearer <key>'"}}}}}},"422":{"description":"Invalid request. `details` lists every problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid":{"summary":"Invalid fields","value":{"error":"invalid request","details":["roomRates[0].index must be an integer","country 'XX' is not an ISO 3166-1 alpha-2 code, for example IN, GB, AE"],"requestId":"req_1bbb2dbce10c40fc"}}}}}},"429":{"description":"Daily quota reached. It resets at 00:00 UTC.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"quota":{"summary":"Quota reached (illustrative)","value":{"error":"daily sandbox quota of 1000 requests reached; it resets at 00:00 UTC","requestId":"req_…"}}}}}},"502":{"description":"The model service failed. Retry once, then send us the `requestId`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"upstream":{"summary":"Model error (illustrative)","value":{"error":"upstream connection failed: ConnectError","requestId":"req_…"}}}}}},"504":{"description":"The model service timed out. Retry once, then send us the `requestId`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"timeout":{"summary":"Timeout (illustrative)","value":{"error":"upstream timed out","requestId":"req_…"}}}}}}}}},"/v1/feedback":{"post":{"tags":["Feedback"],"operationId":"createFeedback","summary":"Report a wrong merge","description":"Stored against your key and applied on every later call for that hotel. Separations only: with `not_in_room` the names leave that room; with `different_rooms`, when names from two sides of the report land in one group, every side but the best-matching one leaves it. Names that leave come back on their own (`decidedBy` `partner_feedback`). A report that repeats an active one answers 200 with the existing row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"},"examples":{"notInRoom":{"summary":"A name that is not that room","value":{"hotelId":"39614731","verdict":"not_in_room","roomId":"39614731:royal-room","roomNames":["Honeymoon Suite"],"note":"The hotel sells the Honeymoon Suite as its own category.","requestId":"req_4f61fcd7d85e4c53"}},"differentRooms":{"summary":"Two names that must never share a group","value":{"hotelId":"39614731","verdict":"different_rooms","roomNames":["Deluxe Twin Room","Deluxe Double Room"]}}}}}},"responses":{"201":{"description":"Recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Feedback"},"examples":{"notInRoom":{"summary":"Recorded","value":{"feedbackId":"fb_3c1f0a9e2b7d4e61","hotelId":"39614731","verdict":"not_in_room","roomId":"39614731:royal-room","roomNames":["Honeymoon Suite"],"note":"The hotel sells the Honeymoon Suite as its own category.","requestId":"req_4f61fcd7d85e4c53","createdAt":"2026-10-01T09:12:44.120+00:00","applied":true}}}}}},"200":{"description":"Already recorded: the active row that says the same thing (its note and requestId are kept; yours are not stored).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Feedback"},"example":{"feedbackId":"fb_3c1f0a9e2b7d4e61","hotelId":"39614731","verdict":"not_in_room","roomId":"39614731:royal-room","roomNames":["Honeymoon Suite"],"note":"The hotel sells the Honeymoon Suite as its own category.","requestId":"req_4f61fcd7d85e4c53","createdAt":"2026-10-01T09:12:44.120+00:00","applied":true}}}},"413":{"description":"The body is larger than 128 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"tooLarge":{"summary":"Too large","value":{"error":"body larger than 131072 bytes"}}}}}},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"badJson":{"summary":"Not JSON","value":{"error":"body must be valid JSON"}}}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"summary":"No key","value":{"error":"missing or invalid API key; send it as 'Authorization: Bearer <key>'"}}}}}},"422":{"description":"Invalid feedback (`details` lists every problem), or a feedback cap was reached (no `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid":{"summary":"Missing roomId","value":{"error":"invalid request","details":["roomId is required for verdict 'not_in_room': the groups[].roomId the names do not belong to"]}},"perHotelCap":{"summary":"200 active reports for the hotel","value":{"error":"this key already holds 200 active feedback rows for hotelId 39614731; withdraw some with DELETE /v1/feedback/{id}"}},"perKeyCap":{"summary":"2,000 reports on the key","value":{"error":"this key holds 2000 feedback rows, withdrawn ones included; contact us to have them purged"}}}}}}}},"get":{"tags":["Feedback"],"operationId":"listFeedback","summary":"List your feedback","parameters":[{"name":"hotelId","in":"query","required":false,"schema":{"type":"string"},"description":"Only this hotel."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"minimum":1,"maximum":2000},"description":"Rows to return. Values outside 1 to 2000 are clamped; a key never holds more than 2000, so everything can be listed in one call."}],"responses":{"200":{"description":"Newest first; withdrawn rows are left out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackList"},"example":{"feedback":[{"feedbackId":"fb_3c1f0a9e2b7d4e61","hotelId":"39614731","verdict":"not_in_room","roomId":"39614731:royal-room","roomNames":["Honeymoon Suite"],"note":"The hotel sells the Honeymoon Suite as its own category.","requestId":"req_4f61fcd7d85e4c53","createdAt":"2026-10-01T09:12:44.120+00:00","applied":true}]}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"summary":"No key","value":{"error":"missing or invalid API key; send it as 'Authorization: Bearer <key>'"}}}}}},"422":{"description":"`limit` is not an integer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"badLimit":{"summary":"Not an integer","value":{"error":"invalid request","details":["query.limit: Input should be a valid integer, unable to parse string as an integer"]}}}}}}}}},"/v1/feedback/{feedbackId}":{"delete":{"tags":["Feedback"],"operationId":"deleteFeedback","summary":"Withdraw feedback","parameters":[{"name":"feedbackId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Withdrawn; later calls no longer apply it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"},"example":{"deleted":true}}}},"401":{"description":"Missing or unknown API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"summary":"No key","value":{"error":"missing or invalid API key; send it as 'Authorization: Bearer <key>'"}}}}}},"404":{"description":"No such feedback on this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing":{"summary":"Unknown id","value":{"error":"no such feedback"}}}}}}}}},"/v1/health":{"get":{"tags":["System"],"operationId":"health","summary":"Health check","security":[],"responses":{"200":{"description":"The service is up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"},"example":{"status":"ok","upstream":"reachable","maxNames":600,"maxRates":2000,"endpoint":"/v1/room-mappings"}}}}}}}},"components":{"schemas":{"Alternative":{"additionalProperties":false,"properties":{"id":{"title":"Id","type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"nameSimilarity":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Namesimilarity"},"roomFit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Roomfit"}},"required":["id","name","city","country","nameSimilarity","roomFit"],"title":"Alternative","type":"object"},"AmenityIn":{"additionalProperties":false,"properties":{"name":{"description":"`key` is accepted as an alias.","maxLength":64,"minLength":1,"title":"Name","type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"type":"null"}],"default":null,"title":"Value"}},"required":["name"],"title":"AmenityIn","type":"object"},"AmenityItem":{"additionalProperties":false,"properties":{"name":{"title":"Name","type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"type":"null"}],"title":"Value"},"standard":{"title":"Standard","type":"boolean"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["name","value","standard","rateIds"],"title":"AmenityItem","type":"object"},"AttributeAlternative":{"additionalProperties":false,"properties":{"attribute":{"title":"Attribute","type":"string"},"values":{"items":{},"title":"Values","type":"array"},"evidence":{"items":{"$ref":"#/components/schemas/AttributeEvidenceSource"},"title":"Evidence","type":"array"}},"required":["attribute","values","evidence"],"title":"AttributeAlternative","type":"object"},"AttributeConflict":{"additionalProperties":false,"properties":{"attribute":{"title":"Attribute","type":"string"},"values":{"items":{},"title":"Values","type":"array"},"evidence":{"items":{"$ref":"#/components/schemas/AttributeEvidenceSource"},"title":"Evidence","type":"array"}},"required":["attribute","values","evidence"],"title":"AttributeConflict","type":"object"},"AttributeEvidenceSource":{"additionalProperties":false,"properties":{"source":{"title":"Source","type":"string"},"quote":{"title":"Quote","type":"string"}},"required":["source","quote"],"title":"AttributeEvidenceSource","type":"object"},"AttributeValue":{"additionalProperties":false,"properties":{"value":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{},"type":"array"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Value"},"quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quote"},"source":{"enum":["roomName","description","beds","view","amenities","catalogue","occupancy"],"title":"Source","type":"string"}},"required":["value","quote","source"],"title":"AttributeValue","type":"object"},"Bed":{"additionalProperties":false,"properties":{"type":{"description":"Bed type as the supplier names it; longer than 64 characters is truncated.","examples":["King"],"title":"Type","type":"string"},"count":{"default":1,"description":"Number of beds of this type. Values outside 1 to 19 become 1.","examples":[1],"title":"Count","type":"integer"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Free text, for example `180x200`; truncated to 32 characters.","title":"Size"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Free text; truncated to 120 characters.","title":"Description"}},"required":["type"],"title":"Bed","type":"object"},"BedItem":{"additionalProperties":false,"properties":{"type":{"title":"Type","type":"string"},"count":{"title":"Count","type":"integer"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["type","count","size","description","rateIds"],"title":"BedItem","type":"object"},"BedResult":{"additionalProperties":false,"properties":{"type":{"title":"Type","type":"string"},"count":{"title":"Count","type":"integer"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Size"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Description"}},"required":["type","count"],"title":"BedResult","type":"object"},"Candidate":{"additionalProperties":false,"properties":{"id":{"title":"Id","type":"string"},"name":{"title":"Name","type":"string"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Country"},"nameSimilarity":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"description":"Similarity of the sent name to this property's name.","title":"Namesimilarity"},"roomFit":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"description":"How well the sent room names fit this property's rooms.","title":"Roomfit"}},"required":["id","name"],"title":"Candidate","type":"object"},"Deleted":{"additionalProperties":false,"properties":{"deleted":{"title":"Deleted","type":"boolean"}},"required":["deleted"],"title":"Deleted","type":"object"},"DescriptionItem":{"additionalProperties":false,"properties":{"description":{"title":"Description","type":"string"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["description","rateIds"],"title":"DescriptionItem","type":"object"},"Error":{"additionalProperties":false,"properties":{"error":{"title":"Error","type":"string"},"details":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"Every problem with the request, on 422.","title":"Details"},"requestId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Present when the call was logged.","title":"Requestid"}},"required":["error"],"title":"Error","type":"object"},"Evidence":{"additionalProperties":false,"properties":{"axis":{"description":"Recognized conflict axis, or `category` for exact recovery from a reviewed property record. Category evidence includes source URLs, checkedAt and revision in room.value.","title":"Axis","type":"string"},"rate":{"$ref":"#/components/schemas/AttributeValue"},"room":{"anyOf":[{"$ref":"#/components/schemas/AttributeValue"},{"type":"null"}]}},"required":["axis","rate","room"],"title":"Evidence","type":"object"},"Feedback":{"additionalProperties":false,"properties":{"feedbackId":{"examples":["fb_3c1f0a9e2b7d4e61"],"title":"Feedbackid","type":"string"},"hotelId":{"title":"Hotelid","type":"string"},"verdict":{"enum":["not_in_room","different_rooms","same_room"],"title":"Verdict","type":"string"},"roomId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Roomid"},"roomNames":{"items":{"type":"string"},"title":"Roomnames","type":"array"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"requestId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requestid"},"createdAt":{"title":"Createdat","type":"string"},"applied":{"description":"True when the verdict changes later calls (`not_in_room`, `different_rooms`); false for `same_room`. Prefer `status`.","title":"Applied","type":"boolean"},"status":{"anyOf":[{"enum":["active","recorded","withdrawn"],"type":"string"},{"type":"null"}],"default":null,"description":"`active`: changes later calls. `recorded`: stored, not applied yet (`same_room`). `withdrawn`: no longer applied.","title":"Status"}},"required":["feedbackId","hotelId","verdict","roomId","roomNames","note","requestId","createdAt","applied"],"title":"Feedback","type":"object"},"FeedbackList":{"additionalProperties":false,"properties":{"feedback":{"items":{"$ref":"#/components/schemas/Feedback"},"title":"Feedback","type":"array"}},"required":["feedback"],"title":"FeedbackList","type":"object"},"FeedbackRequest":{"description":"Your verdict on a mapping. Active corrections are stored against your key and reapplied to the reported\nnames/category on later calls for that hotel. Only separations are applied today; `same_room` is recorded and\ndoes not yet change placements. A report that repeats an active one is answered with the existing row (200).","properties":{"hotelId":{"anyOf":[{"type":"string"},{"type":"integer"}],"description":"`hotel.id` from the mapping response.","examples":["39614731"],"maxLength":64,"title":"Hotelid"},"verdict":{"description":"`not_in_room`: the names leave `roomId`. `different_rooms`: whenever names from two sides of the report land in one group, every side but the best-matching one leaves it. `same_room`: the names are one room (recorded, not applied yet).","enum":["not_in_room","different_rooms","same_room"],"title":"Verdict","type":"string"},"roomId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Required for `not_in_room`: the room the names were wrongly placed in, as `rooms[].roomId` (`rm_...`) or the older `groups[].roomId`. An unresolved card is refused. Ignored for the other verdicts.","examples":["rm_8d02e6a1c4f7"],"title":"Roomid"},"roomNames":{"description":"Supplier room names exactly as you sent them; case, spacing and punctuation are ignored when matching later calls.","examples":[["Honeymoon Suite"]],"items":{"type":"string"},"maxItems":50,"minItems":1,"title":"Roomnames","type":"array"},"note":{"anyOf":[{"maxLength":500,"type":"string"},{"type":"null"}],"default":null,"description":"Why, for your own records and ours.","title":"Note"},"requestId":{"anyOf":[{"maxLength":64,"type":"string"},{"type":"null"}],"default":null,"description":"The `requestId` of the response you are reporting on.","title":"Requestid"}},"required":["hotelId","verdict","roomNames"],"title":"FeedbackRequest","type":"object"},"Group":{"additionalProperties":false,"properties":{"roomId":{"description":"Deprecated in favour of `stableId` (the same room's id in `rooms[]`). Display-only label cleanup preserves the category anchor. Reanchoring to a different category or separating conflicting offers can change IDs. Unresolved groups can have evidence-qualified suffixes.","examples":["39614731:room-1-king-bed-bay-view"],"title":"Roomid","type":"string"},"stableId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The `rooms[].roomId` of this group: `rm_` for a catalogue room, `un_` for an unresolved card. Key your inventory on it.","title":"Stableid"},"label":{"description":"The room's name to display.","examples":["Room, 1 King Bed, Bay View"],"title":"Label","type":"string"},"labelSource":{"description":"`supplier` when the names could not be placed in any known room.","enum":["catalogue","website","supplier"],"title":"Labelsource","type":"string"},"catalogued":{"description":"Deprecated: equals `labelSource != \"supplier\"`; prefer `rooms[].tier`.","title":"Catalogued","type":"boolean"},"matchedRoom":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Deprecated: equals `label` for a catalogued group and null otherwise.","title":"Matchedroom"},"rooms":{"description":"The rates placed in this room.","items":{"$ref":"#/components/schemas/GroupRoom"},"title":"Rooms","type":"array"},"content":{"anyOf":[{"$ref":"#/components/schemas/GroupContent"},{"type":"null"}],"default":null,"description":"The suppliers' content for this room, merged: images, descriptions, amenities, beds, views, occupancy."},"catalogueRooms":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"Consolidated mode only, when the website category spans several catalogue rooms.","title":"Cataloguerooms"}},"required":["roomId","label","labelSource","catalogued","rooms"],"title":"Group","type":"object"},"GroupContent":{"additionalProperties":false,"description":"Everything the suppliers sent for the rates in this group, merged for a room card. Nothing is invented: a field\nno rate carried is an empty list or null.","properties":{"images":{"description":"Every distinct image URL across the group's rates, in request order, at most 40.","items":{"$ref":"#/components/schemas/ImageRef"},"title":"Images","type":"array"},"descriptions":{"description":"Distinct descriptions the rates carried, longest first, at most 5; take the first for a room card.","items":{"$ref":"#/components/schemas/TextRef"},"title":"Descriptions","type":"array"},"amenities":{"description":"The union of the rates' `attributes`, most frequent first, at most 100.","items":{"type":"string"},"title":"Amenities","type":"array"},"beds":{"description":"Distinct bed setups stated by the rates.","items":{"$ref":"#/components/schemas/BedResult"},"title":"Beds","type":"array"},"views":{"description":"Distinct `view` values stated by the rates.","items":{"type":"string"},"title":"Views","type":"array"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"default":null,"description":"The largest required adults and children any rate stated; this is an aggregate of offer inputs, not a room capacity guarantee."},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Set only when every rate explicitly supplies a boolean and all agree. Any missing or conflicting value makes this null; smoking-neutral display labels do not imply non-smoking.","title":"Smokingallowed"},"suppliers":{"description":"Suppliers with at least one rate in the group.","items":{"type":"string"},"title":"Suppliers","type":"array"}},"title":"GroupContent","type":"object"},"GroupRoom":{"additionalProperties":false,"properties":{"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Supplier name; filled from `provider` when only that is sent.","examples":["hotelbeds"],"title":"Supplier"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Same as `supplier`; either name is accepted.","examples":["hotelbeds"],"title":"Provider"},"providerHotelId":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"The hotel's id at that supplier. Preserved exactly, including whitespace; longer values are rejected, never truncated.","title":"Providerhotelid"},"code":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"Supplier room code. Preserved exactly, including whitespace; longer values are rejected, never truncated.","examples":["DLX-SV-K"],"title":"Code"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Room description; truncated to 2,000 characters. Affirmative room-specific bed, view, bedroom and wing statements can inform guard checks; hypothetical or hotel-wide descriptions are not guarantees.","title":"Description"},"view":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The view this offer states. Recognized conflicts are checked per offer when the guard is enabled.","examples":["Sea View"],"title":"View"},"boardBasis":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Meal plan.","examples":["BB"],"title":"Boardbasis"},"refundability":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"examples":["Refundable"],"title":"Refundability"},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Whether smoking is allowed in the room, when the supplier says.","title":"Smokingallowed"},"nightlyPrice":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"title":"Nightlyprice"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"ISO 4217 code; truncated to 3 characters.","examples":["INR"],"title":"Currency"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"default":null,"description":"Required adults and children for this offer. Compared with a reviewed maximum category capacity only when that maximum is known; never treated as room capacity itself."},"beds":{"anyOf":[{"items":{"$ref":"#/components/schemas/BedResult"},"type":"array"},{"type":"null"}],"default":null,"title":"Beds"},"attributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"`{name, value}` pairs are returned as `name: value`.","title":"Attributes"},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Images"},"rateId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Your `rateId`, or the sent `index` as a string.","title":"Rateid"},"composition":{"anyOf":[{"$ref":"#/components/schemas/RoomComposition"},{"type":"null"}],"default":null},"placement":{"anyOf":[{"$ref":"#/components/schemas/Placement"},{"type":"null"}],"default":null,"description":"Where the rate landed and why: `roomId` (stable), `tier` (verified, inferred, unresolved), `reason`, `similarity`, `feedbackId`, `evidence`. Prefer it to `matchedRoom`, `encoderScore` and `decidedBy`."},"mealPlan":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"`boardBasis` normalized: Room Only, Bed and Breakfast, Half Board, Full Board, All Inclusive, Unknown; null when not sent.","title":"Mealplan"},"refundable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"`refundability` normalized; null when not sent or not recognisable.","title":"Refundable"},"roomAttributes":{"anyOf":[{"$ref":"#/components/schemas/RoomAttributes"},{"type":"null"}],"default":null,"description":"Read attributes with evidence (bed, view, room size, bathroom, balcony, smoking and more), with `include: [\"attributes\"]`; pending while extraction runs; poll statusUrl using the same API key. (`attributes` is the amenity list you sent.)"},"index":{"title":"Index","type":"integer"},"roomName":{"title":"Roomname","type":"string"}},"required":["index","roomName"],"title":"GroupRoom","type":"object"},"Health":{"additionalProperties":false,"properties":{"status":{"const":"ok","title":"Status","type":"string"},"upstream":{"title":"Upstream","type":"string"},"maxNames":{"title":"Maxnames","type":"integer"},"maxRates":{"title":"Maxrates","type":"integer"},"endpoint":{"title":"Endpoint","type":"string"}},"required":["status","upstream","maxNames","maxRates","endpoint"],"title":"Health","type":"object"},"HierarchyReview":{"additionalProperties":false,"properties":{"status":{"enum":["pending","reviewed","failed","disabled","unavailable"],"title":"Status","type":"string"},"reviewId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewid"},"revision":{"title":"Revision","type":"string"},"statusUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Poll using the same API key; returns roomHierarchy without rerunning mapping. Replace only the matching inventory's hierarchy.","title":"Statusurl"},"reviewedAt":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"UTC Unix timestamp of successful model review. Null when no successful review is available.","title":"Reviewedat"},"errorCode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Errorcode"}},"required":["status","reviewId","revision","statusUrl","reviewedAt","errorCode"],"title":"HierarchyReview","type":"object"},"Hotel":{"type":"object","properties":{"hotelId":{"type":"string","description":"Plexe hotel ID."},"name":{"type":"string","description":"Name."},"address1":{"type":"string","description":"Street address."},"city":{"type":"string","description":"City."},"postalCode":{"type":["string","null"]},"countryCode":{"type":"string","description":"Country."},"lat":{"type":["number","null"]},"lon":{"type":["number","null"]},"starRating":{"type":["number","null"]},"chain":{"type":["string","null"]},"brand":{"type":["string","null"]},"propertyType":{"type":["string","null"]},"phones":{"type":"array","items":{"type":"string"}},"website":{"type":["string","null"]},"supplierRecords":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","description":"Supplier."},"sourceId":{"type":"string","description":"Supplier's hotel ID."}}},"description":"Every supplier hotel ID mapped to this hotel."}}},"HotelIn":{"additionalProperties":false,"description":"The newer way to name the property: `hotel: {id | name, city, country, supplierIds}`. Either this or `hotelId` / `hotelName`.","properties":{"id":{"anyOf":[{"maxLength":64,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"The catalogue hotel id, as a string.","title":"Id"},"name":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"city":{"anyOf":[{"maxLength":100,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"City"},"country":{"anyOf":[{"pattern":"^[A-Za-z]{2}$","type":"string"},{"type":"null"}],"default":null,"title":"Country"},"supplierIds":{"anyOf":[{"items":{"$ref":"#/components/schemas/SupplierIdIn"},"maxItems":20,"type":"array"},{"type":"null"}],"default":null,"description":"The hotel's id at each of your suppliers; stored for later resolution.","title":"Supplierids"}},"title":"HotelIn","type":"object"},"ImageItem":{"additionalProperties":false,"properties":{"url":{"title":"Url","type":"string"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["url","rateIds"],"title":"ImageItem","type":"object"},"ImageRef":{"additionalProperties":false,"properties":{"url":{"title":"Url","type":"string"},"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The supplier whose rate carried this image.","title":"Supplier"},"index":{"description":"The `index` of that rate.","title":"Index","type":"integer"}},"required":["url","index"],"title":"ImageRef","type":"object"},"NamedAttribute":{"additionalProperties":false,"properties":{"name":{"examples":["Balcony"],"title":"Name","type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"type":"null"}],"default":null,"description":"Returned joined to the name, for example `Floor: 7`.","title":"Value"}},"required":["name"],"title":"NamedAttribute","type":"object"},"Notice":{"additionalProperties":false,"properties":{"code":{"title":"Code","type":"string"},"message":{"title":"Message","type":"string"},"roomId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Roomid"},"retryAfterMs":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Retryafterms"}},"required":["code","message"],"title":"Notice","type":"object"},"Occupancy":{"additionalProperties":false,"description":"The offer's required party size; this is not a guarantee of the room's maximum capacity.","properties":{"adults":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Adults"},"children":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Children"}},"title":"Occupancy","type":"object"},"OccupancyOut":{"additionalProperties":false,"properties":{"adults":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Adults"},"children":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Children"}},"required":["adults","children"],"title":"OccupancyOut","type":"object"},"Options":{"additionalProperties":false,"properties":{"placementPolicy":{"enum":["lenient","guarded","strict"],"title":"Placementpolicy","type":"string"},"doubtfulHotelPolicy":{"enum":["place","hold"],"title":"Doubtfulhotelpolicy","type":"string"},"include":{"items":{"type":"string"},"title":"Include","type":"array"},"contentPreference":{"items":{"type":"string"},"title":"Contentpreference","type":"array"}},"required":["placementPolicy","doubtfulHotelPolicy","include","contentPreference"],"title":"Options","type":"object"},"Placement":{"additionalProperties":false,"properties":{"roomId":{"description":"The current room assignment. Display-only label cleanup preserves IDs; corrected category identity or offer separation can change them.","title":"Roomid","type":"string"},"tier":{"description":"Mapping status. Reviewed category recovery and evidence-based model picks remain `inferred`; neither automatically becomes a booking-verified assignment.","enum":["verified","inferred","unresolved"],"title":"Tier","type":"string"},"reason":{"description":"Why this offer was placed or held. Reviewed exact category recovery uses `name_match` with category evidence; legacy decidedBy is `catalogue_verified`. `evidence_match` requires confirmation that the backend used evidence picking or the opt-in catalogue evidence selector.","enum":["partner_confirmed","booking_confirmed","name_match","evidence_match","best_fit","no_match","no_resemblance","attribute_conflict","partner_separated","room_ambiguous","no_room_list","hotel_doubtful","undecided","multi_room_offer"],"title":"Reason","type":"string"},"similarity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Original encoder similarity, not a correctness probability. Null after recovery changes the target category, because the previous score did not measure the new target.","title":"Similarity"},"feedbackId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedbackid"},"evidence":{"description":"Offer-level conflicts or sourced category recovery. Missing evidence is unknown, not proof that two rooms are equivalent.","items":{"$ref":"#/components/schemas/Evidence"},"title":"Evidence","type":"array"}},"required":["roomId","tier","reason","similarity","feedbackId","evidence"],"title":"Placement","type":"object"},"ProposedAttribute":{"additionalProperties":false,"properties":{"value":{"title":"Value"},"evidence":{"items":{"$ref":"#/components/schemas/AttributeEvidenceSource"},"title":"Evidence","type":"array"}},"required":["value","evidence"],"title":"ProposedAttribute","type":"object"},"Rate":{"additionalProperties":false,"properties":{"rateId":{"title":"Rateid","type":"string"},"roomName":{"title":"Roomname","type":"string"},"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier"},"mealPlan":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"`boardBasis` normalized: Room Only, Bed and Breakfast, Half Board, Full Board, All Inclusive, Unknown; null when not sent.","title":"Mealplan"},"refundable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"`refundability` normalized; null when not sent or not recognisable.","title":"Refundable"},"placement":{"$ref":"#/components/schemas/Placement"},"composition":{"anyOf":[{"$ref":"#/components/schemas/RoomComposition"},{"type":"null"}],"default":null},"sent":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Sent"},"roomAttributes":{"anyOf":[{"$ref":"#/components/schemas/RoomAttributes"},{"type":"null"}],"default":null,"description":"Read attributes with evidence, with `include: [\"attributes\"]`; pending while extraction runs; poll statusUrl using the same API key."}},"required":["rateId","roomName","supplier","placement"],"title":"Rate","type":"object"},"RateIn":{"additionalProperties":false,"description":"A rate in the newer shape: `rateId` instead of `index`, `amenities` instead of `attributes`, `supplier` (the hotel's id at\nthe supplier belongs in `hotel.supplierIds`). A null value means the field was not sent.","properties":{"rateId":{"anyOf":[{"maxLength":64,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"Your id for this rate, unique within the call; defaults to the position as a string.","title":"Rateid"},"roomName":{"description":"The supplier's room name exactly as received.","maxLength":300,"minLength":1,"title":"Roomname","type":"string"},"supplier":{"anyOf":[{"maxLength":64,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"examples":["supplier-a"],"title":"Supplier"},"code":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"Opaque supplier room code, preserved exactly. Longer values are rejected, never truncated.","title":"Code"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"title":"Description"},"view":{"anyOf":[{"maxLength":64,"type":"string"},{"type":"null"}],"default":null,"title":"View"},"beds":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/Bed"}]},"maxItems":8,"type":"array"},{"type":"null"}],"default":null,"title":"Beds"},"amenities":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/AmenityIn"}]},"maxItems":50,"type":"array"},{"type":"null"}],"default":null,"description":"Merged into `rooms[].supplierContent.amenities`; recognised names are normalized. A string must be at most 64 characters.","title":"Amenities"},"images":{"anyOf":[{"items":{"type":"string"},"maxItems":20,"type":"array"},{"type":"null"}],"default":null,"title":"Images"},"boardBasis":{"anyOf":[{"maxLength":64,"type":"string"},{"type":"null"}],"default":null,"description":"Returned as sent, and normalized as `mealPlan` on the rate.","title":"Boardbasis"},"refundability":{"anyOf":[{"maxLength":64,"type":"string"},{"type":"null"}],"default":null,"description":"Returned as sent, and normalized as `refundable` on the rate.","title":"Refundability"},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Supplier-stated value for this offer. Missing means unknown; the room summary requires every offer to state the same boolean.","title":"Smokingallowed"},"nightlyPrice":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"description":"Orders the rates inside a room, cheapest first. Never converted.","title":"Nightlyprice"},"currency":{"anyOf":[{"pattern":"^[A-Z]{3}$","type":"string"},{"type":"null"}],"default":null,"title":"Currency"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"default":null,"description":"Required party size, not room capacity. A known reviewed maximum can rule out a category; absent capacity does not prove suitability."}},"required":["roomName"],"title":"RateIn","type":"object"},"Resolution":{"additionalProperties":false,"properties":{"method":{"enum":["id","supplierId","name"],"title":"Method","type":"string"},"status":{"enum":["matched","doubtful","not_in_catalogue","unresolved"],"title":"Status","type":"string"},"nameSimilarity":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Namesimilarity"},"roomFit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Roomfit"},"alternatives":{"items":{"$ref":"#/components/schemas/Alternative"},"title":"Alternatives","type":"array"}},"required":["method","status","nameSimilarity","roomFit","alternatives"],"title":"Resolution","type":"object"},"Room":{"additionalProperties":false,"properties":{"roomId":{"description":"Opaque category/card identity: `rm_` for a matched category, `un_` for an unresolved card. Display-only cleanup preserves it. Reanchoring to a different category or separating conflicting offers can change it; retain rate IDs to reconcile refreshed mappings.","pattern":"^(rm|un)_[0-9a-f]{12}$","title":"Roomid","type":"string"},"legacyRoomId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Not returned inside a mapping response (there `groups[].stableId` links the two shapes).","title":"Legacyroomid"},"name":{"description":"Category display label. Current shared policies preserve explicitly stated smoking text on unresolved supplier-name cards; catalogue headings omit smoking qualifiers. For inferred rooms, details in this heading are not supplier-confirmed benefits of every offer. Use the original roomName and supplier content attributed to that rate; this label neither changes supplier data nor establishes non-smoking status.","title":"Name","type":"string"},"tier":{"description":"Matched model or reviewed-category placements remain inferred; unresolved cards retain offers that were not safely placed.","enum":["verified","inferred","unresolved"],"title":"Tier","type":"string"},"nearestRoomId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nearestroomid"},"supplierContent":{"$ref":"#/components/schemas/SupplierContent"},"rates":{"anyOf":[{"items":{"$ref":"#/components/schemas/Rate"},"type":"array"},{"type":"null"}],"default":null,"title":"Rates"},"composition":{"anyOf":[{"$ref":"#/components/schemas/RoomComposition"},{"type":"null"}],"default":null}},"required":["roomId","name","tier","nearestRoomId","supplierContent"],"title":"Room","type":"object"},"RoomAttributes":{"additionalProperties":false,"description":"Optional extracted supplier facts, never catalogue verification or mapping input.","properties":{"status":{"enum":["complete","partial","disabled","unavailable","failed","timeout","limit_exceeded","pending"],"title":"Status","type":"string"},"version":{"title":"Version","type":"string"},"attributeId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Attributeid"},"statusUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Statusurl"},"facts":{"additionalProperties":{"$ref":"#/components/schemas/ProposedAttribute"},"title":"Facts","type":"object"},"conflicts":{"items":{"$ref":"#/components/schemas/AttributeConflict"},"title":"Conflicts","type":"array"},"alternatives":{"items":{"$ref":"#/components/schemas/AttributeAlternative"},"title":"Alternatives","type":"array"},"unknownFields":{"items":{"type":"string"},"title":"Unknownfields","type":"array"},"warnings":{"items":{"type":"string"},"title":"Warnings","type":"array"},"errorCode":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Errorcode"},"cached":{"default":false,"title":"Cached","type":"boolean"}},"required":["status","version"],"title":"RoomAttributes","type":"object"},"RoomClass":{"additionalProperties":false,"properties":{"classId":{"description":"Property-scoped browsing ID, stable for the same hotel identity, classification version and terms. Never a booking or room-equivalence ID.","pattern":"^cls_[0-9a-f]{16}$","title":"Classid","type":"string"},"parentClassId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Null for an accommodation type, otherwise the parent type class ID.","title":"Parentclassid"},"name":{"title":"Name","type":"string"},"level":{"enum":["type","class"],"title":"Level","type":"string"},"accommodationType":{"title":"Accommodationtype","type":"string"},"classTerms":{"description":"Normalized grade words such as deluxe or executive, or descriptive name terms when no grade is stated. Multiple terms stay together; no quality ordering or room equivalence is implied.","items":{"type":"string"},"title":"Classterms","type":"array"},"roomIds":{"description":"Direct child references to rooms[].roomId. Also traverse child classes through parentClassId; parent roomIds do not repeat descendants.","items":{"type":"string"},"title":"Roomids","type":"array"}},"required":["classId","parentClassId","name","level","accommodationType","classTerms","roomIds"],"title":"RoomClass","type":"object"},"RoomComponent":{"additionalProperties":false,"properties":{"roomName":{"description":"One complete component phrase from the original supplier name; not a verified category or booking ID.","title":"Roomname","type":"string"}},"required":["roomName"],"title":"RoomComponent","type":"object"},"RoomComposition":{"additionalProperties":false,"properties":{"kind":{"const":"multiple_room_components","title":"Kind","type":"string"},"basis":{"const":"supplier_room_name","title":"Basis","type":"string"},"components":{"description":"Preserves repeated components. The original offer remains one priced item; these are not independently bookable rooms and do not imply connecting rooms.","items":{"$ref":"#/components/schemas/RoomComponent"},"maxItems":4,"minItems":2,"title":"Components","type":"array"}},"required":["kind","basis","components"],"title":"RoomComposition","type":"object"},"RoomHierarchy":{"additionalProperties":false,"properties":{"version":{"enum":["room-classes-2","room-classes-3","room-classes-4"],"title":"Version","type":"string"},"relationship":{"const":"display_group","description":"Shared browsing class only. Child rooms remain distinct, with their original placements and offers.","title":"Relationship","type":"string"},"basis":{"description":"Rules over rooms[].name, LLM-reviewed classifications validated for complete membership and supporting text, or (pair_model, room-classes-4) the pair model's parent rooms: the same room with bed set-up and occupancy ignored. Model review does not establish room equivalence or hotel-authoritative categories.","enum":["room_name","llm_reviewed_room_name","pair_model"],"title":"Basis","type":"string"},"review":{"anyOf":[{"$ref":"#/components/schemas/HierarchyReview"},{"type":"null"}],"default":null},"classes":{"items":{"$ref":"#/components/schemas/RoomClass"},"title":"Classes","type":"array"},"unclassifiedRooms":{"description":"Always display these rooms too. Every response room appears exactly once, either in one class's direct roomIds or here. No unknown room defaults to Standard.","items":{"$ref":"#/components/schemas/UnclassifiedRoom"},"title":"Unclassifiedrooms","type":"array"}},"required":["version","relationship","basis","classes","unclassifiedRooms"],"title":"RoomHierarchy","type":"object"},"RoomMapping":{"additionalProperties":false,"properties":{"requestId":{"examples":["req_4f61fcd7d85e4c53"],"title":"Requestid","type":"string"},"mode":{"enum":["standard","consolidated"],"title":"Mode","type":"string"},"policyVersion":{"description":"Release and rules that produced this answer, for example `2026-10-07.standard`; `.strict` when `mergePolicy` was `strict`, `.guarded` when the attribute guard is on for your key by default. Model proposals may be cached; current offer facts, reviewed property evidence and feedback are reapplied. Updated evidence, catalogue, policy or feedback can change the result.","examples":["2026-10-07.standard"],"title":"Policyversion","type":"string"},"hotel":{"$ref":"#/components/schemas/Hotel"},"rooms":{"anyOf":[{"items":{"$ref":"#/components/schemas/Room"},"type":"array"},{"type":"null"}],"default":null,"description":"The room view: catalogue rooms first, then unresolved cards, each with a stable `roomId`, `tier`, `nearestRoomId`, `supplierContent` and its `rates` (cheapest first when they share a currency). This is the shape to build on.","title":"Rooms"},"roomHierarchy":{"anyOf":[{"$ref":"#/components/schemas/RoomHierarchy"},{"type":"null"}],"default":null,"description":"Returned only with include: [\"hierarchy\"]. Accommodation type → explicit class → existing rooms → offers. Classes organize browsing; they do not merge rooms or improve mapping accuracy."},"roomVariants":{"anyOf":[{"$ref":"#/components/schemas/RoomVariants"},{"type":"null"}],"default":null,"description":"Returned only with include: [\"variants\"]. Attribute-aware selections inside each existing room card. Preserves all original assignments and rates; choose an original rate to book."},"groups":{"description":"Deprecated in favour of `rooms`; retained for compatibility until the announced removal date. Mapping corrections apply to both views.","items":{"$ref":"#/components/schemas/Group"},"title":"Groups","type":"array"},"roomRates":{"description":"Every rate in request order, with its `placement`.","items":{"$ref":"#/components/schemas/RoomRateResult"},"title":"Roomrates","type":"array"},"summary":{"$ref":"#/components/schemas/Summary"},"notices":{"anyOf":[{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},{"type":"null"}],"default":null,"description":"Coded conditions with a sentence each: hotel_doubtful, hotel_not_in_catalogue, hotel_unresolved, mixed_currencies, rates_undecided.","title":"Notices"},"options":{"anyOf":[{"$ref":"#/components/schemas/Options"},{"type":"null"}],"default":null,"description":"The options in effect for this answer: `placementPolicy`, `doubtfulHotelPolicy`, `include`, `contentPreference`."},"warnings":{"description":"Deprecated in favour of `notices`. Sentences a person should read.","items":{"type":"string"},"title":"Warnings","type":"array"},"timingsMs":{"additionalProperties":{"type":"number"},"description":"Deprecated in favour of `latencyMs`.","title":"Timingsms","type":"object"},"usage":{"$ref":"#/components/schemas/Usage","description":"Deprecated."},"latencyMs":{"title":"Latencyms","type":"integer"}},"required":["requestId","mode","policyVersion","hotel","groups","roomRates","summary","warnings","timingsMs","usage","latencyMs"],"title":"RoomMapping","type":"object"},"RoomMappingRequest":{"description":"The original request shape: `hotelId` or `hotelName`, `roomRates[]`, `mode`, `onLowConfidence`, `mergePolicy`. Unknown keys are\nignored; `include` and `contentPreference` are read when present. The newer shape is `RoomsRequest`; the endpoint accepts either\n(a body with `roomRates` is read as this shape).","properties":{"include":{"anyOf":[{"items":{"enum":["sent","attributes","variants","hierarchy","categories","evidence"],"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"`variants` adds roomVariants with attribute-aware offer selections and also extracts attributes. `attributes` adds `roomAttributes` to every rate as extracted supplier facts with evidence; cached immediately or pending with statusUrl. `hierarchy` adds roomHierarchy browsing classes without changing mappings; supported in batch requests too.","title":"Include"},"contentPreference":{"anyOf":[{"items":{"type":"string"},"maxItems":20,"type":"array"},{"type":"null"}],"default":null,"description":"Supplier order for `rooms[].supplierContent`.","title":"Contentpreference"},"bedTypes":{"anyOf":[{"enum":["together","separate"],"type":"string"},{"type":"null"}],"default":null,"description":"`separate`: a stated Twin and a stated King/Queen/Double are different room types, and an offer stating one never joins a room type holding the other. `together`: the hotel's own room list decides, so one room sold with a choice of beds is one room type. Defaults to your key's setting, which is `separate` unless an admin changed it.","title":"Bedtypes"},"response":{"anyOf":[{"enum":["full","compact"],"type":"string"},{"type":"null"}],"default":null,"description":"`compact`: categories, then room types, then offers, each offer once and no pipeline internals (`requestId`, `hotel`, `summary`, `categories`, `unmatched`, plus `warnings`, `roomHierarchy` and `roomVariants` when present); about 80% smaller. See docs/COMPACT_RESPONSE.md. `full` (the default unless your key is set to compact): today's response. Mapping is identical in both.","title":"Response"},"hotelId":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"description":"Plexe catalogue hotel id. Skips the hotel search, the one step that can pick the wrong property. An id the catalogue does not have still gets a full answer: each distinct room name as its own group, with a warning.","examples":["39614731"],"title":"Hotelid"},"hotelName":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Used when there is no `hotelId`; send `city` and `country` with it.","examples":["Taj Lands End, Mumbai"],"title":"Hotelname"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"examples":["Mumbai"],"title":"City"},"country":{"anyOf":[{"pattern":"^[A-Za-z]{2}$","type":"string"},{"type":"null"}],"default":null,"description":"ISO 3166-1 alpha-2, any case. `UK` is read as `GB` and `EL` as `GR`; other unknown codes are rejected.","examples":["IN"],"title":"Country"},"mode":{"default":"standard","description":"`standard`: catalogue room candidates. `consolidated`: website-derived categories when available, with catalogue fallback on pending or failed lookup. Consolidated categories can span saleable variants; inspect placements and warnings. Batches support standard only.","enum":["standard","consolidated"],"title":"Mode","type":"string"},"onLowConfidence":{"default":"flag","description":"When the hotel inventory fits poorly, whether resolved by name or ID: `flag` still groups and warns; `ungrouped` holds the entire inventory unresolved. This is not a per-rate similarity threshold and takes precedence over category recovery.","enum":["flag","ungrouped"],"title":"Onlowconfidence","type":"string"},"mergePolicy":{"default":"standard","description":"`strict`: requests evidence-based picking for close calls and enables offer-level conflict checks. Missing, invalid, failed or late picker answers remain unresolved. Recognized bed, view, bedroom and wing facts are checked across an offer's name and details, including repeated names. Required occupancy is compared with reviewed maximum capacity when known. Strict can choose a different category or abstain; it is not guaranteed only to split standard results. Guards may also be enabled for your key's standard calls (`policyVersion` ends in `.guarded`). `placement.reason: evidence_match` requires the backend to confirm that evidence picking was applied. No numeric threshold option is exposed.","enum":["standard","strict"],"title":"Mergepolicy","type":"string"},"roomRates":{"description":"Every supplier name you have for the hotel, up to 2,000 per call. Without hotelId, or in consolidated mode, at most 600 distinct room names.","items":{"$ref":"#/components/schemas/RoomRate"},"maxItems":2000,"minItems":1,"title":"Roomrates","type":"array"}},"required":["roomRates"],"title":"RoomMappingRequest","type":"object"},"RoomRate":{"properties":{"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Supplier name; filled from `provider` when only that is sent.","examples":["hotelbeds"],"title":"Supplier"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Same as `supplier`; either name is accepted.","examples":["hotelbeds"],"title":"Provider"},"providerHotelId":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"The hotel's id at that supplier. Preserved exactly, including whitespace; longer values are rejected, never truncated.","title":"Providerhotelid"},"code":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"Supplier room code. Preserved exactly, including whitespace; longer values are rejected, never truncated.","examples":["DLX-SV-K"],"title":"Code"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Room description; truncated to 2,000 characters. Affirmative room-specific bed, view, bedroom and wing statements can inform guard checks; hypothetical or hotel-wide descriptions are not guarantees.","title":"Description"},"view":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The view this offer states. Recognized conflicts are checked per offer when the guard is enabled.","examples":["Sea View"],"title":"View"},"boardBasis":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Meal plan.","examples":["BB"],"title":"Boardbasis"},"refundability":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"examples":["Refundable"],"title":"Refundability"},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Whether smoking is allowed in the room, when the supplier says.","title":"Smokingallowed"},"nightlyPrice":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"title":"Nightlyprice"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"ISO 4217 code; truncated to 3 characters.","examples":["INR"],"title":"Currency"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"default":null,"description":"Required adults and children for this offer. Compared with a reviewed maximum category capacity only when that maximum is known; never treated as room capacity itself."},"index":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Your id for this row, returned on every result. Must be unique; defaults to the position.","examples":[0],"title":"Index"},"roomName":{"description":"The supplier's room name, exactly as received. Do not clean it.","examples":[" [1]Bay View Room King Bed(BreakFast)"],"minLength":1,"title":"Roomname","type":"string"},"beds":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/Bed"}]},"maxItems":8,"type":"array"},{"type":"null"}],"default":null,"description":"Strings or bed objects stating this offer's bed setup. Guard checks can separate identical room names carrying conflicting setups; missing beds remain unknown.","title":"Beds"},"attributes":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/NamedAttribute"}]},"maxItems":50,"type":"array"},{"type":"null"}],"default":null,"description":"Strings or `{name, value}` pairs.","title":"Attributes"},"images":{"anyOf":[{"items":{"type":"string"},"maxItems":20,"type":"array"},{"type":"null"}],"default":null,"description":"Room photos as http(s) URLs of at most 512 characters each. Returned on the rate and merged into `groups[].content.images` with the supplier that sent them.","title":"Images"}},"required":["roomName"],"title":"RoomRate","type":"object"},"RoomRateResult":{"additionalProperties":false,"properties":{"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Supplier name; filled from `provider` when only that is sent.","examples":["hotelbeds"],"title":"Supplier"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Same as `supplier`; either name is accepted.","examples":["hotelbeds"],"title":"Provider"},"providerHotelId":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"The hotel's id at that supplier. Preserved exactly, including whitespace; longer values are rejected, never truncated.","title":"Providerhotelid"},"code":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"description":"Supplier room code. Preserved exactly, including whitespace; longer values are rejected, never truncated.","examples":["DLX-SV-K"],"title":"Code"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Room description; truncated to 2,000 characters. Affirmative room-specific bed, view, bedroom and wing statements can inform guard checks; hypothetical or hotel-wide descriptions are not guarantees.","title":"Description"},"view":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The view this offer states. Recognized conflicts are checked per offer when the guard is enabled.","examples":["Sea View"],"title":"View"},"boardBasis":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Meal plan.","examples":["BB"],"title":"Boardbasis"},"refundability":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"examples":["Refundable"],"title":"Refundability"},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Whether smoking is allowed in the room, when the supplier says.","title":"Smokingallowed"},"nightlyPrice":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"default":null,"title":"Nightlyprice"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"ISO 4217 code; truncated to 3 characters.","examples":["INR"],"title":"Currency"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"default":null,"description":"Required adults and children for this offer. Compared with a reviewed maximum category capacity only when that maximum is known; never treated as room capacity itself."},"beds":{"anyOf":[{"items":{"$ref":"#/components/schemas/BedResult"},"type":"array"},{"type":"null"}],"default":null,"title":"Beds"},"attributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"`{name, value}` pairs are returned as `name: value`.","title":"Attributes"},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Images"},"rateId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Your `rateId`, or the sent `index` as a string.","title":"Rateid"},"composition":{"anyOf":[{"$ref":"#/components/schemas/RoomComposition"},{"type":"null"}],"default":null},"placement":{"anyOf":[{"$ref":"#/components/schemas/Placement"},{"type":"null"}],"default":null,"description":"Where the rate landed and why: `roomId` (stable), `tier` (verified, inferred, unresolved), `reason`, `similarity`, `feedbackId`, `evidence`. Prefer it to `matchedRoom`, `encoderScore` and `decidedBy`."},"mealPlan":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"`boardBasis` normalized: Room Only, Bed and Breakfast, Half Board, Full Board, All Inclusive, Unknown; null when not sent.","title":"Mealplan"},"refundable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"`refundability` normalized; null when not sent or not recognisable.","title":"Refundable"},"roomAttributes":{"anyOf":[{"$ref":"#/components/schemas/RoomAttributes"},{"type":"null"}],"default":null,"description":"Read attributes with evidence (bed, view, room size, bathroom, balcony, smoking and more), with `include: [\"attributes\"]`; pending while extraction runs; poll statusUrl using the same API key. (`attributes` is the amenity list you sent.)"},"index":{"title":"Index","type":"integer"},"roomName":{"title":"Roomname","type":"string"},"matchedRoom":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Deprecated: use `placement.roomId`.","title":"Matchedroom"},"encoderScore":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"description":"Deprecated: use `placement.similarity`. Not a calibrated correctness probability. Null when category recovery changes the target because the previous score does not measure that category.","title":"Encoderscore"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"description":"The model's calibrated probability for a close-call decision: the room in `matchedRoom`, or keeping the name apart when it is null. Null when the match was certain.","examples":[0.91],"title":"Confidence"},"decidedBy":{"description":"How the room was chosen. `picker_cached` and `picker_none_cached` are picker decisions, match or no match, reused from an earlier call. `ungrouped_low_confidence`: the hotel match was doubtful and `onLowConfidence` was `ungrouped`, so the name was returned on its own. `no_catalogue_rooms`: the hotel has no catalogue rooms. `attribute_conflict`: the attribute guard (`mergePolicy` `strict`, or on by default for your key when `policyVersion` ends in `.guarded`) kept the name out of the room it matched, because the name or its details conflict with the category, another offer, or each other. `catalogue_candidate`: an opt-in evidence selector chose a supported alternative; placement remains inferred and the original similarity is cleared. `catalogue_verified`: a reviewed property record supplied an exact or explicitly reviewed variant category; modern placement remains `name_match`/`inferred` with sourced evidence. `category_unverified`: a recorded supplier alias needs confirmation; placement is `room_ambiguous`/`unresolved` and offers are retained. `picker_unavailable`: under `strict`, the picker did not answer for this close call, so it stays on its own rather than taking the model's guess. `low_similarity`: nothing in the hotel's room list resembles the name, so it stays on its own rather than being placed on a guess. `partner_feedback`: your own feedback (`POST /v1/feedback`) says this name does not belong where the model put it, so it comes back on its own.","enum":["encoder","picker","picker_cached","picker_none","picker_none_cached","picker_unavailable","no_catalogue_rooms","ungrouped_low_confidence","attribute_conflict","low_similarity","partner_feedback","catalogue_verified","category_unverified","catalogue_candidate","composite_offer"],"title":"Decidedby","type":"string"}},"required":["index","roomName","decidedBy"],"title":"RoomRateResult","type":"object"},"RoomVariant":{"additionalProperties":false,"properties":{"variantId":{"description":"Selection reference, never a booking ID. Changes with evidence or variant version; book using the original rate.","pattern":"^var_[0-9a-f]{20}$","title":"Variantid","type":"string"},"status":{"enum":["stated","isolated"],"title":"Status","type":"string"},"rateIds":{"description":"References to this parent room's existing rates. Every inventory rate occurs exactly once across all variants.","items":{"type":"string"},"title":"Rateids","type":"array"},"attributes":{"additionalProperties":true,"description":"Equal, explicitly extracted facts only. Follow each rate's roomAttributes for source quotes. Matching attributes do not prove category equivalence or a supplier guarantee.","title":"Attributes","type":"object"},"unknownAttributes":{"items":{"type":"string"},"title":"Unknownattributes","type":"array"},"reviewReasons":{"items":{"type":"string"},"title":"Reviewreasons","type":"array"}},"required":["variantId","status","rateIds","attributes","unknownAttributes","reviewReasons"],"title":"RoomVariant","type":"object"},"RoomVariants":{"additionalProperties":false,"properties":{"version":{"const":"room-variants-1","title":"Version","type":"string"},"relationship":{"const":"attribute_profile","title":"Relationship","type":"string"},"status":{"description":"Processing state only. Complete can contain isolated offers, unknowns or conflicts.","enum":["pending","partial","complete"],"title":"Status","type":"string"},"statusUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Same-key GET refreshes this inventory from existing extraction jobs, without remapping or model calls. Present for synchronous requests while their log is retained; null for completed batch results.","title":"Statusurl"},"rooms":{"items":{"$ref":"#/components/schemas/VariantRoom"},"title":"Rooms","type":"array"},"metrics":{"$ref":"#/components/schemas/VariantMetrics"}},"required":["version","relationship","status","statusUrl","rooms","metrics"],"title":"RoomVariants","type":"object"},"RoomsRequest":{"additionalProperties":false,"description":"The newer standard-mode request shape: unknown fields are refused. Folded names share a model proposal;\nenabled guards then evaluate individual offers, which can separate despite identical names.","properties":{"hotel":{"$ref":"#/components/schemas/HotelIn"},"rates":{"items":{"$ref":"#/components/schemas/RateIn"},"maxItems":2000,"minItems":1,"title":"Rates","type":"array"},"placementPolicy":{"anyOf":[{"enum":["lenient","guarded","strict"],"type":"string"},{"type":"null"}],"default":null,"description":"`lenient`: uses the model's best-fit proposals. `guarded` (default): checks recognized offer facts for conflicts, including identical names with different details. `strict`: adds evidence-based picking for close calls. Strict may abstain or choose a different target, so results are not guaranteed to be subdivisions of lenient results. Missing evidence remains unknown; this is not a zero-error guarantee.","title":"Placementpolicy"},"doubtfulHotelPolicy":{"anyOf":[{"enum":["place","hold"],"type":"string"},{"type":"null"}],"default":null,"description":"`place` (default) still places when the property fits your names poorly and adds a notice; `hold` returns every rate unresolved with reason `hotel_doubtful`.","title":"Doubtfulhotelpolicy"},"include":{"anyOf":[{"items":{"enum":["sent","attributes","variants","hierarchy","categories","evidence"],"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"`variants` adds roomVariants with attribute-aware offer selections and also extracts attributes; `sent` echoes every field you sent on each rate in `rooms[].rates[].sent`; `attributes` adds `roomAttributes` as extracted supplier facts with evidence; cached immediately or pending with statusUrl; `hierarchy` adds optional browsing classes in roomHierarchy without changing mappings; `categories` is reserved.","title":"Include"},"contentPreference":{"anyOf":[{"items":{"type":"string"},"maxItems":20,"type":"array"},{"type":"null"}],"default":null,"description":"Supplier order for `supplierContent`: images and descriptions from the first listed supplier come first.","title":"Contentpreference"},"bedTypes":{"anyOf":[{"enum":["together","separate"],"type":"string"},{"type":"null"}],"default":null,"description":"`separate`: a stated Twin and a stated King/Queen/Double are different room types, and an offer stating one never joins a room type holding the other. `together`: the hotel's own room list decides, so one room sold with a choice of beds is one room type. Defaults to your key's setting, which is `separate` unless an admin changed it.","title":"Bedtypes"},"response":{"anyOf":[{"enum":["full","compact"],"type":"string"},{"type":"null"}],"default":null,"description":"`compact`: categories, then room types, then offers, each offer once and no pipeline internals (`requestId`, `hotel`, `summary`, `categories`, `unmatched`, plus `warnings`, `roomHierarchy` and `roomVariants` when present); about 80% smaller. See docs/COMPACT_RESPONSE.md. `full` (the default unless your key is set to compact): today's response. Mapping is identical in both.","title":"Response"}},"required":["hotel","rates"],"title":"RoomsRequest","type":"object"},"Summary":{"additionalProperties":false,"properties":{"inputRooms":{"title":"Inputrooms","type":"integer"},"uniqueNames":{"description":"Distinct `roomName` values. The model proposes once per name; offer-level guards may separate rates sharing that name.","title":"Uniquenames","type":"integer"},"groups":{"title":"Groups","type":"integer"},"reduction":{"description":"1 minus groups divided by input names.","title":"Reduction","type":"number"},"closeCalls":{"title":"Closecalls","type":"integer"},"pickerCalls":{"title":"Pickercalls","type":"integer"},"uncatalogued":{"title":"Uncatalogued","type":"integer"},"attributedRooms":{"description":"Deprecated. Rates sent with fields beyond `roomName` and `supplier`.","title":"Attributedrooms","type":"integer"},"feedbackApplied":{"default":0,"description":"Rates (`roomRates` rows) kept apart because of your feedback on this hotel.","title":"Feedbackapplied","type":"integer"},"rateCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Rates you sent.","title":"Ratecount"},"nameCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Distinct name keys among them (case, spacing and punctuation folded).","title":"Namecount"},"roomCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Entries in `rooms[]`, unresolved cards included.","title":"Roomcount"},"unresolvedRoomCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Entries in `rooms[]` with `tier` `unresolved`.","title":"Unresolvedroomcount"},"ratesByTier":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"default":null,"description":"Rates per `placement.tier`.","title":"Ratesbytier"}},"required":["inputRooms","uniqueNames","groups","reduction","closeCalls","pickerCalls","uncatalogued","attributedRooms"],"title":"Summary","type":"object"},"SupplierContent":{"additionalProperties":false,"properties":{"images":{"items":{"$ref":"#/components/schemas/ImageItem"},"title":"Images","type":"array"},"descriptions":{"items":{"$ref":"#/components/schemas/DescriptionItem"},"title":"Descriptions","type":"array"},"amenities":{"items":{"$ref":"#/components/schemas/AmenityItem"},"title":"Amenities","type":"array"},"beds":{"items":{"$ref":"#/components/schemas/BedItem"},"title":"Beds","type":"array"},"views":{"items":{"$ref":"#/components/schemas/ViewItem"},"title":"Views","type":"array"},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/OccupancyOut"},{"type":"null"}],"description":"Aggregated required adults and children supplied on these offers; not a maximum room capacity."},"smokingAllowed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"A boolean only when every offer explicitly supplies the same value. Missing or conflicting values produce null. Original per-offer values remain available.","title":"Smokingallowed"}},"required":["images","descriptions","amenities","beds","views","occupancy","smokingAllowed"],"title":"SupplierContent","type":"object"},"SupplierIdIn":{"additionalProperties":false,"properties":{"supplier":{"maxLength":64,"minLength":1,"title":"Supplier","type":"string"},"id":{"maxLength":64,"minLength":1,"title":"Id","type":"string"}},"required":["supplier","id"],"title":"SupplierIdIn","type":"object"},"TextRef":{"additionalProperties":false,"properties":{"text":{"title":"Text","type":"string"},"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Supplier"},"index":{"title":"Index","type":"integer"}},"required":["text","index"],"title":"TextRef","type":"object"},"UnclassifiedRoom":{"additionalProperties":false,"properties":{"roomId":{"title":"Roomid","type":"string"},"reason":{"enum":["non_room_or_unspecified","ambiguous_name","ambiguous_type","type_not_stated","llm_uncertain"],"title":"Reason","type":"string"}},"required":["roomId","reason"],"title":"UnclassifiedRoom","type":"object"},"Usage":{"additionalProperties":false,"properties":{"websiteCache":{"anyOf":[{"enum":["hit","miss","joined","pending","skip"],"type":"string"},{"type":"null"}],"default":null,"description":"Consolidated mode. `pending`: the website is still being read; groups are catalogue rooms and a warning says so.","title":"Websitecache"}},"title":"Usage","type":"object"},"VariantMetrics":{"additionalProperties":false,"properties":{"rateCount":{"title":"Ratecount","type":"integer"},"browsingRoomCount":{"title":"Browsingroomcount","type":"integer"},"variantCount":{"title":"Variantcount","type":"integer"},"isolatedRateCount":{"title":"Isolatedratecount","type":"integer"},"reductionDenominator":{"const":"rateCount","title":"Reductiondenominator","type":"string"},"browsingReduction":{"title":"Browsingreduction","type":"number"},"variantReduction":{"description":"Attribute-profile compression against input rateCount, not measured correct-equivalence reduction or accuracy.","title":"Variantreduction","type":"number"}},"required":["rateCount","browsingRoomCount","variantCount","isolatedRateCount","reductionDenominator","browsingReduction","variantReduction"],"title":"VariantMetrics","type":"object"},"VariantOption":{"additionalProperties":false,"properties":{"value":{"title":"Value"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["value","rateIds"],"title":"VariantOption","type":"object"},"VariantRoom":{"additionalProperties":false,"properties":{"roomId":{"title":"Roomid","type":"string"},"sharedAttributes":{"additionalProperties":true,"description":"Stated by every rate in this parent. Not copied from the category heading, and not independently hotel-verified.","title":"Sharedattributes","type":"object"},"availableOptions":{"additionalProperties":{"items":{"$ref":"#/components/schemas/VariantOption"},"type":"array"},"description":"Values available on specific rates; never universal benefits of the parent.","title":"Availableoptions","type":"object"},"variants":{"items":{"$ref":"#/components/schemas/RoomVariant"},"title":"Variants","type":"array"}},"required":["roomId","sharedAttributes","availableOptions","variants"],"title":"VariantRoom","type":"object"},"ViewItem":{"additionalProperties":false,"properties":{"view":{"title":"View","type":"string"},"rateIds":{"items":{"type":"string"},"title":"Rateids","type":"array"}},"required":["view","rateIds"],"title":"ViewItem","type":"object"},"BatchInputRecord":{"type":"object","required":["customId","request"],"additionalProperties":false,"properties":{"customId":{"type":"string","minLength":1,"maxLength":128,"pattern":"\\S","description":"Unique within this file; echoed in results."},"request":{"allOf":[{"oneOf":[{"$ref":"#/components/schemas/RoomMappingRequest"},{"$ref":"#/components/schemas/RoomsRequest"}]},{"properties":{"mode":{"enum":["standard",null]}}}],"description":"Existing mapping request. Only standard mode; optional attributes wait for extraction. Invalid mapping requests become record errors."}}},"BatchJob":{"type":"object","required":["jobId","status","counts","createdAt","updatedAt","expiresAt","mappingRevision"],"properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["queued","running","completed","completed_with_errors"]},"counts":{"type":"object","required":["total","pending","running","succeeded","failed","cacheHits"],"properties":{"total":{"type":"integer","minimum":0},"pending":{"type":"integer","minimum":0},"running":{"type":"integer","minimum":0},"succeeded":{"type":"integer","minimum":0},"failed":{"type":"integer","minimum":0},"cacheHits":{"type":"integer","minimum":0}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","description":"Download results before this time."},"mappingRevision":{"type":["string","null"],"description":"Server mapping/model/catalogue revision associated with this job."},"links":{"type":"object","additionalProperties":{"type":"string"}},"webhookDeliveries":{"type":"array","description":"Newest first: this job's batch.completed webhook deliveries, if a webhook URL applies.","items":{"$ref":"#/components/schemas/BatchWebhookDelivery"}}}},"BatchWebhookDelivery":{"type":"object","required":["deliveryId","url","status","attempts"],"properties":{"deliveryId":{"type":"string","description":"Also sent as the Plexe-Delivery header; use it to ignore duplicates."},"url":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer","minimum":0},"lastStatus":{"type":["integer","null"]},"lastError":{"type":["string","null"]},"deliveredAt":{"type":["string","null"],"format":"date-time"},"nextAttemptAt":{"type":["string","null"],"format":"date-time"}}},"BatchWebhookConfig":{"type":"object","required":["url","secret","event"],"properties":{"url":{"type":["string","null"],"description":"Default https URL for this key's batches; null when none is set."},"secret":{"type":["string","null"],"description":"Signing secret. Shown in full only when set or rotated; otherwise a prefix."},"event":{"const":"batch.completed"},"updatedAt":{"type":"string"}}},"BatchWebhookEvent":{"type":"object","required":["event","deliveryId","createdAt","job"],"description":"The JSON body POSTed to your URL when a batch finishes. Headers: Plexe-Event, Plexe-Delivery, Plexe-Timestamp and Plexe-Signature (v1=hex HMAC-SHA256 of '<Plexe-Timestamp>.<raw body>' with your secret). Answer 2xx within 10 seconds; other answers are retried with backoff for 24 hours. Redirects are not followed.","properties":{"event":{"const":"batch.completed"},"deliveryId":{"type":"string"},"createdAt":{"type":"string"},"redelivery":{"type":"boolean"},"job":{"$ref":"#/components/schemas/BatchJob"}}},"BatchResult":{"oneOf":[{"type":"object","required":["customId","status","attempts","cacheHit","response"],"properties":{"customId":{"type":"string"},"status":{"const":"succeeded"},"attempts":{"type":"integer","minimum":0},"cacheHit":{"type":"boolean"},"response":{"$ref":"#/components/schemas/RoomMapping"}}},{"type":"object","required":["customId","status","attempts","cacheHit","error","request"],"properties":{"customId":{"type":"string"},"status":{"const":"failed"},"attempts":{"type":"integer","minimum":0},"cacheHit":{"type":"boolean"},"request":{},"error":{"type":"object","required":["status","message","retryable"],"properties":{"status":{"type":"integer","description":"Per-record status; 507 means the output could not be retained within storage limits and is not retryable."},"message":{"type":"string"},"retryable":{"type":"boolean"},"code":{"type":"string","description":"Optional error code, including result_too_large or storage_capacity."}}}}}]},"HotelRecord":{"type":"object","required":["name"],"description":"One supplier hotel record. Only `name` is required; coordinates and the street address matter most.","properties":{"name":{"type":"string","description":"Hotel name as the supplier sends it."},"address1":{"type":"string","description":"Street address."},"address2":{"type":"string","description":"Address line 2."},"city":{"type":"string","description":"City."},"state":{"type":"string","description":"State or region."},"postalCode":{"type":"string","description":"Postal code."},"countryCode":{"type":"string","description":"ISO 3166 alpha-2; alpha-3 and UK are accepted."},"lat":{"type":"number","description":"Latitude."},"lon":{"type":"number","description":"Longitude."},"phones":{"type":"array","items":{"type":"string"}},"emails":{"type":"array","items":{"type":"string"}},"website":{"type":"string","description":"Hotel website."},"chain":{"type":"string","description":"Chain."},"brand":{"type":"string","description":"Brand."},"starRating":{"type":"number","description":"Star rating."},"propertyType":{"type":"string","description":"Property type."},"topK":{"type":"integer","minimum":1,"maximum":10,"default":5,"description":"Candidates to return."}}},"HotelMatch":{"type":"object","required":["requestId","decision","hotelId","candidates","issues"],"properties":{"requestId":{"type":"string","description":"This call in your request log."},"decision":{"type":"string","enum":["match","review","new"],"description":"`match`: one Plexe hotel clearly fits. `review`: a likely hotel, evidence not conclusive. `new`: no hotel in the index fits."},"hotelId":{"type":["string","null"],"description":"The matched Plexe hotel ID when the decision is `match`."},"candidates":{"type":"array","items":{"type":"object","properties":{"hotelId":{"type":"string","description":"Plexe hotel ID."},"score":{"type":"number","description":"Probability that the record and this hotel are the same building."},"hotel":{"type":"object","properties":{"name":{"type":["string","number","null"]},"address1":{"type":["string","number","null"]},"city":{"type":["string","number","null"]},"countryCode":{"type":["string","number","null"]},"lat":{"type":["string","number","null"]},"lon":{"type":["string","number","null"]}}},"reasons":{"type":"array","items":{"type":"string"},"description":"The evidence behind the score."}}}},"issues":{"type":"array","items":{"type":"string"},"description":"Data-quality problems in the record you sent."}}},"HotelMapping":{"type":"object","properties":{"source":{"type":"string"},"sourceId":{"type":"string"},"hotelId":{"type":"string"},"hotel":{"$ref":"#/components/schemas/Hotel"}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"rms_...","description":"Your sandbox key, sent as `Authorization: Bearer <key>`. The same key signs you in to the portal."}}}}