Staylah

Core concepts

Responses and errors

One envelope everywhere, and the stable error codes to branch on.

One shape, everywhere on this surface.

Success

json
{
  "success": true,
  "data": { },
  "request_id": "3f9c…",
  "meta": { }
}

meta is present only when there is something to put in it (pagination, batch counts).

Error

json
{
  "success": false,
  "error": {
    "code": "missing_scope",
    "message": "This API key is not permitted to perform that action.",
    "details": { "required_scope": "ari.read" }
  },
  "request_id": "3f9c…"
}

error.code is a stable machine string — never a number, never a sentence. Branch on code, never on message; treat the list as part of the contract.

Error codes#

CodeHTTPMeaningWhat to do
unauthorized401Credentials missing, wrong, revoked, expired, wrong environment, IP blocked, or signature bad/stale/replayedCheck key, secret, environment, clock. Quote request_id to support.
forbidden403Permitted key, refused actionContact Staylah
missing_scope403Key lacks a required scope. details.required_scope names the first one missingAsk for the scope; check /whoami
rate_limited429Per-key ceiling exceeded. details.retry_after seconds, also sent as Retry-AfterBack off for retry_after
validation_failed422Payload rejected. details.fields is a field → messages mapFix the payload; do not retry unchanged
not_found404We have never heard of this identifierCheck the identifier
unmapped_resource404The code exists but is not mapped to you. details.resource, details.codeAsk Staylah to map it — distinct from not_found on purpose, because the fix is different
conflict409Conflicts with existing stateRead the current state before retrying
no_availability409Nothing sellable for that stay. details.reason names which gate failedBusiness outcome, not an error to retry blindly — see below
rate_changed409The total you sent is not the price for the stay. details.expected_total, details.received_totalRe-quote, then re-book at the price you are given
not_implemented501Contract published, data not yet servedNo route emits this today (§0)
internal_error500Our faultRetry with backoff; report with request_id

details.reason on no_availability tells you whether to try other dates or to call us:

details.reasonMeansYours to fix?
no_inventoryFewer rooms left than you asked forNo — try other dates or fewer rooms
inventory_not_loadedWe have not loaded a calendar for one of those datesNo — ours. Report it
restrictedA stop-sell, CTA/CTD or MLOS/MaxLOS rule blocks the stayNo — the hotel closed it
no_rateNo rate is loaded for your account on those datesNo — ours. Report it

occupancy_not_supported is returned as validation_failed (422), not no_availability, because it is the one gate you can fix: the party does not fit the room, or the rate plan is nationality-restricted.

One caveat still stands:

  • details.retry_after is currently a fixed 60, not the true reset window. Honour the value rather than assuming a minute — it will start being computed without a version bump, and a client hardcoded to 60 will hammer us early.