---
description: ONDA Vendor API 3.0 context for AI-assisted development
globs: 
alwaysApply: true
---


---


================================================================================
## Introduction
================================================================================

### ONDA Partner Developer Center

> Developer documentation for ONDA Hub API integration. Connect your accommodation platform quickly and easily! REST API guide for Vendor and Channel Partners, real-time integration with 25,000+ accommodation properties.

# ONDA Partner Developer Center

Welcome to the developer documentation for ONDA Hub API integration.

Expand your business with ONDA Hub's API, an integrated distribution platform connecting accommodation providers with sales channels.

## Before You Start

### What Type of Partner Are You?

ONDA Hub supports two types of partners:

#### Vendor Partner (Provider)
Do you want to supply accommodation properties to ONDA and sell through 70+ channels?
- Hotels, resorts, pensions, and other accommodation facilities
- PMS system providers
- Hotel chains and product suppliers

**→ [View Vendor API Documentation](/docs/api/vendor/vendor-api)**

#### Channel Partner (Seller)
Do you want to sell ONDA's 25,000+ accommodation properties?
- OTA (Online Travel Agency)
- Travel agencies and metasearch engines
- Accommodation booking platforms

**→ [View Channel API Documentation](/docs/api/channel/channel-api)**

## Quick Start Guide

ONDA Hub API integration follows 4 steps:

### 1. Identify Partner Type
Choose the API that fits your business model.

**→ [Partner Type Guide](/docs/getting-started)**

### 2. Contract and API Key Issuance
Receive your API key after signing a partnership agreement with ONDA.

**Contact:** [Contact us](https://onda.me/en/contact/)

### 3. API Integration Development
Average development time: **1 month (1 M/M)**

**Technical Support:** techsupport@onda.me

### 4. Testing and Launch
Launch your service after completing QA.

## Key Documentation

### Guides
- **[ONDA Hub Introduction](/docs/introduction)** - Detailed platform overview
- **[Getting Started](/docs/getting-started)** - Step-by-step integration guide
- **[Glossary](/docs/glossary)** - API terminology

### API Reference
- **[Vendor API](/docs/api/vendor/vendor-api)** - API for providers
- **[Channel API](/docs/api/channel/channel-api)** - API for sellers

### Resources
- **[Changelog](/changelog)** - API update news

## Technical Specifications

### API Endpoints
```
Production: https://gds.tport.io
Development: https://dapi.tport.dev
```

### Basic Information
- **Protocol:** HTTPS
- **Data Format:** JSON (UTF-8)
- **Authentication:** API Key / OAuth 2.0

## Technical Support

### Contact Channels
- **Technical Inquiries:** techsupport@onda.me
- **Partnership Applications:** [Contact us](https://onda.me/en/contact/)

### Support Hours
- Weekdays: 10:00 - 18:00 (KST)
- Emergency Support: 24/7

---

---

### ONDA Hub Introduction

> ONDA Hub is Korea

# ONDA Hub Introduction

ONDA Hub is **Korea's leading B2B accommodation API connectivity platform**, enabling **Vendor Partners** (hotels, PMS systems, property managers) and **Channel Partners** (OTAs, travel apps, booking platforms) to integrate with Korea's accommodation ecosystem through standardized REST APIs.

## What is ONDA Hub?

ONDA Hub is the core distribution infrastructure of the Korean accommodation industry — a central connectivity layer that links accommodation suppliers with sales channels across the full spectrum of Korean stays: hotels, resorts, pensions, motels, hanoks, pool villas, and more.

ONDA Hub provides the following core features:

- **Korean Accommodation Access**: 25,000+ properties across all Korean accommodation categories
- **Integrated Connectivity**: Connecting accommodation providers with 70+ OTAs and booking channels
- **Real-time Synchronization**: Real-time updates for content, inventory, pricing, and booking information
- **Standardized API**: Reduced complexity with consistent interfaces
- **Wide Product Categories**: Full range of Korean accommodation — from 5-star city hotels to unique stays like hanoks, pensions, and pool villas

## Partner Types

ONDA Hub partners are divided into two main categories:

### Vendor Partner (Provider)

Partners who supply accommodation products such as accommodation facilities, hotel chains, and PMS systems

**Key Features:**
- Product registration and management
- Real-time inventory/rate updates
- Booking reception and confirmation
- Settlement management

### Channel Partner (Seller)

Partners who sell accommodation products such as OTAs, travel agencies, and metasearch engines

**Key Features:**
- Sell up to 25,000+ accommodation properties
- Real-time inventory/price inquiry or updates
- Booking creation and management
- Integrated content management

## Development Timeline

Partners typically integrate ONDA Hub API in about one month of development.
(This refers to pure API integration development and may vary depending on the service the partner wants to implement.)
- **Vendor API**: Average 1 month (1 M/M)
- **Channel API**: Average 1 month (1 M/M)

## Technical Support

- **Documentation**: Detailed API documentation and guides
- **Technical Support**: Dedicated technical support team
- **Technical Inquiries**: techsupport@onda.me
- **Partnership Applications**: [Contact us](https://onda.me/en/contact/)

## Next Steps

- [Getting Started with ONDA Hub](/docs/getting-started)
- [Getting Started with Vendor API](/docs/api/vendor/vendor-api)
- [Getting Started with Channel API](/docs/api/channel/channel-api)
- [View Glossary](/docs/glossary)

---


================================================================================
## Getting Started
================================================================================

### Getting Started with ONDA Hub

> Step-by-step guide for ONDA Hub API integration. From identifying partner type to contracting, development, and launch - typically takes 1 month. Detailed instructions for Vendor API and Channel API integration.

# Getting Started with ONDA Hub

Step-by-step guide for integrating with ONDA Hub. Identify your partner type and choose the appropriate API to start your integration.

## Integration Timeline

<table style={{width: '100%'}}>
  <thead>
    <tr>
      <th style={{width: '20%'}}>Phase</th>
      <th style={{width: '20%'}}>Week 1</th>
      <th style={{width: '20%'}}>Week 2</th>
      <th style={{width: '20%'}}>Week 3</th>
      <th style={{width: '20%'}}>Week 4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1. Identify Partner Type</td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center'}}></td>
    </tr>
    <tr>
      <td>2. ONDA hub Contract</td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center'}}></td>
    </tr>
    <tr>
      <td>3. Integration Development</td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
    </tr>
    <tr>
      <td>4. Prepare for Official Launch</td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center'}}></td>
      <td style={{textAlign: 'center', backgroundColor: '#3578e5', color: 'white'}}></td>
    </tr>
  </tbody>
</table>

:::warning Note
The actual schedule may vary depending on your company's circumstances and requirements.
:::

## 1. Identify Partner Type

### Are You a Supplier or a Seller? Self-Assessment

Before integrating with ONDA hub, you must first clearly understand your company's business model.
If you need to discuss with ONDA for a stronger synergy with ONDA hub, please contact us through the following channels!

**Discuss with ONDA:**
- [Contact us](https://onda.me/en/contact/)

### Questions to Help You Identify Your Partner Type

Use the following flowchart to determine the appropriate API type for your company.

```mermaid
flowchart TD
    Start([ONDA Hub Partner Type Decision]) --> Q1{What kind of business<br/>do you want to do?}

    Q1 -->|Sell our accommodation products<br/>on ONDA channels| Vendor[Vendor Partner<br/>Supplier]
    Q1 -->|Sell ONDA accommodation products<br/>on our platform| Channel[Channel Partner<br/>Seller]

    Vendor --> Q2{Do you operate<br/>a CMS system?}
    Q2 -->|YES| Contact1[Separate inquiry required<br/>Contact us]
    Q2 -->|NO| VendorAPI[Vendor API Integration<br/>Property/Inventory/Price/Booking Management]

    Channel --> Q3{How will you manage<br/>inventory/price data?}
    Q3 -->|Store in own DB<br/>for fast search| DBCache[DB Cache Type<br/>High-volume traffic handling]
    Q3 -->|Real-time API calls<br/>for latest data| RealTime[Real Time Type<br/>Simple implementation]

    VendorAPI --> VendorLink[View Vendor API Docs]
    DBCache --> DBLink[DB Cache Type Guide]
    RealTime --> RTLink[Real Time Type Guide]
    Contact1 --> ContactLink[Contact Partner Team]

    click VendorAPI "/docs//api/vendor/vendor-api-intro" "Go to Vendor API Docs"
    click VendorLink "/docs/api/vendor/vendor-api-intro" "Go to Vendor API Docs"
    click DBCache "/docs/api/channel/db-cache-type" "Go to DB Cache Type Guide"
    click DBLink "/docs/api/channel/db-cache-type" "Go to DB Cache Type Guide"
    click RealTime "/docs/api/channel/realtime-type" "Go to Real Time Type Guide"
    click RTLink "/docs/api/channel/realtime-type" "Go to Real Time Type Guide"
    click Contact1 "https://onda.me/en/contact/" "Contact us"
    click ContactLink "https://onda.me/en/contact/" "Contact us"

    classDef startNode stroke:#3b82f6,stroke-width:3px
    classDef partnerNode stroke:#f59e0b,stroke-width:3px
    classDef apiNode stroke:#10b981,stroke-width:3px
    classDef contactNode stroke:#ef4444,stroke-width:3px
    classDef questionNode stroke:#06b6d4,stroke-width:3px
    classDef linkNode stroke:#8b5cf6,stroke-width:2px

    class Start startNode
    class Vendor,Channel partnerNode
    class VendorAPI,DBCache,RealTime apiNode
    class Contact1 contactNode
    class Q1,Q2,Q3 questionNode
    class VendorLink,DBLink,RTLink,ContactLink linkNode
```

---

## 2. ONDA hub Contract

Once you've determined your partner type, proceed with the official contract with ONDA hub.

**Contract Process:**
1. Complete partner application form
2. Submit business registration certificate and required documents
3. Negotiate contract terms
4. Sign contract
5. Receive API credentials

**Contact:**
- [Contact us](https://onda.me/en/contact/)
- Confirm contract terms through consultation with our representative

---

## 3. Integration Development

Once the contract is completed, begin the actual API integration development.

### Review API Documentation
Carefully review the API documentation for the integration you want to implement.

- **Vendor Partner:** [Vendor API Documentation](/docs/api/vendor/vendor-api)
- **Channel Partner:** [Channel API Documentation](/docs/api/channel/channel-api)

### Create Development Requirements Checklist

Before integration development, you must create a development requirements checklist to define your integration concept, synchronize service flows, determine endpoints to use, and confirm policies. This checklist will be provided separately by the ONDA technical support team after contract negotiation.

### Development Kickoff Meeting

Conduct a kickoff meeting with the ONDA technical support team:
- Discuss integration schedule
- Identify technical issues
- Issue test accounts
- Set up development environment

### Integration Development Support

For issues that arise during development, contact the ONDA technical support team:
- **Technical inquiries**: techsupport@onda.me
- **Development guide**: Refer to each API documentation
- **Sample code**: Refer to examples in API documentation

### QA (Quality Assurance)

Once development is complete, conduct QA:
1. **Unit testing**: Test each API endpoint
2. **Integration testing**: Test complete workflow
3. **Performance testing**: Load testing and response time verification
4. **Security testing**: Authentication and data security verification

---

## 4. Prepare for Official Launch

Once QA is complete, proceed with final preparations for production operation.

### Operations Discussion
- Operating hours and incident response procedures
- Monitoring and alert configuration
- Data backup policy

### Settlement Discussion
- Commission policy confirmation
- Settlement cycle and method
- Tax invoice issuance process

### CS (Customer Support) Discussion
- Customer inquiry handling process
- Booking issue resolution procedures
- Emergency response plan

---

## Development Timeline Guide

Average development time for API integration:

- **Vendor API**: Average 1 month (1 M/M)
- **Channel API**: Average 1 month (1 M/M)

:::note Note
Actual development time may vary depending on your company's system environment and implementation scope.
:::

---

## Next Steps

Start your integration by referring to the documents below according to your partner type:

### Vendor Partner (Supplier)
- [Vendor API Documentation](/docs/api/vendor/vendor-api)

### Channel Partner (Seller)
- [Channel API Documentation](/docs/api/channel/channel-api)
- [DB Cache Type Implementation Guide](/docs/api/channel/db-cache-type)
- [Real Time Type Implementation Guide](/docs/api/channel/realtime-type)

### Additional Resources
- [Glossary](/docs/glossary)

---

**Contact Us:**
- Technical inquiries: techsupport@onda.me
- Partner application: [Contact us](https://onda.me/en/contact/)

:::tip Speed up integration with AI tools

If you use AI development tools like Claude Code, Cursor, or Windsurf,
download the ONDA API context files from the [AI Tools](/ai-tools) page.
Your AI assistant will understand the full API structure and suggest more accurate code.

:::

---


================================================================================
## FAQ
================================================================================

### Frequently Asked Questions (FAQ)

> Frequently asked questions about ONDA API integration. FAQ covering API key issuance, development timeline, authentication methods, Real Time Type vs DB Cache Type, Webhook, test environment, and more

# Frequently Asked Questions (FAQ)

Here are frequently asked questions about ONDA API integration.

---

## Basic Information

<details open>
<summary><strong>Q. What is ONDA Hub?</strong></summary>

ONDA Hub is an integrated distribution platform that connects accommodation product suppliers with sales channels.

- **Vendor (Supplier)**: Supply accommodation products to ONDA and sell on 70+ channels
- **Channel (Seller)**: Sell ONDA's 25,000+ accommodation products

Through RESTful-based APIs, you can perform real-time inventory/price inquiries, booking processing, and more.

</details>

<details>
<summary><strong>Q. What is the difference between Vendor and Channel partners?</strong></summary>

**Vendor Partner (Supplier)**
- Hotels, resorts, pensions, and other accommodation facilities
- PMS system providers
- Hotel chains and product suppliers
- **Purpose**: Supply accommodation products to ONDA and sell through multiple channels

**Channel Partner (Seller)**
- OTA (Online Travel Agency)
- Travel agencies and metasearch engines
- Accommodation booking platforms
- **Purpose**: Sell ONDA's accommodation products on their own platform

</details>

<details>
<summary><strong>Q. Which partner type should I choose?</strong></summary>

Choose according to your company's business model:

- Do you **supply accommodation products**? → Use **Vendor API**
- Do you **sell accommodation products**? → Use **Channel API**

For more details, please refer to the [Getting Started](/docs/getting-started) page.

</details>

---

## Getting Started

<details>
<summary><strong>Q. How long does API integration development take?</strong></summary>

**Average Development Timeline**
- **Channel API**: Approximately 2 weeks (average)
- **Vendor API**: Approximately 1 month (1 M/M)

Actual development time may vary depending on implementation scope and development resources.

</details>

<details>
<summary><strong>Q. How do I get an API key?</strong></summary>

**Issuance Process**
1. Sign partnership contract with ONDA
2. Receive API key for development environment
3. Conduct development and testing
4. Receive API key for production environment
5. Start official service

**Contact**
- Partner application: [Contact us](https://onda.me/en/contact/)
- Technical inquiries: techsupport@onda.me

</details>

<details>
<summary><strong>Q. How do I set up the test environment?</strong></summary>

ONDA provides separate development and production environments:

**Development Environment**
- URL: `https://dapi.tport.dev`
- Purpose: API integration development and testing

**Production Environment**
- URL: `https://gds.tport.io`
- Purpose: Actual service operation

A separate API key is issued for each environment.

</details>

---

## Technical Specifications

<details>
<summary><strong>Q. What are the API endpoints?</strong></summary>

**Development Environment**
```
https://dapi.tport.dev
```

**Production Environment**
```
https://gds.tport.io
```

All APIs use the HTTPS protocol.

</details>

<details>
<summary><strong>Q. What data format is used?</strong></summary>

**Basic Information**
- **Protocol**: HTTPS
- **Data format**: JSON (UTF-8 encoding)
- **HTTP methods**: GET, POST, PUT, PATCH, DELETE
- **Content-Type**: `application/json`

All requests and responses are processed in JSON format.

</details>

<details>
<summary><strong>Q. How do I authenticate with the API?</strong></summary>

**Channel API Authentication**

You must include the following two keys in the HTTP header:

1. **Authorization Key**
   - Header: `x-api-key`
   - API authentication key issued by ONDA

2. **Channel Key**
   - Header: `x-channel-key`
   - Unique key to identify the channel

**Vendor API Authentication**

This follows the authentication method provided by ONDA, which will be explained in detail upon contract signing.

</details>

---

## Integration Methods

<details>
<summary><strong>Q. What is the difference between Real Time Type and DB Cache Type?</strong></summary>

**Real Time Type**
- Calls ONDA API in real-time whenever a customer searches
- No need to store inventory/rates in DB
- No Webhook development required
- Relatively simple implementation

**DB Cache Type**
- Stores all product inventory/rates in channel DB
- Queries own DB when customers search (fast response)
- Webhook development required (real-time updates)
- Advantageous for high-volume traffic handling

For more details, refer to:
- [Real Time Type](/docs/api/channel/realtime-type)
- [DB Cache Type](/docs/api/channel/db-cache-type)

</details>

<details>
<summary><strong>Q. What is Webhook and when is it needed?</strong></summary>

**What is Webhook?**

A push-based API that notifies channels in real-time when information changes in ONDA.

**Webhook Types**
- `contents_updated`: Property/room/package content change notification
- `status_updated`: Product addition or sales status change notification
- `inventory_updated`: Rate/inventory change notification

**When Needed**
- **Required for DB Cache Type integration**
- Optional for Real Time Type

For more details, refer to [Webhook Overview](/docs/api/channel/webhook-overview).

</details>

<details>
<summary><strong>Q. What is the difference between ONDA → Vendor and Vendor → ONDA methods?</strong></summary>

Vendor API provides two integration methods:

**ONDA → Vendor (Pull method)**
- ONDA calls the Vendor system's API
- Real-time data synchronization
- Vendor provides API and responds

**Vendor → ONDA (Push method)**
- Vendor calls ONDA API to update information
- Sends immediately when changes occur
- ONDA receives and stores data

In most cases, both methods are used together.

</details>

---

## Booking and Testing

<details>
<summary><strong>Q. How do I use test properties?</strong></summary>

**Test Property IDs**

| Property ID | Type | Includes Rateplan |
|------------|------|------------------|
| 117417 | Hotel | Included |
| 120135 | Hotel | Included |

**Important Notes**
- You cannot test with actual properties
- Booking tests must be conducted only with the above test properties
- You must complete cancellation processing after booking tests

For more details, refer to [Test Properties for Booking](/docs/api/channel/test-property).

</details>

<details>
<summary><strong>Q. Can I query actual property content?</strong></summary>

**Yes** (with conditions)

- Prior consultation with your account manager required
- You can query up to **content information** of actual properties
- However, **booking tests are strictly prohibited** (use test properties only)

Please note that creating a booking with an actual property will result in a real booking.

</details>

---

## Technical Support

<details>
<summary><strong>Q. Where should I send technical inquiries?</strong></summary>

**Contact Channels**

- **Technical inquiries**: techsupport@onda.me
- **Partner application**: [Contact us](https://onda.me/en/contact/)

**Support Hours**
- Weekdays 10:00 - 18:00 (KST)
- Emergency incidents: 24/7 response

When inquiring via email, please include the following information:
- Partner company name
- API type (Vendor/Channel)
- Environment (Development/Production)
- Specific inquiry details

</details>

<details>
<summary><strong>Q. How should I respond to API outages?</strong></summary>

**Emergency Incident Response**
- 24/7 emergency response system in operation
- Contact techsupport@onda.me immediately

**Information to Include When Reporting Incidents**
- Time of occurrence
- Error message or response code
- Request parameters (excluding sensitive information)
- Whether reproducible

Please provide specific information for faster resolution.

</details>

---

## Additional Documentation

For more detailed information, please refer to the following documents:

- [ONDA Hub Introduction](/docs/introduction)
- [Getting Started Guide](/docs/getting-started)
- [Vendor API Documentation](/docs/api/vendor/vendor-api)
- [Channel API Documentation](/docs/api/channel/channel-api)
- [API Changelog](/changelog)

---


================================================================================
## Vendor API 3.0
================================================================================

### Vendor API 3.0

> ONDA Vendor API 3.0 overview and introduction

# Vendor API 3.0

ONDA Vendor API 3.0 is a RESTful API that connects vendors with the ONDA platform. Vendors push property, room type, and rate plan content along with rates and availability to ONDA, and ONDA delivers bookings from OTA channels back to the vendor.

:::info Using version 1.5?
The existing [Vendor API 1.5](/docs/api/vendor/vendor-api) remains supported. Use 3.0 for new integrations.
:::

:::note
Detailed guides and endpoint references are currently available in Korean. English translations are in progress.
:::

## Two directions of integration

The first thing to understand in 3.0 is the **direction of each API call**. A vendor is both a client and a server.

| Type | Direction | Path pattern | Vendor's role |
|---|---|---|---|
| **HUB API** | Vendor → ONDA | `/gds/vendor/...` | **Client** — calls the ONDA API |
| **Vendor API** | ONDA → Vendor | `/bookings` etc. | **Server** — provides endpoints ONDA calls |

```mermaid
flowchart LR
    V["Vendor<br/>(CMS · PMS)"]
    O["ONDA"]
    C["Sales channels<br/>(OTA)"]

    V -->|"HUB API"| O
    O -->|"Vendor API"| V
    O <-->|"channels"| C
```

## Channel types

Sales channels are either **Plus channels** (`channel_type=hub`) or **direct channels** (`channel_type=cms`). For Plus channels ONDA handles the contract, listing, and settlement on the property's behalf; for direct channels the property contracts with the OTA directly and runs the channel itself. Rate and availability sync goes through ONDA either way.

This distinction drives both the channel opening flow and how bookings are confirmed. See [Overview](/docs/api/vendor-v3/guides/overview#channel-types--plus-and-direct) for the full comparison.

## What changed from 1.5

- **Content direction reversed** — instead of ONDA pulling content with `GET`, the vendor now **pushes** it directly with `POST` and `PATCH`.
- **Rate plan models introduced** — a rate plan is the combination of `room type × rate plan model`.
- **Rate and availability split** — rates and business days go to `POST .../ari`, availability to `POST .../ari/avails`.
- **Channel APIs added** — channel discovery, open requests, settings, and mapping are handled through the API.

## Content taxonomy

| Type | Create | Update |
|---|---|---|
| Property | `POST /gds/vendor/properties` | `PATCH /gds/vendor/properties/{vendor_property_id}` |
| Room type | `POST .../{vendor_property_id}/roomtypes` | `PATCH .../roomtypes/{vendor_roomtype_id}` |
| Rate plan model | `POST .../{vendor_property_id}/rateplan-models` | `PATCH .../rateplan-models/{vendor_rateplan_model_id}` |
| Rate plan | `POST .../roomtypes/{vendor_roomtype_id}/rateplans` | `PATCH .../rateplans/{vendor_rateplan_id}` |

## Authentication

Include the issued vendor access token in the `Authorization` header when calling the HUB API.

```http
GET /gds/vendor/meta HTTP/1.1
Authorization: {vendor_access_token}
```

### Base URL

| Environment | URL |
|---|---|
| Development (alpha) | `https://vendor.dapi.tport.dev` |
| Production | Provided by your ONDA contact after the integration agreement |

Access tokens are issued by your ONDA contact after the integration agreement.

The Vendor API works the other way around — **the vendor verifies the request**. Implement token and signature validation for requests coming from ONDA.

## Common rules

- Create `POST` · Update `PATCH` · Read `GET`, with `200 OK` on success
- `PATCH` follows RFC 7396 Merge Patch. Omitted fields keep their existing values.
- Request and response bodies are JSON.
- Dates and timestamps use ISO 8601 (e.g. `2026-01-20T15:00:00+09:00`).

## Getting started

1. **Partnership agreement** — sign the integration agreement with the ONDA business team
2. **Credentials** — receive staging and production access tokens and base URLs
3. **Review the [integration guides](/docs/api/vendor-v3/guides/overview)** — ordered from preparation through channel opening to booking operations
4. **Fetch the code catalog** — call [`GET /gds/vendor/meta`](/docs/api/vendor-v3/get-meta-codes) and build your code mapping table
5. **Verify on staging, then switch to production**

---


================================================================================
## Vendor API 3.0 - Guide
================================================================================

### Overview

> The overall shape of a Vendor API 3.0 integration and what to check before launch

# Overview

Before starting a Vendor API 3.0 integration, get the overall structure and the preparation items straight.

:::info Endpoints at a glance
**38** HUB APIs + **5** vendor APIs (webhooks) = **43** in total

HUB API conventions: `POST` to create, `PATCH` to update, `GET` to read, with `200 OK` on success.

Vendor APIs use different methods depending on the action: `PUT` for booking confirmation and modification, `POST` for creation and cancellation, and `GET` for the policy lookup.
:::

## Direction and roles (read this first)

Channels are either **Plus channels** (`channel_type=hub`) or **direct channels** (`channel_type=cms`). In the HUB API the vendor is the client; in the vendor API (webhooks) the vendor is the server.

| API | Direction | What the vendor implements |
|---|---|---|
| HUB API | Vendor → ONDA | **Client** — calls `/gds/vendor/...` (content push, ARI push, channels, booking and settlement lookups) |
| Vendor API (webhooks) | ONDA → Vendor | **Server** — exposes endpoints ONDA calls (booking processing, policy lookup) |

## Channel types — Plus and direct

Sales channels connected to ONDA fall into two kinds, depending on **who operates the channel**. This distinction drives everything from the opening procedure to how bookings are processed.

| | **Plus channel** | **Direct channel** |
|---|---|---|
| `channel_type` | `hub` | `cms` |
| In one line | One contract with ONDA sells across every channel connected to ONDA | The property contracts with the OTA directly and runs the channel itself |
| Registration, operation, settlement | Handled by ONDA | Handled by the property |

### Who handles each step?

| Step | Plus channel | Direct channel |
|---|---|---|
| Rate and availability sync | ONDA | ONDA |
| Channel contract | ONDA | The property |
| Listing and content distribution | ONDA | The property |
| Settlement and accounting | ONDA | The property |
| Booking confirmation | ONDA validates, then confirms | Created immediately, without validation |

Rate and availability sync goes **through ONDA in both cases.** The content and ARI push code a vendor writes is identical regardless of channel type.

**Plus channel** — everything from contract to settlement goes through ONDA.

```mermaid
flowchart LR
    V1["Vendor"] -->|"Content · rates and availability"| O1["ONDA"]
    O1 -->|"Contract · listing · settlement"| C1["Sales channel"]
```

**Direct channel** — only rates and availability go through ONDA; the property handles the contract and settlement with the channel directly.

```mermaid
flowchart LR
    V2["Vendor"] -->|"Content · rates and availability"| O2["ONDA"]
    O2 -->|"Rates · availability"| C2["Sales channel"]
    V2 -.->|"Contract · listing · settlement"| C2
```

### What differs in the API?

| Aspect | Plus channel | Direct channel |
|---|---|---|
| Channel opening | Just submit a request — [Plus Channel Opening](/docs/api/vendor-v3/guides/channel-open-plus) | Requires pre-mapping steps, channel settings, and mapping — [Direct Legacy Channel Opening](/docs/api/vendor-v3/guides/channel-open-legacy) |
| Mapping API | Not used (ONDA manages it) | `PATCH .../mappings` required |
| Per-channel settings | Not used | `PATCH .../settings` |
| Booking validation | ONDA and the vendor each validate | None — accepted unconditionally |
| Booking modification (`modify`) | Soft changes only | Both soft and hard changes |
| Product `type` | `overnight` | `overnight` (channels that support day-use also send `dayuse`) |

:::tip Channel type is per channel
It is decided **per channel**, not per property. One property can run some channels as Plus and others as direct. Check `channel_type` in the [`GET /gds/vendor/channels`](/docs/api/vendor-v3/list-channels) response.

For an explanation from the property operator's point of view, see [Introducing the ONDA Plus channel manager](https://global.onda.me/ko/onda-plus/).
:::

## Integration lifecycle

```mermaid
flowchart TB
    START(["Vendor API 3.0 integration"])

    subgraph G01["1. Preparation · first sale"]
        direction TB
        a1["Authentication · base URL<br/>HUB API = client · Vendor API = server"]
        a2["Code catalog GET /meta"]
        a3["Create content POST<br/>property → room type → rate plan model → rate plan"]
        a4["Rates and business days POST .../ari<br/>Availability POST .../ari/avails"]
        a1 --> a2 --> a3 --> a4
    end
    START --> a1

    open["Discover channels GET → submit opening request POST<br/>request_type=on"]
    a4 --> open

    branch{"Branch by channel type<br/>channel_type"}
    open --> branch

    subgraph G2["2-A. Plus channel opening (hub)"]
        b2["Validate opening conditions<br/>auto-rejected if unmet"]
    end

    subgraph G34["2-B. Direct channel opening (cms)"]
        direction TB
        c1["Look up channel property info, or OAuth authorization"]
        c2["Channel settings + property, room, rate plan mapping PATCH"]
        c1 --> c2
    end

    branch -->|"hub"| b2
    branch -->|"cms"| c1

    subgraph G56["3. Booking operations (vendor API)"]
        direction TB
        d1["Hold POST /bookings"]
        d2["Confirm PUT .../confirm"]
        d3["Modify PUT .../modify"]
        d4["Cancel POST .../cancel"]
        d1 --> d2 --> d3 --> d4
    end

    b2 --> d1
    c2 --> d1

    subgraph G710["4. Ongoing operations"]
        direction TB
        e1["Status changes PATCH"]
        e2["Content changes PATCH"]
        e3["Settlement lookup GET"]
        e4["Mapping updates · stop sale PATCH"]
    end
    d4 --> e1
    d4 --> e2
    d4 --> e3
    d4 --> e4
```

## Preparation checklist

- [ ] Settle the base URL for HUB API calls and separate staging from production
- [ ] Provide the receiving endpoints and base URL for the vendor API (webhooks)
- [ ] Authentication: send the vendor access token in the `Authorization` header on HUB API calls
- [ ] Authentication: implement token and signature verification for incoming ONDA requests to the vendor API
- [ ] Fetch the [code catalog](/docs/api/vendor-v3/get-meta-codes) and build the code mapping table into your system

## Pre-launch integration testing

- [ ] First-sync end to end: create property → room type → rate plan model → rate plan, then push rates, business days, and availability, and confirm they reach the channel
- [ ] Full flow on a Plus channel: opening → booking → confirmation → cancellation
- [ ] Flow on a direct channel (legacy and OAuth): mapping → ON request → booking
- [ ] Token expiry and renewal, plus error handling for 4xx and 5xx responses
- [ ] Idempotency and retry policy (no duplicate bookings or duplicate cancellations)
- [ ] Sign-off to move to production after staging verification

## Operational and business decisions

Separate from the API work, these are the items to agree on with ONDA before going live. Items marked 📘 have a dedicated guide.

| Area | Item | What to confirm |
|---|---|---|
| Content and language | Multilingual content | Whether languages other than Korean are used, and how to send them |
| Content and language | Special characters in property name and description | Strip characters that cannot be displayed |
| Content and language | Special characters in room name and description | Strip characters that cannot be displayed |
| Scope and products | Property scope | Selling overseas properties, or domestic only |
| Scope and products | Product structure | Selling by room, or by package (rate plan) per room |
| Rates and availability | Extra occupancy charges | Whether extra occupancy charges can be sent |
| Rates and availability | Push horizon | How many months of rates and availability can be sent |
| Booking structure | Booking number structure 📘 | One `gds_booking_number` to many `gds_sub_booking_number`. Vendor bookings are created against `gds_sub_booking_number` |
| Booking structure | Plus channel booking validation | Checks for availability, matching rates, active sales status, and so on |
| Cancellation | Per-date refund policy 📘 | Whether policies differ by date ([Refund Policy](/docs/api/vendor-v3/guides/refund-policy)) |
| Vouchers | Booking vouchers | The vendor sends confirmation and cancellation vouchers to the property directly |

## Booking number structure

| Field | Description |
|---|---|
| `gds_booking_number` | ONDA Hub booking number |
| `gds_sub_booking_number` | ONDA Hub sub-booking number. Vendor bookings are created against **this value** |
| `channel_booking_number` | Sales channel booking number. **The key is omitted entirely** when the sales channel does not supply its own booking number, so do not treat it as always present |

One `gds_booking_number` corresponds to many `gds_sub_booking_number` values, and the vendor's own `booking_number` maps one-to-one with `gds_sub_booking_number`.

---

### Initial Setup

> First sync, from creating content through pushing rates and availability

# Initial Setup

The first sync: the vendor creates property, room type, rate plan model, and rate plan content with `POST`, then pushes rates and availability (ARI) to make the property sellable. Content always flows **vendor → ONDA**; lookups (`GET`) are a fallback.

## Step by step

1. **Fetch the code catalog** — [`GET /gds/vendor/meta`](/docs/api/vendor-v3/get-meta-codes) (tag codes such as `_tags` used when creating properties and rooms)
2. **Create the property** — [`POST /gds/vendor/properties`](/docs/api/vendor-v3/create-property)
3. **Create room types** — [`POST .../{vendor_property_id}/roomtypes`](/docs/api/vendor-v3/create-roomtype)
4. **Create rate plan models** — [`POST .../{vendor_property_id}/rateplan-models`](/docs/api/vendor-v3/create-rateplan-model)
5. **Create rate plans** — [`POST .../roomtypes/{vendor_roomtype_id}/rateplans`](/docs/api/vendor-v3/create-rateplan) (bound via `vendor_rateplan_model_id`)
6. **Push rates and business days** — [`POST .../ari`](/docs/api/vendor-v3/push-rates) (per rate plan)
7. **Push availability** — [`POST .../ari/avails`](/docs/api/vendor-v3/push-avails) (consolidated per room type)

## Checklist

- [ ] Fetch the code catalog (`_tags` and similar) with `GET /gds/vendor/meta` and build the mapping table into your system
- [ ] Create in order: property → room type → rate plan model → rate plan
- [ ] Use tag codes from the meta lookup for `_tags` when creating properties and rooms
- [ ] Verify the `vendor_rateplan_model_id` binding when creating rate plans
- [ ] Push rates and business days per rate plan with `POST .../ari`, and availability per room type with `POST .../ari/avails`
- [ ] Send the shared value (`default`) and the full per-channel declaration (`channels[]`) together in a single call
- [ ] Use partial pushes: include only what changed in `ari` (business day `is_business_day`, or rates `basic_price`, `sale_price`, `net_price`)
- [ ] Align `ari/avails` availability to room-only (room type) counts; each from-to range spans at most 730 days
- [ ] (Optional) Verify the fallback content lookups (`GET`)

## Using the code catalog (meta)

Code-based fields on properties and rooms (`_tags` and similar) must use tag codes defined by ONDA, not arbitrary strings.

- Lookup: [`GET /gds/vendor/meta`](/docs/api/vendor-v3/get-meta-codes) returns the available codes
- Fetch meta and build the code mapping table before the first sync (content creation)
- Reference the same code system when setting `_tags` on content updates
- Establish a policy for periodically re-fetching meta so code additions and removals are picked up

## Sending content in languages other than Korean

To include languages other than Korean, provide per-language values in the `i18n` field.

- Applies to the `i18n` field on properties, room types, rate plan models, and rate plans
- Define the set of supported language codes (for example `ko`, `en`, `ja`, `zh`)
- Decide the per-language content mapping and the fallback language rule
- Apply the same rules for stripping characters that cannot be displayed

## Rate plan model and rate plan structure

A rate plan is a **room type combined with a rate plan model**. Both room types and rate plan models live beneath a property.

```mermaid
erDiagram
    PROPERTY ||--o{ ROOMTYPE : "has room types"
    PROPERTY ||--o{ RATEPLAN_MODEL : "has rate plan models"
    ROOMTYPE ||--o{ RATEPLAN : "rate plans per room"
    RATEPLAN_MODEL ||--o{ RATEPLAN : "bound via vendor_rateplan_model_id"
    PROPERTY {
        string vendor_property_id PK
    }
    ROOMTYPE {
        string vendor_roomtype_id PK
    }
    RATEPLAN_MODEL {
        string vendor_rateplan_model_id PK
        enum   type "standalone(1) / package(N)"
    }
    RATEPLAN {
        string vendor_rateplan_id PK
        string vendor_roomtype_id FK
        string vendor_rateplan_model_id FK
    }
```

:::info Rules for rate plan model `type`
The number you may create differs by `type`, per property.

- `standalone`: **exactly one required** per property; no more than one
- `package`: no limit
:::

:::tip Channel-specific rate plans — the `channels` field on a rate plan model
The optional `channels` field on `POST .../rateplan-models` restricts a model to specific channels.

- Omitted, it sells on all channels; supplied, it sells only on the channels listed in `channels`
- On channel opening, rate plan mappings are created only for the channels listed in `channels` (and only where a room mapping exists and is open on that channel)
- Editing it after opening
  - Adding (`[131] → [131, 132]`): a mapping is created for 132 only
  - Removing (`[131, 132] → [131]`): existing mappings are kept; rate plans created afterwards get no mapping for 132
  - Empty (`[131, 132] → []`): mappings are created on every eligible channel
:::

## Property default extra adult charge (`settings.extra_adult_price`)

A **property-level default extra adult charge**, sent in the `settings` object of the property create or update request.

- **Value**: integer (KRW). Sending `null` clears it.
- **Legacy channels**: included in **occupancy-based** rates on Agoda and Trip.com. Not included in **per-room** rates.
- **Plus channels**: used as the extra occupancy charge.

## Content endpoints

All paths are prefixed with `/gds/vendor`.

| Object | Method | Path | Required fields and notes |
|---|---|---|---|
| Property | `POST` | `/properties` | Requires `id` and `i18n (ko-kr)`; supports `settings.extra_adult_price` |
| Property | `PATCH` | `/properties/{vendor_property_id}` | Update; clear `extra_adult_price` with `null` |
| Property | `GET` | `/properties` · `/{vendor_property_id}` | Fallback lookup |
| Room type | `POST` | `/properties/{vendor_property_id}/roomtypes` | Requires `id` and `i18n (ko-kr)` |
| Room type | `PATCH` | `.../roomtypes/{vendor_roomtype_id}` | Update |
| Room type | `GET` | `.../roomtypes` · `/{vendor_roomtype_id}` | Fallback lookup |
| Rate plan model | `POST` | `/properties/{vendor_property_id}/rateplan-models` | Requires `id` and `i18n (ko-kr)`; optional `channels` |
| Rate plan model | `PATCH` | `.../rateplan-models/{vendor_rateplan_model_id}` | Update |
| Rate plan model | `GET` | `.../rateplan-models` · `/{vendor_rateplan_model_id}` | Fallback lookup |
| Rate plan | `POST` | `.../roomtypes/{vendor_roomtype_id}/rateplans` | Requires `id` and `vendor_rateplan_model_id` |
| Rate plan | `PATCH` | `.../rateplans/{vendor_rateplan_id}` | Update |
| Rate plan | `GET` | `.../rateplans` · `/{vendor_rateplan_id}` | Fallback lookup |

:::warning
When creating a rate plan, always verify that the required `vendor_rateplan_model_id` in the request body binds it to a rate plan model.
:::

## Pushing rates and business days (ARI) and availability (Avails)

Pushing is split into **rates and business days** on one side and **availability** on the other.

| Method | Path | Description |
|---|---|---|
| `POST` | `.../ari` | Rates and business days (per rate plan, `vendor_rateplan_id`) |
| `POST` | `.../ari/avails` | Availability, `vacancy` (consolidated per room type; at most 730 days per item) |

Both endpoints carry the shared value and the per-channel values (the `channels[]` array) in a single call. `channels[]` is a **complete declaration of per-channel settings**, so settings for channels absent from the array are deleted within the item's from-to range.

**Rates and business days — overnight (`overnight`)**

```json
[
  {
    "vendor_roomtype_id": "VRT-001",
    "vendor_rateplan_id": "VRP-001",
    "type": "overnight",
    "from": "2026-01-01",
    "to": "2026-01-31",
    "basic_price": 0,
    "net_price": 0,
    "sale_price": 12000,
    "is_business_day": 0,
    "channels": [
      { "channel_id": 1, "basic_price": 0, "net_price": 0, "sale_price": 10000, "is_business_day": 0 },
      { "channel_id": 2, "basic_price": 0, "net_price": 0, "sale_price": 11000 }
    ]
  }
]
```

**Rates and business days — day-use (`dayuse`)**

```json
[
  {
    "vendor_roomtype_id": "VRT-001",
    "vendor_rateplan_id": "VRP-001",
    "type": "dayuse",
    "from": "2026-01-01",
    "to": "2026-01-31",
    "basic_price": 0,
    "net_price": 0,
    "sale_price": 12000,
    "is_business_day": 0,
    "use_from": "14:00",
    "use_to": "20:00",
    "use_time": 240,
    "channels": [
      { "channel_id": 1, "sale_price": 10000, "use_from": "14:00", "use_to": "20:00", "use_time": 240 }
    ]
  }
]
```

**Availability — consolidated per room type**

```json
[
  {
    "vendor_roomtype_id": "VRT-001",
    "from": "2026-01-01",
    "to": "2026-01-31",
    "vacancy": 5,
    "channels": [
      { "channel_id": 1, "vacancy": 3 },
      { "channel_id": 2, "vacancy": 5 }
    ]
  }
]
```

:::info Push rules
- **`POST .../ari`** — pushes rates and business days per rate plan. Settings for channels absent from `channels` are deleted within the item's range (omitting the field or sending an empty array deletes settings for all channels), and each channel object must carry **at least one** value field (`channel_id` alone returns `400`). Omitted fields keep their existing values. `use_from`, `use_to`, and `use_time` (in minutes) may be sent only on day-use items.
- **`POST .../ari/avails`** — applies availability as a consolidated per-room-type count. `channels[]` is a complete declaration of per-channel availability, so availability for channels absent from the array is deleted within the range. Each from-to range spans at most 730 days.
:::

:::info The `type` field (overnight or day-use)
Both `ari` and bookings (`/bookings`) use `type` to distinguish product types.

| Channel type | `type` used |
|---|---|
| Plus channel | `overnight` |
| Direct channel | `overnight` (channels that support day-use also send `dayuse`) |
:::

:::tip ARI supports partial pushes
- **Partial push**: include only the items you want to change
- **Fields per item**: business day = `is_business_day`; rates = `basic_price`, `sale_price`, `net_price`; availability = `vacancy` (via `ari/avails`)
- **Day-use only**: `use_from`, `use_to`, and `use_time` may be sent only for day-use
- **One call per push**: shared values at the top level, per-channel values in the `channels[]` array
:::

:::warning Availability policy — align to room-only (room type) counts
Channels differ in what availability is counted against.

- **Room-only (room type)**: Booking.com, Agoda, Trip.com
- **Rate plan**: Expedia, Plus channels (with some optional cases)

ONDA counts availability per room type, so **send availability aligned to room-only counts**, and send `package` availability identical to `standalone` availability.
:::

---


================================================================================
## Vendor API 3.0 - Channel
================================================================================

### Plus Channel Opening

> Opening a Plus channel, where ONDA handles the OTA contract

# Plus Channel Opening

How to open a **Plus channel** (`channel_type=hub`), where ONDA handles the OTA contract on your behalf. The vendor submits the request to ONDA through the HUB API.

Unlike direct channels, Plus channels open on the request alone — there is **no mapping or authorization step**.

## Step by step

1. **List all channels** — [`GET /gds/vendor/channels`](/docs/api/vendor-v3/list-channels)
2. **List channels available for the property** — [`GET .../properties/{vendor_property_id}/channels`](/docs/api/vendor-v3/list-property-channel-requests)
3. **Submit the opening request** — [`POST .../properties/{vendor_property_id}/channels/{channel_id}`](/docs/api/vendor-v3/create-property-channel-request)
4. **Check the request status** — [`GET .../properties/{vendor_property_id}/channels/{channel_id}`](/docs/api/vendor-v3/get-property-channel-request)

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA
    participant C as Sales channel

    Note over V,O: Discovering channels
    V->>O: GET /gds/vendor/channels
    O-->>V: Full channel list and usage notes

    V->>O: GET .../properties/{vendor_property_id}/channels
    O-->>V: Channels this property can open

    Note over V,C: Opening request
    V->>O: POST .../properties/{vendor_property_id}/channels/{channel_id}
    Note over O: Validate opening conditions
    O->>C: Set up the channel integration
    C-->>O: Result
    O-->>V: Request result

    Note over V,O: Checking status
    V->>O: GET .../properties/{vendor_property_id}/channels/{channel_id}
    O-->>V: Request status and history
```

## Checklist

- [ ] Review the full channel list and usage notes
- [ ] Check which channels the property can open
- [ ] Track request status and history after submitting
- [ ] Confirm whether rates and availability must be sent before opening completes
- [ ] Verify the property and room conditions are met beforehand — requests are auto-rejected otherwise
- [ ] Confirm the property's `settings.extra_adult_price` is applied as the extra-occupancy charge on Plus channels

## Opening conditions

Opening requests validate the property and room conditions below. **A request is automatically rejected if any one of them fails.**

**Property conditions**

| Check | Requirement |
|---|---|
| Already open | Prevents opening the same channel twice |
| Sales status | Active in both ONDA Hub and the vendor's own system |
| Deleted | Deleted properties are excluded |
| Property name | Rejects invalid values such as `null` or a single character |
| Images | At least 3 |
| Address | A correct address must be entered |
| Category | At least 1 assigned |

**Room conditions**

| Check | Requirement |
|---|---|
| Sellable rooms | At least 1 |
| Room name | Rejects invalid values |
| Room images | At least 3 |
| Bookable dates | At least one bookable date within the next 30 days, meeting all three conditions: rate, availability, and business day |

## How requests are processed

Processing depends on the `processing_mode` value in the channel metadata.

| `processing_mode` | Behavior |
|---|---|
| `auto_approve` | Activated immediately — `request_status: "confirm"` |
| `external_review` | Awaits operator approval — `request_status: "pending"` → `confirm` or `reject` |

For `external_review` channels, poll the request status to find out the result.

## Endpoints

All paths are prefixed with `/gds/vendor`.

| Method | Path | Description |
|---|---|---|
| `GET` | `/channels` | Channel metadata (`channel_type`, `processing_mode`, `requires_cms_authorization`) |
| `GET` | `/properties/{vendor_property_id}/channels` | Channels available for the property, with request status |
| `POST` | `/properties/{vendor_property_id}/channels/{channel_id}` | Submit an ON request (`request_type: "on"`) |
| `GET` | `/properties/{vendor_property_id}/channels/{channel_id}` | Request status and history |

---

### Direct Legacy Channel Opening

> Opening legacy direct channels such as Booking.com, Agoda, and Expedia

# Direct Legacy Channel Opening

How to open a **legacy direct channel** (Booking.com, Agoda, Expedia, Trip.com, and so on) among the `channel_type=cms` channels. Unlike Plus channels, these require **channel settings and mapping**.

## Branching before mapping

What you do before mapping depends on the `requires_cms_authorization` value in the channel metadata.

| `requires_cms_authorization` | Step before mapping | Example channels |
|---|---|---|
| `false` | Look up the property on the channel side — [`GET /gds/vendor/channels/{channel_id}/properties/{channel_property_id}`](/docs/api/vendor-v3/get-channel-property) | Booking.com, Agoda, Expedia, Trip.com |
| `true` | OAuth authorization — [`GET /gds/vendor/channels/{channel_id}/authorization`](/docs/api/vendor-v3/get-channel-authorization-url); see [Airbnb Authorization](/docs/api/vendor-v3/guides/airbnb-authorization) for the implementation | Airbnb |

## Step by step

1. **Discover channels** — [`GET /gds/vendor/channels`](/docs/api/vendor-v3/list-channels)
2. **Submit an ON request** — [`POST .../channels/{channel_id}`](/docs/api/vendor-v3/create-property-channel-request) with `{ "request_type": "on" }`
3. **Pre-mapping step** — look up the channel-side property, or run [OAuth authorization](/docs/api/vendor-v3/guides/airbnb-authorization), per the branch table above
4. **Per-channel settings** — [`PATCH .../channels/{channel_id}/settings`](/docs/api/vendor-v3/update-property-channel-settings) (RFC 7396 Merge Patch)
5. **Mapping** — `PATCH .../mappings` (property → room type → rate plan)
6. **Push rates and business days** — [`POST .../ari`](/docs/api/vendor-v3/push-rates) (`type: overnight`)
7. **Push availability** — [`POST .../ari/avails`](/docs/api/vendor-v3/push-avails)

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA
    participant C as Sales channel

    Note over V,O: 1. Discover channels
    V->>O: GET /gds/vendor/channels
    O-->>V: Channel metadata (channel_type, processing_mode, requires_cms_authorization)

    Note over V,C: 2. ON request
    V->>O: POST .../properties/{vp}/channels/{ch} { "request_type": "on" }
    alt processing_mode = auto_approve
        O->>C: Activate the channel
        O-->>V: { request_status: "confirm" }
    else processing_mode = external_review
        O-->>V: { request_status: "pending" }
        loop Poll for status
            V->>O: GET .../properties/{vp}/channels/{ch}
            O-->>V: pending → confirm | reject
        end
    end

    Note over V,C: 3. Pre-mapping step
    alt requires_cms_authorization = false
        V->>O: GET /gds/vendor/channels/{ch}/properties/{cp}
        O->>C: Request property, room, and rate plan info
        C-->>O: Info returned
        O-->>V: Candidate channel_roomtype_id / channel_rateplan_id
    else requires_cms_authorization = true
        V->>O: GET /gds/vendor/channels/{ch}/authorization
        O-->>V: { url } authorization URL
        V->>C: Browser redirect (host authorizes)
        C-->>V: redirect_url callback
    end

    Note over V,O: 4. Per-channel settings
    V->>O: PATCH .../properties/{vp}/channels/{ch}/settings
    O-->>V: Saved

    Note over V,O: 5. Mapping
    V->>O: PATCH .../channels/{ch}/mappings (property)
    V->>O: PATCH .../roomtypes/{rt}/channels/{ch}/mappings (room type)
    V->>O: PATCH .../rateplans/{rp}/channels/{ch}/mappings (rate plan)
    O-->>V: Mappings applied

    Note over V,C: 6. Push rates and availability
    V->>O: POST .../ari · POST .../ari/avails
    O->>C: Apply per-channel rates, business days, and availability
```

## Checklist

- [ ] Read `requires_cms_authorization` from the channel metadata and branch the pre-mapping step
- [ ] Collect candidate `channel_roomtype_id` and `channel_rateplan_id` values from the channel-side property
- [ ] Complete property → room type → rate plan mapping
- [ ] Airbnb: map rate plans to `0`, since Airbnb has no concept of rate plans
- [ ] Handle the `processing_mode` branch after the ON request (`auto_approve` or `external_review`)
- [ ] Push rates and business days with `POST .../ari` (`type: overnight`)
- [ ] Push availability with `POST .../ari/avails` (consolidated per room type, up to 730 days)
- [ ] Availability is room-only (per room type) — send `package` availability aligned to `standalone`

:::info Airbnb rate plan mapping
Airbnb has no concept of rate plans, so map `channel_rateplan_id` to `0`.
:::

## Opening request body

```json
{
  "request_type": "on",
  "channel_property_id": "CP-001",
  "note": "Opening request agreed with the channel manager."
}
```

| Field | Description |
|---|---|
| `request_type` | `on` to open, `off` to stop |
| `channel_property_id` | Channel-side property ID (optional). Can be sent when requesting a `processing_mode=external_review` channel such as Booking.com. Allowed only with `request_type: "on"`; sending it with `off` returns `400`. If another property already uses the ID on the same channel, it returns `409`. When submitted, it is applied to the property mapping at request time. |
| `note` | Requester's note (optional, up to 16,000 characters). Allowed on both `on` and `off`; use it for information the operator should see. |

## Extra occupancy charges per channel

Per-channel options are configured through the channel settings endpoint (RFC 7396 Merge Patch). Support for extra occupancy charges, and how they are configured, differs by channel.

| Channel | Extra occupancy charge | How it is configured |
|---|---|---|
| Expedia | Not supported | — |
| Booking.com | Not supported | — |
| Trip.com | Supported | `pricing_model` in the property-level settings — `OBP` (occupancy based) or `Standard` (per room). With `OBP`, the property's `settings.extra_adult_price` is included in the rate. |
| Agoda | Supported | Rates are sent per occupancy (occupancy based). Amounts for occupancy above the standard are sent to the channel, including the property's `settings.extra_adult_price`. |
| Airbnb | Supported | The property's `settings.extra_adult_price` is applied automatically when configured per room. |

:::warning
Room-level channel settings (`.../roomtypes/{vendor_roomtype_id}/channels/{channel_id}/settings`) are **Airbnb only**. Calling them for another channel returns an error.
:::

## Parent rate plans (`channel_parent_rateplan_id`)

The `rateplans` object in the [`GET /gds/vendor/channels/{channel_id}/properties/{channel_property_id}`](/docs/api/vendor-v3/get-channel-property) response carries the parent rate plan ID.

- Such a rate plan can be linked, but rates sent to it are not forwarded to the channel
- Its rate follows the parent rate plan pointed to by `channel_parent_rateplan_id` and changes automatically
- Supported on Booking.com and Agoda

## Opening conditions

The full list of checks is the same as in the [Plus channel guide](/docs/api/vendor-v3/guides/channel-open-plus#opening-conditions).

:::tip Relaxed content checks on legacy channels
Legacy channels (`requires_cms_authorization=false`) **skip the property image, category, and room image checks** when opening.
:::

## Endpoints

All paths are prefixed with `/gds/vendor`.

**Channel discovery and authorization**

| Method | Path | Description |
|---|---|---|
| `GET` | `/channels` | Channel metadata |
| `GET` | `/properties/{vendor_property_id}/channels` | Channels available for the property, with request status |
| `GET` | `/channels/{channel_id}/properties/{channel_property_id}` | Channel-side property info; the response includes `channel_parent_rateplan_id` |
| `GET` | `/channels/{channel_id}/authorization` | OAuth authorization URL — only for channels with `requires_cms_authorization=true` ([implementation guide](/docs/api/vendor-v3/guides/airbnb-authorization)) |

**Channel mapping**

| Scope | Method | Path |
|---|---|---|
| Property | `GET` · `PATCH` | `/properties/{vendor_property_id}/channels/{channel_id}/mappings` |
| Room type | `GET` · `PATCH` | `.../roomtypes/{vendor_roomtype_id}/channels/{channel_id}/mappings` |
| Rate plan | `GET` · `PATCH` | `.../rateplans/{vendor_rateplan_id}/channels/{channel_id}/mappings` |

**Opening requests**

| Method | Path | Description |
|---|---|---|
| `POST` | `/properties/{vendor_property_id}/channels/{channel_id}` | ON and OFF requests |
| `GET` | `/properties/{vendor_property_id}/channels/{channel_id}` | Request status and history |

---

### Airbnb Authorization

> Implementing the host OAuth authorization required for Airbnb integration

# Airbnb Authorization

Airbnb requires **OAuth approval from the host's Airbnb account** for every property. The host must sign in and grant access before ONDA can read listings, push rates and availability, and receive bookings under that account.

A vendor system cannot do this on the host's behalf; it must happen **in the host's own browser**. For an account that has already granted access, the approval screen is sometimes skipped and the flow completes immediately.

:::info Prerequisites
- This applies to channels whose channel metadata sets `requires_cms_authorization` to `true` → [Direct Legacy Channel Opening](/docs/api/vendor-v3/guides/channel-open-legacy#branching-before-mapping)
- Find Airbnb's `channel_id` with [`GET /gds/vendor/channels`](/docs/api/vendor-v3/list-channels). The examples below use `133`.
:::

## When it is needed

| Situation | Authorization needed |
|---|---|
| Connecting a property to Airbnb for the first time | **Yes** — once |
| The host switches to a different Airbnb account | **Yes** — re-authorize |
| Token expiry | **No** — ONDA refreshes it automatically |

Where it sits in the overall onboarding: **room type and rate plan mapping cannot proceed until authorization completes.**

```
Property registered (integrated) → Host authorization (this guide) → Room type and rate plan mapping → Start selling
```

## What the vendor implements

1. **An entry point** — put a "Connect Airbnb account" button in your admin screen. On click, call the authorization URL API and send the host to the returned `url` (a new window is recommended)
2. **A dedicated return path** — a callback path for the result (for example `/airbnb/callback`). ONDA returns the host there with `auth_result` appended, so read it from that path and branch the screen
3. **Completion check** — `auth_result` is only a display hint; confirm the actual result through the mapping lookup API

## The full flow

```mermaid
sequenceDiagram
    participant V as Vendor service
    participant B as Host browser
    participant O as ONDA
    participant A as Airbnb

    V->>O: 1. Request an authorization URL
    O-->>V: url
    V->>B: 2. Open in a new window
    B->>A: 3. Sign in and consent
    A-->>B: 302 (code · state)
    B->>O: 4. Callback reaches ONDA
    O->>A: Exchange the token
    O-->>B: 5. 302 to redirect_url
    V->>O: 6. Confirm via the mapping lookup
```

## Step 1 · Issue the authorization URL

Call [`GET /gds/vendor/channels/{channel_id}/authorization`](/docs/api/vendor-v3/get-channel-authorization-url) from the vendor server.

```bash
curl -H "Authorization: {vendor_access_token}" \
  "https://vendor.dapi.tport.dev/gds/vendor/channels/133/authorization?vendor_property_id=VP-001&redirect_url=https%3A%2F%2Fvendor.example.com%2Fairbnb%2Fcallback"
```

| Query parameter | Required | Description |
|---|---|---|
| `vendor_property_id` | Required | The property ID in the vendor's system |
| `redirect_url` | Required | The dedicated path to return to after authorization. Only `http(s)` is allowed, and the pipe character (`\|`) is not permitted. ONDA appends `auth_result` on return, and any query parameters the vendor added are preserved |

**Success response (200)**

```json
{
  "url": "https://www.airbnb.com/oauth2/auth?client_id=...&redirect_uri=...&scope=...&state=..."
}
```

The issued `url` is **valid for one hour**. Proceeding with an expired URL fails without returning, so issue a new one and try again.

**Error responses**

| Status | Cause | What the vendor should do |
|---|---|---|
| `400` | Malformed `redirect_url` — not an `http(s)` URL, or contains a pipe character | Fix the request value |
| `401` | Authentication token error | Check the token |
| `403` `UnsupportedChannel` | The channel does not use OAuth authorization | This channel needs no authorization |
| `404` `Property not found` | The property is not yet integrated, or is not yours | Complete property registration first |

## Step 2 · The host authorizes

Open the issued `url` in a new window as-is. The host signs in with **the Airbnb account to be connected** and grants access.

## Step 3 · Handling the return

After processing the authorization, ONDA returns the host's browser to `redirect_url` with a 302, appending the result as `auth_result`.

| Return | Meaning | What to do |
|---|---|---|
| `redirect_url?auth_result=success` | Success | Confirm the real result immediately with the mapping lookup |
| `redirect_url?auth_result=fail` | Failure | Tell the host authorization failed and to try again |
| No return at all | Undetermined | The host abandoned the flow, or the callback never reached ONDA |

## Step 4 · Confirming completion

Check with [`GET /gds/vendor/properties/{vendor_property_id}/channels/{channel_id}/mappings`](/docs/api/vendor-v3/get-property-mapping).

```bash
curl -H "Authorization: {vendor_access_token}" \
  "https://vendor.dapi.tport.dev/gds/vendor/properties/VP-001/channels/133/mappings"
```

```json
{
  "vendor_property_id": "VP-001",
  "channel_id": "133",
  "channel_name": "Airbnb",
  "status": "enabled",
  "channel_property_id": "123456789",
  "roomtype_mappings": [],
  "rateplan_mappings": []
}
```

Authorization is complete when `channel_property_id` holds the **authorized host account ID**. The mapping is saved before the success redirect (302) is sent, so after a successful return the first lookup usually shows it already.

## How to judge success and failure

| Observed state | Verdict | Next action |
|---|---|---|
| `auth_result=success` and `channel_property_id` present in the mapping | **Authorized** | Proceed with room type and rate plan mapping |
| `auth_result=success` but no value in the mapping | Inconsistent | Verify `vendor_property_id` is correct and retry the lookup once or twice. If it stays empty, treat it as a failure and re-authorize |
| `auth_result=fail` | Failed | Prompt the host to retry |
| No return at all | Undetermined | Run the mapping lookup once more — if a value appears it is complete, otherwise retry |

:::warning The mapping lookup is the single source of truth
`auth_result` is only a hint for deciding which screen to show. The host can edit the browser address bar — appending `?auth_result=success` by hand — so it must never be treated as evidence of a completed integration. Always confirm through the mapping lookup before showing success.
:::

## When there is no return

There are two cases, and they call for opposite handling.

| Case | State in ONDA | What the vendor should do |
|---|---|---|
| **A. The callback never reached ONDA**<br/>(the host abandoned or declined on the Airbnb screen) | **Nothing was created** — no token, no mapping. The authorization code is single-use and simply expires | Start over by issuing a new URL |
| **B. It reached ONDA but the browser return failed**<br/>(vendor site down, window closed) | The token and mapping **already exist**. Only the vendor missed the return signal | The mapping lookup will show it as complete |

:::tip
Treat a closed window or a timeout **as a trigger to run the mapping lookup once more, not as confirmed failure**. Only when the lookup is still empty should you treat it as incomplete (case A).
:::

## Re-authorization and account switching

| Case | What ONDA does | What the vendor should know |
|---|---|---|
| Re-authorizing with the same account | Refreshes the token only; existing room type and rate plan mappings are kept | Nothing further to do |
| Re-authorizing with a different account (host change) | The property mapping switches to the new account, and **all existing room type and rate plan mappings are reset to unmapped** | Room type and rate plan mapping must be redone from scratch before selling can resume |

:::warning
Switching accounts resets the mappings. When entering re-authorization, we recommend warning the host that **"authorizing with a different account will reset your room type and rate plan mappings."**
:::

## Implementation tips

**Use a dedicated return path** — with a path reserved for the authorization return (for example `/airbnb/callback`), arriving at that path is itself the return signal. You only need to read `auth_result`, with no extra marker parameter.

**URL-encode `redirect_url`** — it is a query value on the issuing API, so sending it unencoded truncates the value at `://` and similar. Pass it as `https%3A%2F%2F...`.

**Open a new window and notify the parent** — running the flow in a new window keeps the host's admin screen intact. Have the return path notify the parent window of the result (with `window.opener.postMessage`, for example) and close itself, so the parent can run the mapping lookup and show the outcome.

**Cap the confirmation retries** — the first lookup is usually enough, but if you retry to cover a transient failure, set a limit. Never poll indefinitely. For example, after five lookups at two-second intervals with `channel_property_id` still empty, treat it as a failure and prompt re-authorization.

**Warn against authorizing with the wrong account** — a new window shares the browser's login session, so an already signed-in account may be approved straight away. On re-authorization that counts as an account switch and resets existing mappings. We recommend telling the host to "check you are signed in with the account you want to connect" before starting.

**Authorization is per property** — even when one host account operates several properties, authorization runs per `vendor_property_id`. Issue, approve, and confirm for each property.

**Issue the URL when the button is pressed** — the issued URL is valid for only one hour, so issuing it in advance and storing it in a screen or email risks the host opening an already-expired link.

## Common problems

<details>
<summary>Airbnb shows "You can only authorize a test app to a test user created under that same app"</summary>

The development environment runs as an Airbnb test app. A test app can only be authorized by test users created under that same app, so sign out of the real account and proceed with the test user account you were given.

</details>

<details>
<summary>It returned with auth_result=success, but the mapping lookup is empty</summary>

This combination does not occur in the normal flow, because the success redirect happens only after the mapping is saved. First check that the `vendor_property_id` you looked up matches the property you authorized. If it is still empty after one or two retries, treat it as a failed authorization and re-authorize. It may also be an `auth_result` the host typed into the address bar.

</details>

<details>
<summary>Nothing comes back at all</summary>

This is usually case A — the callback never reached ONDA — and nothing was created. Still, run the mapping lookup once: in case B the integration may already be in place. If it is empty, start over by issuing a new URL.

</details>

<details>
<summary>Room type and rate plan mappings disappeared after authorization</summary>

The authorization used a different account than before. Remapping is required.

</details>

---


================================================================================
## Vendor API 3.0 - Reservation
================================================================================

### Plus Channel Booking

> Holding, confirming, modifying, and cancelling bookings on Plus channels

# Plus Channel Booking

How bookings are held, confirmed, modified, and cancelled on Plus channels. **ONDA and the vendor each validate at every step.**

All booking endpoints are **vendor APIs** (ONDA → vendor), so the vendor implements them as a server.

## Step by step

1. **Look up the refund policy in advance** — [`GET .../rateplans/{vendor_rateplan_id}/refund_policy`](/docs/api/vendor-v3/webhook-get-refund-policy)
2. **Create a held booking** — [`POST /bookings`](/docs/api/vendor-v3/webhook-create-booking) (validated, then availability is decremented)
3. **Confirm the booking** — [`PUT /bookings/{vendor_booking_number}/confirm`](/docs/api/vendor-v3/webhook-confirm-booking)
4. **Modify the booking** — [`PUT /bookings/{vendor_booking_number}/modify`](/docs/api/vendor-v3/webhook-modify-booking) (**soft changes only**)
5. **Cancel the booking** — [`POST /bookings/{vendor_booking_number}/cancel`](/docs/api/vendor-v3/webhook-cancel-booking)
6. **Re-send availability** — [`POST .../ari/avails`](/docs/api/vendor-v3/push-avails)

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA
    participant C as Sales channel

    Note over C,V: A booking is made
    C->>O: Booking created
    Note over O: ONDA validates rates, availability, business days, property, room, package status
    O->>V: POST /bookings (held booking · type: overnight)
    Note over V: Vendor validates against its own rules
    V-->>O: Held booking created (availability decremented)

    Note over O,V: Confirmation (within 15 minutes of the hold)
    Note over O: ONDA validates
    O->>V: PUT /bookings/{vendor_booking_number}/confirm
    V-->>O: Confirmed

    Note over O,V: Cancellation
    Note over O: ONDA validates duplicate cancellation and refund amount
    O->>V: POST /bookings/{vendor_booking_number}/cancel
    V-->>O: Cancelled

    Note over C,V: Booker and guest details change (soft change)
    opt Details change
        C->>O: Booker detail change notice
        O->>V: PUT /bookings/{vendor_booking_number}/modify
        V-->>O: Modified
    end
```

## Checklist

- [ ] Validate at each step — hold, confirm, cancel — against availability, rates, sales status, and business days
- [ ] Implement automatic cancellation (and availability restore) when `confirm` is not called within 15 minutes of the hold
- [ ] Create the vendor-side booking against `gds_sub_booking_number`
- [ ] Return the per-date refund policy just before booking
- [ ] Re-send changed availability after confirmation (`POST .../ari/avails`)
- [ ] Use only `overnight` as the booking `type`
- [ ] Allow only soft changes on `modify` — define how to reject or handle hard change requests
- [ ] Send booking vouchers (confirmation and cancellation) directly to the property

:::warning `modify` on Plus channels allows soft changes only
Only metadata changes such as booker and guest details are possible. **Hard changes — changing stay dates or the booked room — are not allowed on Plus channels**; handle them by cancelling and rebooking. (Direct channels do allow hard changes.)
:::

:::warning Automatic cancellation — 15-minute timeout
If confirmation is not called **within 15 minutes** of creating the held booking, the hold is **cancelled automatically** and availability is restored.
:::

## Key fields

| Field | Endpoint | Description |
|---|---|---|
| `payment_type` | `POST /bookings`, `confirm` response | On Plus channels this is **always `prepayment`**. `paid_amount` has been removed. |
| `net_price` | `POST /bookings`, `modify` request | Settlement amount after fees — what will be paid out |
| `canceled_by` | `cancel` request | Who cancelled — `user`, `admin`, `channel`, or `system` |
| `memo` | `cancel` request | Cancellation reason or note. When a channel administrator cancels, the reason arrives together with `canceled_by=channel` |
| `type` | `POST /bookings` request | Only `overnight` is used |

:::info Fields Plus channels do not use
These exist in the spec but are never sent for Plus channel bookings. You do not need to implement them.

- `visit_type` — arrival method. Used only by some direct channels that support day-use
- `secondary_channel` — the secondary channeling route. Sent only by some legacy channels that support it
:::

For how booking numbers are structured, see [Common · Overview](/docs/api/vendor-v3/guides/overview#booking-number-structure). Always create the vendor-side booking against `gds_sub_booking_number`.

## Endpoints

All of these are **vendor APIs** (ONDA → vendor).

| Method | Path | Description |
|---|---|---|
| `GET` | `.../rateplans/{vendor_rateplan_id}/refund_policy` | Look up the cancellation and refund policy in advance |
| `POST` | `/bookings` | Create a held booking (decrements availability) |
| `PUT` | `/bookings/{vendor_booking_number}/confirm` | Confirm the booking |
| `PUT` | `/bookings/{vendor_booking_number}/modify` | Modify the booking (soft changes only) |
| `POST` | `/bookings/{vendor_booking_number}/cancel` | Cancel the booking |

To read ONDA's booking data, the vendor uses the HUB APIs [`GET /gds/vendor/bookinglist`](/docs/api/vendor-v3/list-bookings) and [`GET /gds/vendor/booking/{vendor_booking_number}`](/docs/api/vendor-v3/get-booking).

---

### Direct Channel Booking

> Holding, confirming, modifying, and cancelling bookings on direct channels

# Direct Channel Booking

How bookings are held, confirmed, modified, and cancelled on direct channels. Unlike Plus channels, **every step is accepted unconditionally, with no validation**.

## Differences from Plus channels

| Aspect | Plus channel | Direct channel |
|---|---|---|
| Validation | ONDA and the vendor each validate | None — accepted unconditionally |
| Booking modification | Soft changes only | Both soft and hard changes |
| Booking `type` | `overnight` | `overnight` (channels that support day-use also send `dayuse`) |

## Step by step

1. **Create a held booking** — [`POST /bookings`](/docs/api/vendor-v3/webhook-create-booking) (accepted without validation; availability is decremented)
2. **Confirm the booking** — [`PUT /bookings/{vendor_booking_number}/confirm`](/docs/api/vendor-v3/webhook-confirm-booking)
3. **Modify the booking** — [`PUT /bookings/{vendor_booking_number}/modify`](/docs/api/vendor-v3/webhook-modify-booking) (soft and hard changes)
4. **Cancel the booking** — [`POST /bookings/{vendor_booking_number}/cancel`](/docs/api/vendor-v3/webhook-cancel-booking)

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA
    participant C as Sales channel

    Note over C,V: A booking is made
    C->>O: Booking created
    Note over O: No validation — accepted unconditionally
    O->>V: POST /bookings (type: overnight | dayuse)
    Note over V: No validation — accepted unconditionally
    V-->>O: Held booking created (availability decremented)

    Note over O,V: Confirmation
    O->>V: PUT /bookings/{vendor_booking_number}/confirm
    V-->>O: Confirmed

    Note over O,V: Cancellation
    Note over O: All cancellations accepted
    O->>V: POST /bookings/{vendor_booking_number}/cancel
    V-->>O: Cancelled

    Note over C,V: Modification (soft and hard)
    opt Booking details change
        C->>O: Modification notice
        O->>V: PUT /bookings/{vendor_booking_number}/modify
        V-->>O: Modified
    end
```

## Types of modification

`modify` requests are classified by what they change. Direct channels allow both types.

| Type | Scope |
|---|---|
| **Soft change** | Booker and guest details — metadata that affects neither availability nor dates |
| **Hard change** | Check-in and check-out dates, the booked room, and similar — affects availability, rates, and mappings |

## Checklist

- [ ] Accept every booking step unconditionally (no validation)
- [ ] Handle the booking `type` — `overnight` plus `dayuse` on channels that support day-use, `overnight` only elsewhere
- [ ] Receive and handle `visit_type` (arrival method: `car` or `walk`)
- [ ] Create the vendor-side booking against `gds_sub_booking_number`
- [ ] Implement the `modify` flow, distinguishing soft changes from hard changes
- [ ] On a hard change, decrement or restore availability and recalculate rates
- [ ] Send booking vouchers (confirmation and cancellation) directly to the property

## Key fields

| Field | Endpoint | Description |
|---|---|---|
| `payment_type` | `POST /bookings`, `confirm` response | Prepaid (`prepayment`) or pay on arrival (`postpayment`). `paid_amount` has been removed. |
| `net_price` | `POST /bookings`, `modify` request | Settlement amount after fees — what will be paid out |
| `canceled_by` | `cancel` request | Who cancelled — `user`, `admin`, `channel`, or `system` |
| `memo` | `cancel` request | Cancellation reason or note. When a channel administrator cancels, the reason arrives together with `canceled_by=channel` |
| `type` | `POST /bookings` request | `overnight` for an overnight stay. Channels that support day-use also send `dayuse` |
| `visit_type` | `POST /bookings` request | How the guest arrives — `car` or `walk`. Sent only by some channels that support day-use |
| `secondary_channel` | `POST /bookings` request | The selling route when the booking came through secondary channeling (informational). Sent only by some legacy channels that support it |

For how booking numbers are structured, see [Common · Overview](/docs/api/vendor-v3/guides/overview#booking-number-structure).

## Endpoints

All of these are **vendor APIs** (ONDA → vendor).

| Method | Path | Description |
|---|---|---|
| `GET` | `.../rateplans/{vendor_rateplan_id}/refund_policy` | Look up the cancellation and refund policy in advance |
| `POST` | `/bookings` | Create a held booking (decrements availability) |
| `PUT` | `/bookings/{vendor_booking_number}/confirm` | Confirm the booking |
| `PUT` | `/bookings/{vendor_booking_number}/modify` | Modify the booking (soft and hard changes) |
| `POST` | `/bookings/{vendor_booking_number}/cancel` | Cancel the booking |

---


================================================================================
## Vendor API 3.0 - Operation
================================================================================

### Status Update

> Changing the sales status of properties, room types, rate plan models, and rate plans

# Status Update

How a vendor pushes active and inactive states for properties, room types, rate plan models, and rate plans to ONDA with `PATCH`.

## Step by step

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA

    Note over V,O: Status change — vendor → ONDA (PATCH)
    V->>O: PATCH .../properties/{vendor_property_id}
    O-->>V: 200 OK (property status updated)

    opt Room type status
        V->>O: PATCH .../roomtypes/{vendor_roomtype_id}
        O-->>V: 200 OK
    end

    opt Rate plan model status
        V->>O: PATCH .../rateplan-models/{vendor_rateplan_model_id}
        O-->>V: 200 OK
    end

    opt Rate plan status
        V->>O: PATCH .../rateplans/{vendor_rateplan_id}
        O-->>V: 200 OK
    end
```

## Endpoints

| Target | Endpoint |
|---|---|
| Property | [`PATCH /gds/vendor/properties/{vendor_property_id}`](/docs/api/vendor-v3/update-property) |
| Room type | [`PATCH .../roomtypes/{vendor_roomtype_id}`](/docs/api/vendor-v3/update-roomtype) |
| Rate plan model | [`PATCH .../rateplan-models/{vendor_rateplan_model_id}`](/docs/api/vendor-v3/update-rateplan-model) |
| Rate plan | [`PATCH .../rateplans/{vendor_rateplan_id}`](/docs/api/vendor-v3/update-rateplan) |

## Checklist

- [ ] Apply activation and deactivation at the property level
- [ ] Apply activation and deactivation at the room type, rate plan model, and rate plan levels
- [ ] Confirm that status changes return `200 OK`

:::info
Status changes and content changes share the same `PATCH` endpoint. Because `PATCH` follows RFC 7396 Merge Patch, sending only the status field leaves the rest of the content untouched.
:::

---

### Content Update

> Pushing content changes for properties, room types, and rate plans

# Content Update

When content changes on the vendor side, push it to ONDA with `PATCH`. Lookups (`GET`) exist as a fallback.

## Step by step

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA

    Note over V,O: Push changed content — vendor → ONDA (PATCH)

    opt Property changed
        V->>O: PATCH .../properties/{vendor_property_id}
        O-->>V: 200 OK
    end
    opt Room type changed
        V->>O: PATCH .../roomtypes/{vendor_roomtype_id}
        O-->>V: 200 OK
    end
    opt Rate plan model changed
        V->>O: PATCH .../rateplan-models/{vendor_rateplan_model_id}
        O-->>V: 200 OK
    end
    opt Rate plan changed
        V->>O: PATCH .../rateplans/{vendor_rateplan_id}
        O-->>V: 200 OK
    end

    opt Fallback lookup (optional)
        V->>O: GET .../properties · .../roomtypes · .../rateplan-models · .../rateplans
        O-->>V: Lookup result
    end
```

## Checklist

- [ ] `PATCH` only the content that changed
- [ ] Confirm the direction is vendor → ONDA (push)
- [ ] Push `settings.extra_adult_price` changes with `PATCH .../properties/{vendor_property_id}`, sending `null` to clear it
- [ ] Apply the mapping rules when changing `channels` on a rate plan model
- [ ] (Optional) Verify the fallback lookups work

:::info Changing the property's default extra adult charge
`settings.extra_adult_price` (integer KRW) is changed through the `settings` object on property update, and sending `null` clears it. It applies to occupancy-based rates on legacy Agoda and Trip.com, and to extra-occupancy charges on Plus channels.
:::

:::warning Mapping changes when editing `channels` on a rate plan model
- Adding (`[131] → [131, 132]`): a mapping is created for 132 only
- Removing (`[131, 132] → [131]`): existing mappings are kept, not deleted. Rate plans created afterwards get no mapping for 132
- Empty (`[131, 132] → []`): mappings are created on every eligible channel
:::

## Endpoints

**Pushing changes (required)**

| Target | Endpoint |
|---|---|
| Property | [`PATCH /gds/vendor/properties/{vendor_property_id}`](/docs/api/vendor-v3/update-property) |
| Room type | [`PATCH .../roomtypes/{vendor_roomtype_id}`](/docs/api/vendor-v3/update-roomtype) |
| Rate plan model | [`PATCH .../rateplan-models/{vendor_rateplan_model_id}`](/docs/api/vendor-v3/update-rateplan-model) |
| Rate plan | [`PATCH .../rateplans/{vendor_rateplan_id}`](/docs/api/vendor-v3/update-rateplan) |

**Fallback lookups (optional)**

| Target | List | Single |
|---|---|---|
| Property | [`GET /gds/vendor/properties`](/docs/api/vendor-v3/list-properties) | [`GET .../{vendor_property_id}`](/docs/api/vendor-v3/get-property) |
| Room type | [`GET .../roomtypes`](/docs/api/vendor-v3/list-roomtypes) | [`GET .../{vendor_roomtype_id}`](/docs/api/vendor-v3/get-roomtype) |
| Rate plan model | [`GET .../rateplan-models`](/docs/api/vendor-v3/list-rateplan-models) | [`GET .../{vendor_rateplan_model_id}`](/docs/api/vendor-v3/get-rateplan-model) |
| Rate plan | [`GET .../rateplans`](/docs/api/vendor-v3/list-rateplans) | [`GET .../{vendor_rateplan_id}`](/docs/api/vendor-v3/get-rateplan) |

:::tip
Content creation and updates flow **vendor → ONDA as a push** (`POST` and `PATCH`) by default. Lookups are an optional fallback; decide whether to implement them based on your operational needs.
:::

---

### Settlement

> Retrieving settlement amounts and reconciling them

# Settlement

How a vendor retrieves ONDA's settlement amounts and reconciles them against its own records.

## Step by step

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA

    Note over V,O: Settlement comparison — vendor → ONDA
    V->>O: GET /gds/vendor/bookings
    O-->>V: Settlement amounts

    opt Reconciling bookings
        V->>O: GET /gds/vendor/bookinglist
        O-->>V: Booking list
        V->>O: GET /gds/vendor/booking/{vendor_booking_number}
        O-->>V: Booking details
    end
```

## Endpoints

| Purpose | Endpoint |
|---|---|
| Settlement comparison | [`GET /gds/vendor/bookings`](/docs/api/vendor-v3/get-settlement-comparison) |
| List bookings | [`GET /gds/vendor/bookinglist`](/docs/api/vendor-v3/list-bookings) |
| Get a single booking | [`GET /gds/vendor/booking/{vendor_booking_number}`](/docs/api/vendor-v3/get-booking) |

## Checklist

- [ ] Retrieve settlement amounts and reconcile them against internal records
- [ ] Confirm that the booking list and detail lookups work together
- [ ] Define a process for handling settlement discrepancies

:::info
`net_price` on a booking is the settlement amount after fees — what will actually be paid out. Reconcile against this value.
:::

---

### Mapping & Stop Sale

> Updating mappings and stopping sales on direct channels

# Mapping & Stop Sale

How to update property, room type, and rate plan mappings on direct channels, and how to remove mappings for a channel you no longer sell on. Everything goes through `PATCH`; to delete, send the mapping value as `null`.

:::warning Direct channels only
Mapping `PATCH` calls work only on **direct channels** (`channel_type=cms`). ONDA manages mappings for Plus channels (`channel_type=hub`), so they cannot be read, updated, or deleted this way.
:::

## Updating a mapping

```mermaid
sequenceDiagram
    participant V as Vendor
    participant O as ONDA

    Note over V,O: Mapping update — vendor → ONDA (PATCH)
    opt Property mapping
        V->>O: PATCH .../channels/{channel_id}/mappings
        O-->>V: Applied
    end
    opt Room type mapping
        V->>O: PATCH .../roomtypes/{vendor_roomtype_id}/channels/{channel_id}/mappings
        O-->>V: Applied
    end
    opt Rate plan mapping
        V->>O: PATCH .../rateplans/{vendor_rateplan_id}/channels/{channel_id}/mappings
        O-->>V: Applied
    end
```

## Stopping sales — deleting a mapping

Sending `null` as the mapping value to the same `PATCH` endpoint deletes the mapping. There is no separate `DELETE` method.

Remove mappings in order: property, then room type, then rate plan.

## Endpoints

| Scope | Read | Update and delete |
|---|---|---|
| Property | [`GET .../channels/{channel_id}/mappings`](/docs/api/vendor-v3/get-property-mapping) | [`PATCH`](/docs/api/vendor-v3/update-property-mapping) |
| Room type | [`GET .../roomtypes/{vendor_roomtype_id}/channels/{channel_id}/mappings`](/docs/api/vendor-v3/get-roomtype-mapping) | [`PATCH`](/docs/api/vendor-v3/update-roomtype-mapping) |
| Rate plan | [`GET .../rateplans/{vendor_rateplan_id}/channels/{channel_id}/mappings`](/docs/api/vendor-v3/get-rateplan-mapping) | [`PATCH`](/docs/api/vendor-v3/update-rateplan-mapping) |

## Checklist

- [ ] Call mapping `PATCH` only on direct channels — verify Plus channels are excluded
- [ ] Handle property, room type, and rate plan mapping updates
- [ ] Send `null` as the mapping value when stopping sales
- [ ] Confirm the result after changing a mapping

:::tip To stop the channel itself
Deleting mappings is separate from stopping the channel. To stop selling on a channel, submit an OFF request to [`POST .../channels/{channel_id}`](/docs/api/vendor-v3/create-property-channel-request) with `{ "request_type": "off" }`.
:::

---


================================================================================
## Vendor API 3.0 - Common
================================================================================

### Refund Policy

> How the property default policy and per-date policies are handled

# Refund Policy

Cancellation and refund terms can differ by date, so the property-level default policy and the per-date policies are handled separately. Sending the wrong policy overcharges the guest's cancellation fee and leads to complaints.

## Two kinds of policy

| Kind | How it is provided |
|---|---|
| **Property default policy** | Exactly **one** entry in the property's `refunds` field |
| **Per-date policy** | Served through the [`GET .../rateplans/{vendor_rateplan_id}/refund_policy`](/docs/api/vendor-v3/webhook-get-refund-policy) lookup API, which the vendor implements |

## Rules

1. Send **exactly one** default policy in the property's `refunds` field.
2. For the policy shown to the guest just before booking, return the **per-date policy** through the `refund_policy` lookup API.
3. When a booking is actually created, return the **per-date policy** rather than the property-level one.
4. Set the property default policy to the **most conservative** of the per-date policies.
5. The property policy must never produce a larger cancellation fee for the guest than the per-date policy would.

:::danger
If the property default policy is worse for the guest than the per-date policy — that is, if it charges more — **complaints will follow.** Always align it with the most conservative per-date policy.
:::

```mermaid
sequenceDiagram
    participant O as ONDA
    participant V as Vendor

    Note over O,V: Checking the refund policy before booking
    O->>V: GET .../rateplans/{vendor_rateplan_id}/refund_policy
    V-->>O: Per-date refund policy
    Note over O: Show the per-date policy to the guest, then proceed
```

## Refund policy on legacy channels

:::info
Direct legacy channels (Booking.com, Agoda, Expedia, and so on) follow the **cancellation terms configured on each channel**, not the shared ONDA and property policy. The rules above apply to Plus channels.
:::

## Checklist

- [ ] Send one default policy in the property's `refunds` field
- [ ] Serve per-date policies through the `refund_policy` lookup API
- [ ] Return the per-date policy when a booking is created
- [ ] Set the default policy to the most conservative per-date policy
- [ ] Verify the default policy never charges the guest more than the per-date policy
- [ ] Handle legacy channels according to the terms configured on each channel

---


================================================================================
## Vendor API 3.0 — Endpoint Reference
================================================================================

### POST /gds/vendor/properties/{vendor_property_id}/channels/{channel_id} — Create Channel Request

- Creates a request to start (ON) or stop (OFF) selling on the channel.
- ON requests run the shared pre-checks and are rejected immediately when the conditions are not met.
- CMS-integrated channels are exempt from the property image, property category, and room image checks.
- OFF requests are auto-approved immediately on every channel.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `request_type` | string | ✓ | Request type. on requests channel opening; off requests channel closure.. Enum: `on`, `off` |
| `channel_property_id` | string |  | Channel-side property ID (optional). Allowed only on on requests; sending it with off is rejected with 400. If another property already uses the ID on the same channel, it is rejected with 409. When submitted, it is applied to the property mapping at request time. |
| `note` | string |  | Note left by the requester (vendor) (optional, up to 16,000 characters). Allowed on both on and off requests. Use it for information the operator should see. |

**Responses:**

**200** — Request processing result:

| Field | Type | Description |
|-------|------|-------------|
| `channel_id` | number | Channel ID |
| `channel_name` | string | Channel name |
| `request_type` | string | Request type. Enum: `on`, `off` |
| `request_status` | string | Request status. Enum: `pending`, `confirm`, `reject` |
| `channel_property_id` | string,null | Channel property ID submitted with the request. null if none was submitted. |
| `note` | string,null | Requester (vendor) note. null if none was submitted. The processor's reason is returned separately in reason. |
| `reason` | string,null | Reason recorded by the processor (operator or system). Usually filled in when a request is rejected. |
| `created_at` | string | Requested at (ISO 8601) |
| `updated_at` | string | Updated at (ISO 8601) |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties — Create Property

- Creates a vendor property in ONDA. Room types, rate plan models, and rate plans are all created beneath this property, making this the first step of content integration.
- `id` (the vendor property ID) is required, and the property name goes in `ko-kr` under `i18n`.
- Code-based fields such as `classifications`, `property_tags`, and `facility_tags` accept only codes obtained from the code catalog.
- `settings.extra_adult_price` is the property's default extra adult charge (integer KRW) and applies to channels that price by occupancy.
- Calling again with an existing `id` returns `409`. Use Update Property to change an existing property.

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  | Content by locale. ko-KR is required when creating; one entry per locale. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Property name |
| `i18n[].description` | string |  | Property description |
| `i18n[].address` | string |  | Address (road name or lot number) on a single line. |
| `i18n[].address_detail` | string |  | Address detail (building, block, floor, unit). Provide together with address. |
| `i18n[].notice` | string |  | General notice |
| `i18n[].reservation_description` | string |  | Booking notice |
| `i18n[].refunds_description` | string |  | Refund notice |
| `email` | string (email) |  | Primary email |
| `phone` | string |  | Primary phone |
| `sms_phone` | string |  | SMS sender number |
| `checkin` | string |  | Check-in time (HH:mm) |
| `checkout` | string |  | Check-out time (HH:mm) |
| `website` | string (uri) |  | Primary website |
| `refunds` | object |  | Refund policy. Keys use the `0d`/`1d` form for how many days before check-in, and values are refund rates (%). For example, `1d: 30` means cancelling one day before check-in refunds 30%. |
| `classifications` | array |  | Property classifications |
| `property_tags` | array |  | Property tags |
| `facility_tags` | array |  | Facility tags |
| `service_tags` | array |  | Service tags |
| `attraction_tags` | array |  | Nearby attraction tags |
| `photos` | array |  | Property images |
| `photos[].url` | string | ✓ | Image URL |
| `photos[].description` | string |  | Image caption |
| `photos[].order` | integer | ✓ | Sort order |
| `settings` | object |  | Property settings |
| `settings.extra_adult_price` | integer,null |  | Default extra adult charge for the property (integer KRW; sending null clears it) |
| `id` | string | ✓ | Vendor property ID (max 40 characters) |

**Responses:**

**200** — Creation result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties/{vendor_property_id}/rateplan-models — Create Rate Plan Model

- Creates a rate plan model at the property level. A rate plan is defined as a room type combined with one of these models, so the model must exist first.
- `id` and `i18n` (with `ko-kr` required) must be supplied.
- Exactly one model with `type` set to `standalone` is required per property. There is no limit on `package` models.
- `sale_from` and `sale_to` must both be sent or both omitted; sending only one is rejected. Setting `max_los` to `0` means no maximum length of stay.
- Sending `channels` restricts sales to those channels; omitting it sells on all channels.
- An existing `id` returns `409`, and a reference or invariant violation returns `422`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string |  | Rate plan model type (standalone \| package). A package model applies a difference (rate_modify) against the base rate.. Enum: `standalone`, `package` |
| `i18n` | array | ✓ | Content by locale. ko-KR is required. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Rate plan model name |
| `i18n[].description` | string |  | Rate plan model description |
| `sale_from` | string (date) |  | Sale start date |
| `sale_to` | string (date) |  | Sale end date |
| `min_los` | integer |  | Minimum length of stay |
| `max_los` | integer |  | Maximum length of stay |
| `refundable` | boolean |  | Refundable |
| `meals` | object |  | Meals included |
| `meals.breakfast` | boolean | ✓ | Breakfast included |
| `meals.lunch` | boolean | ✓ | Lunch included |
| `meals.dinner` | boolean | ✓ | Dinner included |
| `channels` | array |  | List of sales channel IDs. |
| `id` | string | ✓ | Vendor rate plan model ID (max 40 characters) |

**Responses:**

**200** — Creation result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**422** — Unprocessable Entity (reference or invariant violation):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans — Create Rate Plan

- Creates a rate plan beneath a room type. It is the unit that rates and business days are pushed against, so it is required before selling can begin.
- `id` and `vendor_rateplan_model_id` are required. `vendor_rateplan_model_id` binds the rate plan to a model, which determines its selling conditions.
- Both the rate plan model and the room type must already exist.
- An existing `id` returns `409`. A missing referenced rate plan model or an invariant violation returns `422`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |
| `id` | string | ✓ | Vendor rate plan ID (max 40 characters) |
| `vendor_rateplan_model_id` | string | ✓ | Vendor rate plan model ID (max 40 characters) |

**Responses:**

**200** — Creation result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**422** — Unprocessable Entity (reference or invariant violation):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties/{vendor_property_id}/roomtypes — Create Room Type

- Creates a room type beneath a property. The property must exist first.
- `id` (the vendor room type ID) is required, and the room name goes in `ko-kr` under `i18n`.
- Code-based fields such as `roomtype_tags`, `amenity_tags`, and `view_tags` accept only codes obtained from the code catalog.
- Availability is managed per room type, so the `id` created here becomes the key for subsequent availability pushes.
- Calling again with an existing `id` returns `409`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  | Content by locale. ko-KR is required when creating. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Room name |
| `i18n[].description` | string |  | Room description |
| `min_stay` | integer |  | Minimum length of stay |
| `max_stay` | integer |  | Maximum length of stay |
| `size` | number |  | Area |
| `standard_capacity` | integer |  | Standard occupancy |
| `max_capacity` | integer |  | Maximum occupancy |
| `roomtype_tags` | array |  | Room type tags |
| `amenity_tags` | array |  | Amenity tags |
| `view_tags` | array |  | View tags |
| `details` | object |  | Room composition. Keys are components such as `room`, `bedroom`, and `bathroom`; values are counts. |
| `bedtype` | object |  |  |
| `bedtype.single_beds` | integer |  | Single bed count |
| `bedtype.double_beds` | integer |  | Double bed count |
| `bedtype.bunk_beds` | integer |  | Bunk bed count |
| `photos` | array |  | Room images |
| `photos[].url` | string | ✓ | Image URL |
| `photos[].description` | string |  | Image caption |
| `photos[].order` | integer | ✓ | Sort order |
| `id` | string | ✓ | Vendor room type ID (max 40 characters) |

**Responses:**

**200** — Creation result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/booking/{vendor_booking_number} — Get Booking

- Retrieves the details of a single booking by booking number.
- Vendor bookings are created against `gds_sub_booking_number`. A single `gds_booking_number` may correspond to several of them.
- Returns stay details, amounts, and status together with channel information.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_booking_number` | path | string | ✓ | Vendor booking number |

**Responses:**

**200** — Booking information:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `bookingNumber` | string | Vendor booking number |
| `status` | string | Booking status |
| `type` | string | Day-use or overnight (day-use = dayuse, overnight stay = overnight). Omitted when the booking carries no such information.. Enum: `dayuse`, `overnight` |
| `channel_id` | number | Sales channel ID |
| `channel_name` | string | Sales channel name |
| `channel_booking_number` | string | Sales channel booking number |
| `gds_booking_number` | string | ONDA Hub booking number |
| `gds_sub_booking_number` | string | ONDA Hub sub-booking number |
| `property_id` | string | Vendor property ID |
| `roomtype_id` | string | Vendor room type ID |
| `rateplan_id` | string | Vendor rate plan ID. Omitted for bookings with no mapping. |
| `checkin` | string (date-time) | Check-in date and time (ISO 8601 with offset) |
| `checkout` | string (date-time) | Check-out date and time (ISO 8601 with offset) |
| `currency` | string | Currency |
| `amount` | number | Vendor-side amount — the per-night amounts plus any extra charges |
| `total_amount` | number | Channel-side selling price. With postpayment, this is the amount the vendor collects on site. |
| `net_price` | number | Deposit amount (payable to the vendor) |
| `payment_type` | string | Payment type (prepaid = prepayment, pay on arrival = postpayment). Enum: `prepayment`, `postpayment` |
| `guest` | object |  |
| `guest.name` | string | Guest name |
| `guest.adults` | number | Adults |
| `guest.children` | number | Children |
| `guest.infants` | number | Infants |
| `guest.pets` | number | Pets |
| `guest.cars` | number | Vehicles |
| `booker` | object |  |
| `booker.name` | string | Booker name |
| `booker.email` | string | Booker email |
| `booker.phone` | string | Booker phone |
| `reserved_at` | string (date-time) | Booking timestamp (ISO 8601 with offset) — when the guest booked on the sales channel |
| `confirmed_at` | string,null (date-time) | Confirmed at (ISO 8601 with offset) — when the sales channel confirmed the booking. null while unconfirmed. |
| `canceled_at` | string,null (date-time) | Cancelled at (ISO 8601 with offset) — when the sales channel cancelled the booking. null if not cancelled. |
| `update_at` | string (date-time) | Updated at (ISO 8601 with offset) — when the booking was last changed |
| `request_at` | string (date-time) | Retrieved at (ISO 8601 with offset) — when this response was generated |
| `special_comment` | string | Guest requests. Omitted when empty. |
| `visit_type` | string | Arrival method (on foot = walk, by car = car). Present only when the channel supplies it; otherwise omitted.. Enum: `walk`, `car` |
| `secondary_channel` | string | Secondary sales channel (the final selling brand when a channel resells through another brand or OTA). Present only when the channel supplies it; otherwise omitted. |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/channels/{channel_id}/authorization — Get Channel Authorization URL

- **Airbnb only.** It applies to channels whose channel metadata sets `requires_cms_authorization` to `true`, which today means Airbnb alone. Calling it for any other channel returns `403`.
- Airbnb requires OAuth approval from the host account for each property. Use this API to obtain an authorization URL, send the host there, and wait for approval before proceeding with room type and rate plan mappings.
- Once the host finishes authorizing, they return to `redirect_url` with `?auth_result=success|fail` appended.
- `auth_result` is a display hint only. Judge completion by whether `channel_property_id` is present in the property mapping.
- An issued authorization URL must be used within one hour; once it expires you must issue a new one. Issue it at the moment the host presses the button.
- Authorization is per property, so a host account operating several properties must repeat it for each one.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | query | string | ✓ | Vendor property ID |
| `redirect_url` | query | string | ✓ | Redirect URL to return to after authorization. On return, `?auth_result=success\|fail` is appended and any existing query parameters are preserved. |

**Responses:**

**200** — Channel authorization URL retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | Channel authorization URL |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/channels/{channel_id}/properties/{channel_property_id} — Get Channel Property

- Retrieves the property registered on the channel side along with its room types and rate plans.
- Call it before creating mappings on a direct channel to collect candidate channel room type and rate plan IDs.
- `channel_parent_rateplan_id` in the response points to a parent rate plan. Rates sent to such a rate plan are not applied on the channel; it follows its parent's rates automatically.
- Because the request goes through to the channel, a `500` can occur depending on the channel's state. Adding retry handling is advisable.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `channel_property_id` | path | string | ✓ | Channel property ID |

**Responses:**

**200** — Channel property information retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `channel_id` | string | Channel ID |
| `channel_property_id` | string | Channel property ID |
| `name` | string | Channel property name |
| `status` | string | Channel property status |
| `pricing_model` | string,null | The pricing value exactly as the channel supplied it. Each channel uses its own scheme (Ctrip, for example, uses Standard/OBP), and the value is null when the channel supplies none. |
| `roomtypes` | array |  |
| `roomtypes[].channel_roomtype_id` | string | Channel room type ID |
| `roomtypes[].name` | string | Channel room type name |
| `roomtypes[].status` | string | Channel room type status |
| `rateplans` | array |  |
| `rateplans[].channel_rateplan_id` | string | Channel rate plan ID |
| `rateplans[].name` | string | Channel rate plan name |
| `rateplans[].channel_roomtype_id` | string | Channel room type ID |
| `rateplans[].channel_parent_rateplan_id` | string,null | Channel ID of the parent rate plan. Present only for child rate plans and null otherwise. Only the Booking.com and Agoda channels populate it. |
| `rateplans[].status` | string | Channel rate plan status |
| `rateplans[].type` | string | Distinguishes day-use from overnight stays: dayuse is day-use, overnight is an overnight stay.. Enum: `dayuse`, `overnight` |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/meta — Get Meta Codes

- Retrieves the pcs.codes.code values used for content push, as a hierarchical tree grouped by code_type.
- Leaf nodes with no children omit the children field.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `locale` | query | string (ko-KR|en-US|zh-CN|ja-JP|vn-VN|th-TH|zh-TW) |  | Code name locale |

**Responses:**

**200** — Code Catalog:

| Field | Type | Description |
|-------|------|-------------|
| `data` | object | Code catalog by code_type |
| `data.classification` | array |  |
| `data.classification[].code` | string | pcs.codes.code |
| `data.classification[].name` | string | Code names by locale |
| `data.classification[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.property` | array |  |
| `data.property[].code` | string | pcs.codes.code |
| `data.property[].name` | string | Code names by locale |
| `data.property[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.facility` | array |  |
| `data.facility[].code` | string | pcs.codes.code |
| `data.facility[].name` | string | Code names by locale |
| `data.facility[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.service` | array |  |
| `data.service[].code` | string | pcs.codes.code |
| `data.service[].name` | string | Code names by locale |
| `data.service[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.attraction` | array |  |
| `data.attraction[].code` | string | pcs.codes.code |
| `data.attraction[].name` | string | Code names by locale |
| `data.attraction[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.roomtype` | array |  |
| `data.roomtype[].code` | string | pcs.codes.code |
| `data.roomtype[].name` | string | Code names by locale |
| `data.roomtype[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.amenity` | array |  |
| `data.amenity[].code` | string | pcs.codes.code |
| `data.amenity[].name` | string | Code names by locale |
| `data.amenity[].children` | array | Child code list (omitted for leaf nodes with no children) |
| `data.view` | array |  |
| `data.view[].code` | string | pcs.codes.code |
| `data.view[].name` | string | Code names by locale |
| `data.view[].children` | array | Child code list (omitted for leaf nodes with no children) |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/channels/{channel_id} — Get Channel Request Detail

Returns the channel's latest request status and up to 100 history entries, newest first, regardless of the current vendor whitelist or channel state.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Responses:**

**200** — Channel request status:

| Field | Type | Description |
|-------|------|-------------|
| `channel_id` | number | Channel ID |
| `channel_name` | string | Channel name |
| `request_type` | string | Request type. Enum: `on`, `off` |
| `request_status` | string | Request status. Enum: `pending`, `confirm`, `reject` |
| `channel_property_id` | string,null | Channel property ID submitted with the request. null if none was submitted. |
| `note` | string,null | Requester (vendor) note. null if none was submitted. The processor's reason is returned separately in reason. |
| `reason` | string,null | Reason recorded by the processor (operator or system). Usually filled in when a request is rejected. |
| `created_at` | string | Requested at (ISO 8601) |
| `updated_at` | string | Updated at (ISO 8601) |
| `histories` | array | Request history, newest first, up to 100 entries. |
| `histories[].request_type` | string | Request type. Enum: `on`, `off` |
| `histories[].request_status` | string | Processing status. Enum: `pending`, `confirm`, `reject` |
| `histories[].channel_property_id` | string,null | Channel property ID submitted with that request. null if none was submitted. |
| `histories[].note` | string,null | Requester (vendor) note submitted with that request. null if none was submitted. |
| `histories[].reason` | string,null | Reason recorded by the processor (operator or system) |
| `histories[].processed_at` | string,null | Processed at (ISO 8601) |
| `histories[].created_at` | string | Created at (ISO 8601) |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/channels/{channel_id}/settings — Get Property Channel Settings

- Retrieves per-channel supplementary settings for a property.
- An empty object is returned when nothing has been saved. Handle "not configured" separately from an error.
- The available settings differ by channel; some, such as extra-occupancy pricing, apply to only a few channels.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Responses:**

**200** — Channel settings retrieved. Returns an empty object when nothing has been saved.:

| Field | Type | Description |
|-------|------|-------------|
| `pricing_model` | string | Rate model. Enum: `OBP`, `Standard` |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/channels/{channel_id}/mappings — Get Property Mapping

- Retrieves the mapping state between a property and a channel.
- A value in `channel_property_id` means the property is linked to one on the channel side. Use it to judge whether channel opening has completed.
- Mappings apply only to direct channels. ONDA manages mappings for Plus channels, so they are out of scope here.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Property mapping retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_property_id` | string | Vendor property ID |
| `channel_id` | string | Channel ID |
| `channel_name` | string | Channel name |
| `channel_property_id` | string | Channel property ID |
| `status` | string | Property mapping status |
| `roomtype_mappings` | array |  |
| `roomtype_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `roomtype_mappings[].channel_roomtype_id` | string,null | Channel room type ID. Included only in CMS channel responses. |
| `roomtype_mappings[].status` | string | Mapping status |
| `rateplan_mappings` | array |  |
| `rateplan_mappings[].vendor_rateplan_id` | string | Vendor rate plan ID |
| `rateplan_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `rateplan_mappings[].channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `rateplan_mappings[].status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id} — Get Property

- Retrieves the content and sales status of a single property.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- The `vendor_property_id` in the path is the `id` the vendor sent when creating the property.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Property content:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Vendor property ID |
| `status` | string | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  |
| `i18n[].locale` | string | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string | Property name |
| `i18n[].description` | string | Property description |
| `i18n[].address` | string | Address (road name or lot number) on a single line. |
| `i18n[].address_detail` | string | Address detail (building, block, floor, unit). Provide together with address. |
| `i18n[].notice` | string | General notice |
| `i18n[].reservation_description` | string | Booking notice |
| `i18n[].refunds_description` | string | Refund notice |
| `email` | string |  |
| `phone` | string |  |
| `sms_phone` | string |  |
| `checkin` | string | Check-in time (HH:mm) |
| `checkout` | string | Check-out time (HH:mm) |
| `website` | string |  |
| `refunds` | object | Refund policy. Keys use the `0d`/`1d` form for how many days before check-in, and values are refund rates (%). |
| `classifications` | array |  |
| `property_tags` | array |  |
| `facility_tags` | array |  |
| `service_tags` | array |  |
| `attraction_tags` | array |  |
| `photos` | array |  |
| `photos[].url` | string | Image URL |
| `photos[].description` | string | Image caption |
| `photos[].order` | integer | Sort order |
| `settings` | object |  |
| `settings.extra_adult_price` | number,null |  |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans/{vendor_rateplan_id}/channels/{channel_id}/mappings — Get Rate Plan Mapping

- Retrieves the mapping state between a rate plan and a channel rate plan.
- It is meaningful only after the property and room type mappings are in place; mapping proceeds from property to room type to rate plan.
- Mappings apply only to direct channels.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `vendor_rateplan_id` | path | string | ✓ | Vendor rate plan ID |

**Responses:**

**200** — Rate plan mapping retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_rateplan_id` | string | Vendor rate plan ID |
| `vendor_roomtype_id` | string | Vendor room type ID |
| `channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/rateplan-models/{vendor_rateplan_model_id} — Get Rate Plan Model

- Retrieves the selling conditions of a single rate plan model.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- Shows the conditions that apply to every rate plan referencing this model, including sale period, length of stay, refundability, and sales channels.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_rateplan_model_id` | path | string | ✓ | Vendor rate plan model ID |

**Responses:**

**200** — Rate plan model content:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Vendor rate plan model ID |
| `vendor_property_id` | string | Vendor property ID |
| `type` | string | Rate plan model type (standalone \| package). A package model applies a difference (rate_modify) against the base rate.. Enum: `standalone`, `package` |
| `i18n` | array | Content by locale. ko-KR is required. |
| `i18n[].locale` | string | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string | Rate plan model name |
| `i18n[].description` | string | Rate plan model description |
| `sale_from` | string |  |
| `sale_to` | string |  |
| `min_los` | number |  |
| `max_los` | number |  |
| `refundable` | boolean |  |
| `meals` | object | Meals included |
| `meals.breakfast` | boolean | Breakfast included |
| `meals.lunch` | boolean | Lunch included |
| `meals.dinner` | boolean | Dinner included |
| `channels` | array | List of channel IDs currently on sale. The field is absent when the item is sold on all channels. |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans/{vendor_rateplan_id} — Get Rate Plan

- Retrieves a single rate plan's information and sales status.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- Shows which rate plan model the rate plan is bound to.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `vendor_rateplan_id` | path | string | ✓ | Vendor rate plan ID |

**Responses:**

**200** — Rate plan content:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Vendor rate plan ID |
| `vendor_property_id` | string | Vendor property ID |
| `vendor_roomtype_id` | string | Vendor room type ID |
| `vendor_rateplan_model_id` | string | Vendor rate plan model ID |
| `status` | string | Status. Enum: `enabled`, `disabled` |
| `type` | string | Rate plan model type (standalone \| package). A package model applies a difference (rate_modify) against the base rate.. Enum: `standalone`, `package` |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/channels/{channel_id}/settings — Get Room Type Channel Settings

- Retrieves per-channel settings for a vendor room type.
- An empty object is returned when nothing has been saved.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Responses:**

**200** — Room type channel settings retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `booking_lead_time_hours` | number | Booking lead time in hours (Airbnb accepts 1-24, 48, 72, 168). Enum: `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`, `21`, `22`, `23`, `24`, `48`, `72`, `168` |
| `allow_request_to_book` | boolean | Allow booking requests below the lead time |
| `default_min_nights` | integer | Default minimum length of stay |
| `included_guests` | integer | Default included occupancy |
| `weekly_discount_percentage` | number | Weekly discount rate (0-100) |
| `monthly_discount_percentage` | number | Monthly discount rate (0-100) |
| `cleaning_fee` | number | Cleaning fee |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/channels/{channel_id}/mappings — Get Room Type Mapping

- Retrieves the mapping state between a room type and a channel room type.
- It is meaningful only after the property mapping is in place; room type mappings cannot be created while the property is unlinked.
- Mappings apply only to direct channels.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Responses:**

**200** — Room type mapping retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_roomtype_id` | string | Vendor room type ID |
| `channel_roomtype_id` | string,null | Channel room type ID. Included only in CMS channel responses. |
| `status` | string | Mapping status |
| `rateplan_mappings` | array |  |
| `rateplan_mappings[].vendor_rateplan_id` | string | Vendor rate plan ID |
| `rateplan_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `rateplan_mappings[].channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `rateplan_mappings[].status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id} — Get Room Type

- Retrieves the content and sales status of a single room type.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- The `vendor_roomtype_id` in the path is the `id` the vendor sent when creating the room type.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Responses:**

**200** — Room type content:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Vendor room type ID |
| `status` | string | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  |
| `i18n[].locale` | string | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string | Room name |
| `i18n[].description` | string | Room description |
| `min_stay` | number |  |
| `max_stay` | number |  |
| `size` | number |  |
| `standard_capacity` | number |  |
| `max_capacity` | number |  |
| `roomtype_tags` | array |  |
| `amenity_tags` | array |  |
| `view_tags` | array |  |
| `details` | object | Room composition. Keys are components such as `room`, `bedroom`, and `bathroom`; values are counts. |
| `bedtype` | object |  |
| `bedtype.single_beds` | integer | Single bed count |
| `bedtype.double_beds` | integer | Double bed count |
| `bedtype.bunk_beds` | integer | Bunk bed count |
| `photos` | array |  |
| `photos[].url` | string | Image URL |
| `photos[].description` | string | Image caption |
| `photos[].order` | integer | Sort order |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/bookings — Get Settlement Comparison

- Retrieves settlement amounts for reconciliation against the vendor's own records.
- `option` selects the reference date: `checkin` uses the check-in date and `checkout` the check-out date. It is required together with `from` and `to`.
- Use `offset` and `limit` to page through results. Supplying `vendor_property_id` narrows the query to one property.
- `net_price` is the amount payable after fees.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `option` | query | string (checkin|checkout) | ✓ | Query basis |
| `from` | query | string | ✓ | Start date (YYYY-MM-DD) |
| `to` | query | string | ✓ | End date (YYYY-MM-DD) |
| `vendor_property_id` | query | string |  | Vendor property ID |
| `offset` | query | string | ✓ | Offset |
| `limit` | query | string |  | Limit |

**Responses:**

**200** — Settlement comparison data:

| Field | Type | Description |
|-------|------|-------------|
| `count` | number | Total count |
| `offset` | number | Offset |
| `limit` | number | Limit |
| `reservations` | array | Booking list |
| `reservations[].booking_number` | string | Booking number |
| `reservations[].channel_booking_number` | string | Channel booking number |
| `reservations[].status` | string | Booking status |
| `reservations[].type` | string,null | Day-use or overnight (day-use = dayuse, overnight stay = overnight). null when the booking carries no such information.. Enum: `dayuse`, `overnight`, `` |
| `reservations[].property_id` | string | PCS property ID |
| `reservations[].vendor_property_id` | string | Vendor property ID |
| `reservations[].checkin` | string (date-time) | Check-in date and time (ISO 8601 with offset) |
| `reservations[].checkout` | string (date-time) | Check-out date and time (ISO 8601 with offset) |
| `reservations[].currency` | string | Currency code |
| `reservations[].price_type` | string | Rate type |
| `reservations[].total_amount` | number | Total amount |
| `reservations[].refund_amount` | number | Refund amount |
| `reservations[].charged_amount` | number | Charged amount |
| `reservations[].net_price` | number | Net revenue |
| `reservations[].reserved_at` | string (date-time) | Booking timestamp (ISO 8601 with offset) — when the guest booked on the sales channel |
| `reservations[].confirmed_at` | string,null (date-time) | Confirmed at (ISO 8601 with offset) — when the sales channel confirmed the booking. null while unconfirmed. |
| `reservations[].canceled_at` | string,null (date-time) | Cancelled at (ISO 8601 with offset) — when the sales channel cancelled the booking. null if not cancelled. |
| `reservations[].created_at` | string (date-time) | Created at (ISO 8601 with offset) — when the booking record was first stored |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/bookinglist — List Bookings

- Lists bookings within a date range.
- `from` and `to` are required.
- Booking processing itself works by ONDA calling the vendor's endpoints, so this API is for vendors reconciling against their own data or looking for gaps.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `from` | query | string | ✓ | Start date (YYYY-MM-DD) |
| `to` | query | string | ✓ | End date (YYYY-MM-DD) |

**Responses:**

**200** — Booking list:

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/channels — List Channels

**Responses:**

**200** — Channel list:

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties — List Properties

**Responses:**

**200** — Property list:

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/channels — Channel request list by property

Returns the channels this property has actually submitted requests for, along with each channel's latest request status, regardless of the current vendor whitelist or channel state.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Channel request status list. Returns an empty array when there is no request history.:

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/rateplan-models — List Rate Plan Models

- Lists the rate plan models registered for a property.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- Useful for checking the `id` of the model you intend to combine before creating a rate plan.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Rate plan model list:

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans — List Rate Plans

- Lists the rate plans linked to a room type.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- Use it to look up the `vendor_rateplan_id` needed when pushing rates and business days.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Responses:**

**200** — Rate plan list:

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### GET /gds/vendor/properties/{vendor_property_id}/roomtypes — List Room Types

- Lists the room types belonging to a property. The response also carries the rate plans linked to each room type.
- In 3.0 the vendor pushes content to ONDA by default, so lookups serve as a secondary means of confirming what was sent.
- Useful for checking how room types and rate plans are linked in a single call.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Room type list:

| Field | Type | Description |
|-------|------|-------------|
| `property_id` | string | Property ID |
| `roomtypes` | array | Room type list |
| `roomtypes[].id` | string | Room type ID |
| `roomtypes[].name` | string | Room type name |
| `roomtypes[].status` | string | Status |
| `roomtypes[].rateplans` | array | Rate plan list |
| `roomtypes[].rateplans[].rateplan_id` | string | Rate plan ID |
| `roomtypes[].rateplans[].rateplan_name` | string | Rate plan name |
| `roomtypes[].rateplans[].rateplan_status` | string | Rate plan status |
| `roomtypes[].rateplans[].rateplan_type` | string | Rate plan type |
| `roomtypes[].rateplans[].rateplan_description` | string | Rate plan description |
| `roomtypes[].rateplans[].updated_at` | string (date-time) | Last modified (ISO 8601 with offset) |
| `roomtypes[].updated_at` | string (date-time) | Last modified (ISO 8601 with offset) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties/{vendor_property_id}/ari/avails — Push Availability

- Sets availability (vacancy); `vacancy` is required on each item.
- Availability is applied per room type.
- If the room type has no rate plans, the submitted availability is not applied.
- Availability settings for channels absent from the `channels` array are deleted within the from-to range. Omitting the field or sending an empty array deletes the availability settings for all channels.
- Each item may span at most 730 days.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Settings result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /gds/vendor/properties/{vendor_property_id}/ari — Push Rates and Business Days

- Sets rates and business days.
- Each item requires `is_business_day` together with either `net_price` or `sale_price`.
- Every channel under `channels` must repeat `is_business_day` and exactly the rate fields sent on the item. Omitting one, or adding a rate field the item does not carry, returns 400.
- Rate and business-day settings for channels absent from `channels` are deleted within the from-to range. Omitting the field or sending an empty array deletes the settings for all channels.
- use_from, use_to, and use_time may be sent only on day-use (dayuse) items, and use_time is expressed in minutes.
- Availability is sent separately through the availability API (`POST /gds/vendor/properties/{vendor_property_id}/ari/avails`).

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Responses:**

**200** — Settings result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/channels/{channel_id}/settings — Save Property Channel Settings

RFC 7396 JSON Merge Patch. A null value deletes the corresponding key.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pricing_model` | string,null |  | Rate model. Enum: `OBP`, `Standard`, `` |

**Responses:**

**200** — Channel settings saved. Returns the complete settings after the save.:

| Field | Type | Description |
|-------|------|-------------|
| `pricing_model` | string | Rate model. Enum: `OBP`, `Standard` |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/channels/{channel_id}/mappings — Update Property Mapping

- Updates the mapping between a property and a channel.
- Put the channel-side property ID in `channel_property_id` to link them, and use `status` to turn the mapping on or off.
- Sending `null` clears the mapping; omitting the field keeps the existing value. There is no separate `DELETE` for mappings, so use `null` when stopping sales as well.
- Only direct channels can be modified.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `channel_property_id` | string,null |  | Channel property ID. Only CMS channels can modify it. Sending null clears it; omitting it keeps the existing value. |
| `status` | string |  | Mapping status. Enum: `enabled`, `disabled` |

**Responses:**

**200** — Property mapping updated:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_property_id` | string | Vendor property ID |
| `channel_id` | string | Channel ID |
| `channel_name` | string | Channel name |
| `channel_property_id` | string | Channel property ID |
| `status` | string | Property mapping status |
| `roomtype_mappings` | array |  |
| `roomtype_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `roomtype_mappings[].channel_roomtype_id` | string,null | Channel room type ID. Included only in CMS channel responses. |
| `roomtype_mappings[].status` | string | Mapping status |
| `rateplan_mappings` | array |  |
| `rateplan_mappings[].vendor_rateplan_id` | string | Vendor rate plan ID |
| `rateplan_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `rateplan_mappings[].channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `rateplan_mappings[].status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id} — Update Property

- Updates a property's content and sales status.
- Follows RFC 7396 JSON Merge Patch. Omitted fields keep their existing values, and sending `null` clears a value.
- Stopping and resuming sales also goes through this API. Sending only `status` leaves the rest of the content untouched.
- Sending `null` for `settings.extra_adult_price` returns the property's default extra adult charge to unset.
- A mismatch between `vendor_property_id` in the path and the identifier in the body returns `400`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  | Content by locale. ko-KR is required when creating; one entry per locale. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Property name |
| `i18n[].description` | string |  | Property description |
| `i18n[].address` | string |  | Address (road name or lot number) on a single line. |
| `i18n[].address_detail` | string |  | Address detail (building, block, floor, unit). Provide together with address. |
| `i18n[].notice` | string |  | General notice |
| `i18n[].reservation_description` | string |  | Booking notice |
| `i18n[].refunds_description` | string |  | Refund notice |
| `email` | string (email) |  | Primary email |
| `phone` | string |  | Primary phone |
| `sms_phone` | string |  | SMS sender number |
| `checkin` | string |  | Check-in time (HH:mm) |
| `checkout` | string |  | Check-out time (HH:mm) |
| `website` | string (uri) |  | Primary website |
| `refunds` | object |  | Refund policy. Keys use the `0d`/`1d` form for how many days before check-in, and values are refund rates (%). The map you send replaces the policy entirely, so include the full policy even when changing only part of it. |
| `classifications` | array |  | Property classifications |
| `property_tags` | array |  | Property tags |
| `facility_tags` | array |  | Facility tags |
| `service_tags` | array |  | Service tags |
| `attraction_tags` | array |  | Nearby attraction tags |
| `photos` | array |  | Property images |
| `photos[].url` | string | ✓ | Image URL |
| `photos[].description` | string |  | Image caption |
| `photos[].order` | integer | ✓ | Sort order |
| `settings` | object |  | Property settings |
| `settings.extra_adult_price` | integer,null |  | Default extra adult charge for the property (integer KRW; sending null clears it) |

**Responses:**

**200** — Update result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans/{vendor_rateplan_id}/channels/{channel_id}/mappings — Update Rate Plan Mapping

- Updates the mapping between a rate plan and a channel rate plan.
- Put the channel-side rate plan ID in `channel_rateplan_id` to link them, and use `status` to turn the mapping on or off.
- Sending `null` clears the mapping; omitting the field keeps the existing value.
- Map to `0` on channels that have no concept of rate plans.
- The property and room type mappings must be completed first, and only direct channels can be modified.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `vendor_rateplan_id` | path | string | ✓ | Vendor rate plan ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `channel_rateplan_id` | string,null |  | Channel rate plan ID. Only CMS channels can modify it. Sending null clears it; omitting it keeps the existing value. |
| `status` | string |  | Mapping status. Enum: `enabled`, `disabled` |

**Responses:**

**200** — Rate plan mapping updated:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_rateplan_id` | string | Vendor rate plan ID |
| `vendor_roomtype_id` | string | Vendor room type ID |
| `channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/rateplan-models/{vendor_rateplan_model_id} — Update Rate Plan Model

- Updates a rate plan model's selling conditions. The change applies to every rate plan that references the model.
- Follows RFC 7396 JSON Merge Patch. `i18n` is required on update requests as well.
- Changing `channels` changes rate plan mappings. Adding a channel creates mappings only for the added channel, and removing a channel does not delete mappings that already exist. An empty array creates mappings on every eligible channel.
- `sale_from` and `sale_to` must both be sent or both omitted.
- A reference or invariant violation returns `422`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_rateplan_model_id` | path | string | ✓ | Vendor rate plan model ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string |  | Rate plan model type (standalone \| package). A package model applies a difference (rate_modify) against the base rate.. Enum: `standalone`, `package` |
| `i18n` | array | ✓ | Content by locale. ko-KR is required. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Rate plan model name |
| `i18n[].description` | string |  | Rate plan model description |
| `sale_from` | string,null (date) |  | Sale start date (sending null clears it) |
| `sale_to` | string,null (date) |  | Sale end date (sending null clears it) |
| `min_los` | integer,null |  | Minimum length of stay (sending null clears it) |
| `max_los` | integer,null |  | Maximum length of stay (sending null clears it) |
| `refundable` | boolean |  | Refundable |
| `meals` | object |  | Meals included |
| `meals.breakfast` | boolean | ✓ | Breakfast included |
| `meals.lunch` | boolean | ✓ | Lunch included |
| `meals.dinner` | boolean | ✓ | Dinner included |
| `channels` | array,null |  | List of sales channel IDs. Sending null or an empty array resets it to all channels. |

**Responses:**

**200** — Update result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**422** — Unprocessable Entity (reference or invariant violation):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans/{vendor_rateplan_id} — Update Rate Plan

- Updates a rate plan's sales status.
- Follows RFC 7396 JSON Merge Patch. Omitted fields keep their existing values.
- Selling conditions such as sale period, length of stay, and refundability live on the bound rate plan model rather than the rate plan, so use Update Rate Plan Model to change them.
- A mismatch between the identifier in the path and the one in the body returns `400`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `vendor_rateplan_id` | path | string | ✓ | Vendor rate plan ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |

**Responses:**

**200** — Update result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/channels/{channel_id}/settings — Save Room Type Channel Settings

RFC 7396 JSON Merge Patch. A null value deletes the corresponding key.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `channel_id` | path | string | ✓ | Channel ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `booking_lead_time_hours` | number,null |  | Booking lead time in hours (Airbnb accepts 1-24, 48, 72, 168). Enum: `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`, `21`, `22`, `23`, `24`, `48`, `72`, `168`, `` |
| `allow_request_to_book` | boolean,null |  | Whether to accept bookings below the lead time as requests (Airbnb) |
| `default_min_nights` | integer,null |  | Default minimum length of stay |
| `included_guests` | integer,null |  | Default included occupancy |
| `weekly_discount_percentage` | number,null |  | Weekly discount rate (0-100) |
| `monthly_discount_percentage` | number,null |  | Monthly discount rate (0-100) |
| `cleaning_fee` | number,null |  | Cleaning fee |

**Responses:**

**200** — Room type channel settings saved. Returns the complete settings after the save.:

| Field | Type | Description |
|-------|------|-------------|
| `booking_lead_time_hours` | number | Booking lead time in hours (Airbnb accepts 1-24, 48, 72, 168). Enum: `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`, `21`, `22`, `23`, `24`, `48`, `72`, `168` |
| `allow_request_to_book` | boolean | Allow booking requests below the lead time |
| `default_min_nights` | integer | Default minimum length of stay |
| `included_guests` | integer | Default included occupancy |
| `weekly_discount_percentage` | number | Weekly discount rate (0-100) |
| `monthly_discount_percentage` | number | Monthly discount rate (0-100) |
| `cleaning_fee` | number | Cleaning fee |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/channels/{channel_id}/mappings — Update Room Type Mapping

- Updates the mapping between a room type and a channel room type.
- Put the channel-side room type ID in `channel_roomtype_id` to link them, and use `status` to turn the mapping on or off.
- Sending `null` clears the mapping; omitting the field keeps the existing value.
- The property mapping must be completed first, and only direct channels can be modified.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `channel_id` | path | string | ✓ | Channel ID |
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `channel_roomtype_id` | string,null |  | Channel room type ID. Only CMS channels can modify it. Sending null clears it; omitting it keeps the existing value. |
| `status` | string |  | Mapping status. Enum: `enabled`, `disabled` |

**Responses:**

**200** — Room type mapping updated:

| Field | Type | Description |
|-------|------|-------------|
| `vendor_roomtype_id` | string | Vendor room type ID |
| `channel_roomtype_id` | string,null | Channel room type ID. Included only in CMS channel responses. |
| `status` | string | Mapping status |
| `rateplan_mappings` | array |  |
| `rateplan_mappings[].vendor_rateplan_id` | string | Vendor rate plan ID |
| `rateplan_mappings[].vendor_roomtype_id` | string | Vendor room type ID |
| `rateplan_mappings[].channel_rateplan_id` | string,null | Channel rate plan ID. Included only in CMS channel responses. |
| `rateplan_mappings[].status` | string | Mapping status |

**400** — Bad Request:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**500** — Internal Server Error:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### PATCH /gds/vendor/properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id} — Update Room Type

- Updates a room type's content and sales status.
- Follows RFC 7396 JSON Merge Patch. Omitted fields keep their existing values, and sending `null` clears a value.
- Stopping and resuming sales also goes through this API. Sending only `status` leaves the rest of the content untouched.
- A mismatch between the identifier in the path and the one in the body returns `400`.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string |  | Status. Enum: `enabled`, `disabled` |
| `i18n` | array |  | Content by locale. ko-KR is required when creating. |
| `i18n[].locale` | string | ✓ | locale. Enum: `ko-KR`, `en-US`, `zh-CN`, `ja-JP`, `vn-VN`, `th-TH`, `zh-TW` |
| `i18n[].name` | string |  | Room name |
| `i18n[].description` | string |  | Room description |
| `min_stay` | integer |  | Minimum length of stay |
| `max_stay` | integer |  | Maximum length of stay |
| `size` | number |  | Area |
| `standard_capacity` | integer |  | Standard occupancy |
| `max_capacity` | integer |  | Maximum occupancy |
| `roomtype_tags` | array |  | Room type tags |
| `amenity_tags` | array |  | Amenity tags |
| `view_tags` | array |  | View tags |
| `details` | object |  | Room composition. Keys are components such as `room`, `bedroom`, and `bathroom`; values are counts. |
| `bedtype` | object |  |  |
| `bedtype.single_beds` | integer |  | Single bed count |
| `bedtype.double_beds` | integer |  | Double bed count |
| `bedtype.bunk_beds` | integer |  | Bunk bed count |
| `photos` | array |  | Room images |
| `photos[].url` | string | ✓ | Image URL |
| `photos[].description` | string |  | Image caption |
| `photos[].order` | integer | ✓ | Sort order |

**Responses:**

**200** — Update result:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message (empty string on success) |

**400** — Bad Request (identifier mismatch between path and body, etc.):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**401** — Unauthorized:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**403** — Forbidden:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**404** — Not Found:

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

**409** — Conflict (id already exists):

| Field | Type | Description |
|-------|------|-------------|
| `message` | string | Error message |

---

### POST /bookings/{vendor_booking_number}/cancel — Cancel Booking

- The vendor implements this endpoint and ONDA calls it.
- ONDA requests that the vendor cancel a booking.
- The vendor also receives the cancellation reason (memo).

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_booking_number` | path | string | ✓ | Vendor booking number |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `canceled_at` | string (date-time) | ✓ | Cancellation date and time (ISO 8601) |
| `canceled_by` | string | ✓ | Who cancelled the booking. Defaults to system when unknown.. Enum: `user`, `admin`, `channel`, `system` |
| `refund` | object | ✓ | Refund information |
| `refund.amount` | number | ✓ | Refund amount |
| `refund.percent` | number | ✓ | Refund rate (%) |
| `property_id` | string | ✓ | Vendor property ID |
| `roomtype_id` | string | ✓ | Vendor room type ID |
| `rateplan_id` | string | ✓ | Vendor rate plan ID |
| `memo` | string |  | Cancellation reason (optional). Sent when a reason exists, such as a cancellation by a channel administrator. |

**Responses:**

**200** — Booking cancellation request succeeded:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `reservation` | object |  |
| `reservation.booking_number` | string | Vendor booking number |
| `reservation.requested_at` | string (date-time) | Request date and time (ISO 8601 with offset) |
| `reservation.updated_at` | string (date-time) | Modification date and time (ISO 8601 with offset) |
| `reservation.status` | string | Booking status. Enum: `cancel` |

**400** — Validation Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**403** — Access Denied:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**404** — Reservation not found:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**409** — Business Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**500** — System Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

---

### PUT /bookings/{vendor_booking_number}/confirm — Confirm Booking

- The vendor implements this endpoint and ONDA calls it.
- ONDA requests that the vendor confirm a booking.
- The paid amount (paid_amount) is not sent to the vendor.
- Identify the booking to confirm by `vendor_booking_number` in the path. `property_id` and `roomtype_id` in the body are informational and may be absent, so do not rely on them to locate the booking. `rateplan_id` is always sent.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_booking_number` | path | string | ✓ | Vendor booking number |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `confirmed_at` | string (date-time) | ✓ | Booking confirmation date and time (ISO 8601) |
| `property_id` | string |  | Vendor property ID |
| `roomtype_id` | string |  | Vendor room type ID |
| `rateplan_id` | string | ✓ | Vendor rate plan ID |

**Responses:**

**200** — Booking confirmation request succeeded:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `reservation` | object |  |
| `reservation.booking_number` | string | Vendor booking number |
| `reservation.requested_at` | string (date-time) | Request date and time (ISO 8601 with offset) |
| `reservation.updated_at` | string (date-time) | Modification date and time (ISO 8601 with offset) |
| `reservation.status` | string | Booking status. Enum: `confirm` |

**400** — Validation Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**403** — Access Denied:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**404** — Reservation not found:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**409** — Business Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**500** — System Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

---

### POST /bookings — Create Booking

- The vendor implements this endpoint and ONDA calls it.
- ONDA requests that the vendor create a booking.

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `channel_id` | number | ✓ | Sales channel ID |
| `channel_name` | string | ✓ | Sales channel name |
| `channel_booking_number` | string |  | Sales channel booking number. Omitted when the channel does not supply its own booking number. |
| `gds_booking_number` | string | ✓ | ONDA Hub booking number |
| `gds_sub_booking_number` | string | ✓ | ONDA Hub sub-booking number |
| `property_id` | string | ✓ | Vendor property ID |
| `roomtype_id` | string | ✓ | Vendor room type ID |
| `rateplan_id` | string | ✓ | Vendor rate plan ID |
| `checkin` | string (date-time) | ✓ | Check-in date and time (ISO 8601) |
| `checkout` | string (date-time) | ✓ | Check-out date and time (ISO 8601) |
| `currency` | string | ✓ | Currency |
| `total_amount` | number | ✓ | Total stay amount |
| `guest` | object | ✓ |  |
| `guest.name` | string | ✓ | Guest name |
| `guest.adults` | number | ✓ | Adults |
| `guest.children` | number | ✓ | Children |
| `guest.infants` | number | ✓ | Infants |
| `guest.pets` | number |  | Pets |
| `guest.cars` | number |  | Vehicles |
| `booker` | object | ✓ |  |
| `booker.name` | string | ✓ | Booker name |
| `booker.email` | string | ✓ | Booker email |
| `booker.phone` | string | ✓ | Booker phone |
| `special_comment` | string |  | Guest requests |
| `type` | string |  | Day-use or overnight (day-use = dayuse, overnight stay = overnight). Enum: `dayuse`, `overnight` |
| `payment_type` | string | ✓ | Payment type (prepaid = prepayment, pay on arrival = postpayment). With postpayment, total_amount is collected on site.. Enum: `prepayment`, `postpayment` |
| `net_price` | number | ✓ | Deposit amount (payable to the vendor) |
| `reserved_at` | string (date-time) | ✓ | Booking date and time (ISO 8601 with offset) |
| `visit_type` | string |  | Arrival method (on foot = walk, by car = car). Present only when the channel supplies it; otherwise omitted.. Enum: `walk`, `car` |
| `secondary_channel` | string |  | Secondary sales channel (the final selling brand when a channel resells through another brand or OTA). Present only when the channel supplies it; otherwise omitted. |

**Responses:**

**200** — Booking creation request succeeded:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `reservation` | object |  |
| `reservation.booking_number` | string | Vendor booking number |
| `reservation.requested_at` | string (date-time) | Request date and time (ISO 8601 with offset) |
| `reservation.updated_at` | string (date-time) | Modification date and time (ISO 8601 with offset) |
| `reservation.status` | string | Booking status. Enum: `pending` |
| `reservation.refunds` | array | Refund policy list |
| `reservation.refunds[].type` | string | Refund type. Enum: `refund` |
| `reservation.refunds[].until` | string (date-time) | Refundable until (ISO 8601 with offset) |
| `reservation.refunds[].percent` | number | Refund rate (%) |
| `reservation.refunds[].amount` | number | Refund amount |

**400** — Validation Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**403** — Access Denied:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**404** — Reservation not found:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**409** — Business Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**500** — System Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

---

### GET /properties/{vendor_property_id}/roomtypes/{vendor_roomtype_id}/rateplans/{vendor_rateplan_id}/refund_policy — Cancellation and Refund Policy

- The vendor implements this endpoint and ONDA calls it.
- ONDA retrieves the vendor's cancellation and refund policy.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_property_id` | path | string | ✓ | Vendor property ID |
| `vendor_roomtype_id` | path | string | ✓ | Vendor room type ID |
| `vendor_rateplan_id` | path | string | ✓ | Vendor rate plan ID |
| `checkin` | query | string | ✓ | Check-in date and time |
| `checkout` | query | string | ✓ | Check-out date and time |

**Responses:**

**200** — Cancellation and refund policy retrieved:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `checkin` | string (date-time) | Check-in date and time (ISO 8601) |
| `checkout` | string (date-time) | Check-out date and time (ISO 8601) |
| `refunds` | array | Refund policy list |
| `refunds[].type` | string | Refund type. Enum: `refund` |
| `refunds[].until` | string (date-time) | Refundable until (ISO 8601 with offset) |
| `refunds[].percent` | number | Refund rate (%) |
| `refunds[].amount` | number | Refund amount |

**400** — Validation Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**403** — Access Denied:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**500** — System Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

---

### PUT /bookings/{vendor_booking_number}/modify — Modify Booking

- The vendor implements this endpoint and ONDA calls it.
- ONDA requests that the vendor modify a booking.

**Parameters:**

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `vendor_booking_number` | path | string | ✓ | Vendor booking number |

**Request Body** (`application/json`):

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `roomtype_id` | string | ✓ | Vendor room type ID |
| `rateplan_id` | string | ✓ | Vendor rate plan ID |
| `total_amount` | number | ✓ | Total stay amount |
| `guest` | object | ✓ |  |
| `guest.name` | string | ✓ | Guest name |
| `guest.adults` | number | ✓ | Adults |
| `guest.children` | number | ✓ | Children |
| `guest.infants` | number | ✓ | Infants |
| `guest.pets` | number |  | Pets |
| `guest.cars` | number |  | Vehicles |
| `booker` | object | ✓ |  |
| `booker.name` | string | ✓ | Booker name |
| `booker.email` | string | ✓ | Booker email |
| `booker.phone` | string | ✓ | Booker phone |
| `special_comment` | string |  | Guest requests |
| `net_price` | number | ✓ | Deposit amount (payable to the vendor) |
| `checkin` | string (date-time) | ✓ | Check-in date and time (ISO 8601) |
| `checkout` | string (date-time) | ✓ | Check-out date and time (ISO 8601) |
| `updated_at` | string (date-time) | ✓ | Modification date and time (ISO 8601) |
| `secondary_channel` | string |  | Secondary sales channel (the final selling brand when a channel resells through another brand or OTA). Present only when the channel supplies it; otherwise omitted. |

**Responses:**

**200** — Booking modification request succeeded:

| Field | Type | Description |
|-------|------|-------------|
| `error` | string | Error message |
| `reservation` | object |  |
| `reservation.booking_number` | string | Vendor booking number |
| `reservation.requested_at` | string (date-time) | Request date and time (ISO 8601 with offset) |
| `reservation.updated_at` | string (date-time) | Modification date and time (ISO 8601 with offset) |

**400** — Validation Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**403** — Access Denied:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**404** — Reservation not found:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**409** — Business Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

**500** — System Error:

| Field | Type | Description |
|-------|------|-------------|
| `code` | string | Error code. Enum: `1000`, `2000`, `3000`, `4000`, `4001`, `4002`, `4003`, `4004`, `4005`, `4006`, `4007`, `9999` |
| `error` | string | Error message. Enum: `An error occurred during system processing`, `Access denied`, `Please check your input information`, `No rooms available for booking`, `Requested amount differs from actual amount`, `Minimum guest requirement not met`, `Maximum guest capacity exceeded`, `Reservation not found`, `Reservation already cancelled`, `Reservation cannot be cancelled`, `Reservation cannot be confirmed`, `An unknown error occurred` |

---

