---
name: "maison-apps"
description: "Manage the developer apps (OAuth clients) a hotel registers to call the Maison public API on its behalf: register, read, edit, revoke and reinstate apps, read each app's analytics, request log and audit trail; and, for Maison staff, verify, suspend, revoke or reinstate any app on the platform. Use when a hotel wants its own software or a partner to call the API, or when an app must be stopped. Tools: create_client_app, get_client_app, get_client_app_analytics, list_client_app_audit, list_client_app_requests, list_client_apps, list_oauth_apps, reinstate_client_app, reinstate_oauth_app, revoke_client_app, revoke_oauth_app, suspend_oauth_app, unsuspend_oauth_app, unverify_oauth_app, update_client_app, verify_oauth_app."
license: "Proprietary. Use with a Maison API key under the Maison terms of service."
metadata:
  author: "maison-labs"
  version: "1.0.0"
  category: "apps"
  api-base: "https://maison-labs.com"
  openapi: "https://maison-labs.com/api/v1/openapi.json"
  docs: "https://docs.maison-labs.com/skills/maison-apps/"
---

# Maison — Developer apps

Manage the developer apps (OAuth clients) a hotel registers to call the Maison public API on its behalf: register, read, edit, revoke and reinstate apps, read each app's analytics, request log and audit trail; and, for Maison staff, verify, suspend, revoke or reinstate any app on the platform. Use when a hotel wants its own software or a partner to call the API, or when an app must be stopped.

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

- Register an app for a hotel's own integration — `create_client_app`.
- See a hotel's apps or change one — `list_client_apps`, `get_client_app`, `update_client_app` (name, redirect URIs, scopes; live apps only).
- Stop an app and everyone connected through it — `revoke_client_app` (or `revoke_oauth_app` as Maison staff).
- Audit what an app did and who changed it — `get_client_app_analytics`, `list_client_app_requests`, `list_client_app_audit`.
- Review apps across the platform (Maison staff) — `list_oauth_apps`, `verify_oauth_app`, `suspend_oauth_app`; `reinstate_oauth_app` undoes `revoke_oauth_app`.

## 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 |
| --- | --- | --- | --- |
| `create_client_app` | Register an OAuth app for a hotel: name, redirect URIs (https, or http on localhost), the scopes it may ask for, and its type — `public` (PKCE, no secret) or `confidential` (a client secret). | `POST /api/v1/client/{clientId}/app` | oauth-apps WRITE (client); plan: `oauth-apps` |
| `get_client_app` | One of a hotel's developer apps, revoked or not, with its registration, state, actors and usage. | `GET /api/v1/client/{clientId}/app/{appId}` | oauth-apps READ (client); plan: `oauth-apps`; read-only |
| `get_client_app_analytics` | How an app is used over a window of UTC days (`from`/`to` inclusive, at most 92 days; default the last 30 days): requests per day split ok / error (error = status 400 and above), totals (requests, errors, median duration, active users, last use), the top tools, transports, people and sources, and consents (active now, granted / revoked per day). | `GET /api/v1/client/{clientId}/app/{appId}/analytics` | oauth-apps READ (client); plan: `oauth-apps`; read-only |
| `list_client_app_audit` | Every change to the app and every consent to it, newest first (`oauth_app_*` events: created, updated, scopes / redirects changed, revoked, grants revoked, reinstated, suspended, verified, consent granted / revoked), each with the actor's name and the detail. | `GET /api/v1/client/{clientId}/app/{appId}/audit` | oauth-apps READ (client); plan: `oauth-apps`; read-only |
| `list_client_app_requests` | The app's access log, newest first: every tool call and token exchange with its tool, transport, status, error code, duration, the person it ran as, the hotel it named and where it came from. | `GET /api/v1/client/{clientId}/app/{appId}/requests` | oauth-apps READ (client); plan: `oauth-apps`; read-only |
| `list_client_apps` | The OAuth apps a hotel registered to call the Maison API on its behalf, newest first: registration fields, verified / suspended state, who created, changed or revoked each, `last_used_at` and `requests_30d`. | `GET /api/v1/client/{clientId}/apps` | oauth-apps READ (client); plan: `oauth-apps`; read-only |
| `list_oauth_apps` | Maison staff: every OAuth app on the platform, oldest first — hotel portal apps, Claude / ChatGPT style CIMD and DCR registrations, and first-party apps — with the portal fields, `is_first_party` and the redirect URI count. | `GET /api/v1/oauthapps` | oauth-apps WRITE (global); read-only |
| `reinstate_client_app` | Bring a revoked app back. | `POST /api/v1/client/{clientId}/app/{appId}/reinstate` | oauth-apps WRITE (client); plan: `oauth-apps` |
| `reinstate_oauth_app` | Maison staff: bring a revoked app back; consents stay revoked. | `POST /api/v1/oauthapp/{appId}/reinstate` | oauth-apps WRITE (global) |
| `revoke_client_app` | Stop a hotel's app and everyone connected through it: the app is revoked (never deleted) and every person's consent to it is revoked too (access tokens are refused within 30 seconds). | `DELETE /api/v1/client/{clientId}/app/{appId}` | oauth-apps WRITE (client); plan: `oauth-apps`; destructive |
| `revoke_oauth_app` | Maison staff: revoke any app on the platform (any registration type) and every person's consent to it; access tokens are refused within 30 seconds. | `DELETE /api/v1/oauthapp/{appId}` | oauth-apps WRITE (global); destructive |
| `suspend_oauth_app` | Maison staff: a temporary stop that keeps consents — authorize and token requests are refused, and access tokens already issued run out within 15 minutes. | `POST /api/v1/oauthapp/{appId}/suspend` | oauth-apps WRITE (global); destructive |
| `unsuspend_oauth_app` | Maison staff: lift a suspension; the app works again with the consents it had. | `POST /api/v1/oauthapp/{appId}/unsuspend` | oauth-apps WRITE (global) |
| `unverify_oauth_app` | Maison staff: remove a live app's verified mark; the consent screen warns again. | `POST /api/v1/oauthapp/{appId}/unverify` | oauth-apps WRITE (global) |
| `update_client_app` | Change a live app's name, logo, home page, redirect URIs or scopes (send only what changes). | `PUT /api/v1/client/{clientId}/app/{appId}` | oauth-apps WRITE (client); plan: `oauth-apps` |
| `verify_oauth_app` | Maison staff: mark a live app verified — the consent screen drops its "Unverified app" warning. | `POST /api/v1/oauthapp/{appId}/verify` | oauth-apps WRITE (global) |

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

## Recommended workflows

### Register an app

1. `create_client_app` with a name, redirect URIs, the scopes it needs and its client type (public, or confidential with a client secret). Use a user's own API key: an OAuth access token is refused, so one app can never register another.
2. A confidential app's client secret is returned once in the response and can never be read again — hand it over straight away. There is no secret rotation: revoke the app and register a new one.
3. The app starts unverified; people can still approve it, with an "Unverified app" warning on the consent screen until Maison verifies it.

### Stop an app

1. `revoke_client_app` revokes the app and fans out to revoke every person's grant to it; access tokens are refused within 30 seconds.
2. `reinstate_client_app` brings the app back, but consents stay revoked — people connect again.
3. Maison staff use `suspend_oauth_app` for a temporary stop that keeps consents (tokens already issued run out within 15 minutes); `unsuspend_oauth_app` lifts it.

## Tips

- An app is never deleted: revoke is the delete, and every change and every access is kept for audits.
- Verification (`verify_oauth_app` / `unverify_oauth_app`) is a consent-screen badge only; it never gates the flow.
- Scopes cap what an app may ask for; each person's own grants still cap what a token can do.
- `list_oauth_apps` (Maison staff) includes Claude / ChatGPT style CIMD and DCR registrations; revoked apps join the list only with `includeRevoked` or `status: revoked`.

## Pitfalls

- Only a live app can be edited; reinstate a revoked app first.
- Reinstating answers 409 `client_id_in_use` when another live app now holds the same OAuth client id.
- Never log or echo a client secret.
- The per-hotel tools need the hotel's `oauth-apps` plan entitlement (403 `not_entitled`) as well as the `oauth-apps` grant; `canWrite` on the reads says whether you may change the hotel's apps.

## 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>/app" -H "x-api-key: $MAISON_API_KEY" -H "content-type: application/json" -d '{ "name": …, "redirectUris": …, "requestedScopes": …, "clientType": … }'
```

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

```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": "create_client_app", "input": { "name": …, "redirectUris": …, "requestedScopes": …, "clientType": … } } }] } } }
```

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.
