# MOGU Public API Documentation

Welcome to the MOGU Public API documentation. This API allows you to programmatically manage trips and catalogs for your travel business.

## Getting Started

### Authentication

The API supports two authentication modes. Pick the one that matches your integration:

#### 1. API Key (user-bound)

Used by single-tenant integrations acting as one specific agent. Generate the key from your MOGU account; it starts with `ak_`.

Include it in the `Authorization` header of every request:


```
Authorization: Bearer YOUR_API_KEY
```

#### 2. M2M on-behalf-of (server-to-server)

Used by partner backends that act on behalf of multiple agents inside one or more accounts. The integration uses a single Auth0 `client_credentials` token; the per-agent context is sent in the `x-on-behalf-of` header on each request.


```
Authorization: Bearer YOUR_AUTH0_M2M_TOKEN
x-on-behalf-of: agent@example.com           # uses the agent's current account
x-on-behalf-of: agent@example.com,456       # explicit account
```

**Scope-based access.** Your M2M client is provisioned with a set of Auth0 scopes (`create:trips`, `read:trips`, `update:trips`, …). Those scopes determine the full set of operations your backend can invoke — there is no per-endpoint allowlist. If your client is granted `update:trips`, every documented operation that requires `update:trips` is callable (for example `PATCH /trips/{tripId}` and `PATCH /trips/{tripId}/config`), not just the ones that explicitly show the M2M scheme in the parameters list.

**Account scope.** Your client is also bound to one or more accounts (its grant). The account resolved from `x-on-behalf-of` (either the explicit `,account_id` or the agent's current account) must be within that grant — or, when the grant points at an organization parent, one of its active children.

**Failure responses are uniform.** Every on-behalf-of failure branch — unknown email, agent without a usable account, account outside the grant, agent without membership — returns the same `403 Forbidden`. This prevents M2M callers from enumerating platform email addresses by inspecting response shapes.

### Base URLs

- **Trips & Catalogs**: `https://trips.api.moguplatform.com`
- **AI Trip Imports**: `https://ai.api.moguplatform.com`


## Quick Start

### 1. Obtain an API Key

To access the API, you'll need to generate an API Key from your MOGU account:

1. Log in to your MOGU account
2. Navigate to the **Integrations** tab in your account settings
3. Generate a new API Key


The API Key will start with `ak_` and will include the necessary permissions based on your account role.

**Learn how to generate an API Key**: For step-by-step instructions, see our [How to generate an API Key guide](https://help.moguplatform.com/en/articles/12868814-how-to-generate-an-api-key).

### 2. Make Your First Request

Here's a simple example to list your trips:


```bash
curl -X GET https://trips.api.moguplatform.com/trips \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

## Core Concepts

### What is a Trip?

A **Trip** is a travel proposal or itinerary that you share with your travelers.
Each trip has a title, dates, settings, and a visual layout defined by its **TripConfig**.

### What is a TripConfig?

The **TripConfig** is the full content and layout of a trip. It contains:

- **blocks**: The visual content tree (services, text, media, etc.)
- **contact**: Agency/agent contact information
- **settings**: Language, currency, date format preferences
- **theme**: Branding (colors, logo, fonts)


The workflow is fetch, edit, replace:

1. `GET /trips/{tripId}/config` — fetch the current config
2. Modify the returned object locally
3. `PUT /trips/{tripId}/config` — send the full updated config back


## Blocks

A trip is visually represented as a **tree of blocks** (`TripConfig.blocks`).

- Keep `id` **unique** across all blocks.
- Use `parent` + `children` to model the hierarchy.
- `children` order matters (it defines the visual order).
- Container blocks (like `itinerary`, `itineraryDay`, `group`, `box`, `dropdown`) can hold other blocks.
- `hidden: true` hides a block without removing it.


### How to build a trip (recommended workflow)

The trip configuration endpoint is **replace-based**:

- `GET /trips/{tripId}/config` returns the full config.
- `PUT /trips/{tripId}/config` **replaces** the full config.


Recommended flow:

1. Create or pick a trip (`POST /trips` or list existing trips)
2. Fetch config: `GET /trips/{tripId}/config`
3. Modify the returned object locally (typically `blocks`)
4. Send the full updated config back: `PUT /trips/{tripId}/config`


### Minimal example: itinerary with 2 days

This is an example of a minimal, practical block tree:


```json
{
  "blocks": [
    {
      "id": "b-itinerary",
      "type": "itinerary",
      "content": {
        "selectedDay": 0,
        "showDates": true,
        "showMap": true,
        "design": "tabs"
      },
      "children": [
        {
          "id": "b-day-1",
          "type": "itineraryDay",
          "parent": "b-itinerary",
          "content": {
            "title": "Day 1 - Arrival",
            "description": "<p>Arrival and hotel check-in.</p>"
          },
          "children": [
            {
              "id": "b-title-1",
              "type": "title",
              "parent": "b-day-1",
              "content": { "title": "Welcome" }
            },
            {
              "id": "b-text-1",
              "type": "text",
              "parent": "b-day-1",
              "content": { "text": "<p>Meet your guide at the airport.</p>" }
            },
            {
              "id": "b-transport-1",
              "type": "transport",
              "parent": "b-day-1",
              "content": {
                "title": "Private transfer",
                "description": "<p>Airport → Hotel</p>"
              }
            }
          ]
        },
        {
          "id": "b-day-2",
          "type": "itineraryDay",
          "parent": "b-itinerary",
          "content": {
            "title": "Day 2 - City highlights"
          },
          "children": [
            {
              "id": "b-activity-1",
              "type": "activity",
              "parent": "b-day-2",
              "content": {
                "title": "Guided city tour",
                "description": "<p>Walking tour of the historic center.</p>"
              }
            },
            {
              "id": "b-map-1",
              "type": "map",
              "parent": "b-day-2",
              "content": {
                "mapUrl": "https://www.google.com/maps/d/viewer?mid=..."
              }
            }
          ]
        }
      ]
    },
    {
      "id": "b-services-summary",
      "type": "servicesSummary",
      "content": {
        "included": true,
        "includedText": "<ul><li>Hotel</li><li>Transfers</li></ul>",
        "notIncluded": true,
        "notIncludedText": "<ul><li>Travel insurance</li></ul>"
      }
    }
  ]
}
```

### Example: adding a hotel to a day


```json
{
  "id": "b-hotel-1",
  "type": "accomodation",
  "parent": "b-day-1",
  "content": {
    "title": "Hotel Ritz Paris",
    "location": "15 Place Vendôme, 75001 Paris",
    "startDate": "2025-07-15T00:00:00Z",
    "finishDate": "2025-07-22T00:00:00Z",
    "nights": 7,
    "roomType": "Double",
    "stayRegime": "BedAndBreakfast",
    "description": "5-star hotel in the heart of Paris",
    "images": [
      { "url": "https://example.com/ritz.jpg", "display": "cover" }
    ]
  }
}
```

### Example: adding a flight


```json
{
  "id": "b-flight-1",
  "type": "flight",
  "parent": "b-day-1",
  "content": {
    "flightNumber": "IB3170",
    "departureDateTime": "2025-07-15T08:00:00Z",
    "arrivalDateTime": "2025-07-15T10:15:00Z",
    "departureAirport": { "iata": "MAD", "name": "Adolfo Suárez Madrid–Barajas", "city": "Madrid", "country": "Spain" },
    "arrivalAirport": { "iata": "CDG", "name": "Charles de Gaulle Airport", "city": "Paris", "country": "France" },
    "airline": { "iata": "IB", "name": "Iberia" },
    "elapsedTime": 135
  }
}
```

### Full workflow example

Create a trip, fetch its config, add a block, and save:


```bash
# 1. Create a new trip
curl -X POST https://trips.api.moguplatform.com/trips \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Paris Summer 2025", "duration": 7}'

# Response: { "id": 12345, "slug": "paris-summer-2025-abc", ... }

# 2. Fetch the trip config
curl -X GET https://trips.api.moguplatform.com/trips/12345/config \
  -H "Authorization: Bearer YOUR_API_KEY"

# 3. Modify the blocks array locally (add your blocks), then PUT the full config back
curl -X PUT https://trips.api.moguplatform.com/trips/12345/config \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d @updated-config.json
```

> **Important:** `PUT` replaces the entire config. Always preserve existing blocks you don't intend to change.


### Block types

Core block types you will commonly use:

- `itinerary` (container) with `itineraryDay` children
- Service blocks: `accomodation`, `transport`, `train`, `cruise`, `info`, `activity`, `flight`, `food`
- Content blocks: `text`, `title`, `box`, `dropdown`
- Media blocks: `map`, `video`, `file`, `gallery`
- Layout: `pageBreak`, `group`
- Other: `servicesSummary`, `price`, `form`, `practicalInfo`


### Required content fields by block type

| Block type | Required fields in `content` |
|  --- | --- |
| `text` | `text` (HTML string) |
| `title` | `title` |
| `accomodation` | `title` |
| `activity` | `title` |
| `flight` | `flightNumber`, `departureDateTime`, `arrivalDateTime`, `departureAirport`, `arrivalAirport`, `airline`, `elapsedTime` |
| `train` | `title`, `location` (departure), `arrivalLocation` |
| `transport` | `title` |
| `cruise` | `title` |
| `food` | `title` |
| `info` | `title` |
| `box` | `color` (CSS hex) |
| `dropdown` | `title` |
| `map` | `mapUrl` (Google Maps URL) |
| `video` | `videoUrl` (YouTube/Vimeo) |
| `file` | `fileName`, `fileUrl` |
| `gallery` | (none — just `images` array) |
| `itinerary` | (none — uses `children` for days) |
| `itineraryDay` | (none — uses `children` for day content) |
| `price` | `title`, `price`, `currency` |
| `servicesSummary` | (none) |
| `practicalInfo` | `selectedCountry`, `selectedLanguage`, `flag` |
| `pageBreak` | (no content needed) |
| `form` | (see FormContent schema) |
| `group` | (no content fields) |


Some block content fields (e.g. `text.content.text`, `itineraryDay.content.description`) accept **HTML-formatted rich text**. See sanitization rules below.

### HTML sanitization (important)

Rich text fields are sanitized before rendering.

- `<img>` tags are not allowed.
- `style` attributes are restricted (text-align only).
- Use basic formatting tags (p, headings, lists, links, tables, etc.).


## Trips

Trips are the main entities in the MOGU platform. A trip represents a travel itinerary with all its details, configurations, and associated travelers.

**Key operations:**

- Create and manage trips
- Configure trip settings and branding
- Manage trip visibility (public/private)
- Track trip visualizations


## Catalogs

Catalogs are collections of trips that can be organized and shared. They allow you to group related trips for easier management and presentation.

**Key operations:**

- Create and organize catalogs
- Add/remove trips from catalogs
- Make catalogs publicly accessible
- Filter and search catalog trips


## API Features

### Versioning

The API is currently at version `1.0.0`. The version is specified in the OpenAPI specification and can be verified through the `/status` endpoint.

**Current version:** `1.0.0`

We follow semantic versioning (SemVer) principles:

- **Major version** changes indicate breaking changes
- **Minor version** changes add functionality in a backward-compatible manner
- **Patch version** changes are for backward-compatible bug fixes


When breaking changes are introduced, we will communicate them in advance through our changelog and support channels.

### Pagination

List endpoints support pagination to handle large datasets efficiently:


```bash
GET /trips?page=1&pageSize=20
```

**Parameters:**

- `page`: Page number (starts at 1)
- `pageSize`: Number of items per page (default: 20, max: 100)


**Response format:**


```json
{
  "data": [...],
  "page": 1,
  "pageSize": 20,
  "totalCount": 150,
  "totalPages": 8
}
```

### Filtering

Apply filters to narrow down results using JSON syntax:


```bash
GET /trips?filters=[{"field":"duration","operator":"gte","value":5}]
```

**Supported operators:**

- `eq`: Equal to
- `neq`: Not equal to
- `gt`: Greater than
- `gte`: Greater than or equal to
- `lt`: Less than
- `lte`: Less than or equal to
- `in`: In array
- `contains`: Contains value


### Searching

Search across multiple fields using JSON syntax:


```bash
GET /trips?search={"fields":["title","code"],"term":"Paris"}
```

### Sorting

Sort results by any field:


```bash
GET /trips?orderBy={"field":"createdAt","direction":"desc"}
```

**Directions:**

- `asc`: Ascending order
- `desc`: Descending order


## Error Handling

The API uses standard HTTP status codes to indicate success or failure:

### Success Codes

- `200 OK`: Request succeeded
- `201 Created`: Resource created successfully


### Client Error Codes

- `400 Bad Request`: Invalid request parameters or body
- `401 Unauthorized`: Missing or invalid authentication token
- `403 Forbidden`: Insufficient permissions
- `404 Not Found`: Resource not found
- `429 Too Many Requests`: Rate limit exceeded


### Server Error Codes

- `500 Internal Server Error`: Server-side error
- `503 Service Unavailable`: Service temporarily unavailable


### Error Response Format


```json
{
  "errors": {
    "title": ["Title is required"],
    "duration": ["Duration must be a positive number"]
  }
}
```

## Support

Need help? We're here for you!

- **Email**: [support@moguplatform.com](mailto:support@moguplatform.com)
- **Help Center**: [https://help.moguplatform.com](https://help.moguplatform.com)


## Terms of Service

By using the MOGU Public API, you agree to our [Terms of Service](https://moguplatform.com/terms) and [Privacy Policy](https://moguplatform.com/privacy).

**Ready to get started?** Check out our [API Reference](/openapi) for detailed endpoint documentation.