# Agency API and MCP server

An agency can let its own AI assistant (Claude, Cursor, Copilot or any other MCP client) do setup work in SiteHome: list customers and their websites, add a customer, invite a contact person and connect a website. The assistant works through SiteHome's hosted MCP server, with the same rules as the app.

The API cannot edit or publish content. Publishing always stays a person's own action in SiteHome, after reviewing the exact changes.

## Create a key

1. In SiteHome, open **Customers** and choose **API keys for AI assistants**. Only agency administrators see it.
2. Give the key a name, choose what it may do and how long it stays valid (30, 90 or 365 days).
3. Copy the key. You see it only once; SiteHome keeps only a fingerprint of it.

The same screen shows the **MCP server address** and the agency's recent API actions. Revoking a key stops it right away. A key acts as the administrator who created it, and stops working when that person is no longer an administrator of the agency.

Each right unlocks these tools:

- **View customers and websites**: `list_customers`
- **Add customers**: `add_customer`
- **Invite contact persons**: `invite_contact`
- **Connect websites**: `inspect_sanity`, `connect_sanity`, `inspect_payload`, `connect_payload`, `inspect_wordpress`, `connect_wordpress`

## Add SiteHome to your assistant

The server uses Streamable HTTP. Send the key as `Authorization: Bearer <key>`.

Claude Code:

```text
claude mcp add --transport http sitehome <MCP server address> --header "Authorization: Bearer <key>"
```

Cursor (`.cursor/mcp.json`):

```json
{"mcpServers": {"sitehome": {"url": "<MCP server address>", "headers": {"Authorization": "Bearer <key>"}}}}
```

Keep the key out of shared files and repositories.

## What the tools do

- **list_customers**: customers with their websites (CMS, address, publishing off, test or live, last content read), contact persons and open invitations.
- **add_customer**: a new customer of the agency.
- **invite_contact**: an invitation for a customer's contact person, for all of the customer's websites, also ones added later. It returns a link that you share yourself; SiteHome sends no email. Nothing is granted until the person signs in with that email address and accepts.
- **inspect_…** and **connect_…**: the same connection as in the app, under one of your customers. The assistant needs a CMS key that you created yourself in the website's own CMS (see [Sanity](/docs/sanity), [Payload](/docs/payload), [WordPress](/docs/wordpress)). SiteHome never creates CMS keys or projects. As in the app, every content group that holds items is connected (at most 20) unless you name the groups to connect. A website that is already connected is never taken over, and a new connection starts with publishing off until a website administrator switches it on.

You become administrator of a website connected with your key, just as when you connect it in the app.

## Example prompts

```text
List my SiteHome customers and tell me which websites still have publishing off.
```

```text
Add the customer "Bakkerij de Korenschoof" in SiteHome and invite
info@korenschoof.nl as editor. Give me the invitation link.
```

```text
Connect the Payload website at https://cms.korenschoof.nl for the customer
"Bakkerij de Korenschoof". Leave out the "settings" collection.
```

## Errors

A refused call returns a code and never echoes the values you sent.

- **KEY_INVALID** (HTTP 401): the key is unknown, revoked or expired, or its creator is no longer an agency administrator.
- **SCOPE_DENIED**: the key may not use this tool. Create a key with that right.
- **RATE_LIMITED**: more than 30 calls in a minute or 300 in an hour with this key.
- **CUSTOMER_NOT_FOUND**: the customer does not exist in your agency.
- **ALREADY_CONNECTED** or **ADDRESS_IN_USE**: the website is already in SiteHome. Ask its administrator to invite you.
- **TOKEN_REJECTED**, **PLUGIN_MISSING**, **SOURCE_UNAVAILABLE**: the CMS did not accept the key or could not be reached. See the CMS's own page.
- **INVALID_INPUT:<field>**: a value is missing or not valid.

