---
id: mcp-safety
title: Safety and limits
slug: /mcp/safety
description: How Chatley keeps an AI agent from doing damage — drafts, two-step deletes, account isolation, rate limits, and what every error means.
---
An AI agent works quickly, repeats itself when something fails, and does not
pause the way a person does before an irreversible step. The Chatley MCP server
is built for that, and several of its behaviours are deliberately less
convenient than the dashboard's.

## Nothing goes live by accident

Changes to an AI voice agent are saved as a **draft**. The agent keeps answering
calls exactly as before until `publish_agent_draft` is called.

This means a mistaken `update_agent` is not an outage. It also means a change
that is never published never takes effect — if a caller reports that nothing has
changed, check for an unpublished draft with `get_agent`.

## Nothing is deleted in one step

Destructive tools require two calls. The first returns what *would* be removed
and changes nothing. Only a second call carrying `confirmed: true` removes
anything.

The check is enforced by Chatley, not by your client. A client that sends
`confirmed: true` on the first call is still refused — the point is that a person
sees the list in between.

## Some tools do not exist

Chatley deliberately does not expose deleting an AI voice agent, deleting a
knowledge base, deleting a contact, or any update-everything-matching-a-filter
operation. These are the operations where a single confused call does the most
damage. Do them in the dashboard, where the blast radius is visible.

## Your account is your account

Every call is checked against what your connection is actually allowed to reach.

If you administer several accounts, tools accept an account parameter — but
naming an account is a **request**, not permission. Chatley verifies on every
single call that your connection administers that account. An account id
belonging to someone else is refused, and the attempt is recorded.

The same applies within an account: the tools you see reflect your Chatley role.
An assistant connected with a member account cannot do owner things by asking
differently.

## Rate limits

Requests are limited per connection and per account, so one assistant in a retry
loop cannot exhaust your capacity or crowd out your dashboard.

When a limit is reached, calls return a `rate_limited` error with the time to
wait. Wait for it. Retrying immediately makes the wait longer, and an agent that
retries in a tight loop will stay limited indefinitely.

## Every call is recorded

Each tool call is written to your account's audit trail: which connection made
it, which account it ran in, which tool, and what came of it. Arguments are
recorded as a fingerprint rather than in full, so the record shows what was done
without storing the contents.

If someone asks what an AI agent did in your Chatley account, this is the
answer, and it does not depend on the client having kept its own logs.

## What we never send back

Tool responses never contain API keys, tokens, credentials, or the secrets of any
service you have connected. This holds for error messages too — a failure
somewhere underneath is translated into a Chatley error before it reaches you,
rather than being passed along.

If you ever see something that looks like a credential in a tool response,
treat it as a security issue and tell us.

## Errors

Errors are stable and machine-readable — branch on the code, show the message.

| Code | Meaning | What to do |
| --- | --- | --- |
| `authentication_required` | Not signed in, or the session expired | Reconnect from your client |
| `permission_denied` | Your role does not allow this | Use an account with the right role, or do it in the dashboard |
| `account_not_found` | The account named is not one you administer | Call `list_tenants` to see which are |
| `not_found` | The id does not exist, or is not yours | Re-read the id from a listing tool |
| `invalid_request` | A parameter is missing or malformed | The message names the parameter |
| `confirmation_required` | A destructive call arrived without `confirmed: true` | Show the person what would be removed, then call again |
| `conflict` | Something changed underneath you | Re-read the resource and retry once |
| `rate_limited` | Too many requests | Wait the stated time. Do not retry immediately |
| `insufficient_balance` | A metered account is out of credit | Top up. Read tools keep working |
| `dependency_unavailable` | Something Chatley depends on is not responding | Transient — retry once, then report it |
| `timeout` | The operation took too long | Retry once. If it repeats, report it |
| `internal_error` | A fault on our side | Report it with the time of the call |

## Metered access

Metered connections pay per operation instead of holding a subscription.

- **Read tools are free or negligible.** Reading calls, agents, and analytics
  does not meaningfully spend.
- **Check the price first.** `get_rate_card` exists so an agent can know what an
  operation costs *before* calling it.
- **Balance is checked before, not after.** An operation that cannot be paid for
  is refused with `insufficient_balance` and does not half-run.
- **Zero balance stops metered operations, not reads.** You can still see your
  account and your history with an empty balance.
- **Every metered operation is recorded** with the operation, quantity, rate, and
  time — the same record your invoice is built from.

Outbound calling on a metered connection is **off by default** and unlocked only
after business verification, completed messaging registration, and a recorded
consent attestation. This is not a formality: metered outbound is the one place
where an AI agent could dial real people at scale, and it stays closed until
those are in place.
