---
name: "maison-knowledge"
description: "Manage what a hotel's concierge knows: website and document knowledge sources (add, sync, rescrape, disable/reactivate, follow a run and its log and report, rediscover booking links, versions, download, large uploads), curated Q&A pairs (add, update, publish, unpublish, delete, version history), unanswered-question gaps (list, detect, answer, dismiss, reopen) and knowledge quality runs (deduplicate, consistency check, their reports). Use when a hotel's answers are missing, wrong or stale, or when adding new content. Tools: add_document_source, add_document_version, add_qa_pairs, add_website_source, answer_knowledge_gap, check_knowledge_consistency, create_document_upload, deduplicate_knowledge, delete_document_version, delete_qa_pairs, detect_knowledge_gaps, disable_source, discover_booking_links, dismiss_knowledge_gap, download_document, get_knowledge_quality_run, get_qa_counts, get_qa_pair, get_source_run_logs, get_source_run_report, get_website_scrape, list_document_versions, list_knowledge_gaps,…"
license: "Proprietary. Use with a Maison API key under the Maison terms of service."
metadata:
  author: "maison-labs"
  version: "1.0.0"
  category: "knowledge"
  api-base: "https://maison-labs.com"
  openapi: "https://maison-labs.com/api/v1/openapi.json"
  docs: "https://docs.maison-labs.com/skills/maison-knowledge/"
---

# Maison — Knowledge base

Manage what a hotel's concierge knows: website and document knowledge sources (add, sync, rescrape, disable/reactivate, follow a run and its log and report, rediscover booking links, versions, download, large uploads), curated Q&A pairs (add, update, publish, unpublish, delete, version history), unanswered-question gaps (list, detect, answer, dismiss, reopen) and knowledge quality runs (deduplicate, consistency check, their reports). Use when a hotel's answers are missing, wrong or stale, or when adding new content.

Part of the Maison public API. Read the `maison-public-api` skill first for authentication, discovery and transport details (`https://docs.maison-labs.com/skills/maison-public-api/SKILL.md`).

## When to use

- Add a page or site to a hotel's knowledge — `add_website_source`.
- Upload a PDF/brochure/policy — `add_document_source` (≤ 64 KiB inline as `bytesBase64`).
- Upload a document over 64 KiB — `create_document_upload`, PUT the file to its `url` with its `headers`, then `add_document_source` (or `add_document_version`) with the `uploadId`.
- Take a source out of the agent's knowledge without losing it — `disable_source`; undo with `reactivate_source`.
- Follow a scrape, sync or ingestion — `list_source_runs` (the source's `id` from `list_knowledge_sources`), then `get_source_run_logs` with `after: lastSeq` until `finished`; `get_website_scrape` for the step-by-step view of a website run.
- Read what a finished website scrape or sync changed — `get_source_run_report` (the run's `sessionId`): the report inline, or `format: "pdf"` for a download link to the PDF the report email attaches.
- A hotel changed booking engine — `discover_booking_links` (no full rescrape).
- Fix a specific wrong answer — `list_qa_pairs` with `search`, then `update_qa_pair`.
- Find what guests asked that the concierge could not answer — `list_knowledge_gaps`, then `answer_knowledge_gap`.
- Find conflicting or duplicate answers — `check_knowledge_consistency` or `deduplicate_knowledge` (each returns a `sessionId`), then `get_knowledge_quality_run` until `finished` for the report; `list_knowledge_quality_runs` for past runs.

## Before you start

1. Send `x-api-key: <key>` on every call; the key only reaches `/api/v1`.
2. `GET /api/v1/tools` (or MCP `tools/list`) returns the tools THIS key may run — if a tool below is missing there, you lack a grant; do not retry it.
3. Client-scoped tools take `clientId` (a hotel id). `whoami` lists the ids you can act on.

## Tools

| Tool | What it does | REST | Needs |
| --- | --- | --- | --- |
| `add_document_source` | Add a document (PDF, DOCX, etc.) as a knowledge source and queue its ingestion. | `POST /api/v1/client/{clientId}/source/document` | document-source WRITE (client); plan: `document-source` |
| `add_document_version` | Upload a new version of an existing document source and queue its ingestion. | `POST /api/v1/client/{clientId}/source/document/{documentId}/version` | document-source WRITE (client); plan: `document-source` |
| `add_qa_pairs` | Create one or more Q&A pairs. | `POST /api/v1/client/{clientId}/qapairs` | qa-editor WRITE (client) |
| `add_website_source` | Add a website as a knowledge source for a hotel and queue its first scrape. | `POST /api/v1/client/{clientId}/source/website` | document-source WRITE (client); plan: `document-source`; composite of `set_source_sync_schedule` |
| `answer_knowledge_gap` | Answer an open knowledge gap and mark it ANSWERED. | `POST /api/v1/client/{clientId}/knowledgegap/{gapId}/answer` | qa-editor WRITE (client); plan: `unanswered-questions` |
| `check_knowledge_consistency` | Start a consistency-check run: a pipeline task that checks Q&A pairs for answers that conflict or are ambiguous. | `POST /api/v1/client/{clientId}/qapairs/consistencycheck` | consistency-check EXECUTE (client); plan: `consistency-check` |
| `create_document_upload` | Get a presigned URL to upload a document larger than the 64 KiB inline limit (up to 50 MiB). | `POST /api/v1/client/{clientId}/source/document/upload` | document-source WRITE (client); plan: `document-source` |
| `deduplicate_knowledge` | 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`. | `POST /api/v1/client/{clientId}/qapairs/deduplicate` | deduplication EXECUTE (client); plan: `deduplication` |
| `delete_document_version` | Soft-delete a non-current version of a document. | `DELETE /api/v1/client/{clientId}/source/document/{documentId}/version/{version}` | document-source WRITE (client); plan: `document-source`; destructive |
| `delete_qa_pairs` | Soft-delete one or more Q&A pairs by id. | `POST /api/v1/client/{clientId}/qapairs/delete` | qa-editor WRITE (client); destructive |
| `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. | `POST /api/v1/client/{clientId}/knowledgegaps/detect` | clients EXECUTE (client); plan: `unanswered-questions` |
| `disable_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. | `POST /api/v1/client/{clientId}/source/{kind}/{sourceId}/disable` | document-source WRITE (client); plan: `document-source`; destructive |
| `discover_booking_links` | Re-run booking-link discovery for one website source without a full re-scrape — after the hotel changed booking engine, say. | `POST /api/v1/client/{clientId}/source/website/{websiteId}/bookinglinks` | document-source EXECUTE (client); plan: `document-source` |
| `dismiss_knowledge_gap` | Mark an open knowledge gap as DISMISSED without creating a Q&A pair; 404 unknown, 409 already resolved. | `POST /api/v1/client/{clientId}/knowledgegap/{gapId}/dismiss` | qa-editor WRITE (client); plan: `unanswered-questions` |
| `download_document` | Download a document source. | `GET /api/v1/client/{clientId}/source/document/{documentId}/download` | document-source READ (client); plan: `document-source`; read-only |
| `get_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. | `GET /api/v1/client/{clientId}/knowledge/quality/run/{sessionId}` | test-session READ (client); plan: `test-session`; read-only |
| `get_qa_counts` | Active, deleted and total Q&A pair counts for a hotel's knowledge base. | `GET /api/v1/client/{clientId}/qapairs/counts` | qa-editor READ (client); read-only |
| `get_qa_pair` | Fetch a single Q&A pair by id. | `GET /api/v1/client/{clientId}/qapair/{qaId}` | clients READ (client); read-only |
| `get_source_run_logs` | 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). | `GET /api/v1/client/{clientId}/source/run/{sessionId}/logs` | document-source READ (client); plan: `document-source`; read-only |
| `get_source_run_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`. | `GET /api/v1/client/{clientId}/source/run/{sessionId}/report` | document-source READ (client); plan: `document-source`; read-only |
| `get_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. | `GET /api/v1/client/{clientId}/source/website/{websiteId}/scrape` | document-source READ (client); plan: `document-source`; read-only |
| `list_document_versions` | List all versions of a document source, newest first. | `GET /api/v1/client/{clientId}/source/document/{documentId}/versions` | document-source READ (client); plan: `document-source`; read-only |
| `list_knowledge_gaps` | List unanswered questions detected in guest chat sessions, ordered by occurrence count. | `GET /api/v1/client/{clientId}/knowledgegaps` | clients READ (client); plan: `unanswered-questions`; read-only |
| `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). | `GET /api/v1/client/{clientId}/knowledge/quality/runs` | test-session READ (client); plan: `test-session`; read-only |
| `list_knowledge_sources` | List every website and document knowledge source attached to a hotel, including scrape/sync status, schedule and current version. | `GET /api/v1/client/{clientId}/sources` | document-source READ (client); plan: `document-source`; read-only |
| `list_qa_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. | `GET /api/v1/client/{clientId}/qapairs` | clients READ (client); read-only |
| `list_qa_versions` | Page through the version history of a Q&A pair (DynamoDB-backed audit log), newest first. | `GET /api/v1/client/{clientId}/qapair/{qaId}/versions` | clients READ (client), qa-editor READ (client); read-only |
| `list_source_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. | `GET /api/v1/client/{clientId}/source/{sourceId}/runs` | document-source READ (client); plan: `document-source`; read-only |
| `publish_qa_pairs` | Publish one or more Q&A pairs so the chat agent uses them. | `POST /api/v1/client/{clientId}/qapairs/publish` | qa-editor WRITE (client) |
| `reactivate_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. | `POST /api/v1/client/{clientId}/source/{kind}/{sourceId}/reactivate` | document-source WRITE (client); plan: `document-source` |
| `remove_source` | Permanently remove a website (`kind: website`) or document (`kind: document`) source from a hotel. | `DELETE /api/v1/client/{clientId}/source/{kind}/{sourceId}` | document-source WRITE (client); plan: `document-source`; destructive |
| `rename_document_source` | Change the display name of a document source without changing its content or version. | `PATCH /api/v1/client/{clientId}/source/document/{documentId}` | document-source WRITE (client); plan: `document-source` |
| `reopen_knowledge_gap` | Move an answered or dismissed knowledge gap back to OPEN, clearing its resolution (the Q&A pair that answered it is kept). | `POST /api/v1/client/{clientId}/knowledgegap/{gapId}/reopen` | qa-editor WRITE (client); plan: `unanswered-questions` |
| `restore_document_version` | Restore a previous version of a document source by creating a new version with the old content, then re-queueing ingestion. | `POST /api/v1/client/{clientId}/source/document/{documentId}/version/{version}/restore` | document-source WRITE (client); plan: `document-source` |
| `restore_qa_version` | Restore a Q&A pair to a previous version by copying its question and answer into a new update. | `POST /api/v1/client/{clientId}/qapair/{qaId}/version/{version}/restore` | clients READ (client), qa-editor WRITE (client) |
| `scrape_website_source` | Queue an immediate scrape of a specific website source. | `POST /api/v1/client/{clientId}/source/website/{websiteId}/scrape` | document-source EXECUTE (client); plan: `document-source` |
| `set_main_website_source` | Mark a website source as the main knowledge website for the hotel. | `PATCH /api/v1/client/{clientId}/source/website/{websiteId}/main` | document-source WRITE (client); plan: `document-source` |
| `set_source_sync_schedule` | Change the automatic sync schedule for a website source (NONE, DAILY, WEEKLY, BI_WEEKLY, MONTHLY). | `PATCH /api/v1/client/{clientId}/source/website/{websiteId}/schedule` | document-source WRITE (client), schedules WRITE (global); plan: `document-source` |
| `sync_source` | Queue an on-demand sync for a website (`kind: website`) or document (`kind: document`) source. | `POST /api/v1/client/{clientId}/source/{kind}/{sourceId}/sync` | document-source EXECUTE (client); plan: `document-source` |
| `unpublish_qa_pairs` | Unpublish one or more Q&A pairs so the agent no longer uses them without deleting them. | `POST /api/v1/client/{clientId}/qapairs/unpublish` | qa-editor WRITE (client) |
| `update_qa_pair` | Change the question, the answer or both of an existing Q&A pair. | `PUT /api/v1/client/{clientId}/qapair/{qaId}` | qa-editor WRITE (client) |

Full input fields for every tool: [references/tools.md](references/tools.md).

## Recommended workflows

### Add content and make it live

1. `add_website_source` (domain URL) or `add_document_source` (name, filename, mimeType, and `bytesBase64` or an `uploadId` from `create_document_upload`).
2. Ingestion is queued; `list_knowledge_sources` shows `scrapeStatus` / `lastSuccessfulIngestAt` when it has landed.
3. For hand-written facts use `add_qa_pairs` — new pairs are published on creation; `unpublish_qa_pairs` takes one out of answers without deleting it (unpublished pairs are not used).

### Close knowledge gaps

1. `detect_knowledge_gaps` answers 202 `{ queued: true }` at once; poll `list_knowledge_gaps` (its `lastSession`) with `status: "OPEN"`.
2. For each gap: `answer_knowledge_gap` with an `answer` (optionally a reworded `question`) to create the Q&A pair — live at once, new pairs are stored published — or with `qaId` to link a pair that already answers it (nothing is created, its publish state is unchanged); `dismiss_knowledge_gap` when it is noise (`gapIds` dismisses many in one call); `reopen_knowledge_gap` to undo either.
3. Run `deduplicate_knowledge` and `check_knowledge_consistency` after a large import; read each report with `get_knowledge_quality_run` and fix the conflicting pairs with `update_qa_pair`.

## Tips

- Q&A versions are kept: `list_qa_versions` + `restore_qa_version` undo a bad edit.
- `set_main_website_source` marks the website that also drives the preview site; `set_source_sync_schedule` is Maison-staff only.

## Pitfalls

- A `sync_source` on a document re-ingests the stored file; a `scrape_website_source` fetches the live site. Do not confuse them.
- `list_qa_pairs` can return 503 when semantic search is down — retry later rather than concluding the knowledge base is empty.
- Inline base64 uploads over 64 KiB are rejected with 413 `too_large` — use `create_document_upload`.
- `download_document` returns `bytesBase64` up to 64 KiB, else `downloadUrl` + `expiresAt` (15 minutes).
- `update_qa_pair` takes `question`, `answer` or both; the field left out keeps its stored value.
- `sync_source` / `scrape_website_source` / `discover_booking_links` answer 409 for a disabled source — `reactivate_source` first.
- `add_website_source` with a `syncSchedule` other than NONE needs Maison-staff `schedules` WRITE (403 otherwise, nothing created).

## Calling conventions

Same tool, three transports — pick whichever your runtime already speaks. Always send `x-api-key`.

```bash
# REST
curl -X POST "https://maison-labs.com/api/v1/client/<clientId>/source/document" -H "x-api-key: $MAISON_API_KEY" -H "content-type: application/json" -d '{ "name": …, "filename": …, "mimeType": … }'
```

```json
// MCP (https://maison-labs.com/mcp, Streamable HTTP): tools/call
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "add_document_source", "arguments": { "name": …, "filename": …, "mimeType": … } } }
```

```json
// A2A (https://maison-labs.com/a2a): tasks/send with a data part
{ "jsonrpc": "2.0", "id": 1, "method": "tasks/send", "params": { "message": { "role": "user", "parts": [{ "type": "data", "data": { "tool": "add_document_source", "input": { "name": …, "filename": …, "mimeType": … } } }] } } }
```

Errors are the same everywhere: `401` no/invalid key; `403 forbidden` with `details.missing` (the grants you lack — fall back to the composite's parts or stop); `403 not_entitled` (the hotel's plan lacks the feature); `404`, `409`, `413 too_large`, `503`. Inputs that fail validation return `400 invalid_input` with the Zod issues — fix and retry once.
