# Connect an AI assistant

> Let Claude, ChatGPT, Cursor or any MCP host run your Borga account from a conversation, with the permissions you choose.

Source: https://docs.borga.is/mcp

Borga runs an [MCP](https://modelcontextprotocol.io) server at `https://mcp.borga.is/mcp`. Connect it to an assistant and the assistant can send payment links, look up customers, subscriptions and invoices, manage products, prices and webhook endpoints, change settings, read the audit log and report usage on your merchant account, all through the same API the dashboard uses. You decide what it may see and do; Borga enforces it on every call.

This page is for merchants connecting an assistant. If you are building an assistant or another third-party client, see [OAuth for third-party clients](/oauth).

## How a connection works

1. Add `https://mcp.borga.is/mcp` to your assistant (snippets below).
2. The assistant sends you to Borga, where you sign in with Kenni.is as on the dashboard.
3. You pick the merchant and the mode, tick the permissions the assistant may use, and optionally give the connection a label such as "Claude on my laptop".
4. The assistant receives tokens bound to exactly that: one merchant, one mode, the permissions you ticked. It cannot switch merchant or mode later, and it never sees an API key.

Some rules that hold for every connection:

- **One connection is one merchant in one mode.** To use both test and live, connect twice.
- **Test mode never moves money.** Live mode is a separate, conscious choice on the consent screen and is only offered once your merchant has live access.
- **Money permissions are never preselected.** Refunds and off-session charges have to be ticked on purpose, and only an owner or admin can grant them.
- **Revoke any time** under **Settings, Connected apps** on the merchant, or on your account page for every merchant you belong to. Every call that touches your data is refused as soon as you revoke; the server may keep listing the tools for up to a minute. Removing a member from the merchant revokes their connections too.

## Add Borga to your assistant

_claude.ai and Claude Desktop_

Open **Settings**, then **Connectors**, choose **Add custom connector**, name it Borga and paste the URL:

```
https://mcp.borga.is/mcp
```

Claude discovers the sign-in flow from the server. Click **Connect** and approve the connection in Borga.

_Claude Code_

```bash
claude mcp add --transport http borga https://mcp.borga.is/mcp
```

Then run `/mcp` inside Claude Code and choose Borga to sign in. The browser opens Borga's consent screen; Claude Code listens on a loopback port for the return.

_ChatGPT_

Turn on **Developer mode** under **Settings, Apps and connectors, Advanced**, then **Create** a connector with the server URL `https://mcp.borga.is/mcp` and OAuth as the authentication. ChatGPT registers itself with Borga; the first use sends you through the consent screen.

_Codex_

```bash
codex mcp add borga --url https://mcp.borga.is/mcp
```

or, in `~/.codex/config.toml`:

```toml
[mcp_servers.borga]
url = "https://mcp.borga.is/mcp"
```

Run `codex mcp login borga` to sign in. Check your Codex version's documentation for the exact command names; both forms describe the same HTTP server.

_Cursor_

Add the server to `.cursor/mcp.json` in your project, or to the global file under **Cursor Settings, MCP**:

```json
{
  "mcpServers": {
    "borga": {
      "url": "https://mcp.borga.is/mcp"
    }
  }
}
```

Cursor shows a **Needs login** state until you complete the consent screen.

_VS Code_

Add to `.vscode/mcp.json` (or the user-level `mcp.json`):

```json
{
  "servers": {
    "borga": {
      "type": "http",
      "url": "https://mcp.borga.is/mcp"
    }
  }
}
```

Start the server from the MCP view and sign in when prompted.

Any other host that supports remote MCP servers with OAuth (the server implements dynamic client registration and client ID metadata documents, so there is nothing to configure on Borga's side) connects the same way.

## Permissions

The consent screen lists these in three groups. Read permissions are preselected when the assistant asks for them; manage permissions as well; money permissions never are. A permission is checked on every API call, so a connection can never do more than what was ticked.

| Permission | Allows | Group | Who can grant it |
| --- | --- | --- | --- |
| `account:read` | Merchant profile, onboarding and live status, and this connection | Always granted | Any member |
| `payments:read` | View payments, payment sessions, refunds and saved payment methods (brand, last 4 and expiry only) | Read | Any member |
| `customers:read` | View customers and their billing state | Read | Any member |
| `catalog:read` | View products, prices and meters | Read | Any member |
| `subscriptions:read` | View subscriptions and recurring revenue | Read | Any member |
| `invoices:read` | View invoices, credit notes and invoice PDFs | Read | Any member |
| `usage:read` | View usage events and meter quantities | Read | Any member |
| `webhooks:read` | View webhook endpoints (without secrets) and deliveries | Read | Any member |
| `settings:read` | View merchant settings, the accounting connection (without credentials) and the API key list (without keys) | Read | Any member |
| `audit_log:read` | View the audit log | Read | Any member |
| `payments:write` | Create payment links | Manage | Any member |
| `customers:write` | Create and update customers, customer portal links and payment method defaults | Manage | Any member |
| `catalog:write` | Create, update and archive products, prices and meters | Manage | Any member |
| `subscriptions:write` | Create, change, pause, resume and cancel subscriptions and their items | Manage | Any member |
| `invoices:write` | Retry accounting sync for invoices | Manage | Any member |
| `usage:write` | Report usage events | Manage | Any member |
| `webhooks:write` | Create, update and delete webhook endpoints and retry deliveries | Manage | Owners and admins |
| `settings:write` | Update merchant and accounting settings and revoke API keys | Manage | Owners and admins |
| `refunds:write` | Refund payments | Move money (never preselected) | Owners and admins |
| `payments:charge` | Charge saved payment methods and retry invoice payments | Move money (never preselected) | Owners and admins |

Every connection also has `account:read`, which covers the merchant's name and id, the mode and the connection itself; the consent screen does not list it.

## Tools

What the assistant can do, grouped by the Borga resource each tool works on. The assistant only sees the tools its permissions allow, so it never plans around something it cannot do. Each tool carries hints (read-only, destructive, idempotent) that hosts use to decide what to confirm with you; how a host acts on them is up to the host.

Amounts are integers in the currency's smallest unit (whole krónur for ISK), and every result says which mode it came from.

### Account and documentation

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `get_account` (Get account) | The merchant this connection acts for, the mode (test or live), the role of the person who connected, the permissions the connection holds, the live money safeguards (approval threshold and daily cap) set for it, whether live mode is enabled, and the onboarding steps still open. Terms, the live application and verification are completed by the merchant in the dashboard, never here. | `account:read` | read-only |
| `search_docs` (Search the Borga docs) | Searches docs.borga.is, the public documentation of the Borga API, checkout, billing and webhooks. Returns matching pages with a short excerpt; read one in full with get_doc_page. | none | read-only |
| `get_doc_page` (Read a Borga docs page) | One page of docs.borga.is as Markdown, by the slug search_docs returned (for example "webhooks" or "payments/hosted-checkout"). | none | read-only |

### Payments

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `list_payments` (List payments) | Payments on the merchant account, newest first, with their status, amount and the card or wallet used (brand and last 4 only). | `payments:read` | read-only |
| `get_payment` (Get a payment) | One payment with its line items and refund total. | `payments:read` | read-only |
| `get_payment_session` (Get a payment session) | A checkout session: whether it is open, complete or expired, the checkout link and the payment it collects. | `payments:read` | read-only |
| `create_payment_session` (Create a payment link) | Creates a hosted checkout session and returns the link the payer opens to pay by card, wallet or bank invoice. Give either a total amount or line items (the amount is then the sum including tax), and optionally the return and cancel URLs (both or neither). Without them the payer stays on Borga's confirmation page after paying, which is right for a link sent by email or chat. In test mode the link charges nothing. | `payments:write` |  |
| `list_refunds` (List refunds) | Refunds on the merchant account, optionally for one payment. | `payments:read` | read-only |
| `get_refund` (Get a refund) | One refund with its status and the payment it belongs to. | `payments:read` | read-only |
| `list_payment_methods` (List saved payment methods) | Cards saved for a customer: brand, last 4 digits, expiry and whether the card is still active. Never the card number. | `payments:read` | read-only |
| `create_refund` (Refund a payment) | Refunds a succeeded payment in full or in part; the money goes back to the payer's card and a credit note is issued if the payment was invoiced. In live mode this moves real money: above the connection's approval threshold or daily cap the result is approval_required with a dashboard link, an owner or admin approves it there, and the same call is then made again with approval_id. An identical call within ten minutes returns the earlier refund instead of refunding twice. | `refunds:write` | destructive |
| `charge_payment_method` (Charge a saved payment method) | Charges a customer's saved card off-session, with the payer not present, and returns the payment. Give a total amount or line items. In live mode this moves real money: above the connection's approval threshold or daily cap the result is approval_required with a dashboard link, an owner or admin approves it there, and the same call is then made again with approval_id. An identical call within ten minutes returns the earlier payment instead of charging twice. Set idempotency_key so a retry can never charge twice. | `payments:charge` | destructive |

### Customers

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `set_default_payment_method` (Set the default payment method) | Makes a saved card the customer's default for subscription charges and invoice retries. The card must be active. Open invoices whose charge failed are retried with it shortly after. | `customers:write` | idempotent |
| `detach_payment_method` (Detach a payment method) | Removes a saved card from a customer. It can no longer be charged; if it was the default, subscription charges fail until the customer saves a new card. Detaching an already detached card changes nothing. | `customers:write` | destructive |
| `list_customers` (List customers) | Customers of the merchant, newest first. Filter by exact email address. | `customers:read` | read-only |
| `get_customer` (Get a customer) | One customer with contact details, billing address and default payment method. | `customers:read` | read-only |
| `get_customer_state` (Get a customer's billing state) | The customer's active subscriptions and meter balances for the current period: what they are entitled to and what they have used. | `customers:read` | read-only |
| `create_customer` (Create a customer) | Creates a customer record to attach payments, saved cards and subscriptions to. Email is required and should be unique per customer. | `customers:write` |  |
| `update_customer` (Update a customer) | Changes a customer's contact details, billing address or metadata. Only the fields given are changed. | `customers:write` | idempotent |
| `create_customer_portal_link` (Create a customer portal link) | A time-limited link to the hosted portal where the customer sees invoices, updates their card and manages subscriptions. Send it to the customer; it signs them in without a password. | `customers:write` |  |

### Catalog

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `list_products` (List products) | Products in the merchant's catalog, newest first, including archived ones (active false). | `catalog:read` | read-only |
| `get_product` (Get a product) | One product by id. | `catalog:read` | read-only |
| `list_prices` (List prices) | Prices in the catalog, optionally for one product. A price is one-off or recurring and may be tiered or metered. | `catalog:read` | read-only |
| `list_meters` (List meters) | Usage meters: how usage events aggregate into billable units. | `catalog:read` | read-only |
| `get_meter` (Get a meter) | One usage meter by id. | `catalog:read` | read-only |
| `create_product` (Create a product) | Adds a product to the catalog. Prices are created separately with create_price. | `catalog:write` |  |
| `update_product` (Update a product) | Renames, describes or archives a product (active false hides it from new sales). Only the fields given are changed. | `catalog:write` | idempotent |
| `create_price` (Create a price) | Adds a price to a product: one-off or recurring, a flat unit amount or tiers, optionally metered. Amounts are in the smallest currency unit. A price's amount never changes afterwards; create a new price instead. | `catalog:write` |  |
| `update_price` (Update a price) | Archives or reactivates a price, or changes its accounting code or metadata. Amounts cannot change. | `catalog:write` | idempotent |
| `create_meter` (Create a meter) | Creates a usage meter: which usage events feed it (event_name) and how they aggregate into a billable quantity per period (count events, or sum, max, min, avg or unique over a metadata property). Metered prices then reference the meter. Accepts idempotency_key for safe retries. | `catalog:write` |  |
| `delete_meter` (Archive a meter) | Archives a meter: it stops accepting usage events and keeps its history. A meter that a price still uses cannot be archived; archive the price first. | `catalog:write` | destructive |

### Subscriptions

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `list_subscriptions` (List subscriptions) | Subscriptions on the merchant account, newest first. Filter by customer or status. | `subscriptions:read` | read-only |
| `get_subscription` (Get a subscription) | One subscription with its items, current period and cancellation state. | `subscriptions:read` | read-only |
| `create_subscription` (Create a subscription) | Starts recurring billing for a customer on one or more recurring prices, from now. With charge_automatically the customer's default saved card is charged at the end of each period (and immediately for any cycle that is due), so this can move money; with send_invoice the customer gets a bank invoice instead. An identical call within ten minutes returns the earlier subscription instead of starting a second one. | `subscriptions:write` | destructive |
| `get_mrr` (Get recurring revenue) | Monthly recurring revenue across active, trialing and past-due subscriptions, one figure per currency; amounts in different currencies are never added together. Licensed items only, so metered usage is not forecast. A point-in-time figure, not a history. | `subscriptions:read` | read-only |
| `update_subscription` (Update a subscription) | Changes how a subscription collects (saved card or bank invoice), its payment terms, its discount or its metadata. Prices and quantities are changed through the subscription item tools; cancellation through cancel_subscription. Only the fields given change. | `subscriptions:write` |  |
| `cancel_subscription` (Cancel a subscription) | Cancels a subscription at the end of the current period (default, the customer keeps what they paid for) or right now. An immediate cancel closes the period with a final invoice for usage so far and the elapsed share of licensed items; nothing is refunded. Confirm which the person wants before calling. | `subscriptions:write` | destructive |
| `uncancel_subscription` (Undo a scheduled cancellation) | Keeps a subscription that was set to cancel at the end of its period. Only possible while the period has not ended; a subscription already canceled cannot be revived. | `subscriptions:write` |  |
| `pause_subscription` (Pause a subscription) | Pauses the licensed charge. Periods still close on schedule: usage is invoiced in full, licensed items only for the time before the pause, so a fully paused period with no usage produces no invoice. Canceled subscriptions cannot be paused. | `subscriptions:write` | idempotent |
| `resume_subscription` (Resume a subscription) | Resumes a paused subscription. Any period that lapsed during the pause is settled first and the paused stretch of the current period is credited, so the time paused is never charged. | `subscriptions:write` |  |
| `add_subscription_item` (Add a subscription item) | Adds a recurring price (seats, a plan, a metered add-on) to a subscription. Bills from the next cycle invoice; a mid-period addition is prorated unless told otherwise. An identical call within ten minutes returns the earlier item instead of adding a second one. | `subscriptions:write` |  |
| `update_subscription_item` (Update a subscription item) | Changes the quantity (seats) or the included units of a subscription item. Takes effect on the next cycle invoice; a decrease mid-period is prorated unless told otherwise. | `subscriptions:write` |  |
| `remove_subscription_item` (Remove a subscription item) | Removes a price from a subscription. The last item cannot be removed; cancel the subscription instead. The dropped quantity is prorated unless told otherwise. | `subscriptions:write` | destructive |
| `add_invoice_item` (Add an invoice item) | Queues a one-off charge (a setup fee, extra seats for a month) or a credit (a negative amount, a goodwill discount) for a customer's next subscription invoice. Nothing is charged now; the item lands on the next cycle invoice. An identical call within ten minutes returns the earlier item instead of queueing a second one. | `subscriptions:write` |  |

### Invoices and credit notes

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `list_invoices` (List invoices) | Invoices Borga recorded with the merchant's accounting system, newest first, with their sync status. sync_failed means the accounting system rejected the invoice. | `invoices:read` | read-only |
| `get_invoice` (Get an invoice) | One invoice with its lines, totals, the accounting system's number and the PDF link when available. | `invoices:read` | read-only |
| `list_credit_notes` (List credit notes) | Credit notes issued against invoices when payments were refunded, optionally for one invoice. | `invoices:read` | read-only |
| `get_credit_note` (Get a credit note) | One credit note by id. | `invoices:read` | read-only |
| `get_invoice_pdf` (Get an invoice PDF) | The PDF of an invoice, as the accounting system rendered it, as an MCP resource link the host can open or attach, plus the provider's own URL when it has one. Only invoices that were synced to the accounting system have a PDF. | `invoices:read` | read-only |
| `retry_invoice_sync` (Retry accounting sync) | Sends an invoice whose sync to the accounting system failed (status sync_failed) there again, for example after the merchant fixed the accounting settings or the customer's details. Any other status is refused. | `invoices:write` | idempotent |
| `retry_invoice_payment` (Retry an invoice payment) | Charges the customer's saved card now for an open subscription invoice, outside the automatic retry schedule, for example after the customer updated their card. In live mode this moves real money: above the connection's approval threshold or daily cap the result is approval_required with a dashboard link, an owner or admin approves it there, and the same call is then made again with approval_id. Counts as an attempt in the dunning schedule. | `payments:charge` | destructive |

### Usage

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `report_usage` (Report usage) | Records usage events for metered billing. Each event names the meter's event_name and the customer (by Borga id or the merchant's external id); an idempotency_key per event makes re-sending safe. Up to 1000 events per call. | `usage:write` | idempotent |
| `list_usage_events` (List usage events) | Raw usage events, newest first. Filter by customer, event name and time range. | `usage:read` | read-only |
| `get_meter_quantities` (Get a meter's quantity for a customer) | The aggregated quantity a meter has recorded for one customer in a period (the current billing period by default). | `usage:read` | read-only |

### Webhooks, settings and audit

| Tool | What it does | Permission | Hints |
| --- | --- | --- | --- |
| `list_webhook_endpoints` (List webhook endpoints) | The merchant's webhook endpoints in this mode, newest first, with the events each receives and a hint of its signing secret. Secrets are never returned. | `webhooks:read` | read-only |
| `list_webhook_deliveries` (List webhook deliveries) | The latest delivery attempts to the merchant's webhook endpoints, newest first, with the HTTP status and the receiver's reply. Filter by endpoint, status or event type to find failures; the filters apply before the limit, so the result is the newest matches. There is no paging beyond the limit: narrow the filters or raise the limit instead. | `webhooks:read` | read-only |
| `create_webhook_endpoint` (Create a webhook endpoint) | Registers an HTTPS URL to receive the merchant's events. The response carries the signing secret (whsec_...) exactly once: pass it on to the person right away, since Borga cannot show it again, and remind them that it now exists in this conversation. Prefer the merchant creating endpoints in the dashboard when the secret must stay out of transcripts. | `webhooks:write` |  |
| `update_webhook_endpoint` (Update a webhook endpoint) | Changes the URL, the events or the status (enabled or disabled) of a webhook endpoint. The signing secret is unchanged. | `webhooks:write` | idempotent |
| `delete_webhook_endpoint` (Delete a webhook endpoint) | Removes a webhook endpoint and its delivery history. Events stop being sent to it at once. To stop deliveries temporarily, update its status to disabled instead. | `webhooks:write` | destructive |
| `retry_webhook_delivery` (Retry a webhook delivery) | Queues a delivery for an immediate new attempt, for example after the receiver was fixed. The retry schedule continues from the delivery's attempt count. | `webhooks:write` |  |
| `get_settings` (Get merchant settings) | The merchant's settings: name and contact email, enabled payment methods, allowed redirect domains, checkout branding, bank invoice options, subscription dunning (retry schedule and failure action) and the live and verification status. | `settings:read` | read-only |
| `update_settings` (Update merchant settings) | Changes merchant settings. Only the fields given are written; they apply to test and live alike. The legal name, kennitala and terms cannot be changed here. Lists replace the existing list, so include every domain or payment method that should remain. | `settings:write` | idempotent |
| `get_accounting_link` (Get the accounting connection) | Which accounting system (PayDay or DK+) issues the merchant's invoices in this mode, its status and the chosen settings: payment type, default account and VAT codes. With include_options, also the payment types, accounts and VAT codes the provider offers, for picking valid values. Never credentials. Connecting a provider is done in the dashboard, under Settings, Accounting. | `settings:read` | read-only |
| `update_accounting_settings` (Update accounting settings) | Changes how invoices are booked at the connected accounting system: the payment type card payments go to, the default account code, the default tax rate for payments without line items, and the VAT code per tax rate. Use get_accounting_link with include_options to find valid ids first. Only the fields given change. | `settings:write` | idempotent |
| `list_api_keys` (List API keys) | The merchant's active API keys in this mode: label, type, prefix and a hint of the value. Key values are never returned, and keys cannot be created or rotated through this connection; that happens in the dashboard under Developers. | `settings:read` | read-only |
| `revoke_api_key` (Revoke an API key) | Revokes an API key. Every integration using it stops working at once and the key cannot be restored; a new one has to be created in the dashboard. | `settings:write` | destructive |
| `list_audit_log` (List the audit log) | The merchant's audit log in this mode, newest first: who did what to which resource, including this and other assistants' actions (actor_type oauth_grant). Filter by action, resource type or actor type. | `audit_log:read` | read-only |
| `list_members` (List members) | The people on the merchant's team and their roles. Inviting, removing and role changes are done in the dashboard, never through this connection. | `settings:read` | read-only |

## Cards in the conversation

In Claude, ChatGPT, VS Code and other hosts that support MCP Apps, some results come with an interactive card instead of plain text:

- **Payment links** show the amount, a QR code to scan and buttons to copy or open the link, and the status updates when the payer pays.
- **Refunds and charges** show what moved. When a live move needs an owner's or admin's approval, the card shows the pending request with a link to the dashboard and a button to carry it out once it has been approved.
- **Payments and recurring revenue** show a table you can filter and page through, and MRR per currency.
- **Invoices** show their status and the accounting system's number, with buttons for the PDF, the customer portal and retrying the sync when the accounting system rejected it.

Everything a card does is an ordinary request on your connection, under the same permissions, safeguards and audit log as the assistant's own requests. Hosts without card support show the same information as text.

## Prompts

Hosts that support prompts (Claude's slash commands, for example) also get four ready-made ones, each listed only when the connection holds the permissions its steps need:

| Prompt | What it does |
| --- | --- |
| `send_payment_link` | Creates a checkout link for an amount and tells you where to send it. |
| `month_end_summary` | Payments, refunds, invoice sync failures and recurring revenue for a period, as a short report. |
| `chase_unpaid_invoices` | Invoices still open some days after they were issued, with a customer portal link per customer for the follow-up. |
| `integration_checklist` | What a developer still has to do before going live, from the account, webhooks and recent test payments. |

## Safeguards

- **Host confirmation.** Every tool that moves money, cancels, deletes or detaches carries the `destructiveHint` annotation. Hosts use it to decide when to ask you first; Claude, for example, confirms destructive calls. The hint informs the host, it does not enforce anything, which is why permissions and the server-side controls below exist.
- **Mode is fixed.** A test-mode connection cannot reach live data or the card processor's live rail, whatever the assistant asks for.
- **Approval threshold and daily cap** for live refunds and charges, set per connection on the Connected apps page. A refund or charge above the threshold, or one that would take the connection past what it may move in 24 hours, is not carried out: the assistant gets `approval_required` with a link, an owner or admin approves or denies the request in the dashboard within 24 hours, and the assistant then repeats the same call with the approval id. Both are off until you set them.
- **No double moves.** An identical refund, charge, invoice payment retry, new subscription, added subscription item or invoice item asked for twice within ten minutes, after the first one succeeded, is answered with the earlier result (`duplicate: true`) instead of being carried out again. A call that failed is not remembered, so repeating it tries again. The API's idempotency keys apply on top.
- **Roles keep applying.** Permissions that only an owner or admin can grant (webhooks, settings, refunds, charges) stop working the moment that person is demoted to member, even on a connection they approved earlier.
- **Rate limits.** A connection is limited to 300 requests per minute per endpoint, well under the API-key limit.
- **Audit log.** Every call the assistant makes is an entry in your [audit log](/security) with the connection's name and label, for example "Claude (Jón's laptop)", and the tool that made it.

## Privacy and data

The MCP server is a client of the Borga API and holds no database of its own. It keeps two things in memory: a 60 second cache of each connection's permissions, so that a revoked connection stops within a minute even when the cache is warm, and the results of successful refunds, charges, invoice payment retries, new subscriptions, added subscription items and invoice items for ten minutes, which is what lets it answer an identical repeat with the earlier result instead of moving money again. Nothing else about a tool call is stored.

Results carry what you would see in the dashboard and nothing more: no webhook secrets, no accounting credentials, no processor references, and saved cards only as brand, last four digits and expiry. Customer names, descriptions and other text people typed are returned as data, and the server tells the assistant to treat them that way.

Some things are never offered through the MCP server, whatever permissions a connection holds:

- Creating, rotating or revealing API keys. A secret in a chat transcript is a leaked secret. Listing keys (by label and hint) and revoking one is allowed.
- Inviting or removing members, or changing roles. Listing the team is allowed.
- Connecting an accounting provider or entering its credentials. Reading the connection's status and changing which accounts and VAT codes it books to is allowed.
- Accepting terms, applying for live mode, signing, and anything else that is a legal act. Those need your own Kenni confirmation in the dashboard.
- The payer-facing checkout and portal, which stay unauthenticated and separate.

One exception to the no-secrets rule: creating a webhook endpoint returns its signing secret once, exactly as the dashboard does, because the developer asking for it usually needs it in the same session. The tool says so, and the assistant is told to hand the secret over and to point out that it now exists in the conversation. Create endpoints in the dashboard when that is not acceptable.

## For client authors

If you are building an assistant, a plugin or another third-party client rather than connecting an existing one, the authorization server, its discovery document and the registration options are described in [OAuth for third-party clients](/oauth). Merchant backends that act for themselves keep using [API keys](/authentication); the MCP server does not accept them.
