# Feature plans and entitlements (Maison staff) — 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.

## `apply_plan_template` — Apply plan template

Sync the client's features to its assigned template: grant missing features, optionally revoke extras. Requires a template to be assigned — use `assign_plan_template` first. Maison-only.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/plan/apply`
- MCP tool / A2A skill: `apply_plan_template`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `prune` | body | boolean | no | When true, also revoke features the template does not include; default false |

## `assign_plan_template` — Assign plan template

Assign or remove the client's plan template (pass null to unassign). Assigning reconciles the client's features to the template with its overrides kept; unassigning clears the overrides and keeps the current features as direct entitlements. Maison-only. 404 when the template id is unknown.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/plan`
- MCP tool / A2A skill: `assign_plan_template`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `templateId` | body | string \| null | yes | Template id to assign, or null to unassign |

## `clear_client_feature_override` — Clear client feature override

Remove the client's override for one feature so it follows the plan template again, and reconcile its features. Maison-only. 404 when the client has no template, the feature is unknown or there is no override to clear.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/plan/override/{feature}`
- MCP tool / A2A skill: `clear_client_feature_override`
- Needs: client-feature-management WRITE (global)
- Destructive — confirm with the user first.

Inputs:

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

## `clear_client_gateway_fee` — Clear a client's gateway fee

Remove the hotel's own fee for one gateway so its plan's fee applies again. Maison-only. Returns whether a fee was removed (`removed: false` when there was none).

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/gatewayfee/{gateway}`
- MCP tool / A2A skill: `clear_client_gateway_fee`
- Needs: clients WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `gateway` | path | enum: stripe \| elavon | yes | Payment gateway |

## `clear_plan_gateway_fee` — Clear a plan's gateway fee

Remove a plan template's fee for one gateway; its clients without a fee of their own then pay none. Maison-only. Returns whether a fee was removed.

- REST: `DELETE https://maison-labs.com/api/v1/featureplan/{templateId}/gatewayfee/{gateway}`
- MCP tool / A2A skill: `clear_plan_gateway_fee`
- Needs: clients WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |
| `gateway` | path | enum: stripe \| elavon | yes | Payment gateway |

## `create_feature` — Create a feature

Add a feature to the catalogue that grants (policy rules) and plans name. Maison-only (`maison-roles` WRITE). 409 when the name is taken. Returns the new feature's id. A feature does nothing until code checks it; prefer existing features (`list_features`).

- REST: `POST https://maison-labs.com/api/v1/feature`
- MCP tool / A2A skill: `create_feature`
- Needs: maison-roles WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `name` | body | string | yes | Feature key, e.g. client-notes (what grants and plans name) |
| `description` | body | string | yes | What the feature gates |

## `create_plan_template` — Create plan template

Create a new plan template. Each feature is "on" or "locked" (a plain name means on); a feature left out is off. A Maison-only feature cannot be locked (400). Maison-only. 409 when a template with the same name already exists. Returns the created template.

- REST: `POST https://maison-labs.com/api/v1/featureplan`
- MCP tool / A2A skill: `create_plan_template`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `name` | body | string | yes | Template name |
| `description` | body | string \| null | no | Optional description |
| `locked_message` | body | string \| null | no | Copy a client sees on a locked feature; null or blank uses the default ("This feature isn't part of your plan. Contact Maison support to enable it.") |
| `features` | body | array of string \| object | no | Initial features with their states; default [] |

## `delete_plan_template` — Delete plan template

Soft-delete a plan template. Maison-only. Destructive. Refused (409) while any active client is on the template — move them to another plan first. 404 when already deleted or unknown.

- REST: `DELETE https://maison-labs.com/api/v1/featureplan/{templateId}`
- MCP tool / A2A skill: `delete_plan_template`
- Needs: client-feature-management WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |

## `deprecate_feature` — Deprecate a feature

Mark a catalogue feature deprecated: it leaves the catalogue offered to grants and plans and every client's eligibility. Nothing is deleted. Maison-only. 404 when unknown or already deprecated.

- REST: `DELETE https://maison-labs.com/api/v1/feature/{featureId}`
- MCP tool / A2A skill: `deprecate_feature`
- Needs: maison-roles WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `featureId` | path | string | yes | Feature id (from `list_features`); format uuid |

## `get_client_access` — 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). Shows which features are in the client's plan and what the caller may do with each.

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

Inputs:

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

## `get_client_plan` — 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`). Maison-only. 404 when no template is assigned.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/plan`
- MCP tool / A2A skill: `get_client_plan`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

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

## `get_gateway_fee_totals` — 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. Use for invoicing hotels. Maison-only.

- REST: `GET https://maison-labs.com/api/v1/gatewayfees/totals`
- MCP tool / A2A skill: `get_gateway_fee_totals`
- Needs: clients READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `from` | query | string | yes | First day, YYYY-MM-DD; format date |
| `to` | query | string | yes | Last day (inclusive), YYYY-MM-DD; format date |
| `clientId` | query | string | no | Only this client; format uuid |

## `get_plan_template` — Get plan template

Return one plan template by id, including its features with their states, locked message and counts. Maison-only. 404 when the template has been deleted or the id is unknown.

- REST: `GET https://maison-labs.com/api/v1/featureplan/{templateId}`
- MCP tool / A2A skill: `get_plan_template`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |

## `grant_client_feature` — Grant client feature

Add a single feature to the client's plan. Maison-only. On a client with a plan template the grant is kept as an "on" override, so a later reconcile does not undo it. Returns `{ feature }` on success. 404 when the feature name is not in the catalogue.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/feature`
- MCP tool / A2A skill: `grant_client_feature`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `feature` | body | string | yes | Feature name to grant |

## `grant_feature_to_children` — Grant feature to children

Add a feature to every child property's plan. Returns a per-child result array. Maison-only. Use for bulk-entitling a feature across an entire portfolio hierarchy.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/children/feature`
- MCP tool / A2A skill: `grant_feature_to_children`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `feature` | body | string | yes | Feature name to grant to all children |

## `list_client_features` — List client features

Returns the feature strings currently in the client's plan (layer-1 entitlements). Maison-only. Use `get_client_access` to see what the caller can do with each feature.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/features`
- MCP tool / A2A skill: `list_client_features`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

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

## `list_client_gateway_fees` — List a client's 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). Shown on the client's integrations page.

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

Inputs:

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

## `list_client_plan_history` — Client plan history

A client's plan history, newest first: the plans it was assigned and its feature override changes. Offset paging (`limit`, `offset`; the page carries `total`). Maison-only. 404 for an unknown client.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/plan/history`
- MCP tool / A2A skill: `list_client_plan_history`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `limit` | query | integer | no | Page size (1–200); default 50 |
| `offset` | query | integer | no | Rows to skip; default 0 |

## `list_plan_gateway_fees` — List a plan's gateway fees

The Maison fees a plan template sets per gateway; every client on the plan without its own fee pays these. Maison-only.

- REST: `GET https://maison-labs.com/api/v1/featureplan/{templateId}/gatewayfees`
- MCP tool / A2A skill: `list_plan_gateway_fees`
- Needs: clients READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |

## `list_plan_template_clients` — List a plan's clients

The active clients on one plan template (id and name), as the plan page lists them. Maison-only. 404 when the template is unknown or deleted. Use before `delete_plan_template` to see who must move first.

- REST: `GET https://maison-labs.com/api/v1/featureplan/{templateId}/clients`
- MCP tool / A2A skill: `list_plan_template_clients`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |

## `list_plan_template_history` — 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. A deleted plan keeps its history. Offset paging (`limit`, `offset`; the page carries `total`). Maison-only. 404 for an unknown id.

- REST: `GET https://maison-labs.com/api/v1/featureplan/{templateId}/history`
- MCP tool / A2A skill: `list_plan_template_history`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |
| `limit` | query | integer | no | Page size (1–200); default 50 |
| `offset` | query | integer | no | Rows to skip; default 0 |

## `list_plan_templates` — 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`). Maison-only. Use `get_plan_template` for a single template's full detail.

- REST: `GET https://maison-labs.com/api/v1/featureplans`
- MCP tool / A2A skill: `list_plan_templates`
- Needs: client-feature-management READ (global)
- Read-only.

Inputs:

_No inputs._

## `revoke_client_feature` — Revoke client feature

Remove a single feature from the client's plan. Maison-only. Destructive — the client will immediately lose access to anything gated on this feature. On a client with a plan template the revoke is kept as an "off" override.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/feature/{feature}`
- MCP tool / A2A skill: `revoke_client_feature`
- Needs: client-feature-management WRITE (global)
- Destructive — confirm with the user first.

Inputs:

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

## `set_client_feature_override` — 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. An override stays even when it equals the plan; `clear_client_feature_override` returns the feature to the plan default. Maison-only. 404 when the client has no template or the feature is unknown; 400 when locking a Maison-only feature.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/plan/override/{feature}`
- MCP tool / A2A skill: `set_client_feature_override`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `feature` | path | string | yes | Feature name |
| `state` | body | enum: on \| off \| locked | yes | The client's own state for the feature, winning over its plan |

## `set_client_features` — Set client features

Grant and/or revoke a set of features. Applies grants first, then revocations; on a client with a plan template each is kept as an override. Returns the resulting feature list. Maison-only.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/features`
- MCP tool / A2A skill: `set_client_features`
- Needs: client-feature-management WRITE (global)
- Composite of: `grant_client_feature`, `revoke_client_feature`.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `grant` | body | array of string | no | Feature names to add to the plan |
| `revoke` | body | array of string | no | Feature names to remove from the plan |

## `set_client_gateway_fee` — Set a client's gateway fee

Set the hotel's own Maison fee for one gateway, overriding its plan's fee; recorded on each later payment for invoicing. Maison-only. Returns the fee stored.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/gatewayfee/{gateway}`
- MCP tool / A2A skill: `set_client_gateway_fee`
- Needs: clients WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `gateway` | path | enum: stripe \| elavon | yes | Payment gateway |
| `feeType` | body | enum: percent \| flat | yes | percent of the payment, or a flat amount |
| `feeValue` | body | number | yes | Percent (e.g. 1.5) or flat amount in the payment currency's major unit |

## `set_client_plan` — 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. Returns the drift after applying (should be `inSync: true` when prune is true). A Maison-only feature cannot be locked (400). Maison-only.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/plan/set`
- MCP tool / A2A skill: `set_client_plan`
- Needs: client-feature-management WRITE (global)
- Composite of: `assign_plan_template`, `set_client_feature_override`, `apply_plan_template`.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `templateId` | body | string | yes | Template id to assign and apply; format uuid |
| `overrides` | body | array of string \| object | no | Client overrides to set on top of the template (existing ones are kept) |
| `prune` | body | boolean | no | When true, also revoke features the template does not include; default false |

## `set_plan_gateway_fee` — Set a plan's 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. Maison-only. Returns the fee stored.

- REST: `PUT https://maison-labs.com/api/v1/featureplan/{templateId}/gatewayfee/{gateway}`
- MCP tool / A2A skill: `set_plan_gateway_fee`
- Needs: clients WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |
| `gateway` | path | enum: stripe \| elavon | yes | Payment gateway |
| `feeType` | body | enum: percent \| flat | yes | percent of the payment, or a flat amount |
| `feeValue` | body | number | yes | Percent (e.g. 1.5) or flat amount in the payment currency's major unit |

## `update_feature` — Update a feature

Rename a catalogue feature or change its description; at least one field. Renaming changes the key every grant and plan uses, so only rename a feature nothing checks yet. Maison-only. 404 when unknown, 409 when the new name is taken. Returns the feature.

- REST: `PUT https://maison-labs.com/api/v1/feature/{featureId}`
- MCP tool / A2A skill: `update_feature`
- Needs: maison-roles WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `featureId` | path | string | yes | Feature id (from `list_features`); format uuid |
| `name` | body | string | no | Feature key, e.g. client-notes (what grants and plans name) |
| `description` | body | string | no | New description |

## `update_plan_template` — 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. Maison-only. 404 when the template is unknown or deleted; 400 for a locked Maison-only feature.

- REST: `PUT https://maison-labs.com/api/v1/featureplan/{templateId}`
- MCP tool / A2A skill: `update_plan_template`
- Needs: client-feature-management WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `templateId` | path | string | yes | Plan template id; format uuid |
| `name` | body | string | no | New template name |
| `description` | body | string \| null | no | New description |
| `locked_message` | body | string \| null | no | Copy a client sees on a locked feature; null or blank uses the default ("This feature isn't part of your plan. Contact Maison support to enable it.") |
| `features` | body | array of string \| object | no | Replacement features with their states (replaces the whole list) |
