Authentication: All API endpoints require a Bearer token with either read or read_write permissions. This /v1/manual page is public for convenience.
If you do not have an API token, please create one in your SafariOffice account under Settings → SafariOffice API.
If you need help, please contact us at dev@safarioffice.com.
Status-specific fields: For requests with status booked, responses include booked_value, booked_currency, booked_at, start_date and booked_quotes (IDs). For requests with status notbooked, responses include notbooked_reason when provided.
| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /v1/clients | List clients | Query: page (int, optional) limit (int, optional) search (string, optional) |
| GET | /v1/clients/{id} | Get client by ID | |
| GET | /v1/clients/{id}/requests | List requests for client | Query: status (string, optional): new|working|open|prebooked|booked|completed|notbooked search (string, optional) page (int, optional) limit (int, optional) |
| GET | /v1/clients/{id}/quotes | List quotes for client | Query: status (string, optional): draft|sent|archived search (string, optional) page (int, optional) limit (int, optional) |
| POST | /v1/clients | Create client | JSON Body: lastname (string, required) email (string, required, valid email) salutation (string, optional) firstname (string, optional) phone (string, optional) country (string, optional) |
| PUT | /v1/clients/{id} | Update client | JSON Body: salutation (string, optional) firstname (string, optional) lastname (string, optional) email (string, optional, valid email) phone (string, optional) country (string, optional) |
| DELETE | /v1/clients/{id} | Delete client (no non-deleted requests) |
| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /v1/requests | List requests | Query: status (string, optional): new|working|workingopen|open|prebooked|booked|completed|notbooked client_id (int, optional) source (string, optional): filter by lead source, exact match (e.g. "SafariBookings", "Website") search (string, optional) page (int, optional) limit (int, optional) |
| GET | /v1/requests/{id} | Get request by ID | |
| GET | /v1/requests/{id}/quotes | List quotes for a request | Query: status (string, optional): draft|sent|archived page (int, optional) limit (int, optional) |
| POST | /v1/requests | Create a new request | JSON Body: client_id (int, required) language (string, optional): en|de|es|fr|it — defaults to account default language lead_source (string, optional): e.g. "Zoho CRM", "Website" reference_id (string, optional): external reference ID (e.g. CRM lead ID) tour_template_id (int, optional): template ID from GET /v1/templates tour_title (string, optional): ignored when tour_template_id is set tour_type (string, optional): ignored when tour_template_id is set tour_length (int or string, optional): e.g. 14 or "14 days" — ignored when tour_template_id is set countries (array[int], optional): country IDs — ignored when tour_template_id is set start_destination (string, optional) end_destination (string, optional) start_date (string, optional): YYYY-MM-DD format travelers (array, optional): [{"amount": 2, "name": "Adults"}] room_settings (array, optional): [{"amount": 1, "name": "Double Room"}] notes (string, optional): client question or request info Requires read_write token. New traveler types, room types, lead sources, and tour types are automatically added to your autocomplete suggestions. When tour_template_id is set, tour details (title, type, length, countries) are derived from the template. |
| PUT | /v1/requests/{id} | Update request (limited fields) | JSON Body: lead_source (string, optional) reference_id (string, optional) Only lead_source and reference_id can be updated via API. Other status changes use dedicated endpoints. |
| POST | /v1/requests/{id}/booked | Book a request | JSON Body: quote_ids (array[int], required) booking_value (number, required) booking_currency (string, required) booking_confirmation_date (string, required, ISO datetime) start_date (string, required, ISO datetime) Only available with read_write tokens. Quotes must be in sent status. When booked, responses include booked_value, booked_currency, booked_at, start_date and booked_quotes. GET /v1/requests/{id} additionally returns payment_milestones for the booked quote — see the Quotes group for its shape. |
| POST | /v1/requests/{id}/notbooked | Mark request as not booked | JSON Body: reason (string, optional) Can be used on open, working, pre-booked, or booked requests. Optional reason is stored for audit purposes. |
| DELETE | /v1/requests/{id} | Delete request (only if it has no quotes) |
| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /v1/templates | List tour templates | Query: search (string, optional): filter by template name page (int, optional) limit (int, optional) |
| GET | /v1/templates/{id} | Get template details (includes day-level itinerary) | Response always includes: template_id, name, status, language (code), days_count, countries, created_at, updated_at. Included only when set (omitted otherwise): image_url (cover image, 1240x1754), image_thumb_url (400x566), online_tour_url (public tour page URL, when the Share Tours add-on is active and the template is published), from_price ({currency, rates: {"Rate for 2026": 9500, ...}}). The detail endpoint also returns days. Each day always has day_number and no_of_days, and — only when set — country, title, main_destination, accommodation, meal_plan (a string, or an object keyed by day number when it varies across a multi-day block), and activities (each with name, location, from, to, via, transfer_time, time and optional, each present only when set). The list endpoint returns the same top-level fields except days. |
| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /v1/quotes | List quotes | Query: status (string, optional): draft|sent|archived request_id (int, optional) page (int, optional) limit (int, optional) |
| GET | /v1/quotes/{id} | Get quote by ID | Includes payment_milestones when the company has the Payment Milestones feature, otherwise null — null means no feature, an empty milestones array means the feature with nothing set up. Each milestone carries label, description, timing_kind, timing_offset, percentage, amount, currency, status (pending|invoice_sent|payment_received), due_date and excluded_from_total. Amount and percentage are null on a quote with no priced lines: 25% of nothing is not 0.00. due_date is only calculable for departure-relative milestones on a quote with a parseable start_date; where it is null, due_date_unresolved_reason says which case it was rather than implying no deadline. The block also returns quote_total, scheduled_total, outstanding_total (excludes payment_received and anything excluded_from_total; a magnitude for triage, not an invoice), by_status and inherited_from_company_default. |
| GET | /v1/quotes/for-request/{requestId} | List quotes for a request | |
| DELETE | /v1/quotes/{id} | Delete quote |
| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /v1/analytics/usage | API usage metrics | |
| GET | /v1/analytics/rate-limit | Rate limit status |
Standard error response contains: success=false, error, error_msg, error_code. HTTP statuses: 4xx for client errors, 5xx for server errors.