Welcome to the MOGU Public API documentation. This API allows you to programmatically manage trips and catalogs for your travel business.
The API supports two authentication modes. Pick the one that matches your integration:
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_KEYUsed 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 accountScope-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.
- Trips & Catalogs:
https://trips.api.moguplatform.com - AI Trip Imports:
https://ai.api.moguplatform.com
To access the API, you'll need to generate an API Key from your MOGU account:
- Log in to your MOGU account
- Navigate to the Integrations tab in your account settings
- 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.
Here's a simple example to list your trips:
curl -X GET https://trips.api.moguplatform.com/trips \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"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.
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:
GET /trips/{tripId}/config— fetch the current config- Modify the returned object locally
PUT /trips/{tripId}/config— send the full updated config back
A quick primer on the domain vocabulary used throughout this reference:
- Block — the atomic unit of trip content. A block has a
type, acontentpayload specific to that type, and shared envelope fields (id,parent,children,hidden,metadata). - Container block — a block that holds other blocks via
children(e.g.itinerary,itineraryDay,group,box,dropdown). It structures layout rather than carrying a service. - Service block — a block representing a bookable/real-world item the traveler experiences:
accomodation,flight,activity,food,cruise,transport,train,info. - TripConfig — the full content + layout of a trip: its
blockstree,contact,settingsandtheme. Fetched and replaced as a whole (see the workflow above). - Proposal — the traveler-facing presentation of a trip (the published itinerary the agent shares). "Trip" and "proposal" are often used interchangeably.
- joinCode — a short code that lets a traveler join/access a trip.
- slug — the URL-friendly identifier in a trip's public link (e.g.
paris-summer-2025-abc). Generated on creation (but it can be edited too). - visualization — a recorded view of a trip by a traveler; used for engagement tracking.
A trip is visually represented as a tree of blocks (TripConfig.blocks).
Every block, regardless of type, carries the same envelope fields (the type-specific payload lives under content):
id(string, required) — keep it unique across the whole block tree.type(string, required) — the discriminator that selects thecontentshape (e.g.accomodation,flight,text). See Block types below for the full list.parent(string) — theidof the containing block. Useparent+childrento model the hierarchy.children(array) — child blocks. Order matters — it defines the visual order.hidden(boolean) —hidden: truehides a block from public view, without removing it.metadata(object) — a free-form key/value bag. MOGU stores it and echoes it back verbatim onGET /trips/{tripId}/config; it is never interpreted by the platform, so use it to carry your own external references (e.g. your own booking IDs). It is not surfaced to travelers.
Container blocks (like itinerary, itineraryDay, group, box, dropdown) can hold other blocks via children.
Spelling note: the accommodation service type is
accomodation(single middle "m"). This is the canonical, intentional value — do not "correct" it toaccommodation, or the block will be rejected.
optionalon service blocks: settingoptional: trueon a service block marks it as a traveler-selectable add-on (shown as something the traveler can choose to include or exclude) rather than a guaranteed part of the trip.
The trip configuration endpoint is replace-based:
GET /trips/{tripId}/configreturns the full config.PUT /trips/{tripId}/configreplaces the full config.
Recommended flow:
- Create or pick a trip (
POST /tripsor list existing trips) - Fetch config:
GET /trips/{tripId}/config - Modify the returned object locally (typically
blocks) - Send the full updated config back:
PUT /trips/{tripId}/config
This is an example of a minimal, practical block tree:
{
"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>"
}
}
]
}{
"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" }
]
}
}{
"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
}
}A complete, copy-pasteable itineraryDay wiring a flight, a hotel and an activity into one day — with dates, locations (including coordinates and a Google Place ID), and images. Place this itinerary inside your config's blocks array (see the replace-based workflow below).
{
"id": "b-itinerary",
"type": "itinerary",
"content": { "showDates": true, "showMap": true, "design": "tabs" },
"children": [
{
"id": "b-day-1",
"type": "itineraryDay",
"parent": "b-itinerary",
"content": {
"title": "Day 1 — Arrival in Paris",
"description": "<p>Morning flight from Madrid, hotel check-in, evening at leisure.</p>"
},
"children": [
{
"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
}
},
{
"id": "b-hotel-1",
"type": "accomodation",
"parent": "b-day-1",
"content": {
"title": "Hôtel Ritz Paris",
"location": "15 Place Vendôme, 75001 Paris, France",
"lat": 48.8686,
"lon": 2.3294,
"googlePlaceId": "ChIJN1blFLsV44cRga5d4gjV62Q",
"startDate": "2025-07-15T00:00:00Z",
"finishDate": "2025-07-22T00:00:00Z",
"nights": 7,
"roomType": "Double",
"stayRegime": "BedAndBreakfast",
"description": "<p>5-star hotel in the heart of Paris.</p>",
"images": [
{ "url": "https://example.com/ritz.jpg", "display": "cover" }
]
}
},
{
"id": "b-activity-1",
"type": "activity",
"parent": "b-day-1",
"content": {
"title": "Eiffel Tower — skip-the-line",
"location": "Champ de Mars, 5 Av. Anatole France, 75007 Paris",
"lat": 48.8584,
"lon": 2.2945,
"googlePlaceId": "ChIJLU7jZClu5kcR4PcOOO6p3I0",
"startDate": "2025-07-15T18:00:00Z",
"startTime": "2025-07-15T18:00:00Z",
"description": "<p>Skip-the-line summit access at sunset.</p>",
"optional": true,
"images": [
{ "url": "https://example.com/eiffel.jpg", "display": "cover" }
]
}
}
]
}
]
}This single tree shows the common conventions in one place: container (itinerary) → day (itineraryDay) → service blocks (flight, accomodation, activity); parent/children linking; ISO 8601 dates; free-text location enriched with lat/lon + googlePlaceId; sanitized HTML descriptions; and an optional add-on activity.
Create a trip, fetch its config, add a block, and save:
# 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.jsonImportant:
PUTreplaces the entire config. Always preserve existing blocks you don't intend to change.
Core block types you will commonly use:
itinerary(container) withitineraryDaychildren- 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
| 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.
Rich text fields (e.g. text.content.text, itineraryDay.content.description, service block description, servicesSummary.includedText/notIncludedText) are sanitized server-side before rendering. Anything outside the allowlist below is silently stripped — it will not be stored or shown.
Allowed tags:
p, h1, h2, h3, h4, h5, h6,
ul, ol, li, strong, b, em, i, u, s,
a, br, hr, blockquote, pre, code,
table, thead, tbody, tr, td, th,
span, div, sub, supAllowed attributes:
| Attribute | Notes |
|---|---|
href | On <a> links. |
class | |
style | Only text-align is kept; all other declarations are removed. |
target | On <a>. Links with target="_blank" get rel="noopener noreferrer" added automatically. |
rel | |
colspan, rowspan | On table cells. |
Not allowed:
<img>tags (no inline images / tracking pixels) — use agallery,accomodation/activityetc.imagesarray, or amap/videoblock instead.data-*attributes.- Any tag or attribute not listed above.
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 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
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.
List endpoints support pagination to handle large datasets efficiently:
GET /trips?page=1&pageSize=20Parameters:
page: Page number (starts at 1)pageSize: Number of items per page (default: 20, max: 100)
Response format:
{
"data": [...],
"page": 1,
"pageSize": 20,
"totalCount": 150,
"totalPages": 8
}Apply filters to narrow down results using JSON syntax:
GET /trips?filters=[{"field":"duration","operator":"gte","value":5}]Supported operators:
eq: Equal toneq: Not equal togt: Greater thangte: Greater than or equal tolt: Less thanlte: Less than or equal toin: In arraycontains: Contains value
Search across multiple fields using JSON syntax:
GET /trips?search={"fields":["title","code"],"term":"Paris"}Sort results by any field:
GET /trips?orderBy={"field":"createdAt","direction":"desc"}Directions:
asc: Ascending orderdesc: Descending order
The API uses standard HTTP status codes to indicate success or failure:
200 OK: Request succeeded201 Created: Resource created successfully
400 Bad Request: Invalid request parameters or body401 Unauthorized: Missing or invalid authentication token403 Forbidden: Insufficient permissions404 Not Found: Resource not found429 Too Many Requests: Rate limit exceeded
500 Internal Server Error: Server-side error503 Service Unavailable: Service temporarily unavailable
{
"errors": {
"title": ["Title is required"],
"duration": ["Duration must be a positive number"]
}
}Need help? We're here for you!
- Email: support@moguplatform.com
- Help Center: https://help.moguplatform.com
By using the MOGU Public API, you agree to our Terms of Service and Privacy Policy.
Ready to get started? Check out our API Reference for detailed endpoint documentation.