# Effective Tours REST API v1: agent instructions

## 1. Purpose and scope

These instructions are for an AI assistant that helps a developer build an integration with the Effective Tours (ET) REST API v1 (a PMS, a channel manager, a pricing tool, a reporting job). Everything here uses `curl` so that it is independent of any programming language. Translate the calls into the language of the project when the user asks for code.

You have no access to ET source code. Work only from the OpenAPI specification and from this file. Do not guess endpoints, fields or behaviour that are not in the specification. If something is missing, tell the user and point to the contact form (section 10).

## 2. Before every session

1. Fetch the specification from https://api.effectivetours.com/v1/openapi.json (a plain GET, no key needed).
2. List its operations (method + path) and compare them with the catalogue in section 5.
3. If they differ, the specification wins. Tell the user what differs.
4. `info.version` is a fixed label (currently `0.1.0`). It does not change when operations are added. It is not a change signal: compare the operation list, not the version.

## 3. Base URL and authentication

- Base URL: `https://api.effectivetours.com/v1`
- Every call except `/health` sends `Authorization: Bearer <key>`.
- There is one key per company. The company administrator finds or generates it in the Effective Tours web app under Company Settings > Company Profile > API Access. Generating a new key invalidates the old one.
- No public sign-up creates a company. A prospect without an ET account uses the contact form (section 10).
- Examples below read the key from the environment variable `ET_API_KEY`.

```bash
curl -s "https://api.effectivetours.com/v1/health"
```

## 4. The key stays server-side

- Keep the key in an environment variable or a secret store on a server.
- Never put it in a URL, a log, a commit, browser code or a mobile app. CORS is open, so a browser technically can call the API, but then every visitor can read the key.
- Do not print the key in your answers or paste it into prompts.

## 5. Operation catalogue

There are 16 operations. Each deep link opens the reference at that operation.

| Method and path | Purpose | Reference |
|---|---|---|
| GET /health | Liveness check, no key | https://api.effectivetours.com/docs#/core/health_health_get |
| GET /auth/me | The company the key belongs to | https://api.effectivetours.com/docs#/auth/me_auth_me_get |
| GET /hotels/ | Hotels the company can access | https://api.effectivetours.com/docs#/hotels/get_hotels_hotels__get |
| GET /hotels/{id} | One hotel | https://api.effectivetours.com/docs#/hotels/get_hotel_hotels__id__get |
| GET /hotels/{id}/rooms | Rooms of a hotel | https://api.effectivetours.com/docs#/hotels/get_hotel_rooms_hotels__id__rooms_get |
| GET /hotels/{id}/rates | Rate types of a hotel | https://api.effectivetours.com/docs#/hotels/get_hotel_rates_hotels__id__rates_get |
| GET /hotels/{id}/rates/{rate_id}/prices | Daily prices of a rate (every day held, no date parameters) | https://api.effectivetours.com/docs#/hotels/get_hotel_rate_prices_hotels__id__rates__rate_id__prices_get |
| POST /hotels/{id}/rates/{rate_id}/prices | Write daily prices (distributors only, cannot be undone) | https://api.effectivetours.com/docs#/hotels/update_hotel_rate_prices_hotels__id__rates__rate_id__prices_post |
| POST /hotels/{hotel_id}/ota-update | Enqueue channel availability updates | https://api.effectivetours.com/docs#/hotels/launch_ota_update_hotels__hotel_id__ota_update_post |
| GET /hotels/{hotel_id}/reservations | List reservations (use start and end) | https://api.effectivetours.com/docs#/reservations/get_reservations_hotels__hotel_id__reservations_get |
| POST /hotels/{hotel_id}/reservations | Create a reservation | https://api.effectivetours.com/docs#/reservations/create_reservation_hotels__hotel_id__reservations_post |
| DELETE /hotels/{hotel_id}/reservations | Cancel reservations by reference | https://api.effectivetours.com/docs#/reservations/cancel_reservations_by_reference_hotels__hotel_id__reservations_delete |
| GET /hotels/{hotel_id}/reservations/{id} | One reservation | https://api.effectivetours.com/docs#/reservations/get_reservation_hotels__hotel_id__reservations__id__get |
| PATCH /hotels/{hotel_id}/reservations/{reservation_id} | Change a reservation | https://api.effectivetours.com/docs#/reservations/update_reservation_hotels__hotel_id__reservations__reservation_id__patch |
| DELETE /hotels/{hotel_id}/reservations/{reservation_id} | Delete one reservation by id | https://api.effectivetours.com/docs#/reservations/delete_reservation_hotels__hotel_id__reservations__reservation_id__delete |
| GET /hotels/{hotel_id}/bookings/cancelled | Cancelled bookings in a window | https://api.effectivetours.com/docs#/reservations/get_cancelled_bookings_hotels__hotel_id__bookings_cancelled_get |

What the API does not have: no availability read, no webhooks or push feed, no pagination, no "changed since" filter, no restriction (minimum stay, closed dates) update, no sandbox. There is no operation to "push availability": Effective Tours computes availability from units and reservations, so every booking from the user's system must be written as a reservation. A reservation written through the API sends no guest voucher or confirmation e-mail and no owner notification; the user's system owns those messages.

## 6. Recipes

Read the full request and response shapes in the specification. Values below are fictional.

### 6.1 First connection

Check the service, the key and the hotels. All three are reads and safe.

```bash
curl -s "https://api.effectivetours.com/v1/health"
```

```bash
curl -s "https://api.effectivetours.com/v1/auth/me" \
  -H "Authorization: Bearer $ET_API_KEY"
```

```bash
curl -s "https://api.effectivetours.com/v1/hotels/" \
  -H "Authorization: Bearer $ET_API_KEY"
```

### 6.2 Map rooms and rates

Read the hotel, its rooms and its rate types, then build a mapping table in the user's system (their room id to ET room id, their rate to ET rate id).

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{id}" \
  -H "Authorization: Bearer $ET_API_KEY"
```

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{id}/rooms" \
  -H "Authorization: Bearer $ET_API_KEY"
```

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{id}/rates" \
  -H "Authorization: Bearer $ET_API_KEY"
```

### 6.3 Read and update prices

Reading is safe. The GET returns every day held and takes no date parameters. Writing is for distributor companies only and cannot be undone: the only "undo" is to overwrite with the right values. Ask the user for an explicit go-ahead before the POST (never write prices as a test unless the user asks for it). The body is a JSON array of day objects with the keys `date`, `sgl`, `dbl`, `tpl`, `full`.

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{id}/rates/{rate_id}/prices" \
  -H "Authorization: Bearer $ET_API_KEY"
```

```bash
curl -s -X POST "https://api.effectivetours.com/v1/hotels/{id}/rates/{rate_id}/prices" \
  -H "Authorization: Bearer $ET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{"date": "2027-03-01", "sgl": 80, "dbl": 100, "tpl": 120, "full": 140}]'
```

Then read the prices again to verify. To send the new state to the connected channels, enqueue an update (this is a write: ask for the user's explicit go-ahead first). It only enqueues jobs; no per-channel delivery result is returned. Read the allowed `channel_id` values in the specification; omit `channel_id` to update all channels.

```bash
curl -s -X POST "https://api.effectivetours.com/v1/hotels/{hotel_id}/ota-update" \
  -H "Authorization: Bearer $ET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"start_date": "2027-03-01", "end_date": "2027-03-31"}'
```

### 6.4 Reservation sync

There is no pagination, no cap and no "changed since" filter. Without `start` the list returns stays ending after today; without `end` it has no upper bound. Always send both `start` and `end`, in small windows. Reconcile on the reservation `id`, and on `reference_code` (the value you sent as `reference` at create time) for bookings you wrote yourself. Read cancellations separately. Poll a property no more often than every 2-5 minutes.

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{hotel_id}/reservations?start=2027-03-01&end=2027-03-31&full=true" \
  -H "Authorization: Bearer $ET_API_KEY"
```

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{hotel_id}/bookings/cancelled?start=2027-03-01&end=2027-03-31" \
  -H "Authorization: Bearer $ET_API_KEY"
```

Guest data (`first_name`, `last_name`, `client`, `comment`) is visible on every reservation to the hotel owner. An agent or distributor company sees it only on reservations it created; elsewhere the names and `client` are null and `comment` is an empty string.

### 6.5 Create a booking safely

A reservation is real and reaches the connected channels. Ask for the user's explicit go-ahead first (a write test only when the user explicitly asks for it; cancel each test booking at once). Read the rooms and rates first, then create with a `reference` of your own. A retry with the same `reference` returns the existing live reservation with status 200 instead of creating a duplicate. Give either `room_id` (wins when supplied) or `rate_id` (the server assigns a free room of that type). `accommodation`, `checkin` and `checkout` are required. `accommodation` is `sgl`, `dbl`, `tpl` or `full` (lower case; `full` is the whole unit). The OpenAPI document types it as a plain string and lists no values; any other value is refused with `422`.

```bash
curl -s -X POST "https://api.effectivetours.com/v1/hotels/{hotel_id}/reservations" \
  -H "Authorization: Bearer $ET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rate_id": 2001, "accommodation": "dbl", "checkin": "2027-06-10", "checkout": "2027-06-12", "adults": 2, "children": 0, "reference": "example-booking-0001", "first_name": "Jane", "last_name": "Example", "email": "jane.example@example.com"}'
```

Then read it back by id, and set `is_accepted` when your flow requires it.

```bash
curl -s "https://api.effectivetours.com/v1/hotels/{hotel_id}/reservations/{id}" \
  -H "Authorization: Bearer $ET_API_KEY"
```

### 6.6 Modify and cancel

These are writes: ask for the user's explicit go-ahead before the PATCH and before every cancel. PATCH sends only the fields to change, plus the required `id`. Re-read afterwards. Cancel a whole booking by `reference`; cancelling a reference that is already cancelled is a success no-op. Verify through the reservation list.

```bash
curl -s -X PATCH "https://api.effectivetours.com/v1/hotels/{hotel_id}/reservations/{reservation_id}" \
  -H "Authorization: Bearer $ET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id": 3001, "checkin": "2027-06-11", "checkout": "2027-06-13"}'
```

```bash
curl -s -X DELETE "https://api.effectivetours.com/v1/hotels/{hotel_id}/reservations?reference=example-booking-0001" \
  -H "Authorization: Bearer $ET_API_KEY"
```

## 7. Errors and handling

| Status | Body | What to do |
|---|---|---|
| 401 | `{"detail":"Invalid API key"}` (also an inactive company) | Re-check the key and the Bearer header. Do not retry in a loop. |
| 403 | `{"detail":"You are not allowed to access this hotel"}` (write without access) | The key has no access to the hotel, or the company type may not do this call. Tell the user. |
| 404 | `{"detail":"Hotel not found"}` (read without access); `{"detail":"Reservation not found"}` | Check the id and that the hotel belongs to the key's company. |
| 410 | `{"detail":"Requested HotelID: <id> not found."}` | The hotel was removed. Stop using that id. |
| 400 | `{"detail":"Room is not available for this period"}` | Choose other dates or another room. |
| 409 | On auto-assign: `detail` is a list. Also `{"error":"company_moved"}` (the key is `error`, not `detail`) | No room of the type is free for the dates; or contact the user, the company has moved. |
| 422 | Standard validation error | Fix the request body or parameters against the specification. |
| 503 | `{"detail":"Authentication backend unavailable"}` | Back off and retry later. |
| 429 | Not documented in the specification | Treat as a signal to slow down: back off. |
| Network error or timeout | none | Retry reads. Retry a create only with the same `reference`. Never blindly retry a price POST: read the state first. |

There is no published rate limit. Be gentle: keep date windows small and poll no more often than every 2-5 minutes.

## 8. MUST

- Fetch and compare the specification at the start of every session (section 2).
- Ask for the user's explicit go-ahead before any write call (POST, PATCH, DELETE). There is no sandbox: a reservation or a price you write is real and reaches the connected channels.
- Make a write test only when the user explicitly asks for it. Then use dates far in the future, keep it to a few bookings and cancel each one at once.
- Always send `start` and `end` when listing reservations.
- Create reservations with a `reference`, and re-read after every write.
- Read the key from the environment variable `ET_API_KEY`.

## 9. MUST NOT

- Never put the key in a URL, a log, browser code or a mobile app.
- Never assume pagination, a "changed since" filter, webhooks or an availability read.
- Never assume a rate limit exists or that none exists; back off on 429 and 503.
- Never POST prices casually: a price POST cannot be undone.
- Never invent endpoints, fields or behaviour that are not in the specification.
- Never write anything (reservation, price, update, change or cancel) as a test unless the user explicitly asked for that test.

## 10. Links

- Reference: https://api.effectivetours.com/docs
- Specification: https://api.effectivetours.com/v1/openapi.json
- Help center chapter: https://effectivetours.com/docs/effective-tours-rest-api-v-1
- Contact form (missing features, or a prospect without an account): https://effectivetours.com/contacts
