# Conversations and the concierge — tool reference

Generated from the OpenAPI document (`https://maison-labs.com/api/v1/openapi.json`). Field types are JSON Schema types; GET/DELETE inputs travel as path or query, other methods as a JSON body.

## `export_chat_logs` — Export chat logs as XLSX

Export a hotel's conversation history as a base64-encoded XLSX workbook. Includes channel attribution and routed multi-property sessions. Returns filename, MIME type and base64 bytes. Throws a 413 error when the workbook exceeds 8 MiB; use a shorter date window. list_conversations returns the same rows as JSON.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversations/export`
- MCP tool / A2A skill: `export_chat_logs`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `startDate` | query | string | yes | Export window start as ISO 8601 date or datetime (e.g. 2024-01-01) |
| `endDate` | query | string | yes | Export window end as ISO 8601 date or datetime |
| `skipAdmin` | query | boolean | no | Exclude sessions created by authenticated operators |
| `skipTestSessions` | query | boolean | no | Exclude test sessions |
| `skipFresh` | query | boolean | no | Exclude sessions with only one message |

## `get_common_questions` — Most common guest questions

What guests actually ask a hotel's concierge: every guest message in a date window, grouped by normalised wording and ranked by how often it was asked and in how many conversations. Use it to build or refresh a website FAQ, to spot recurring topics (amenities, booking dates, parking, check-in), or to decide what knowledge to add. Combine with list_knowledge_gaps (questions the concierge could not answer) and add_qa_pairs to publish the answers. Defaults to the last 30 days.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversations/questions`
- MCP tool / A2A skill: `get_common_questions`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `from` | query | string | no | Window start as ISO 8601 datetime or epoch ms; default 30 days ago |
| `to` | query | string | no | Window end as ISO 8601 datetime or epoch ms; default now |
| `limit` | query | integer | no | How many question groups to return (1–200); default 50 |
| `search` | query | string | no | Only questions containing this text (case-insensitive), e.g. parking |
| `minLength` | query | integer | no | Ignore guest messages shorter than this many characters; default 8 |
| `skipAdmin` | query | boolean | no | Exclude operator test chats; default true |
| `skipTestSessions` | query | boolean | no | Exclude automated test sessions; default true |

## `get_conversation` — Get a conversation session

Retrieve a full chat session transcript from DynamoDB by session id. Returns the raw session record including all messages, timestamps, and session metadata. Use list_conversations to browse multiple sessions; this fetches a single known session.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}`
- MCP tool / A2A skill: `get_conversation`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex session id to retrieve |

## `get_conversation_attention` — Count conversations needing an operator

How many guest conversations need an operator across the hotels you can read (or the `clientIds` you name): `needsOperator` (unread or flagged), `unread`, `flagged`, and the same per hotel in `byClient`. Every number counts conversations, not messages. Use list_recent_conversations with `unread` or `attention` to list them.

- REST: `GET https://maison-labs.com/api/v1/conversations/attention`
- MCP tool / A2A skill: `get_conversation_attention`
- Needs: chat-logs READ (any)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientIds` | query | array of string | no | Only these hotels (at most 25); intersected with the hotels you can read |

## `get_conversation_guest` — Get the guest behind a conversation

The known guest behind a conversation: `guestRef` (a guest id, or `identity:<id>` for a guest known only by a claimed email or phone), display name, the decrypted identities (kind and value) and how many conversations they have at the hotel. Needs guest-identity access (separate from conversation access). 404 when the conversation has no linked guest. Use list_guest_conversations with the guestRef for their other conversations.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/guest`
- MCP tool / A2A skill: `get_conversation_guest`
- Needs: chat-logs READ (client), guest-identity READ (client); client plan must include `guest-identity`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |

## `list_conversation_inbox` — List a hotel's conversation inbox

One summary per guest conversation for a hotel, newest activity first: channel, message count, first and latest message preview, topic, whether the guest is identified (`guestLinked`; the identity itself needs get_conversation_guest and the guest-identity grant), who holds it (agent or an operator), unread count and attention flag. Filter by date window, channel, operator-held, needing attention or unread; page with `cursor` (the previous page's `nextCursor`). Use this for operator work; list_conversations returns raw message rows instead, and get_conversation reads one transcript.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversations/inbox`
- MCP tool / A2A skill: `list_conversation_inbox`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `from` | query | string | no | Last activity on or after this ISO 8601 datetime |
| `to` | query | string | no | Last activity on or before this ISO 8601 datetime |
| `limit` | query | integer | no | Page size (1–100); default 50 |
| `cursor` | query | string | no | nextCursor from the previous page |
| `channel` | query | string | no | Only this channel, e.g. web, whatsapp |
| `mode` | query | enum: operator | no | operator: only conversations an operator holds |
| `attention` | query | boolean | no | true: only conversations flagged for attention |
| `unread` | query | boolean | no | true: only conversations with unread guest messages |

## `list_conversations` — List conversation sessions

Return raw chat session rows for a client within a date window. Defaults to the last 7 days. Optionally filter out admin, test and fresh (first-message-only) sessions. Returns items, total count and the window used. Use export_chat_logs for an XLSX workbook or get_conversation for a single session. For operator work (who holds a conversation, unread and attention, one row per conversation) use list_conversation_inbox instead.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversations`
- MCP tool / A2A skill: `list_conversations`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `from` | query | string | no | Window start as ISO 8601 datetime or epoch ms; default 7 days ago |
| `to` | query | string | no | Window end as ISO 8601 datetime or epoch ms; default now |
| `limit` | query | integer | no | Maximum number of rows to return (1–500); default 100 |
| `skipAdmin` | query | boolean | no | Exclude sessions created by authenticated operators |
| `skipTestSessions` | query | boolean | no | Exclude test sessions |
| `skipFresh` | query | boolean | no | Exclude sessions with only one message |

## `list_guest_conversations` — List one guest's conversations

Every conversation of one guest at a hotel, newest activity first: session id, channel, started and last activity times, message count and the first guest message (120 characters). `guestRef` comes from get_conversation_guest. Needs guest-identity access. Use get_conversation to read one.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/guest/{guestRef}/conversations`
- MCP tool / A2A skill: `list_guest_conversations`
- Needs: chat-logs READ (client), guest-identity READ (client); client plan must include `guest-identity`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `guestRef` | path | string | yes | A guest id or identity:<id> |

## `list_recent_conversations` — List recent conversations across hotels

The most recently active guest conversations across every hotel you can read conversations of (or the `clientIds` you name), newest first, each with its hotel id and name, preview, topic, guest name (null for a hotel where you lack guest-identity access), who holds it, unread count and attention. Also returns `clients`, the hotels that have conversations (for a filter). Use list_conversation_inbox for one hotel with paging.

- REST: `GET https://maison-labs.com/api/v1/conversations/recent`
- MCP tool / A2A skill: `list_recent_conversations`
- Needs: chat-logs READ (any)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientIds` | query | array of string | no | Only these hotels (at most 25); intersected with the hotels you can read |
| `from` | query | string | no | Last activity on or after; format date-time |
| `to` | query | string | no | Last activity before; format date-time |
| `limit` | query | integer | no | How many conversations (1–100); default 50 |
| `channel` | query | string | no | Only this channel |
| `unread` | query | boolean | no | true: only conversations with unread guest messages |
| `attention` | query | boolean | no | true: only conversations flagged for attention |

## `mark_conversation_read` — Mark a conversation read

Mark a guest conversation read: clears its unread count and attention, resolves its shared attention notice and catches up your guest-message notices. Does not change who holds the conversation. Returns `cleared`, the unread count that was cleared (0 when nothing was unread).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/read`
- MCP tool / A2A skill: `mark_conversation_read`
- Needs: chat-logs WRITE (client); client plan must include `chat-logs`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |

## `reply_to_conversation` — Reply to a guest as an operator

Send a message to the guest in a conversation as yourself (an operator), delivered on the conversation's channel (web chat, WhatsApp, Instagram, Messenger, email). A reply is a takeover: when the agent is still answering, the conversation is taken over first (as set_conversation_mode with `operator`) so the agent and you never both answer the guest; `tookOver` says whether it happened. The guest sees your first name on the reply. Clears your unread count for it. 409 when the channel cannot be reached. Use whisper_to_agent to instruct the agent privately instead.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/reply`
- MCP tool / A2A skill: `reply_to_conversation`
- Needs: chat-logs EXECUTE (client); client plan must include `chat-logs`
- Composite of: `set_conversation_mode`.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |
| `text` | body | string | yes | The message the guest receives |
| `takeOver` | body | boolean | no | Accepted for compatibility and no longer needed: every reply takes the conversation over while the agent is answering; default false |

## `search_conversations` — Search conversation topics

Find a hotel's conversations by the meaning of guest-visible messages. Returns session references and semantic relevance scores, not transcripts. Use get_conversation to read a result.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/conversations/search`
- MCP tool / A2A skill: `search_conversations`
- Needs: chat-logs READ (client); client plan must include `chat-logs`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `query` | query | string | yes |  |
| `limit` | query | integer | no | default 10 |
| `channel` | query | string | no |  |
| `from` | query | string | no | format date-time |
| `to` | query | string | no | format date-time |

## `send_message` — Send a message to the hotel concierge

Send one message to a hotel's AI concierge (the same brain as the guest chat widget) as an operator and receive its reply. Optionally supply a sessionId to continue a prior conversation; omit it to start a new one. Returns the reply text, its type, the sessionId to continue with, and whether a new session was created. Use get_conversation to read the full transcript of a session.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/message`
- MCP tool / A2A skill: `send_message`
- Needs: update-client READ (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `message` | body | string | yes | The message text to send to the hotel concierge |
| `sessionId` | body | string | no | Existing session id to continue; omit to start a new conversation; format uuid |
| `locale` | body | string | no | BCP 47 locale hint, e.g. en-US; omit for the client default |

## `set_conversation_mode` — Take over or hand back a conversation

Switch who answers a guest conversation: `operator` takes it over (the agent stops replying to the guest; you reply with reply_to_conversation), `agent` hands it back and the agent answers whatever the guest asked while it was held. Records the takeover or hand-back in the hotel's activity feed, tells a web-chat guest that a team member joined or left, and updates the console live. Idempotent and atomic: asking for the mode the conversation is already in changes nothing (`changed: false`), and of two simultaneous changes only one takes effect; a takeover that another operator won, or already holds, is 409 (`mode_conflict`, with the mode and operator now holding it). Returns the mode, the operator name recorded (the current holder's when unchanged, null when the agent holds it) and `changed`.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/mode`
- MCP tool / A2A skill: `set_conversation_mode`
- Needs: chat-logs WRITE (client); client plan must include `chat-logs`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |
| `mode` | body | enum: agent \| operator | yes | operator: take over; agent: hand back |

## `stop_conversation_turn` — Stop the agent's turn in a conversation

Stop the agent while it is working on a turn: its reply is discarded and the agent run is cancelled. `kind: guest` stops a turn answering a guest message (turnId from the live thinking event), `kind: whisper` stops the turn a whisper started (the whisperId from whisper_to_agent). Returns `{ stopped: true }`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/turn/{turnId}/stop`
- MCP tool / A2A skill: `stop_conversation_turn`
- Needs: chat-logs WRITE (client); client plan must include `chat-logs`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |
| `turnId` | path | string | yes | The turn id, or the whisperId for a whisper; format uuid |
| `kind` | body | enum: guest \| whisper | no | What started the turn; default "guest" |

## `whisper_to_agent` — Whisper an instruction to the agent

Send a private instruction to the agent in a guest conversation (the guest never sees it); the agent answers the guest with it in mind. Needs a live agent: 409 (`operator_mode`) while the conversation is taken over. Returns 202 with a `whisperId` at once: on web chat the agent's reply follows asynchronously, on WhatsApp, Instagram and Messenger it is delivered before the call returns, and 409 (`agent_unavailable`) when the agent could not answer there (nothing reaches the guest). 409 on email conversations (no route) and on a signed-in guest's web chat (take over and reply_to_conversation instead). Stop it with stop_conversation_turn (`kind: whisper`).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/conversation/{sessionId}/whisper`
- MCP tool / A2A skill: `whisper_to_agent`
- Needs: chat-logs EXECUTE (client); client plan must include `chat-logs`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | The web UUID or channel 32-hex conversation id |
| `text` | body | string | yes | The instruction for the agent |
