> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pageonetravel.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PageOne Travel AI Agent API

> Partner Core Chat API for AI-powered hotel search and booking flows.

## Overview

* Version: `v1`
* Endpoints: `3`
* Base path: `/api/partner/v1/core-chat`
* Source: Migrated from `https://www.brek.ai/docs/partner-core-chat` on February 19, 2026.

## Onboard now action

1. Contact `leo@pageonelab.com`.
2. Brek will issue your partner API key (and sandbox base URL).
3. Start testing with Postman or your own HTTP client.

## Authentication (API key usage)

Send one of these headers on every request (`POST /sessions`,
`POST /events`, `GET /sessions/{sessionId}`):

* `x-partner-api-key: <partner_api_key>` (recommended)
* `Authorization: Bearer <partner_api_key>`
* `api-key: <partner_api_key>`

Example:

```bash theme={null}
curl "$BREK_BASE_URL/api/partner/v1/core-chat/sessions" \
  -H "x-partner-api-key: $BREK_PARTNER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"actor":{"actorId":"partner_user_001"}}'
```

`POST /events` uses the same auth header:

```bash theme={null}
curl "$BREK_BASE_URL/api/partner/v1/core-chat/events" \
  -H "x-partner-api-key: $BREK_PARTNER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"sessionId":"sess_xxx","text":"find me a refundable vegas hotel"}'
```

## Base path

`/api/partner/v1/core-chat`

## Common response headers

* `x-request-id`: request trace ID
* `x-ratelimit-limit`
* `x-ratelimit-remaining`
* `x-partner-id`

## POST `/sessions`

Creates a new partner chat session.

Input variables (`field | definition | required`):

* `actor.actorId` | Stable end-user ID in your partner system (for
  profile/history continuity). | `Yes`
* `actor.timezoneOffsetMinutes` | End-user timezone offset in minutes
  (for date parsing like "tomorrow"). | `No`
* `channel.workspaceId` | Optional partner workspace/group identifier
  used for routing/analytics. | `No`
* `conversation.channelId` | Optional conversation bucket key from your
  client app. | `No`
* `conversation.id` | Optional explicit conversation ID for your own
  traceability. | `No`
* `conversation.threadTs` | Optional explicit thread key for your own
  thread model. | `No`

Auth header:

* `x-partner-api-key: <partner_api_key>` (or
  `Authorization: Bearer <partner_api_key>`, or `api-key`)

Request body:

```json theme={null}
{
  "actor": {
    "actorId": "partner_user_001",
    "timezoneOffsetMinutes": -480
  },
  "channel": {
    "workspaceId": "partner-workspace"
  },
  "conversation": {
    "channelId": "partner-chat"
  }
}
```

Example response (`201`):

```json theme={null}
{
  "data": {
    "sessionId": "sess_1771456953407_g7p6233d",
    "tenantId": "demo-tenant",
    "channel": {
      "type": "partner_api",
      "workspaceId": "partner-workspace"
    },
    "conversation": {
      "id": "partner-chat:sess_1771456953407_g7p6233d",
      "channelId": "partner-chat",
      "threadTs": "sess_1771456953407_g7p6233d"
    },
    "actor": {
      "actorId": "partner_user_001",
      "timezoneOffsetMinutes": -480
    },
    "createdAt": "2026-02-18T23:22:33.407Z",
    "updatedAt": "2026-02-18T23:22:33.407Z",
    "events": []
  }
}
```

## POST `/events`

Sends one event into an existing session.

Input variables (`field | definition | required`):

* `sessionId` | Session ID returned by `POST /sessions`. | `Yes`
* `actor.actorId` | Optional safety check. If provided, it must match
  the actor bound to the session. | `No`
* `text` | Natural-language user message. | `Yes` for natural-language
  flow
* `kind` | Explicit event type (for action-based integrations). | `No`
  (but required for explicit action-kind calls)
* `payload` | Structured action payload (for example `optionId`,
  `bookingId`, `paymentMethodId`). | `No` (but required by certain
  `kind` values)
* `idempotencyKey` | Client idempotency token to make write retries
  safe. | `No` (required for write kinds listed below)

Auth header:

* `x-partner-api-key: <partner_api_key>` (or
  `Authorization: Bearer <partner_api_key>`, or `api-key`)

Request body:

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "actor": { "actorId": "partner_user_001" },
  "text": "Seattle Mar 20-23 1 room refundable",
  "payload": {},
  "idempotencyKey": "demo:sess_1771456953407_g7p6233d:search:001"
}
```

Integration rule:

* Persist `sessionId` by `actorId` on your side. Reusing one session
  across different actors will mix user state.

`kind` behavior:

* Optional for normal natural-language messages.
* Required only when you explicitly call action kinds (for example:
  `action_book_option`, `action_cancel_booking`).

Partner UI mapping:

* `data.result.status` = action/state code for your UI logic.
* `data.result.message.text` = assistant text to render to end users.
* `data.result.artifacts` = structured payload (shortlist, payment setup
  URL, booking confirmation, etc.).

Idempotency key is required for write kinds:

* `command_book_by_option_id`
* `action_book_option`
* `action_confirm_price_change`
* `action_confirm_payment_card`
* `action_cancel_booking`

Example response (`200`):

```json theme={null}
{
  "data": {
    "sessionId": "sess_1771456953407_g7p6233d",
    "event": {
      "id": "evt_1771456966998",
      "kind": "search_query"
    },
    "result": {
      "traceId": "demo-tenant:partner_api:partner-chat:sess_1771456953407_g7p6233d:evt_1771456966998",
      "status": "shortlist",
      "message": {
        "text": "I found options matching your request in Seattle."
      },
      "artifacts": {
        "shortlist": [
          {
            "optionId": "3d49d4cb-22b1-4963-9280-508fb1190f5c",
            "name": "Inn at the Market",
            "pricePerNight": 276.66,
            "totalPrice": 980.67,
            "currency": "USD",
            "refundable": true,
            "rating": 4.8
          }
        ]
      }
    }
  }
}
```

## GET `/sessions/{sessionId}`

Returns current session snapshot and event history.

Input variables (`field | definition | required`):

* `sessionId` (path) | Existing session ID. Must belong to your partner
  tenant. | `Yes`

Auth header:

* `x-partner-api-key: <partner_api_key>` (or
  `Authorization: Bearer <partner_api_key>`, or `api-key`)

Example response (`200`):

```json theme={null}
{
  "data": {
    "sessionId": "sess_1771456953407_g7p6233d",
    "tenantId": "demo-tenant",
    "actor": {
      "actorId": "partner_user_001"
    },
    "events": [
      {
        "eventId": "evt_1771456966998",
        "kind": "search_query",
        "resultStatus": "shortlist",
        "recordedAt": "2026-02-18T23:22:46.998Z"
      }
    ]
  }
}
```

## Golden path example (search -> booking intent -> guest name -> payment setup -> done -> booked)

The sequence below was captured from a real sandbox run on **February
18, 2026**.

### Step 0: Create session

Variables for this step (`field | definition | required`):

* `actor.actorId` | Stable end-user ID in your partner system. | `Yes`
* `actor.timezoneOffsetMinutes` | User timezone offset in minutes. |
  `No`
* `channel.workspaceId` | Optional partner workspace/group identifier.
  \| `No`
* `conversation.channelId` | Optional conversation key in your client
  app. | `No`

Input (`POST /sessions`):

```json theme={null}
{
  "actor": {
    "actorId": "partner_golden_user_done",
    "timezoneOffsetMinutes": -420
  },
  "channel": {
    "workspaceId": "partner-demo"
  },
  "conversation": {
    "channelId": "partner-chat"
  }
}
```

Output (`201`):

```json theme={null}
{
  "data": {
    "sessionId": "sess_1771456953407_g7p6233d",
    "tenantId": "demo-tenant",
    "channel": {
      "type": "partner_api",
      "workspaceId": "partner-demo"
    },
    "conversation": {
      "id": "partner-chat:sess_1771456953407_g7p6233d",
      "channelId": "partner-chat",
      "threadTs": "sess_1771456953407_g7p6233d"
    },
    "actor": {
      "actorId": "partner_golden_user_done",
      "timezoneOffsetMinutes": -420
    }
  }
}
```

### Step 1: Search request

Variables for this step (`field | definition | required`):

* `sessionId` | Session ID returned in Step 0. | `Yes`
* `text` | Natural-language search request from the user. | `Yes`
* `idempotencyKey` | Retry-safe dedupe key from partner side. |
  `Recommended`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "text": "find me a las vegas hotel from 5/15 to 5/16 that is cheap and refundable",
  "idempotencyKey": "demo:sess_1771456953407_g7p6233d:search:001"
}
```

Output (`status=shortlist`):

```json theme={null}
{
  "data": {
    "result": {
      "status": "shortlist",
      "message": {
        "text": "I found options matching your request in Las Vegas. Matched core filters: refundable only. Sorted by lowest total price first."
      },
      "artifacts": {
        "shortlist": [
          {
            "optionId": "1375108c-d021-4e45-a54a-e11ef553af07",
            "name": "Branding Iron Motel",
            "pricePerNight": 98.11,
            "totalPrice": 110.26,
            "currency": "USD",
            "refundable": true,
            "rating": 2,
            "distanceTo": "2.7 miles from Las Vegas",
            "detailsUrl": "https://portal.example.com/book/hotel/69450b14fc4f2dc72399a9ba?..."
          }
        ]
      }
    }
  }
}
```

### Step 2: User asks to book option 1

Variables for this step (`field | definition | required`):

* `sessionId` | Session ID returned in Step 0. | `Yes`
* `text` | User booking-intent message. | `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "text": "can you help me book option 1"
}
```

Output (`status=guest_name_required`):

```json theme={null}
{
  "data": {
    "result": {
      "status": "guest_name_required",
      "message": {
        "text": "Before I book, I need the check-in guest name for room 1: first name + last name exactly as shown on an official ID."
      }
    }
  }
}
```

### Step 3: User provides guest name

Variables for this step (`field | definition | required`):

* `sessionId` | Session ID returned in Step 0. | `Yes`
* `text` | Guest-name message (for example: "guest name is John Doe").
  \| `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "text": "guest name is John Doe"
}
```

Output (`status=payment_setup_required`):

```json theme={null}
{
  "data": {
    "result": {
      "status": "payment_setup_required",
      "message": {
        "text": "No payment method is set up for this account. Please add a card before booking. Set up here: https://portal.example.com/portal/payment?token=psu_1771456992743_e2634ddd\nPlease reply \"Done\" once you finished set up."
      },
      "artifacts": {
        "payment": {
          "setupUrl": "https://portal.example.com/portal/payment?token=psu_1771456992743_e2634ddd"
        }
      }
    }
  }
}
```

### Step 3.5: User finishes card setup in payment portal

This happens outside Partner Core Chat endpoints (in Brek payment
stack), but shown here for end-to-end clarity.

Variables for this step (`field | definition | required`):

* `portalToken` | Payment setup token returned in
  `artifacts.payment.setupUrl`. | `Yes`
* `paymentMethodId` | Payment method token from payment provider. |
  `Yes`
* `cardBrand` | Card brand (for example `visa`). | `Yes`
* `cardLast4` | Last 4 digits of card. | `Yes`
* `expMonth` | Card expiration month. | `Yes`
* `expYear` | Card expiration year. | `Yes`
* `setAsDefault` | Whether to set this card as default. | `No`

Input (`POST /v1/hotel-ea/payment-methods/attach` on Brek payment
backend):

```json theme={null}
{
  "portalToken": "psu_1771456992743_e2634ddd",
  "paymentMethodId": "pm_card_visa",
  "cardBrand": "visa",
  "cardLast4": "4242",
  "expMonth": 12,
  "expYear": 2030,
  "setAsDefault": true
}
```

Output:

```json theme={null}
{
  "data": {
    "status": "ATTACHED",
    "paymentMethod": {
      "paymentMethodId": "pm_card_visa",
      "brand": "visa",
      "last4": "4242",
      "expMonth": 12,
      "expYear": 2030
    }
  },
  "message": "success"
}
```

### Step 4: User returns to chat and says "Done"

Variables for this step (`field | definition | required`):

* `sessionId` | Session ID returned in Step 0. | `Yes`
* `text` | Completion message after card setup (for example: "Done").
  \| `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "text": "Done"
}
```

Output (`status=payment_confirmation_required`):

```json theme={null}
{
  "data": {
    "result": {
      "status": "payment_confirmation_required",
      "message": {
        "text": "I found your updated card: VISA **** 4242. Use this card to continue booking?"
      },
      "artifacts": {
        "payment": {
          "paymentMethodId": "pm_card_visa",
          "cardBrand": "visa",
          "cardLast4": "4242",
          "expMonth": 12,
          "expYear": 2030
        }
      }
    }
  }
}
```

### Step 5: User confirms payment and booking

Variables for this step (`field | definition | required`):

* `sessionId` | Session ID returned in Step 0. | `Yes`
* `text` | Confirmation message (for example: "yes, use this card"). |
  `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771456953407_g7p6233d",
  "text": "yes, use this card"
}
```

Output (`status=booked` + `confirmationUrl`):

```json theme={null}
{
  "data": {
    "result": {
      "status": "booked",
      "message": {
        "text": "Booked Branding Iron Motel."
      },
      "artifacts": {
        "booking": {
          "bookingId": "A1WDDC",
          "confirmationBookingId": "699649fae0ee4bb440ddccf7",
          "hotelName": "Branding Iron Motel",
          "totalPrice": 110.26,
          "currency": "USD",
          "confirmationUrl": "https://portal.example.com/my-bookings/699649fae0ee4bb440ddccf7",
          "cancelPolicy": "Refundable until 2026-05-15T01:00:00Z"
        }
      }
    }
  }
}
```

## More scenarios and outputs

### Scenario A: user says "Done" but card is not attached yet

Variables for this scenario (`field | definition | required`):

* `sessionId` | Existing session ID. | `Yes`
* `text` | Completion message (for example: "Done"). | `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771457039985_uf2ydexs",
  "text": "Done"
}
```

Output (`status=payment_setup_required` again):

```json theme={null}
{
  "data": {
    "result": {
      "status": "payment_setup_required",
      "message": {
        "text": "Sure, you can update your payment method here: https://portal.example.com/portal/payment?token=psu_1771457050204_3d35ca69\nAfter setup, reply \"done\" in this thread and I will verify the new card before booking."
      },
      "artifacts": {
        "payment": {
          "setupUrl": "https://portal.example.com/portal/payment?token=psu_1771457050204_3d35ca69"
        }
      }
    }
  }
}
```

### Scenario B: user already has a saved card

Variables for this scenario (`field | definition | required`):

* `sessionId` | Existing session ID. | `Yes`
* `text` | Guest-name message after booking intent. | `Yes`

Input (`POST /events`, after guest name):

```json theme={null}
{
  "sessionId": "sess_1771457174011_gavkkfv5",
  "text": "guest name is Jane Doe"
}
```

Output (`status=payment_confirmation_required`, no setup URL):

```json theme={null}
{
  "data": {
    "result": {
      "status": "payment_confirmation_required",
      "message": {
        "text": "Are you ok to pay with VISA **** 4242?"
      },
      "artifacts": {
        "payment": {
          "paymentMethodId": "pm_card_visa",
          "cardBrand": "visa",
          "cardLast4": "4242",
          "expMonth": 12,
          "expYear": 2030
        }
      }
    }
  }
}
```

### Scenario C: user declines payment confirmation

Variables for this scenario (`field | definition | required`):

* `sessionId` | Existing session ID. | `Yes`
* `text` | Decline message (for example: "no"). | `Yes`

Input (`POST /events`):

```json theme={null}
{
  "sessionId": "sess_1771457174011_gavkkfv5",
  "text": "no"
}
```

Output:

```json theme={null}
{
  "data": {
    "result": {
      "status": "error",
      "message": {
        "text": "No problem. I will not charge this card or continue this booking."
      }
    }
  }
}
```

## Error responses

Missing API key (`401`):

```json theme={null}
{
  "success": false,
  "error": "Missing partner API key.",
  "code": "missing_partner_api_key",
  "details": null,
  "requestId": "d813d7e3-ba0b-4a97-899d-0847590d7560"
}
```

Invalid API key (`403`):

```json theme={null}
{
  "success": false,
  "error": "Invalid partner API key.",
  "code": "invalid_partner_api_key",
  "details": null,
  "requestId": "c75e34ff-aa06-4393-ae7d-cf1d31c3af25"
}
```

Missing idempotency key for write action (`400`):

```json theme={null}
{
  "success": false,
  "error": "idempotencyKey is required for kind=action_book_option",
  "requestId": "2a5ea99c-9884-448f-a442-35a883b64b31"
}
```

Unknown session (`404`):

```json theme={null}
{
  "success": false,
  "error": "session_not_found",
  "details": {
    "error": "session_not_found"
  },
  "requestId": "ca6fc510-87ef-434a-a945-ba19ed0e118f"
}
```

## Retry guidance

* Retry `429` and `5xx` with exponential backoff.
* Reuse the same `idempotencyKey` when retrying write actions.
* Log `x-request-id` for every failed request.

## Security requirements

* Keep partner API keys server-side only.
* Use HTTPS/TLS for all requests.
* Do not send raw PCI card data in chat payloads.
