# Knowledge base — 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.

## `add_document_source` — Add a document source

Add a document (PDF, DOCX, etc.) as a knowledge source and queue its ingestion. Send the file inline as canonical base64 (`bytesBase64`, ≤ 64 KiB decoded) or, for anything larger (≤ 50 MiB), PUT it to the URL from `create_document_upload` and pass that `uploadId` — exactly one of the two. A file attached to a Maison Agent message is staged for the person, not for a client: pass its `uploadId` with the `clientId` of any client the caller may write knowledge for (document-source WRITE) to add it there. Returns the document record and whether ingestion was queued.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/document`
- MCP tool / A2A skill: `add_document_source`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `name` | body | string | yes | Display name for the document |
| `filename` | body | string | yes | Original filename |
| `mimeType` | body | string | yes | MIME type, e.g. application/pdf |
| `bytesBase64` | body | string | no | File content as canonical base64 (≤ 64 KiB decoded). Give this OR `uploadId`, never both. |
| `uploadId` | body | string | no | Id from `create_document_upload` once the file was PUT to its URL (≤ 50 MiB). Give this OR `bytesBase64`, never both.; format uuid |

## `add_document_version` — Upload a new document version

Upload a new version of an existing document source and queue its ingestion. Send the file inline as canonical base64 (`bytesBase64`, ≤ 64 KiB decoded) or pass the `uploadId` of a `create_document_upload` (≤ 50 MiB) — exactly one of the two. Returns the updated document record and whether ingestion was queued.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}/version`
- MCP tool / A2A skill: `add_document_version`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |
| `filename` | body | string | yes | Original filename |
| `mimeType` | body | string | yes | MIME type, e.g. application/pdf |
| `bytesBase64` | body | string | no | File content as canonical base64 (≤ 64 KiB decoded). Give this OR `uploadId`, never both. |
| `uploadId` | body | string | no | Id from `create_document_upload` once the file was PUT to its URL (≤ 50 MiB). Give this OR `bytesBase64`, never both.; format uuid |

## `add_qa_pairs` — Add Q&A pairs

Create one or more Q&A pairs. When `publish` is true, pairs are published immediately after creation. Returns the raw llm-service response. Use `publish_qa_pairs` separately to publish later.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs`
- MCP tool / A2A skill: `add_qa_pairs`
- Needs: qa-editor WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `items` | body | array of object | yes | Q&A pairs to create |
| `publish` | body | boolean | no | Publish each pair immediately after creation; default false |

## `add_website_source` — Add a website source

Add a website as a knowledge source for a hotel and queue its first scrape. An optional `syncSchedule` other than NONE needs the Maison-staff `schedules` WRITE grant (403 without it; the source is not created); otherwise set it later with `set_source_sync_schedule`. Returns the created source and whether the scrape was queued (`scrapeQueued` is false when the queue did not take it or the hotel's knowledge is managed manually).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/website`
- MCP tool / A2A skill: `add_website_source`
- Needs: document-source WRITE (client); client plan must include `document-source`
- Composite of: `set_source_sync_schedule`.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `domain` | body | string | yes | Website URL; format uri |
| `updateClientWebsite` | body | boolean | no | Make this the main knowledge website for the hotel; default false |
| `language` | body | string | no | Scrape language hint, e.g. en |
| `syncSchedule` | body | enum: NONE \| DAILY \| WEEKLY \| BI_WEEKLY \| MONTHLY | no | Automatic sync schedule; anything but NONE needs Maison-staff `schedules` WRITE; default "NONE" |

## `answer_knowledge_gap` — Answer a knowledge gap

Answer an open knowledge gap and mark it ANSWERED. Either give `answer` (optionally with a reworded `question`; the gap's own question otherwise) and a new Q&A pair is created, or give `qaId` to link a pair that already answers it (nothing is created). 409 if the gap is already resolved.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/knowledgegap/{gapId}/answer`
- MCP tool / A2A skill: `answer_knowledge_gap`
- Needs: qa-editor WRITE (client); client plan must include `unanswered-questions`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `gapId` | path | string | yes | Knowledge gap id; format uuid |
| `answer` | body | string | no | Answer text for a new Q&A pair (omit when linking `qaId`) |
| `question` | body | string | no | Question for the new pair; defaults to the gap's question |
| `qaId` | body | string | no | Link this existing Q&A pair instead of creating one (no `answer`/`question`) |

## `check_knowledge_consistency` — Check knowledge consistency

Start a consistency-check run: a pipeline task that checks Q&A pairs for answers that conflict or are ambiguous. Requires `consistency-check` EXECUTE. Returns the run's `sessionId`; follow it with `get_knowledge_quality_run` (steps, then the report listing each conflict and ambiguity) and read its log with `get_source_run_logs`. Recorded in the hotel's activity.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs/consistencycheck`
- MCP tool / A2A skill: `check_knowledge_consistency`
- Needs: consistency-check EXECUTE (client); client plan must include `consistency-check`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |

## `create_document_upload` — Start a document upload

Get a presigned URL to upload a document larger than the 64 KiB inline limit (up to 50 MiB). PUT the raw file bytes to `url` with the returned `headers` before `expiresAt` (no credentials), then pass `uploadId` to `add_document_source` or `add_document_version`, which ingest it. An upload is bound to this hotel and is consumed by the call that ingests it.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/document/upload`
- MCP tool / A2A skill: `create_document_upload`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `mimeType` | body | string | yes | MIME type of the file, e.g. application/pdf (sent back as the PUT Content-Type) |
| `sizeBytes` | body | integer | yes | File size in bytes (at most 50 MiB) |

## `deduplicate_knowledge` — Deduplicate knowledge base

Start a deduplication run: a pipeline task that finds and merges near-duplicate Q&A pairs in the knowledge base, optionally with a `similarityThreshold` (0–1) and `batchSize`. Requires `deduplication` EXECUTE. Returns the run's `sessionId`; follow it with `get_knowledge_quality_run` (steps, then the report with answers before, after and removed) and read its log with `get_source_run_logs`. Recorded in the hotel's activity.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs/deduplicate`
- MCP tool / A2A skill: `deduplicate_knowledge`
- Needs: deduplication EXECUTE (client); client plan must include `deduplication`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `similarityThreshold` | body | number | no | How alike two pairs must be to count as duplicates (0–1) |
| `batchSize` | body | integer | no | Pairs compared per batch |

## `delete_document_version` — Delete a document version

Soft-delete a non-current version of a document. The current (newest) version cannot be deleted. Use this to remove old versions you no longer need.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}/version/{version}`
- MCP tool / A2A skill: `delete_document_version`
- Needs: document-source WRITE (client); client plan must include `document-source`
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |
| `version` | path | integer | yes | Version number to delete |

## `delete_qa_pairs` — Delete Q&A pairs

Soft-delete one or more Q&A pairs by id. Deleted pairs are hidden from `list_qa_pairs` by default but visible with `includeDeleted: true`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs/delete`
- MCP tool / A2A skill: `delete_qa_pairs`
- Needs: qa-editor WRITE (client)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `ids` | body | array of string | yes | Q&A pair ids |

## `detect_knowledge_gaps` — Detect knowledge gaps

Start a manual knowledge-gap detection run for a hotel and return at once (202 `{ queued: true }`); the run analyses recent guest chats in the background and inserts or updates gap records. Follow it with `list_knowledge_gaps` (its `lastSession`).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/knowledgegaps/detect`
- MCP tool / A2A skill: `detect_knowledge_gaps`
- Needs: clients EXECUTE (client); client plan must include `unanswered-questions`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |

## `disable_source` — Disable a knowledge source

Disable a website (`kind: website`) or document (`kind: document`) source: it stays linked with its history, its answers are frozen (the agent stops using them; they cannot be edited, published or deleted) and it is left out of syncs. Reversible with `reactivate_source`, which restores every answer exactly as it was. Returns `{ disabled, changed, answers }`; `changed: false` when it was already disabled. 502 when the knowledge base did not take the change (nothing recorded; retry).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/{kind}/{sourceId}/disable`
- MCP tool / A2A skill: `disable_source`
- Needs: document-source WRITE (client); client plan must include `document-source`
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `kind` | path | enum: website \| document | yes | Source type |
| `sourceId` | path | string | yes | Website id or document id; format uuid |

## `discover_booking_links` — Rediscover a website's booking links

Re-run booking-link discovery for one website source without a full re-scrape — after the hotel changed booking engine, say. The pipeline is idempotent: an unchanged link costs nothing and hand-tuned booking instructions are kept. Returns 202 with whether the task was queued (`queued: false` with `reason: manual_knowledge` when the hotel's knowledge is managed manually). 409 when the source is disabled.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/website/{websiteId}/bookinglinks`
- MCP tool / A2A skill: `discover_booking_links`
- Needs: document-source EXECUTE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `websiteId` | path | string | yes | Website source id; format uuid |

## `dismiss_knowledge_gap` — Dismiss a knowledge gap

Mark an open knowledge gap as DISMISSED without creating a Q&A pair; 404 unknown, 409 already resolved. Pass `gapIds` to dismiss more gaps in the same call: the answer then lists `dismissed`, `notFound` and `conflicts` per id instead of failing. Use `answer_knowledge_gap` to resolve it with new knowledge, `reopen_knowledge_gap` to undo.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/knowledgegap/{gapId}/dismiss`
- MCP tool / A2A skill: `dismiss_knowledge_gap`
- Needs: qa-editor WRITE (client); client plan must include `unanswered-questions`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `gapId` | path | string | yes | Knowledge gap id; format uuid |
| `gapIds` | body | array of string | no | More gap ids to dismiss with this one (bulk; per-id outcome) |

## `download_document` — Download a document

Download a document source. Up to 64 KiB the content comes back inline as canonical base64 (`bytesBase64`); above that the answer carries a short-lived presigned `downloadUrl` (GET it, no credentials) and its `expiresAt` instead. An optional `version` selects a specific revision; omit for the current version.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}/download`
- MCP tool / A2A skill: `download_document`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |
| `version` | query | integer | no | Version number; omit for the current version |

## `get_knowledge_quality_run` — Get a knowledge quality run

One deduplication or consistency-check run (a `sessionId` from `deduplicate_knowledge`, `check_knowledge_consistency` or `list_knowledge_quality_runs`) with its full report: for a consistency check each conflict and ambiguity (the questions involved, why, and the Q&A pair ids when known); for a deduplication the answer counts at each stage. `report` is null until the run reported. 404 when the run is not this hotel's.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/knowledge/quality/run/{sessionId}`
- MCP tool / A2A skill: `get_knowledge_quality_run`
- Needs: test-session READ (client); client plan must include `test-session`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | Run id from `list_knowledge_quality_runs`; format uuid |

## `get_qa_counts` — Q&A counts

Active, deleted and total Q&A pair counts for a hotel's knowledge base. Use as a quick health check before listing or as a dashboard metric.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/qapairs/counts`
- MCP tool / A2A skill: `get_qa_counts`
- Needs: qa-editor READ (client)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |

## `get_qa_pair` — Get a Q&A pair

Fetch a single Q&A pair by id. 404 when the id is unknown or the record is deleted. Use `list_qa_pairs` to search.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/qapair/{qaId}`
- MCP tool / A2A skill: `get_qa_pair`
- Needs: clients READ (client)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `qaId` | path | string | yes | Q&A pair id (UUID or legacy 24-hex) |

## `get_source_run_logs` — Read a source run's log

The log lines of one knowledge-source run (a `sessionId` from `list_source_runs`), oldest first, after line `after` (its `seq`; omit for the start). Poll with the returned `lastSeq` until `finished` is true. Each line's `data` is included only for callers with `document-source` WRITE.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | Run id from `list_source_runs`; format uuid |
| `after` | query | integer | no | Return lines after this `seq` |
| `limit` | query | integer | no | Most lines to return; default 500 |

## `get_source_run_report` — Fetch a website run's report

The report of one website scrape or sync run (a `sessionId` from `list_source_runs`): with `format: "json"` (default) the stored report (`summary`, `added`, `updated`, `deleted`, `merged`, `conflicts`, `unreadable`, `failedUpdates`, `needsRescrape`), `generatedAt` and `pdfUrl`, a download link for the PDF version valid for 15 minutes (null when no PDF was stored); with `format: "pdf"` only the download `url` and its `expiresAt`. 404 when the run is not a website run of this hotel, has no report yet, or (pdf) has no PDF.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/source/run/{sessionId}/report`
- MCP tool / A2A skill: `get_source_run_report`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sessionId` | path | string | yes | Run id from `list_source_runs`; format uuid |
| `format` | query | enum: json \| pdf | no | json: the report inline. pdf: a 15-minute download URL for the PDF version; default "json" |

## `get_website_scrape` — Get a website scrape

The latest scrape or sync of a linked website source, told step by step: status, the running step, countable progress, a bounded message log and stalled/stopped state; `scrape: null` when it never ran. Pass `sessionId` (a run from `list_source_runs`) to open that run instead. Use after `scrape_website_source` or `sync_source` to follow progress.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/source/website/{websiteId}/scrape`
- MCP tool / A2A skill: `get_website_scrape`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `websiteId` | path | string | yes | Website source id; format uuid |
| `sessionId` | query | string | no | A specific run; omit for the latest; format uuid |

## `list_document_versions` — List document versions

List all versions of a document source, newest first. Use this before restoring or deleting a specific version.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}/versions`
- MCP tool / A2A skill: `list_document_versions`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |

## `list_knowledge_gaps` — List knowledge gaps

List unanswered questions detected in guest chat sessions, ordered by occurrence count. Includes the last gap-detection run. Use `answer_knowledge_gap` or `dismiss_knowledge_gap` to resolve them.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `status` | query | enum: OPEN \| ANSWERED \| DISMISSED \| CLOSED | no | Filter by status; CLOSED = ANSWERED + DISMISSED |
| `limit` | query | integer | no | Page size (default 50, max 200) |
| `paginationToken` | query | string | no | Opaque pagination cursor |

## `list_knowledge_quality_runs` — List knowledge quality runs

The hotel's deduplication and consistency-check runs, newest first: each with its kind, status, steps, countable progress, who started it, its headline and, once reported, the report's summary and counts (conflicts and ambiguities, or answers before, after and removed). Page with `cursor` from `nextCursor`. Open one with `get_knowledge_quality_run`.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/knowledge/quality/runs`
- MCP tool / A2A skill: `list_knowledge_quality_runs`
- Needs: test-session READ (client); client plan must include `test-session`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `limit` | query | integer | no | Page size; default 20 |
| `cursor` | query | string | no | `nextCursor` of the previous page; omit for the first |

## `list_knowledge_sources` — List knowledge sources

List every website and document knowledge source attached to a hotel, including scrape/sync status, schedule and current version. `activeRun` names the run a source is busy with — `{ kind: scrape | rescrape | sync | ingest, status: queued | running, sessionId }` — or is null when its latest run finished. Use this to inspect the knowledge pipeline before scraping or uploading.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/sources`
- MCP tool / A2A skill: `list_knowledge_sources`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |

## `list_qa_pairs` — List Q&A pairs

Page through the Q&A knowledge base for a hotel with optional text search, source and origin filters, a creation-time window, publish-state filter, exact ids and sort. Returns a paginationToken when there are more results. Use `get_qa_pair` for a single item.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/qapairs`
- MCP tool / A2A skill: `list_qa_pairs`
- Needs: clients READ (client)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `limit` | query | integer | no | default 50 |
| `after` | query | string | no | Opaque pagination cursor from the previous page |
| `search` | query | string | no | Text search across question and answer |
| `sources` | query | array of string | no | Filter by source ids (SHA-256 hex) or the special value 'manual' |
| `filterStatus` | query | enum: published \| unpublished | no | Filter by publish state |
| `source` | query | enum: Website \| Document \| User | no | Filter by origin: website-sourced, document-sourced or written by a user |
| `createdFrom` | query | integer | no | Created at or after this time (epoch milliseconds) |
| `createdTo` | query | integer | no | Created at or before this time (epoch milliseconds) |
| `ids` | query | array of string | no | Exact pair ids (1–50), e.g. a conversation turn's retrieval ids; without a sort the result keeps this order |
| `includeDeleted` | query | boolean | no | Include soft-deleted pairs; default false |
| `sortBy` | query | enum: version \| created \| updated \| source \| status | no | Sort field |
| `sortDir` | query | enum: ascending \| descending | no | Sort direction (default: descending) |

## `list_qa_versions` — List Q&A version history

Page through the version history of a Q&A pair (DynamoDB-backed audit log), newest first. Each version names its `actor`: the person whose edit wrote it (`kind: "person"`, `userId`, `name`), or the run that did (`kind` sync, scrape, rescrape, ingestion, dedup, quality or run, with its `sessionId` and the `websiteId` or `documentId` it read). Use `restore_qa_version` to roll back to a previous version.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/qapair/{qaId}/versions`
- MCP tool / A2A skill: `list_qa_versions`
- Needs: clients READ (client), qa-editor READ (client)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `qaId` | path | string | yes | Q&A pair id (UUID or legacy 24-hex) |
| `limit` | query | integer | no | default 20 |
| `after` | query | string | no | Opaque cursor from the previous page's `nextCursor`; omit for the first page |

## `list_source_runs` — List a source's runs

The runs of one knowledge source, newest first: website scrapes and syncs or document ingestions, each with its steps, countable progress, who started it and its headline. `sourceId` is the source's `id` from `list_knowledge_sources` (the hotel's link to it). Page with `cursor` from `nextCursor`. Read a run's log lines with `get_source_run_logs`.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/source/{sourceId}/runs`
- MCP tool / A2A skill: `list_source_runs`
- Needs: document-source READ (client); client plan must include `document-source`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `sourceId` | path | string | yes | The source's `id` from `list_knowledge_sources`; format uuid |
| `limit` | query | integer | no | Page size; default 20 |
| `cursor` | query | string | no | `nextCursor` of the previous page; omit for the first |

## `publish_qa_pairs` — Publish Q&A pairs

Publish one or more Q&A pairs so the chat agent uses them. Returns succeeded and failed id lists. Use `unpublish_qa_pairs` to revert.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs/publish`
- MCP tool / A2A skill: `publish_qa_pairs`
- Needs: qa-editor WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `ids` | body | array of string | yes | Q&A pair ids |

## `reactivate_source` — Reactivate a knowledge source

Reactivate a disabled website or document source: its answers come back exactly as they were before `disable_source`, and syncs and rescrapes are allowed again. Returns `{ disabled, changed, answers }`; `changed: false` when it was already active. 502 when the knowledge base did not take the change (nothing recorded; retry).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/{kind}/{sourceId}/reactivate`
- MCP tool / A2A skill: `reactivate_source`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `kind` | path | enum: website \| document | yes | Source type |
| `sourceId` | path | string | yes | Website id or document id; format uuid |

## `remove_source` — Remove a knowledge source

Permanently remove a website (`kind: website`) or document (`kind: document`) source from a hotel. This deletes the source record and all associated scrape history. Irreversible.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/source/{kind}/{sourceId}`
- MCP tool / A2A skill: `remove_source`
- Needs: document-source WRITE (client); client plan must include `document-source`
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `kind` | path | enum: website \| document | yes | Source type |
| `sourceId` | path | string | yes | Website or document source id; format uuid |

## `rename_document_source` — Rename a document source

Change the display name of a document source without changing its content or version.

- REST: `PATCH https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}`
- MCP tool / A2A skill: `rename_document_source`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |
| `name` | body | string | yes | New display name |

## `reopen_knowledge_gap` — Reopen a knowledge gap

Move an answered or dismissed knowledge gap back to OPEN, clearing its resolution (the Q&A pair that answered it is kept). Reopening an open gap simply restamps it. Returns the gap as it now stands; 404 when it is not this hotel's.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/knowledgegap/{gapId}/reopen`
- MCP tool / A2A skill: `reopen_knowledge_gap`
- Needs: qa-editor WRITE (client); client plan must include `unanswered-questions`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `gapId` | path | string | yes | Knowledge gap id; format uuid |

## `restore_document_version` — Restore a document version

Restore a previous version of a document source by creating a new version with the old content, then re-queueing ingestion. Returns the new version number and whether re-ingestion was queued.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/document/{documentId}/version/{version}/restore`
- MCP tool / A2A skill: `restore_document_version`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `documentId` | path | string | yes | Document source id; format uuid |
| `version` | path | integer | yes | Version number to restore |

## `restore_qa_version` — Restore a Q&A version

Restore a Q&A pair to a previous version by copying its question and answer into a new update. Fetch the version history with `list_qa_versions` first to find the target version number.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapair/{qaId}/version/{version}/restore`
- MCP tool / A2A skill: `restore_qa_version`
- Needs: clients READ (client), qa-editor WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `qaId` | path | string | yes | Q&A pair id (UUID or legacy 24-hex) |
| `version` | path | integer | yes | Version number to restore |

## `scrape_website_source` — Scrape a website source now

Queue an immediate scrape of a specific website source. Returns whether the pipeline task was accepted (queued). 409 when the source is disabled (`reactivate_source` first). Follow the run with `get_website_scrape` or `list_source_runs`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/website/{websiteId}/scrape`
- MCP tool / A2A skill: `scrape_website_source`
- Needs: document-source EXECUTE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `websiteId` | path | string | yes | Website source id; format uuid |
| `language` | body | string | no | Language hint for this scrape run |

## `set_main_website_source` — Set the main website source

Mark a website source as the main knowledge website for the hotel. Only one website is the main source at a time. Use after adding multiple website sources to pick the canonical one.

- REST: `PATCH https://maison-labs.com/api/v1/client/{clientId}/source/website/{websiteId}/main`
- MCP tool / A2A skill: `set_main_website_source`
- Needs: document-source WRITE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `websiteId` | path | string | yes | Website source id; format uuid |

## `set_source_sync_schedule` — Set the sync schedule for a website source

Change the automatic sync schedule for a website source (NONE, DAILY, WEEKLY, BI_WEEKLY, MONTHLY). Requires Maison-staff `schedules` WRITE in addition to `document-source` WRITE. Use `add_website_source` to create new sources (schedule is always NONE on creation).

- REST: `PATCH https://maison-labs.com/api/v1/client/{clientId}/source/website/{websiteId}/schedule`
- MCP tool / A2A skill: `set_source_sync_schedule`
- Needs: document-source WRITE (client), schedules WRITE (global); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `websiteId` | path | string | yes | Website source id; format uuid |
| `syncSchedule` | body | enum: NONE \| DAILY \| WEEKLY \| BI_WEEKLY \| MONTHLY | yes | New automatic sync schedule |

## `sync_source` — Sync a knowledge source

Queue an on-demand sync for a website (`kind: website`) or document (`kind: document`) source. `language` applies to a website sync only. Returns whether the pipeline task was queued; 409 when the source is disabled (`reactivate_source` first).

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/source/{kind}/{sourceId}/sync`
- MCP tool / A2A skill: `sync_source`
- Needs: document-source EXECUTE (client); client plan must include `document-source`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `kind` | path | enum: website \| document | yes | Source type |
| `sourceId` | path | string | yes | Website or document source id; format uuid |
| `language` | body | string | no | Language for a website sync, e.g. en (ignored for documents) |

## `unpublish_qa_pairs` — Unpublish Q&A pairs

Unpublish one or more Q&A pairs so the agent no longer uses them without deleting them. Returns succeeded and failed id lists.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/qapairs/unpublish`
- MCP tool / A2A skill: `unpublish_qa_pairs`
- Needs: qa-editor WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `ids` | body | array of string | yes | Q&A pair ids |

## `update_qa_pair` — Update a Q&A pair

Change the question, the answer or both of an existing Q&A pair. A field left out keeps its stored value (the pair is read and merged for you); give at least one. 404 when the pair is unknown or deleted, 409 when its source is disabled.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/qapair/{qaId}`
- MCP tool / A2A skill: `update_qa_pair`
- Needs: qa-editor WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `qaId` | path | string | yes | Q&A pair id (UUID or legacy 24-hex) |
| `question` | body | string | no | New question text |
| `answer` | body | string | no | New answer text |
