# Connect an AI tool (/ai/connect)

To add Poltura to a tool, follow [Add the MCP server](/ai/install). This page is the detail behind it. Every AI
client connects to the same address:

```text
https://api.poltura.com/mcp
```

It speaks Streamable HTTP and keeps no session, so any client that supports remote MCP servers can use it. The
first call is usually `get_store_info`, which names the stores the connection reaches.

## Choose a mode [#choose-a-mode]

| Address                                  | Mode                                       | Use it for                                                                                                           |
| ---------------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| `https://api.poltura.com/mcp`            | Compact: 11 root tools                     | Most clients. The model finds an action with `find_actions`, reads it with `describe` and runs it with `run_action`. |
| `https://api.poltura.com/mcp?mode=typed` | Typed: one tool per record type and action | Clients that search a large tool list themselves.                                                                    |

Add `store_id=` to pin the connection to one store, for example `?mode=typed&store_id=0199f3a2-7c41-7d2e-9b1a-3c5e8f0a1b2c`. The
[MCP server](/ai/mcp) page lists the compact tools, and the [tool reference](/ai/reference) lists the typed ones.

## Sign in with OAuth [#sign-in-with-oauth]

Use this when a person connects an AI tool to their own stores. Claude, ChatGPT, Cursor, VS Code and Codex all
start it when you add the address. You see one page, in Poltura ID, and nothing else to set up:

1. The client reads `https://api.poltura.com/.well-known/oauth-protected-resource/mcp`, registers itself with
   Poltura ID and opens your browser at Poltura ID. If you are signed out, you sign in first.
2. The page is titled "Claude wants to access your stores", with the client's own name. It shows the client's name and a badge: **Verified**
   ("Poltura has checked who runs this app") or **Unverified**, with the address it sends you back to. Claude on the
   web, ChatGPT and Cursor are verified, and VS Code when it signs in through vscode.dev. Programs that listen on
   your own computer, such as Claude Code and Codex, always show as unverified, and the page says the client only
   sends you back to this computer.
3. **Stores.** Pick the stores it may reach. If you have three or fewer, they are already ticked on a first
   connection. "Grant access to all stores" includes stores you add later.
4. **Permissions.** They are grouped by area, and each row says what it lets the client do in plain words, with a
   tag: **Can only see**, **Can change**, **Can delete** and **Personal data** for permissions that expose customer
   details. A delete row always carries "Always asks you first". A line under the list states the promise for the
   whole connection: the client asks you before every delete and refund, and it can only act in the stores you
   choose until the access expires. Reading is offered first, and you switch on only what you want.
5. **Expiry.** 7 days, 30 days (the default), 90 days, 1 year, a date you pick, or no expiry. A reconnection shows
   the access you hold now and when it ends.
6. **Allow access** or **Deny**. If the choice adds a write permission, Poltura first asks you to **confirm it's
   you** with your passkey, authenticator code or password. Reconnecting with the same or fewer permissions asks for
   nothing more.
7. The client receives a short-lived access token and refreshes it by itself.

The connection never holds more than you do. Each call is allowed only if the grant allows it and your own
role in that store still allows it, so a team member who cannot refund never refunds through an agent either.
A call the grant does not cover answers `insufficient_scope` and lists the permission to ask for; the client
shows the page again with the new permission marked. See [Access errors](/api/errors).

If Poltura cannot connect the client, the page says why instead of the form: Poltura has blocked the client, your
organization has turned off third-party apps and agents, your account belongs to no organization, or the request
expired and you should connect again from the client.

Manage connections in Poltura ID under **Account, Connected apps**: narrow a connection, shorten its expiry
or revoke it. A store owner also sees them in the admin under **Settings, AI agents**, with each connection's last use
and recent changes, and can revoke from there. A revoked connection stops working within 30 seconds.

### Switches that stop a connection [#switches-that-stop-a-connection]

- **Allow third-party apps and agents** is an organisation switch. When an owner turns it off, every OAuth
  connection to that organisation stops until it is turned on again.
- **AI agents (MCP)** is a switch on each store, on by default. When an owner turns it off, every connection signed
  in with OAuth is refused on that store with `access_disabled`, on `/mcp` and on the admin API alike, reads included.
  API tokens and the official Poltura CLI are not affected.

## Use an API token [#use-an-api-token]

Use a token for scripts, CI and clients that cannot run a browser sign-in.

1. Create a token in Poltura ID under **Account, API tokens** (a personal `pltu_` token), or under your
   organisation's **API and access** (an organisation `plto_` token).
2. Pick its stores, permissions and expiry, then copy the secret. It is shown once.
3. Send it as a bearer token:

```bash
curl https://api.poltura.com/v1/tokens/verify -H "Authorization: Bearer $POLTURA_TOKEN"
```

A token limited to one store needs no `store_id`. A token that reaches several stores takes `?store_id=` or the
`store_id` argument on each tool. On `/mcp` the same permission checks, approvals and limits apply as for OAuth. A
first-party admin session token is not accepted on `/mcp`.

A token can also call the store's admin API directly. There its writes, deletes and refunds run at once, without
approval and without the daily caps. Only its permissions, stores, expiry, IP filter and points budget bound it.
The promise that deletes and refunds always ask a person first covers connected apps and agents, not API tokens and
not the official Poltura CLI, which acts as you in the same way.
See [How agents act on your store](/ai/agents#who-the-approval-covers).

### A token in each client [#a-token-in-each-client]

`poltura mcp connect <tool> --token-env` writes the entry below for you, reading the token from `POLTURA_TOKEN`.

| Client      | Send the token with                                                                     |
| ----------- | --------------------------------------------------------------------------------------- |
| Claude Code | `--header "Authorization: Bearer $POLTURA_TOKEN"` on `claude mcp add`                    |
| Cursor      | `"headers": { "Authorization": "Bearer ${env:POLTURA_TOKEN}" }` in the server entry     |
| VS Code     | A `headers` entry that reads the token from a `promptString` input, so it is never committed |
| Codex       | `bearer_token_env_var = "POLTURA_TOKEN"` in `[mcp_servers.poltura]`, and no login        |

## When something is refused [#when-something-is-refused]

| You see                  | Meaning                                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `401 invalid_token`      | The token or grant is unknown, expired, revoked or disabled. Sign in again or create a new token. |
| `403 insufficient_scope` | The grant lacks a permission. Consent again with the permission the response names.               |
| `403 permission_denied`  | Your role in that store does not allow it. Consent cannot change that.                            |
| `403 access_disabled`    | The organisation or the store switched agents off.                                                |
| `429 rate_limited`       | Too many calls. Wait for `Retry-After` seconds.                                                   |

The full list is on [Access errors](/api/errors) and, for errors inside a tool call, on
[How agents act on your store](/ai/agents#errors).
