Skip to main content

Error Response Format

Every error response follows the same structure:
{
  "status": "error",
  "code": "ERROR_CODE",
  "message": "Human-readable description of what went wrong",
  "field": "fieldName",
  "requestId": "req_a1b2c3d4"
}
FieldAlways PresentDescription
statusYesAlways "error"
codeYesMachine-readable error code (use this for programmatic handling)
messageYesHuman-readable description (may change — don’t parse this)
fieldNoWhich field caused the error (only on validation errors)
requestIdYesUnique request ID for support inquiries
Always use the code field for error handling in your code, not message. The message text may be updated to be more helpful — the code will remain stable.

Error Codes

HTTP StatusCodeDescription
400INVALID_JSONRequest body is not valid JSON
400FIELD_REQUIREDA required field is missing. The field property tells you which one.
400FIELD_INVALIDField value is invalid (wrong type, out of range, too long, etc.)
401UNAUTHORIZEDMissing or invalid API key. See Authentication.
401KEY_REVOKEDAPI key has been revoked by the hotel admin
404GUEST_NOT_FOUNDNo active guest with the given pmsGuestId
409ROOM_OCCUPIEDRoom already has an active guest. Response includes currentGuest info.
409DUPLICATE_GUESTAn active guest with the same pmsGuestId already exists
422INVALID_DATE_FORMATDate is not in ISO 8601 format (YYYY-MM-DD)
422INVALID_PHONE_FORMATPhone number is not in E.164 format
422ROOM_NOT_FOUNDRoom number doesn’t exist in the hotel’s configured room list
429RATE_LIMITEDToo many requests. See Retry-After header and Rate Limits.
500INTERNAL_ERRORServer error. Contact support with the requestId.

Validation Rules

Every field is validated on the server. Here are the rules for each field:
FieldTypeConstraints
pmsGuestIdstringRequired. 1–255 characters. Must be unique among active guests.
lastNamestringRequired. 1–100 characters.
firstNamestringOptional. 1–100 characters.
roomNumberstringRequired for check-in. Must match a room in the hotel’s configured room list.
phonestringOptional. Must be E.164 format (see below).
checkInDatestringOptional (defaults to today). Must be YYYY-MM-DD.
checkOutDatestringRequired for check-in. Must be YYYY-MM-DD. Must be on or after checkInDate.
languagestringOptional. ISO 639-1 code: en, tr, de, fr, es, ru, etc.
nationalitystringOptional. ISO 3166-1 alpha-2 code: US, TR, DE, etc.
emailstringOptional. Must be a valid email format.
notesstringOptional. Maximum 2,000 characters.
adultsintegerOptional. Range: 1–20. Defaults to 1.
childrenintegerOptional. Range: 0–20. Defaults to 0.

Date Format

Strict ISO 8601. No exceptions.
FormatExampleStatus
YYYY-MM-DD2026-02-18Accepted
DD.MM.YYYY18.02.2026Rejected (422)
MM/DD/YYYY02/18/2026Rejected (422)
DD-Mon-YYYY18-Feb-2026Rejected (422)
Unix timestamp1708214400Rejected (422)
When a date fails validation, the error message includes what you sent and what’s expected:
{
  "status": "error",
  "code": "INVALID_DATE_FORMAT",
  "message": "Date must be in ISO 8601 format: YYYY-MM-DD. Received: '18.02.2026'",
  "field": "checkInDate",
  "requestId": "req_y5z6a7b8"
}
Turkish PMS systems (Protel, Byte, Hicell) commonly use DD.MM.YYYY format. Make sure your integration converts dates to YYYY-MM-DD before sending to RecepAI.

Phone Number Format

E.164 format required. Starts with +, followed by country code and number. No spaces, dashes, or parentheses.
ExampleStatusWhy
+905551234567AcceptedCorrect E.164
+491234567890AcceptedCorrect E.164
+442071234567AcceptedCorrect E.164
05551234567RejectedMissing country code (+90)
+90 555 123 4567RejectedContains spaces
0090-555-123-4567RejectedContains dashes and leading zeros
(555) 123-4567RejectedUS local format — not E.164
{
  "status": "error",
  "code": "INVALID_PHONE_FORMAT",
  "message": "Phone must be in E.164 format (+905551234567). Received: '05551234567'",
  "field": "phone",
  "requestId": "req_c3d4e5f6"
}

Common Mistakes

This is the most common error for Turkish PMS integrations. Protel, Byte, and Hicell all default to DD.MM.YYYY internally.Fix: Convert all dates to YYYY-MM-DD before sending. For example, 18.02.20262026-02-18.
Sending 05551234567 instead of +905551234567. The phone field requires the full international number with country code.Fix: Prepend the country code with +. For Turkey, that’s +90. For Germany, +49. For the UK, +44.
Sending {"roomNumber": 301} instead of {"roomNumber": "301"}. Room numbers are strings because they can contain letters (e.g., "301A", "P2-105").Fix: Always send room numbers as strings: "301", not 301.
All POST endpoints require an Idempotency-Key header. Without it, you’ll receive a 400 error.Fix: Include Idempotency-Key: your_unique_key in every POST request. See Idempotency for key format recommendations.
Each unique operation needs its own key. If you use the same key for two different guests, the second request will return the first guest’s cached response.Fix: Use a key format like {operation}_{pmsGuestId}_{date}. Example: checkin_RES2026001_20260218.
If Room 301 still has an active guest and you try to check in a new guest to Room 301, you’ll get a 409 ROOM_OCCUPIED error.Fix: Always send a checkout for the departing guest before checking in the new guest. Or use the Full Sync endpoint which handles this automatically.

Handling Errors in Your Code

Here’s a recommended error handling pattern:
async function checkinGuest(slug, apiKey, guestData, idempotencyKey) {
  const response = await fetch(
    `https://app.recepai.ai/api/pms/v1/${slug}/guests/checkin`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey
      },
      body: JSON.stringify(guestData)
    }
  );

  const data = await response.json();

  if (response.ok) {
    // Success — guest created (or idempotent replay)
    return data;
  }

  // Handle specific error codes
  switch (data.code) {
    case 'ROOM_OCCUPIED':
      // Room has another guest — check them out first
      console.log(`Room occupied by ${data.currentGuest.lastName}`);
      break;
    case 'DUPLICATE_GUEST':
      // Guest already checked in — maybe use update instead
      console.log(`Guest already exists: ${data.existingGuest.guestId}`);
      break;
    case 'RATE_LIMITED':
      // Wait and retry
      const retryAfter = data.retryAfter || 60;
      await sleep(retryAfter * 1000);
      return checkinGuest(slug, apiKey, guestData, idempotencyKey);
    case 'FIELD_REQUIRED':
    case 'FIELD_INVALID':
    case 'INVALID_DATE_FORMAT':
    case 'INVALID_PHONE_FORMAT':
      // Fix your payload — don't retry with the same data
      console.error(`Validation error on ${data.field}: ${data.message}`);
      break;
    default:
      console.error(`Unexpected error: ${data.code}${data.message}`);
  }

  throw new Error(`PMS API error: ${data.code}`);
}
Retry guidance:
  • 4xx errors (except 429): Fix your request before retrying. Retrying the same payload will return the same error.
  • 429 errors: Wait for the retryAfter period, then retry.
  • 5xx errors: Retry with exponential backoff (1s, 2s, 4s, 8s). Include the requestId if contacting support.