Documentation / Guides
Batch processing
Submit complete hotel inventories, get notified when they finish, and download results.
Submit a job
Use POST /v1/room-mapping-batches with the same bearer key. Upload UTF-8 JSONL with one complete hotel inventory per line. Each line contains exactly a unique customId and request using either supported mapping shape.
{"customId":"hotel-001","request":{"hotelId":"15489679","roomRates":[{"index":0,"roomName":"Classic King Room"}]}}
This is an illustrative one-offer record; your upload should contain each hotel's full supplier inventory. Standard mode and include: ["hierarchy"] are supported. Optional include: ["attributes"] is supported; the record waits for extraction. Consolidated mode is not supported in batches. Attribute processing and retries.
curl --fail-with-body -X POST https://model.plexe.ai/v1/room-mapping-batches \
-H "Authorization: Bearer $PLEXE_KEY" \
-H "Content-Type: application/x-ndjson" \
-H "Idempotency-Key: inventory-2026-10-08-001" \
--data-binary @hotels.jsonl
Choose a new idempotency key for each new submission: 1–128 visible ASCII characters without spaces. Retrying the same key and identical file bytes returns the existing retained job; different bytes return 409. Preserve both when an upload response is lost.
Get results
- A new upload returns
202and ajobId. - Poll
GET /v1/room-mapping-batches/{jobId}untilcompletedorcompleted_with_errors. - Download
/{jobId}/resultsfor all records or/{jobId}/errorsfor failed records. Both return JSONL under the same collection.
Join results using customId, not line order. Each result includes status, attempts and cacheHit, plus the normal mapping response or an error and original request. The Batches page also uploads files and tracks jobs.
Get notified by webhook
Instead of polling, register an https URL once per API key. When any batch from that key finishes, Plexe POSTs a signed batch.completed event with the job's final status, counts and download links. Polling keeps working either way.
curl --fail-with-body -X PUT https://model.plexe.ai/v1/webhooks/room-mapping-batches \
-H "Authorization: Bearer $PLEXE_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/plexe/batches"}'
The response shows the signing secret (whsec_…) in full: store it. Later reads show only its start; POST /v1/webhooks/room-mapping-batches/rotate-secret issues a new one. To send one batch somewhere else, add a Webhook-Url header to its upload; it is signed with the same secret.
POST https://example.com/plexe/batches
Plexe-Event: batch.completed
Plexe-Delivery: whd_3f2a…
Plexe-Timestamp: 1794250000
Plexe-Signature: v1=5c1e…
{"event": "batch.completed", "deliveryId": "whd_3f2a…", "createdAt": "…",
"job": {"jobId": "batch_…", "status": "completed", "counts": {…},
"links": {"results": "https://model.plexe.ai/v1/room-mapping-batches/batch_…/results", …}}}
Verify every event before trusting it: compute HMAC-SHA256 of <Plexe-Timestamp>.<raw body> with your secret, compare it with the hex after v1= in constant time, and reject timestamps more than five minutes old.
import hashlib, hmac, time
def verify(secret: str, headers, body: bytes) -> bool:
stamp = headers["Plexe-Timestamp"]
expected = "v1=" + hmac.new(secret.encode(), stamp.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, headers["Plexe-Signature"]) and abs(time.time() - int(stamp)) < 300
Answer with any 2xx within 10 seconds, then download results. Other answers, timeouts and redirects are retried with backoff (30 seconds, 2, 10 and 30 minutes, then hourly and longer) for 24 hours. Deliveries can repeat, so use Plexe-Delivery to ignore duplicates. Each job's webhookDeliveries shows attempts and the last answer; POST /v1/room-mapping-batches/{jobId}/webhook/resend sends the event again. URLs must use https and resolve to public internet addresses.
Retry failures
POST /v1/room-mapping-batches/{jobId}/retry retries only failures marked retryable: true and preserves successful records. Repeating it while a retry is active is safe.
Malformed JSONL, duplicate custom IDs and invalid envelopes reject the whole upload. An invalid mapping request inside a valid line fails only that record. Correct non-retryable requests and submit them with a new idempotency key.
Limits and quota
| Limit | Value |
|---|---|
| Hotels per job / offers per hotel | 1,000 / 2,000 |
| Uncompressed upload / line size | 32 MiB / 512 KiB |
| Active jobs per account | 5 |
| Job and result retention | 7 days; download before expiresAt. |
Every accepted hotel reserves one daily request, including cache hits and invalid mapping requests. Polling, downloads and retries consume no additional quota. Batch processing shares your single-hotel quota. Accounts configured as unlimited have no daily submission cap; usage is still recorded. This bounded pilot does not provide a throughput or completion-time SLA.
Storage and revision errors
Deployment storage limits can reject a new job. Default limits are a 512 MiB bulk reservation budget, 2 MiB serialized output per hotel and a 256 MiB free-disk floor. A result that cannot be retained fails individually with status 507, retryable: false and its original request. Resolve the limit, then resubmit the complete hotel. Splitting by supplier or room subset changes the mapping problem.
If a pinned model/catalogue revision changes before a queued record runs, that record fails with a non-retryable 409. Submit it in a new job under the current revision; earlier successful results remain available.
Refresh an inventory
Completed results are submission snapshots. Use a new job and idempotency key to evaluate updated inventory, evidence or policy. Refresh prices and availability at shopping time rather than treating a periodic mapping batch as current availability.
When versioned model/catalogue reuse is configured, eligible repeats can skip model work. Current supplier content, prices, feedback and guards are still applied. Reuse is disabled without that revision pin. Reviewed property evidence is reapplied without fetching websites or calling an LLM for each offer.