Launching soon.Get early access
Veridien Docs
API Reference

Conventions

Every endpoint follows the same conventions for status codes, errors, retries, and pagination. Learn them once and they apply everywhere.

Status codes

StatusMeaning
200Success.
201A resource was created (a guest, hold, reservation confirmation, or charge).
400Invalid request — malformed JSON or a parameter that failed validation.
401Authentication failed — missing or invalid API key.
403The key is valid but lacks the required scope.
404The route or resource does not exist (or is not yours).
405The path exists but not for this HTTP method.
409Conflict — no availability, an idempotency-key reuse, or an insufficient loyalty balance.
429Rate limited. Retry after the Retry-After header.
500Internal server error.

The error envelope

Every failure returns the matching status and a single error object:

{
  "error": {
    "type": "permission",
    "code": "insufficient_scope",
    "message": "Missing required scope: reservations:write.",
    "param": null,
    "request_id": "req_a1b2c3d4e5"
  }
}
  • type — the category: invalid_request, authentication, permission, not_found, conflict, rate_limit, or server.
  • code — a stable, machine-readable identifier. Switch on this, not on message.
  • message — a human-readable explanation. Safe to log; do not parse.
  • param — when present, the request field that caused the error (for 400s).
  • request_id — uniquely identifies the request. Include it when contacting support.

Code over message

message text may change; code will not. Branch your error handling on error.code (for example, no_availability, rate_plan_not_found, insufficient_points).

Common codes you will encounter:

CodeTypeWhen
missing_api_key / invalid_api_keyauthenticationThe Authorization header is absent or the key is unknown/disabled/expired.
insufficient_scopepermissionThe key lacks the endpoint's scope.
invalid_parameters / invalid_jsoninvalid_requestA field failed validation, or the body was not valid JSON.
unknown_routenot_foundNo endpoint matches the path.
no_availabilityconflictThe room type is sold out for the requested dates.
idempotency_key_reuseconflictAn Idempotency-Key was reused with a different request body.
insufficient_pointsconflictA loyalty redemption exceeds the available balance.
rate_limitedrate_limitThe per-key request limit was exceeded.

Idempotency

POST and PATCH requests are the ones that change state — creating a guest, holding inventory, confirming a reservation, redeeming points, posting a charge. To make retries safe, send an Idempotency-Key header with a unique value (a UUID works well):

curl -X POST https://veridien.app/api/v1/holds \
  -H "Authorization: Bearer vrdn_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1d2c9a-8b3e-4a17-9c2f-1e5b7d0a4c83" \
  -d '{ "room_type_id": "rt_deluxe", "rate_plan_id": "rp_flex", "check_in": "2026-08-01", "check_out": "2026-08-04", "guest_id": "G7K2P9QX", "adults": 2 }'
  • The first request with a given key runs normally and its response is stored.
  • A retry with the same key and the same body replays the stored response — no second hold, no second charge.
  • Reusing the key with a different body returns 409 idempotency_key_reuse.

Keys are scoped per API key. Use a fresh Idempotency-Key per logical operation.

Confirm is doubly idempotent

Confirming a hold (POST /holds/{id}/confirm) is also idempotent on its payment_reference: re-confirming an already-confirmed reservation, or replaying the same payment reference, returns the existing confirmed result with "already_confirmed": true rather than charging again.

Pagination

List endpoints (such as GET /reservations) page with limit and offset:

ParameterDefaultMaxMeaning
limit25100How many records to return.
offset0How many records to skip.

The response wraps results in data and reports whether more remain:

{
  "has_more": true,
  "data": [ /* ... */ ]
}

To fetch the next page, add limit to your previous offset. Stop when has_more is false.

Rate limits

Rate limits use a leaky bucket (token bucket), per API key. Your bucket holds up to 600 tokens and refills at a constant 10 tokens per second (600 per minute). Every request spends one token:

  • A burst can spend the whole bucket at once — up to 600 requests back to back.
  • Sustained throughput is the refill rate, 10 requests per second.

When the bucket is empty, requests return 429 with a Retry-After header. Back off for the indicated number of seconds, then retry. Prefer caching reads (availability and rates carry a short Cache-Control) and avoid tight polling loops.

Request IDs

Every response — success or error — is associated with a request_id (also returned in error bodies and from /me). Log it. When you report an issue, the request id lets support trace the exact call.