Skip to main content

Sandbox Mode

Use a test API key (pms_test_ prefix) to validate your integration without writing any data to the hotel’s system. When the hotel admin generates an API key, they receive both a live key and a test key as a pair — just like Stripe. Ask the hotel admin for the test key to start development. Test keys run the exact same validation as live keys — the only difference is that no data is persisted.
BehaviorLive (pms_live_)Test (pms_test_)
Payload validationFull validationFull validation
Data written to databaseYesNo
Real-time dashboard updatesYesNo
Response formatReal IDsFake IDs (test_xxx)
Rate limitsNormalSame limits
Request loggingYesYes (marked as test)

Test Response Example

{
  "status": "created",
  "guestId": "test_abc123",
  "pmsGuestId": "RES-2026-001",
  "roomNumber": "301",
  "requestId": "req_test_a1b2",
  "_sandbox": true
}
The _sandbox: true flag confirms you’re in test mode and no data was written. This flag is never present in live responses.

What to Test

Use sandbox mode to verify:
  1. Payload format — Confirm all your field formats are correct (dates, phone numbers, etc.)
  2. Error handling — Send intentionally invalid data to test your error handling code
  3. Idempotency — Verify that duplicate requests return cached responses
  4. Rate limit handling — Confirm your code handles 429 responses gracefully
Recommended workflow: Develop and test with a pms_test_ key first. Once your integration passes all test cases, switch to the pms_live_ key for production. The only change needed is the API key — all endpoints and request formats are identical.

Idempotency

All POST endpoints require an Idempotency-Key header. This prevents duplicate operations when network issues cause retries — for example, if your system sends a check-in request but doesn’t receive the response due to a timeout, you can safely resend the same request.

How It Works

  1. You include an Idempotency-Key header with every POST request
  2. RecepAI processes the request and stores the response, keyed to your idempotency key
  3. If you send another request with the same key within 24 hours, RecepAI returns the cached response without re-processing

Rules

RuleDetail
RequiredAll POST endpoints require this header. Omitting it returns 400.
Length1–255 characters
ScopeKeys are scoped to your API key. Different hotels can use the same idempotency key safely.
TTLKeys expire after 24 hours. After that, the same key can be reused.
CachingOnly 2xx responses are cached. If a request fails with 4xx, the key is NOT consumed — you can retry with a corrected payload using the same key.
Use a structured format that’s unique per operation:
{operation}_{pmsGuestId}_{date}
Examples:
OperationIdempotency Key
Check in guest RES-001 on Feb 18checkin_RES001_20260218
Update guest RES-001 room changeupdate_RES001_room_20260218
Check out guest RES-001checkout_RES001_20260218
Morning sync on Feb 19sync_20260219_morning

Duplicate Response

When a cached response is returned, it includes an _idempotent flag:
{
  "status": "created",
  "guestId": "abc123-def456",
  "pmsGuestId": "RES-2026-001",
  "roomNumber": "301",
  "requestId": "req_a1b2c3d4",
  "_idempotent": true
}
The _idempotent: true flag tells you this is a cached replay, not a new operation. The response body, HTTP status, and requestId are all identical to the original response.

Important: Failed Requests Don’t Consume Keys

If your request returns a 4xx error (validation failure, room conflict, etc.), the idempotency key is not consumed. This means you can fix the issue and retry with the same key:
# First attempt — has a date format error
curl -X POST .../guests/checkin \
  -H "Idempotency-Key: checkin_RES001_20260218" \
  -d '{"checkOutDate": "18.02.2026"}'  # Wrong format!
# Returns 422 INVALID_DATE_FORMAT — key NOT consumed

# Second attempt — fixed payload, same key
curl -X POST .../guests/checkin \
  -H "Idempotency-Key: checkin_RES001_20260218" \
  -d '{"checkOutDate": "2026-02-18"}'  # Correct format
# Returns 200 — key now consumed with this response

Rate Limits

Each endpoint has its own rate limit. Limits are per API key (not per hotel or per IP).

Limits by Endpoint

EndpointLimit
POST /guests/checkin60 requests / minute
POST /guests/update60 requests / minute
POST /guests/checkout60 requests / minute
GET /guests30 requests / minute
POST /guests/sync10 requests / hour

Response Headers

Every response includes rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1708180260
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Rate Limited Response (429)

When you exceed the limit:
HTTP/1.1 429 Too Many Requests
Retry-After: 45
Content-Type: application/json
{
  "status": "error",
  "code": "RATE_LIMITED",
  "message": "Rate limit exceeded. Retry after 45 seconds.",
  "retryAfter": 45,
  "requestId": "req_u1v2w3x4"
}

Handling Rate Limits

Implement exponential backoff in your integration:
async function callWithRetry(requestFn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await requestFn();

    if (response.status !== 429) {
      return response;
    }

    // Use the server's Retry-After value
    const data = await response.json();
    const waitSeconds = data.retryAfter || Math.pow(2, attempt);
    console.log(`Rate limited. Waiting ${waitSeconds}s before retry...`);
    await new Promise(r => setTimeout(r, waitSeconds * 1000));
  }

  throw new Error('Max retries exceeded');
}
For the sync endpoint (10/hour): Schedule your daily sync at a fixed time (e.g., 08:00). One sync per day is sufficient. For real-time updates throughout the day, use the individual check-in, update, and checkout endpoints which have much higher limits (60/minute).

Request Logging

Every API request is logged — including test mode requests. Logs include:
  • Full request payload
  • Response status and body
  • IP address and User-Agent
  • Processing duration
  • Idempotency key (if provided)
Logs are retained for 90 days and are accessible to RecepAI support for debugging. When contacting support, always include the requestId from the response.

Security Notes

  • All API traffic is encrypted via HTTPS (TLS 1.2+). HTTP requests are rejected.
  • API keys are stored as SHA-256 hashes — the full key is never stored on our servers.
  • PMS API paths are exempt from CSRF middleware (machine-to-machine authentication, no browser cookies).
  • All guest changes from the PMS are recorded in an activity log with the actor type pms, visible alongside staff actions on the Front Desk.