# create_itinerary

Start a trip with one empty day and its trip fields.

`POST /v1/guides/{productId}/itineraries` · MCP tool `create_itinerary` · scope `write` · beta · since 2026-10-06.3

Create once spots, routes and helpers exist. At most 50 itineraries per guide, archived ones included. The new trip starts with one empty day; write its full days with set_itinerary_days next. Trip fields and the cover are checked atomically. The reply is the stored, normalized row. The whole itinerary must fit 100000 JSON characters.

## Path parameters

- `productId` (string, required): Id of the guide (a product of type map). From list_guides or create_guide.

## Body parameters

- `name` (string, required): Trip name, required when sent, at most 120 characters. Cannot be empty.
- `body` (string, optional): Before you go: who the trip suits, what to book and the pace, at most 5000 characters. Empty string clears.
- `coverMediaId` (string, optional): Optional photo of this guide, from list_media. Omit for no cover; null is not accepted on create.
- `description` (string, optional): Short description, at most 300 characters. Empty string clears.
- `endsAt` (string, optional): Where the trip ends, in words, at most 120 characters. Empty string clears.
- `season` (ItinerarySeason, optional): Optional months and a season note. Omit when unknown; null is not accepted on create.
  Fields of ItinerarySeason: https://developers.sceniq.earth/fields/itinerary-season.md
- `startsAt` (string, optional): Where the trip starts, in words, at most 120 characters. Empty string clears.
- `travelMode` (string, optional): Optional getting around on the whole trip: car, public_transport, campervan, bike, on_foot, boat, mixed. Omit when unknown; null is not accepted on create. One of `car`, `public_transport`, `campervan`, `bike`, `on_foot`, `boat`, `mixed`.

## Returns

201: ItineraryWrite.

- `itineraryId` (string, required): The itinerary's id.
- `productId` (string, required): The guide's id.
- `name` (string, required): The trip's name.
- `description` (string or null, required): Short description, or null when empty.
- `body` (string or null, required): Before you go: the creator's trip notes, or null.
- `coverMediaId` (string or null, required): A photo of this guide, or null for no cover.
- `travelMode` (string or null, required): How to get around on the whole trip, or null when unknown. One of `car`, `public_transport`, `campervan`, `bike`, `on_foot`, `boat`, `mixed`.
- `season` (ItinerarySeason or null, required): Months and a season note, or null when unset.
  Fields of ItinerarySeason: https://developers.sceniq.earth/fields/itinerary-season.md
- `startsAt` (string or null, required): Where the trip starts, in words, or null.
- `endsAt` (string or null, required): Where the trip ends, in words, or null.
- `days` (array of ItineraryDay, required): The days exactly as stored, with absent optional fields and no nulls inside. Edit this array and send it straight to set_itinerary_days.
  Fields of ItineraryDay: https://developers.sceniq.earth/fields/itinerary-day.md
- `order` (number, required): Position in the guide, ascending and fractional.
- `archived` (boolean, required): true hides this itinerary from buyers and keeps it editable.
- `createdAt` (number, required): When the row was created (Unix time in milliseconds).
- `updatedAt` (number, required): When the row last changed (Unix time in milliseconds).
- `warnings` (array of strings, optional): Notes about things the write accepted but you should look at (a plain http link). Present only when there are any.

## Examples

### First call: create the trip fields with one empty day

```bash
curl -X POST "https://sceniq.earth/api/v1/guides/j5738kd1q9x2m7v4c6b8n0p3r5t1w9y2/itineraries" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Yellowstone over two days",
  "description": "Thermal colors, a night at Old Faithful and first light in Lamar Valley.",
  "body": "Book the inn ahead. Check road conditions and keep wildlife at a distance.",
  "coverMediaId": "kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9",
  "travelMode": "car",
  "season": {
    "months": [
      6,
      7,
      8,
      9
    ],
    "note": "Check the park road status before setting out."
  },
  "startsAt": "Old Faithful",
  "endsAt": "Lamar Valley"
}'
```

MCP `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_itinerary",
    "arguments": {
      "productId": "j5738kd1q9x2m7v4c6b8n0p3r5t1w9y2",
      "name": "Yellowstone over two days",
      "description": "Thermal colors, a night at Old Faithful and first light in Lamar Valley.",
      "body": "Book the inn ahead. Check road conditions and keep wildlife at a distance.",
      "coverMediaId": "kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9",
      "travelMode": "car",
      "season": {
        "months": [
          6,
          7,
          8,
          9
        ],
        "note": "Check the park road status before setting out."
      },
      "startsAt": "Old Faithful",
      "endsAt": "Lamar Valley"
    }
  }
}
```

Response 201:

```json
{
  "itineraryId": "ki7a2c4e6g8j0m2p4s6v8y0b2d4f6h8k",
  "productId": "j5738kd1q9x2m7v4c6b8n0p3r5t1w9y2",
  "name": "Yellowstone over two days",
  "description": "Thermal colors, a night at Old Faithful and first light in Lamar Valley.",
  "body": "Book the inn ahead. Check road conditions and keep wildlife at a distance.",
  "coverMediaId": "kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9",
  "travelMode": "car",
  "season": {
    "months": [
      6,
      7,
      8,
      9
    ],
    "note": "Check the park road status before setting out."
  },
  "startsAt": "Old Faithful",
  "endsAt": "Lamar Valley",
  "days": [
    {
      "stops": []
    }
  ],
  "order": 1024,
  "archived": false,
  "createdAt": 1790673240000,
  "updatedAt": 1790673240000
}
```

## Errors

- `invalid_argument` (400): A required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
- `invalid_request` (400): A writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
- `unauthenticated` (401): The request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
- `invalid_api_key` (401): The key is unknown, revoked or expired.
- `api_scope_required` (403): A read-only key called a write operation, or a key without the publish option called publish_changes.
- `forbidden` (403): The key belongs to a creator account that is no longer active.
- `not_found` (404): The id does not exist or belongs to another creator. The two answers are identical on purpose, so ids never reveal what exists.
- `payload_too_large` (413): A JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
- `rate_limited` (429): Over a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
- `internal_error` (500): An unexpected failure. The message is hidden on purpose.

Reference: https://developers.sceniq.earth/reference/create_itinerary
