Agent Skills / maison-webhooks
Install this skill alone, or read it below. Raw file: SKILL.md · references/tools.md
npx skills add https://docs.maison-labs.com --skill maison-webhooksMaison — Incoming webhooks
Manage a hotel's incoming webhooks: named inboxes its other systems (survey tools, CRMs, form builders, automation platforms) POST to, which start the agents that list them as inputs. Create, read, update and delete webhooks; add, rename, reveal, rotate and revoke their named keys; read the request log and delivery analytics. Use when wiring an external system to a hotel's agents or diagnosing why a sender's requests are refused.
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
- Give an external system an address to send events to —
create_webhook, then list it in the agent'sscope.webhookIds. - Issue a separate key per sending app, or replace a leaked one —
add_webhook_key,rotate_webhook_key,revoke_webhook_key. - Find out why a sender's requests fail or what it sent —
list_webhook_requests,get_webhook_request,get_webhook_analytics.
Before you start
- Send
x-api-key: <key>on every call; the key only reaches/api/v1. GET /api/v1/tools(or MCPtools/list) returns the tools THIS key may run — if a tool below is missing there, you lack a grant; do not retry it.- Client-scoped tools take
clientId(a hotel id).whoamilists the ids you can act on.
Tools
| Tool | What it does | REST | Needs |
|---|---|---|---|
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). | POST /api/v1/client/{clientId}/webhook/{webhookId}/key | update-client WRITE (client) |
create_webhook | Create an incoming webhook: a permanent address (/webhooks/{clientId}/{slug}) other systems POST to. | POST /api/v1/client/{clientId}/webhook | update-client WRITE (client) |
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). | DELETE /api/v1/client/{clientId}/webhook/{webhookId} | update-client WRITE (client); destructive |
get_webhook | Fetch one webhook (deleted ones included) with its address, settings, live keys (prefixes only — never a secret) and 24-hour request count. | GET /api/v1/client/{clientId}/webhook/{webhookId} | update-client READ (client); read-only |
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. | GET /api/v1/client/{clientId}/webhooks/analytics | update-client READ (client); read-only |
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). | GET /api/v1/client/{clientId}/webhook/{webhookId}/request/{requestId} | update-client READ (client); read-only |
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. | GET /api/v1/client/{clientId}/webhook/{webhookId}/requests | update-client READ (client); read-only |
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). | GET /api/v1/client/{clientId}/webhooks | update-client READ (client); read-only |
rename_webhook_key | Rename a live key with its current version; the secret does not change. | PUT /api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId} | update-client WRITE (client) |
reveal_webhook_key | Return a live key's secret (apiKey) again. | GET /api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}/secret | update-client WRITE (client) |
revoke_webhook_key | Revoke a key at once (soft: the request log keeps its name). | DELETE /api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId} | update-client WRITE (client); destructive |
rotate_webhook_key | Issue a new secret for a key, with the key's current version. | POST /api/v1/client/{clientId}/webhook/{webhookId}/key/{keyId}/rotate | update-client WRITE (client) |
update_webhook | Edit a webhook's name, description, enabled state, access mode or key rotation, with its current version. | PUT /api/v1/client/{clientId}/webhook/{webhookId} | update-client WRITE (client) |
Full input fields for every tool: references/tools.md.
Recommended workflows
Connect a sending system
create_webhookwith a name, aslug(the permanent address segment) and an access mode (api_keyby default). The response carries the webhook's address and its first key,Default— the key secret is returned once, so hand it to the sender straight away.- Configure the sender to POST to the address with the key in
X-Maison-Key(orAuthorization: Bearer). add_webhook_keyfor each further sending app, so the request log shows which app called.- Point an agent at it:
update_agentwith the webhook id inscope.webhookIds. list_webhook_requeststo confirm the first request arrived and was accepted.
Rotate or recover a key
reveal_webhook_keyreturns a live key's secret again (the reveal is recorded in the hotel's activity log; it rotates the key first when automatic rotation is due).rotate_webhook_keywith the key'sversionissues a new secret; the previous one keeps working for 24 hours so the sender can switch.revoke_webhook_keystops a key at once.
Tips
- Automatic rotation is lazy: a due key rotates on its next inbound request or reveal, never on a schedule.
- Request bodies are kept for 90 days; the log row stays after that with
body: null. - A webhook does nothing on its own — it starts work only when an enabled agent lists it as an input.
Pitfalls
- The address (
/webhooks/{clientId}/{slug}) never changes; to get a new one, create a new webhook. - The last live key of an
api_keywebhook cannot be revoked (409last_key); switch the webhook toopenaccess instead. - Switching a webhook to
openrevokes every live key; switching back creates a newDefaultkey, returned once. - Never log or echo a key secret back to the user beyond the one hand-off they asked for.
- Revealing, rotating and adding keys need WRITE on
update-client; a READ-only key can list webhooks and the request log but never sees a secret.
Calling conventions
Same tool, three transports — pick whichever your runtime already speaks. Always send x-api-key.
# REST
curl -X POST "https://maison-labs.com/api/v1/client/<clientId>/webhook/<webhookId>/key" -H "x-api-key: $MAISON_API_KEY" -H "content-type: application/json" -d '{ "name": … }'// MCP (https://maison-labs.com/mcp, Streamable HTTP): tools/call
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "add_webhook_key", "arguments": { "name": … } } }// 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": "add_webhook_key", "input": { "name": … } } }] } } }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.