---
name: "maison-features"
description: "Control what a hotel's plan includes: read the caller's per-feature access snapshot for a hotel, list and change the hotel's entitled features (grant/revoke in one call), push a feature to every child property, and manage plan templates (create, update, assign, apply with optional pruning, drift report, per-client overrides, plan history), Maison's payment gateway fees and the feature catalogue. Each plan feature is on or locked (shown to the hotel locked, with the plan's message); a feature left out is off. Use when a hotel upgrades or downgrades, when a tool answers 403 not_entitled, or when standardising plans across a group. Tools: apply_plan_template, assign_plan_template, clear_client_feature_override, clear_client_gateway_fee, clear_plan_gateway_fee, create_feature, create_plan_template, delete_plan_template, deprecate_feature, get_client_access, get_client_plan, get_gateway_fee_totals, get_plan_template, grant_client_feature, grant_feature_to_children, list_client_features, list_client_gateway_fees,…"
license: "Proprietary. Use with a Maison API key under the Maison terms of service."
metadata:
  author: "maison-labs"
  version: "1.0.0"
  category: "features"
  api-base: "https://maison-labs.com"
  openapi: "https://maison-labs.com/api/v1/openapi.json"
  docs: "https://docs.maison-labs.com/skills/maison-features/"
---

# Maison — Feature plans and entitlements (Maison staff)

Control what a hotel's plan includes: read the caller's per-feature access snapshot for a hotel, list and change the hotel's entitled features (grant/revoke in one call), push a feature to every child property, and manage plan templates (create, update, assign, apply with optional pruning, drift report, per-client overrides, plan history), Maison's payment gateway fees and the feature catalogue. Each plan feature is on or locked (shown to the hotel locked, with the plan's message); a feature left out is off. Use when a hotel upgrades or downgrades, when a tool answers 403 not_entitled, or when standardising plans across a group.

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

- A tool returned `not_entitled` — `get_client_access` shows what the plan lacks; `set_client_features` fixes it.
- Roll out a plan — `set_client_plan` (assign + apply) or `apply_plan_template`.
- Who changed a plan and when — `list_plan_template_history` / `list_client_plan_history`.
- Invoice hotels for payments — `get_gateway_fee_totals` with `from`/`to` (optionally `clientId`).

## 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 |
| --- | --- | --- | --- |
| `apply_plan_template` | Sync the client's features to its assigned template: grant missing features, optionally revoke extras. | `POST /api/v1/client/{clientId}/plan/apply` | client-feature-management WRITE (global) |
| `assign_plan_template` | Assign or remove the client's plan template (pass null to unassign). | `PUT /api/v1/client/{clientId}/plan` | client-feature-management WRITE (global) |
| `clear_client_feature_override` | Remove the client's override for one feature so it follows the plan template again, and reconcile its features. | `DELETE /api/v1/client/{clientId}/plan/override/{feature}` | client-feature-management WRITE (global); destructive |
| `clear_client_gateway_fee` | Remove the hotel's own fee for one gateway so its plan's fee applies again. | `DELETE /api/v1/client/{clientId}/gatewayfee/{gateway}` | clients WRITE (global); destructive |
| `clear_plan_gateway_fee` | Remove a plan template's fee for one gateway; its clients without a fee of their own then pay none. | `DELETE /api/v1/featureplan/{templateId}/gatewayfee/{gateway}` | clients WRITE (global); destructive |
| `create_feature` | Add a feature to the catalogue that grants (policy rules) and plans name. | `POST /api/v1/feature` | maison-roles WRITE (global) |
| `create_plan_template` | Create a new plan template. | `POST /api/v1/featureplan` | client-feature-management WRITE (global) |
| `delete_plan_template` | Soft-delete a plan template. | `DELETE /api/v1/featureplan/{templateId}` | client-feature-management WRITE (global); destructive |
| `deprecate_feature` | Mark a catalogue feature deprecated: it leaves the catalogue offered to grants and plans and every client's eligibility. | `DELETE /api/v1/feature/{featureId}` | maison-roles WRITE (global); destructive |
| `get_client_access` | For every feature in the catalogue, returns whether the caller is entitled (plan), and whether they may read, write or execute it on this client; plus `lockedFeatures` (features the plan shows locked — offered as an upgrade, not hidden) and `lockedMessage` (the plan's locked copy, or the default). | `GET /api/v1/client/{clientId}/access` | clients READ (client); read-only |
| `get_client_plan` | Return the client's assigned plan template, its overrides (`[{ feature, state }]`), the effective entitlement (`effective`), the features shown locked (`locked`) and the drift: features the client has that its plan and overrides don't call for (`extra`), and effective features it is missing (`missing`). | `GET /api/v1/client/{clientId}/plan` | client-feature-management READ (global); read-only |
| `get_gateway_fee_totals` | Paid payments and the Maison fees recorded on them for an inclusive date range, per client, gateway and currency (amounts in minor units), optionally for one client. | `GET /api/v1/gatewayfees/totals` | clients READ (global); read-only |
| `get_plan_template` | Return one plan template by id, including its features with their states, locked message and counts. | `GET /api/v1/featureplan/{templateId}` | client-feature-management READ (global); read-only |
| `grant_client_feature` | Add a single feature to the client's plan. | `POST /api/v1/client/{clientId}/feature` | client-feature-management WRITE (global) |
| `grant_feature_to_children` | Add a feature to every child property's plan. | `POST /api/v1/client/{clientId}/children/feature` | client-feature-management WRITE (global) |
| `list_client_features` | Returns the feature strings currently in the client's plan (layer-1 entitlements). | `GET /api/v1/client/{clientId}/features` | client-feature-management READ (global); read-only |
| `list_client_gateway_fees` | Maison's fee per payment gateway (stripe, elavon) for one hotel as it applies: the client's own fee, else its plan's, else none (`source`: client \| template \| null). | `GET /api/v1/client/{clientId}/gatewayfees` | update-client READ (client); read-only |
| `list_client_plan_history` | A client's plan history, newest first: the plans it was assigned and its feature override changes. | `GET /api/v1/client/{clientId}/plan/history` | client-feature-management READ (global); read-only |
| `list_plan_gateway_fees` | The Maison fees a plan template sets per gateway; every client on the plan without its own fee pays these. | `GET /api/v1/featureplan/{templateId}/gatewayfees` | clients READ (global); read-only |
| `list_plan_template_clients` | The active clients on one plan template (id and name), as the plan page lists them. | `GET /api/v1/featureplan/{templateId}/clients` | client-feature-management READ (global); read-only |
| `list_plan_template_history` | A plan template's change history, newest first: its own field and feature-state changes, its creation and deletion, and clients assigned to it or moved off it. | `GET /api/v1/featureplan/{templateId}/history` | client-feature-management READ (global); read-only |
| `list_plan_templates` | List all non-deleted plan templates with their features (`[{ feature, state }]`, state "on" or "locked"), locked message and counts (`client_count`, `on_count`, `locked_count`). | `GET /api/v1/featureplans` | client-feature-management READ (global); read-only |
| `revoke_client_feature` | Remove a single feature from the client's plan. | `DELETE /api/v1/client/{clientId}/feature/{feature}` | client-feature-management WRITE (global); destructive |
| `set_client_feature_override` | Set the client's own state for one feature ("on", "off" or "locked"), winning over its plan template, and reconcile its features. | `PUT /api/v1/client/{clientId}/plan/override/{feature}` | client-feature-management WRITE (global) |
| `set_client_features` | Grant and/or revoke a set of features. | `PUT /api/v1/client/{clientId}/features` | client-feature-management WRITE (global); composite of `grant_client_feature`, `revoke_client_feature` |
| `set_client_gateway_fee` | Set the hotel's own Maison fee for one gateway, overriding its plan's fee; recorded on each later payment for invoicing. | `PUT /api/v1/client/{clientId}/gatewayfee/{gateway}` | clients WRITE (global) |
| `set_client_plan` | Assign a template, set the client's overrides (each `{ feature, state }` with state "on", "off" or "locked"; a plain name means on) and apply it. | `PUT /api/v1/client/{clientId}/plan/set` | client-feature-management WRITE (global); composite of `assign_plan_template`, `set_client_feature_override`, `apply_plan_template` |
| `set_plan_gateway_fee` | Set a plan template's Maison fee for one gateway; it applies to every client on the plan that has no fee of its own. | `PUT /api/v1/featureplan/{templateId}/gatewayfee/{gateway}` | clients WRITE (global) |
| `update_feature` | Rename a catalogue feature or change its description; at least one field. | `PUT /api/v1/feature/{featureId}` | maison-roles WRITE (global) |
| `update_plan_template` | Update the name, description, locked message or features (with their states) of an existing plan template; changing the features reconciles every client on the plan. | `PUT /api/v1/featureplan/{templateId}` | client-feature-management WRITE (global) |

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

## Recommended workflows

### Upgrade a hotel to a plan template

1. `list_plan_templates` → pick the template id.
2. `set_client_plan` with `templateId` and `prune: false` (keeps extra grants) or `true` (exact match).
3. `get_client_plan` to review drift afterwards.

### Give one hotel an exception to its plan

1. `set_client_feature_override` with `state` `"on"`, `"off"` or `"locked"` — it wins over the plan and the hotel's features are reconciled at once.
2. `clear_client_feature_override` returns the feature to the plan default.
3. `get_client_plan` lists the hotel's overrides and its `locked` features.

## Tips

- `get_client_access` is the fastest way to answer 'what can I do for this hotel?' — it merges plan entitlement with the caller's grants.
- Plan history pages by offset (`limit`, `offset`) and returns `total`.
- Gateway fees: a client's own fee wins over its plan's fee (`source` says which applies); setting and clearing fees is Maison staff only (global `clients` WRITE).
- `create_feature` / `update_feature` / `deprecate_feature` change the platform feature catalogue (global `maison-roles` WRITE) — prefer an existing feature.

## Pitfalls

- All plan tools except `get_client_access` need the GLOBAL `client-feature-management` grant (Maison staff).
- `apply_plan_template` with `prune: true` revokes features not in the template — confirm with the user first.
- A Maison-only feature cannot be locked (400) — use off instead.
- `delete_plan_template` answers 409 while any hotel is on the plan — `list_plan_template_clients` lists them; move them to another plan first.

## 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>/plan/apply" -H "x-api-key: $MAISON_API_KEY" -H "content-type: application/json" -d '{}'
```

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

```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": "apply_plan_template", "input": {} } }] } } }
```

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.
