# Developer apps — 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.

## `create_client_app` — Register a developer 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). Returns the app with its OAuth `clientId`; a confidential app's `clientSecret` is in THIS response only and can never be read again — hand it over at once. The app starts unverified. Recorded in the hotel's audit log. Call it with a user's own API key: an OAuth access token is refused (403 `credential_not_allowed`), so an app can never register further apps.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `name` | body | string | yes | Shown on the consent screen |
| `logoUri` | body | string \| null | no |  |
| `clientUri` | body | string \| null | no | The app's home page |
| `redirectUris` | body | array of string | yes |  |
| `requestedScopes` | body | array of string | yes | The scopes the app may ask people for (e.g. `clients:read`); never `admin` |
| `clientType` | body | enum: public \| confidential | yes |  |

## `get_client_app` — Get a developer app

One of a hotel's developer apps, revoked or not, with its registration, state, actors and usage. Never returns the client secret (it was shown once, by `create_client_app`). 404 when the app is not this hotel's.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/app/{appId}`
- MCP tool / A2A skill: `get_client_app`
- Needs: oauth-apps READ (client); client plan must include `oauth-apps`
- Read-only.

Inputs:

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

## `get_client_app_analytics` — Developer 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). 404 when the app is not this hotel's. Use `list_client_app_requests` for the calls themselves.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/app/{appId}/analytics`
- MCP tool / A2A skill: `get_client_app_analytics`
- Needs: oauth-apps READ (client); client plan must include `oauth-apps`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |
| `from` | query | string | no | First day (YYYY-MM-DD, UTC) |
| `to` | query | string | no | Last day, inclusive (YYYY-MM-DD, UTC); default today |

## `list_client_app_audit` — Developer app audit log

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. Returns `{ events, nextCursor }`. 404 when the app is not this hotel's.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/app/{appId}/audit`
- MCP tool / A2A skill: `list_client_app_audit`
- Needs: oauth-apps READ (client); client plan must include `oauth-apps`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |
| `cursor` | query | string | no | `nextCursor` of the previous page |
| `limit` | query | integer | no | Page size (1–100); default 50 |

## `list_client_app_requests` — Developer app request log

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. Filter by `from` / `to` (a day — `to` inclusive — or an ISO datetime — `to` exclusive) and `outcome` (`ok` | `error`). Returns `{ requests, nextCursor }`. 404 when the app is not this hotel's.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/app/{appId}/requests`
- MCP tool / A2A skill: `list_client_app_requests`
- Needs: oauth-apps READ (client); client plan must include `oauth-apps`
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |
| `from` | query | string \| string | no | A day (YYYY-MM-DD, UTC) or an ISO datetime |
| `to` | query | string \| string | no | A day (YYYY-MM-DD, UTC) or an ISO datetime |
| `outcome` | query | enum: ok \| error | no |  |
| `cursor` | query | string | no | `nextCursor` of the previous page |
| `limit` | query | integer | no | Page size (1–100); default 50 |

## `list_client_apps` — List a hotel's developer 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`. Revoked apps only with `includeRevoked`. Returns `{ apps, nextCursor, canWrite }`. Never returns a client secret. Use `get_client_app` for one app.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `includeRevoked` | query | boolean | no | Also list revoked apps; default false |
| `limit` | query | integer | no | Page size (1–200); default 100 |
| `cursor` | query | string | no | `nextCursor` of the previous page |

## `list_oauth_apps` — List every developer app

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. Filter by `search` (name), `type` and `status` (`active`, `suspended`, `revoked`); revoked apps join the list only with `includeRevoked` or `status: revoked`. Returns `{ apps, cursor, hasMore }`.

- REST: `GET https://maison-labs.com/api/v1/oauthapps`
- MCP tool / A2A skill: `list_oauth_apps`
- Needs: oauth-apps WRITE (global)
- Read-only.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `search` | query | string | no |  |
| `type` | query | enum: portal \| cimd \| dcr \| first_party | no |  |
| `status` | query | enum: active \| suspended \| revoked | no |  |
| `includeRevoked` | query | boolean | no | Revoked apps alongside the live ones; default false |
| `cursor` | query | string | no | `cursor` of the previous page |
| `limit` | query | integer | no | Page size (1–100); default 20 |

## `reinstate_client_app` — Reinstate a developer app

Bring a revoked app back. Consents stay revoked: people connect again. 404 when the app is not revoked (or not this hotel's); 409 `client_id_in_use` when another live app now holds its OAuth client id.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/app/{appId}/reinstate`
- MCP tool / A2A skill: `reinstate_client_app`
- Needs: oauth-apps WRITE (client); client plan must include `oauth-apps`

Inputs:

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

## `reinstate_oauth_app` — Reinstate any developer app

Maison staff: bring a revoked app back; consents stay revoked. 404 when the app is not revoked; 409 `client_id_in_use` when another live app now holds its OAuth client id.

- REST: `POST https://maison-labs.com/api/v1/oauthapp/{appId}/reinstate`
- MCP tool / A2A skill: `reinstate_oauth_app`
- Needs: oauth-apps WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |

## `revoke_client_app` — Revoke a developer 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). Returns `{ revoked, revokedGrants }`. `reinstate_client_app` brings the app back, but consents stay revoked. 404 when there is no live app.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/app/{appId}`
- MCP tool / A2A skill: `revoke_client_app`
- Needs: oauth-apps WRITE (client); client plan must include `oauth-apps`
- Destructive — confirm with the user first.

Inputs:

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

## `revoke_oauth_app` — Revoke any developer 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. Returns `{ revoked, revokedGrants }`. `reinstate_oauth_app` brings the app back without its consents. 404 when there is no live app.

- REST: `DELETE https://maison-labs.com/api/v1/oauthapp/{appId}`
- MCP tool / A2A skill: `revoke_oauth_app`
- Needs: oauth-apps WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |

## `suspend_oauth_app` — Suspend a developer 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. `unsuspend_oauth_app` lifts it; use `revoke_oauth_app` to stop the app for good. 404 when there is no live app.

- REST: `POST https://maison-labs.com/api/v1/oauthapp/{appId}/suspend`
- MCP tool / A2A skill: `suspend_oauth_app`
- Needs: oauth-apps WRITE (global)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |

## `unsuspend_oauth_app` — Lift an app's suspension

Maison staff: lift a suspension; the app works again with the consents it had. 404 when there is no live app.

- REST: `POST https://maison-labs.com/api/v1/oauthapp/{appId}/unsuspend`
- MCP tool / A2A skill: `unsuspend_oauth_app`
- Needs: oauth-apps WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |

## `unverify_oauth_app` — Remove an app's verified mark

Maison staff: remove a live app's verified mark; the consent screen warns again. 404 when there is no live app.

- REST: `POST https://maison-labs.com/api/v1/oauthapp/{appId}/unverify`
- MCP tool / A2A skill: `unverify_oauth_app`
- Needs: oauth-apps WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |

## `update_client_app` — Update a developer app

Change a live app's name, logo, home page, redirect URIs or scopes (send only what changes). The audit log keeps the before and after, and separate events when the scopes or redirect URIs change. People already connected keep the scopes they consented to. 404 when the app is not this hotel's or is revoked (reinstate it first).

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/app/{appId}`
- MCP tool / A2A skill: `update_client_app`
- Needs: oauth-apps WRITE (client); client plan must include `oauth-apps`

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |
| `name` | body | string | no |  |
| `logoUri` | body | string \| null | no |  |
| `clientUri` | body | string \| null | no |  |
| `redirectUris` | body | array of string | no |  |
| `requestedScopes` | body | array of string | no | The scopes the app may ask people for (e.g. `clients:read`); never `admin` |

## `verify_oauth_app` — Verify a developer app

Maison staff: mark a live app verified — the consent screen drops its "Unverified app" warning. A badge only; it never gates the flow. 404 when there is no live app. Recorded in the owning hotel's audit log.

- REST: `POST https://maison-labs.com/api/v1/oauthapp/{appId}/verify`
- MCP tool / A2A skill: `verify_oauth_app`
- Needs: oauth-apps WRITE (global)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `appId` | path | string | yes | Developer app id (`id` from list_client_apps); format uuid |
