Quickstart

Five minutes from zero to your first successful call. Everything here runs against staging (https://staging-api.quendoo.com/v1/qc).

0. Get a Bearer token

Two ways to get one — pick the one that matches your role. Both result in a token you send as Authorization: Bearer <token> on every subsequent call, so the rest of this page is identical after this step. Details on both flows on the Auth concepts page.

0a. Dashboard-minted token (hoteliers, agencies, quick tests)

Open the Quendoo dashboard → Pem systems → Quendoo Connect → API tokens → Add. Pick your scopes, properties and endpoint scopes; the token (a qc_… string) is shown once — copy it before closing the dialog. Use it directly:

export TOKEN="qc_………"

No OAuth exchange, no expiry (unless you set one). This is what you want if you are testing the API from the interactive reference.

0b. OAuth 2.0 client credentials (partner PMS/CM)

Requires a client_id / client_secret provisioned by Quendoo — contact [email protected]. There is no self-service form, and the dashboard tokens page does NOT create these.

curl -X POST https://staging-api.quendoo.com/v1/qc/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$QC_CLIENT_ID" \
  -d "client_secret=$QC_CLIENT_SECRET"
{
  "access_token": "eyJhbGciOiJSUzI1NiJ9…",
  "token_type":   "Bearer",
  "expires_in":   3600,
  "scope":        "properties.read inventory.write bookings.read"
}

Save the access_token. It is a JWT that lasts one hour — refresh from the same call proactively.

1. Read a property — this is where external_property_id comes from

Every property-scoped endpoint on QC is addressed by {external_property_id} (/properties/{external_property_id}/room_types, /properties/{external_property_id}/inventory/*, etc). You get one from the top-level properties list:

curl -H "Authorization: Bearer $TOKEN" \
  https://staging-api.quendoo.com/v1/qc/properties

You get every property the caller can touch — copy the external_property_id of the one you want to work with:

{
  "status": "ok",
  "message": "ok",
  "data": {
    "properties": [
      { "external_property_id": "01J2XZ0K…", "name": "Sunrise Test Hotel 1", "mode": "shadow" }
    ]
  }
}

Getting an empty list is not a bug — it means the caller has no enrolled properties yet. Enrol one from the dashboard under Pem systems → Quendoo Connect → App Store, or from the API with POST /v1/qc/properties.

On the interactive reference: paste the value into the external_property_id variable field on any property-scoped operation and hit Send.

export EXT_PID="01J2XZ0K…"

2. Find a room type

Inventory is written per room type, addressed by its external_room_type_id:

curl -H "Authorization: Bearer $TOKEN" \
  https://staging-api.quendoo.com/v1/qc/properties/$EXT_PID/room_types

Copy the external_room_type_id of one of them:

export ROOM_TYPE="98a87689-…"

3. Push some inventory

You send the number of free rooms for a date range (both ends inclusive):

curl -X POST https://staging-api.quendoo.com/v1/qc/properties/$EXT_PID/inventory/availability \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "values": [
      { "room_type_id": "'"$ROOM_TYPE"'", "date_from": "2027-02-14", "date_to": "2027-02-14", "availability": 3 }
    ]
  }'
{ "status": "ok", "message": "accepted",
  "data": { "written": 0, "unchanged": 0, "skipped_out_of_window": 0, "dry": true, "warnings": [] } }

A 202. "dry": true and all-zero counters mean the property is still in shadow: the body was validated, nothing was written and nothing was counted (the mode gate). Re-run the identical curl → same body, plus Idempotency-Replayed: true in the response headers. The same key with a different body is a 409. That's the core write contract in one call.

4. What now

Common first mistakes