# Incoming webhooks — 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.

## `add_webhook_key` — Add webhook key

Add a named key to an `api_key` webhook — one per sending app, so the request log shows which app called (at most 20 live). Returns the webhook, the key and its secret `apiKey`, returned once. 409 `webhook_has_no_key` for an `open` webhook, `key_name_taken`, `too_many_keys`, `webhook_deleted`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}/key`
- MCP tool / A2A skill: `add_webhook_key`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `name` | body | string | yes | Key name, e.g. the sender |

## `create_webhook` — Create webhook

Create an incoming webhook: a permanent address (`/webhooks/{clientId}/{slug}`) other systems POST to. With `api_key` access (the default) the response carries `apiKey`, the secret of its first key `Default` — returned only here, never again by a read; hand it to the sender at once (`reveal_webhook_key` recovers it later). 409 `slug_taken` when the slug is in use. Then list the webhook in an agent's `scope.webhookIds`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/webhook`
- MCP tool / A2A skill: `create_webhook`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `name` | body | string | yes | Display name |
| `slug` | body | string | yes | Address segment: lowercase letters, digits and hyphens; never changes |
| `description` | body | string | no |  |
| `enabled` | body | boolean | no | Accept requests (default true) |
| `authMode` | body | enum: open \| api_key | no | `api_key`: senders must present a key; `open`: anyone with the address may post; default "api_key" |
| `keyRotation` | body | enum: manual \| auto | no | `auto` rotates each key lazily once `keyRotationDays` have passed |
| `keyRotationDays` | body | integer | no | Days between automatic rotations; required when `keyRotation` is `auto` |

## `delete_webhook` — Delete webhook

Soft-delete a webhook: it stops accepting requests at once, its request log stays readable and its address stays reserved (it cannot be reused). Returns the deleted webhook. 409 `already_deleted`. To pause instead, `update_webhook` with `enabled: false`.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |

## `get_webhook` — Get webhook

Fetch one webhook (deleted ones included) with its address, settings, live keys (prefixes only — never a secret) and 24-hour request count. Use `reveal_webhook_key` for a key's secret, `list_webhook_requests` for its traffic.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |

## `get_webhook_analytics` — Get webhook analytics

Request counts for a hotel's webhooks over `from`–`to` (inclusive UTC days, at most 92): per day (total, accepted, rejected), per webhook (with the last request time) and per outcome. Optionally one webhook. Use `list_webhook_requests` for the individual requests.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `from` | query | string | yes | First day, YYYY-MM-DD (UTC) |
| `to` | query | string | yes | Last day, YYYY-MM-DD (UTC, inclusive) |
| `webhookId` | query | string | no | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |

## `get_webhook_request` — Get webhook request

One logged request with the caller's IP, its stored headers and body (bodies are kept 90 days; after that `body` is null). `truncated` is true when the body exceeded the stored limit. Use to see exactly what a sender posted.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `requestId` | path | string | yes | Request id from `list_webhook_requests`; format uuid |

## `list_webhook_requests` — List webhook requests

A webhook's request log, newest first: when, status, outcome (`accepted` or why it was refused), size, the key that authenticated it and how many agent runs it started. Filter by time, outcome or key; page with `cursor` from `nextCursor`. Bodies are not included — `get_webhook_request` returns one request's headers and body.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `from` | query | string \| string | no | Start: a UTC day (YYYY-MM-DD) or an ISO datetime |
| `to` | query | string \| string | no | End: a UTC day (inclusive) or an ISO datetime (exclusive) |
| `outcome` | query | enum: accepted \| rejected_auth \| rejected_disabled \| rejected_payload \| store_failed | no |  |
| `keyId` | query | string | no | Only requests authenticated by this key; format uuid |
| `cursor` | query | string | no | nextCursor from the previous page |
| `limit` | query | integer | no | Page size (1–100); default 50 |

## `list_webhooks` — List webhooks

List a hotel's incoming webhooks with their addresses, live keys (prefixes only, never secrets) and 24-hour request counts, plus how many are deleted (`deletedCount`). Set `deleted` to include soft-deleted webhooks. Use to find the id to put in an agent's `scope.webhookIds`; use `get_webhook` for one.

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

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `deleted` | query | boolean | no | Include soft-deleted webhooks; default false |

## `rename_webhook_key` — Rename webhook key

Rename a live key with its current `version`; the secret does not change. 409 `version_conflict`, `key_name_taken`, `key_revoked`.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}`
- MCP tool / A2A skill: `rename_webhook_key`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `keyId` | path | string | yes | Key id (a webhook's `keys[].id`); format uuid |
| `name` | body | string | yes | New key name |
| `version` | body | integer | yes | Current `version` from a prior read (optimistic concurrency; 409 when stale) |

## `reveal_webhook_key` — Reveal webhook key

Return a live key's secret (`apiKey`) again. Needs WRITE: the reveal is recorded in the hotel's activity log, and when the key's automatic rotation is due it is rotated first, so the secret returned is the new one (the old one keeps working for 24 hours). 409 `key_revoked`, `webhook_deleted`.

- REST: `GET https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}/secret`
- MCP tool / A2A skill: `reveal_webhook_key`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `keyId` | path | string | yes | Key id (a webhook's `keys[].id`); format uuid |

## `revoke_webhook_key` — Revoke webhook key

Revoke a key at once (soft: the request log keeps its name). Returns the revoked key and the webhook's remaining live keys. The last live key of an `api_key` webhook cannot be revoked (409 `last_key`) — switch the webhook to `open` with `update_webhook` instead. To replace a leaked key without downtime use `rotate_webhook_key`.

- REST: `DELETE https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}`
- MCP tool / A2A skill: `revoke_webhook_key`
- Needs: update-client WRITE (client)
- Destructive — confirm with the user first.

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `keyId` | path | string | yes | Key id (a webhook's `keys[].id`); format uuid |

## `rotate_webhook_key` — Rotate webhook key

Issue a new secret for a key, with the key's current `version`. Returns the webhook, the key and the new `apiKey` (returned once); the previous secret keeps working for 24 hours so the sender can switch. 409 `version_conflict`, `key_revoked`, `webhook_deleted`.

- REST: `POST https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}/rotate`
- MCP tool / A2A skill: `rotate_webhook_key`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `keyId` | path | string | yes | Key id (a webhook's `keys[].id`); format uuid |
| `version` | body | integer | yes | Current `version` from a prior read (optimistic concurrency; 409 when stale) |

## `update_webhook` — Update webhook

Edit a webhook's name, description, enabled state, access mode or key rotation, with its current `version`. The slug (address) never changes — sending `slug` is refused; create a new webhook for a new address. Switching to `open` revokes every live key; switching to `api_key` with no live key creates a `Default` key whose secret is returned once as `apiKey`. 409 `version_conflict` when stale, `webhook_deleted` for a deleted webhook.

- REST: `PUT https://maison-labs.com/api/v1/client/{clientId}/webhook/{webhookId}`
- MCP tool / A2A skill: `update_webhook`
- Needs: update-client WRITE (client)

Inputs:

| Field | Where | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `clientId` | path | string | yes | Client (hotel) id; format uuid |
| `webhookId` | path | string | yes | Webhook id (from `list_webhooks` or `create_webhook`); format uuid |
| `name` | body | string | no |  |
| `description` | body | string \| null | no | null clears it |
| `enabled` | body | boolean | no |  |
| `authMode` | body | enum: open \| api_key | no | `api_key`: senders must present a key; `open`: anyone with the address may post |
| `keyRotation` | body | enum: manual \| auto | no | `auto` rotates each key lazily once `keyRotationDays` have passed |
| `keyRotationDays` | body | integer | no | Days between automatic rotations; required when `keyRotation` is `auto` |
| `version` | body | integer | yes | Current `version` from a prior read (optimistic concurrency; 409 when stale) |
