# Integrations: MCP, API and webhooks

> How a business connects its Atenta to Claude, ChatGPT, Zapier, Make, n8n or its own system: tokens with permissions, MCP server, REST API with OpenAPI and signed webhooks.

Atenta is **open**: with **one token** a business connects its Atenta to Claude, ChatGPT, Zapier, Make, n8n or its own code. Everything done from there is the same as it would be in the app: same data, same rules, same limits.

- **MCP server:** `https://mcp.atenta.mx` (Claude Code, Claude Desktop, Cursor, n8n and any MCP client that sends an `Authorization` header).
- **REST API:** `https://api.atenta.mx/v1` · OpenAPI 3.1 at `https://api.atenta.mx/v1/openapi.json` · documentation at `https://api.atenta.mx/v1/docs`.
- **Outgoing webhooks:** Atenta notifies your URL when something happens (message, escalation, order, appointment, payment, call).

The per-integration pages, in the owner's language, are at [/integraciones](https://atenta.mx/en/integraciones).

## 1. The token

It's created in the app → **Settings → Integrations** ("Connect" on the card you want, with the suggested permissions). It has the form `atk_<business>_<32 characters>` and **is shown only once**: store it in a password manager or an environment variable, never in a chat or a repository. Atenta keeps only its hash and a prefix to recognize it.

Permissions (pick only the ones the integration needs):

| Permission | What it allows |
|---|---|
| `leer_chats` | read conversations, messages, quick replies and team |
| `escribir_chats` | reply in a chat, take over or hand back, pin/archive |
| `pedidos` | view, create and update orders |
| `citas` | view availability, book, move and cancel appointments; dates |
| `clientes` | search customers, profile, notes, tags, follow-ups, segments, import/export |
| `finanzas` | wallet, credit and installments, collections, payment links, charge for orders |
| `reportes` | monthly report, PDF, call history and metrics |
| `base` | read the knowledge base, the menu, today's menu, the catalog and the settings; **propose** corrections (approving them is the owner's job, in the app) |
| `integraciones` | view tokens and manage webhooks |

Rules that always apply: **120 calls per minute** per token; every write is recorded in the business's log with the token's name; whatever is only done from the app (approving the base, switching on, paying for the plan, campaigns, deleting) answers `403 solo_desde_la_app` (app only); customers' phone numbers arrive redacted; a token only sees its own business. **Revoking** a token cuts the integration off instantly; "Revoke all" (red, with the confirmation `REVOCAR TODO`) cuts them all off.

## 2. Claude Code and Claude Desktop

**Claude Code** (terminal). Store the token in an environment variable; the configuration references it, it doesn't contain it:

```bash
export ATENTA_TOKEN="atk_..."   # the token the app gave you
claude mcp add --transport http atenta https://mcp.atenta.mx --header 'Authorization: Bearer ${ATENTA_TOKEN}'
claude mcp list                 # atenta: https://mcp.atenta.mx (HTTP) - ✓ Connected
```

Then, inside Claude Code: *"Which conversations need me today?"*, *"Book Laura for Thursday at 5"*, *"Give me the day's summary"*. The tools are called `atenta_chats_listar`, `atenta_pedidos_crear`, `atenta_citas_disponibles`… (only the ones your token allows appear; `atenta_yo` tells you which).

**Claude Desktop** connects through a file (`claude_desktop_config.json`) with the `mcp-remote` bridge, which sends the token as a header. claude.ai's "custom connectors" authenticate with OAuth and Atenta authenticates with a token, so that path doesn't apply.

```json
{
  "mcpServers": {
    "atenta": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.atenta.mx", "--header", "Authorization: Bearer ${ATENTA_TOKEN}"],
      "env": { "ATENTA_TOKEN": "atk_..." }
    }
  }
}
```

Generic `mcpServers` block for any client that speaks Streamable HTTP (Cursor, Windsurf, VS Code…):

```json
{
  "mcpServers": {
    "atenta": {
      "type": "http",
      "url": "https://mcp.atenta.mx",
      "headers": { "Authorization": "Bearer atk_..." }
    }
  }
}
```

### What the MCP server exposes

Only the tools the token allows appear (plus `atenta_yo`, always).

| Tools | Permission |
|---|---|
| `atenta_chats_listar` · `atenta_chats_leer` | `leer_chats` |
| `atenta_chats_escribir` · `atenta_chats_tomar_control` · `atenta_chats_devolver` | `escribir_chats` |
| `atenta_pedidos_listar` · `atenta_pedidos_crear` · `atenta_pedidos_actualizar` | `pedidos` |
| `atenta_pedidos_cobrar` · `atenta_finanzas_saldo` · `atenta_finanzas_recargar` · `atenta_finanzas_abonar` · `atenta_finanzas_cobranza` · `atenta_finanzas_link` | `finanzas` |
| `atenta_citas_listar` · `atenta_citas_disponibles` · `atenta_citas_crear` · `atenta_citas_mover` · `atenta_citas_cancelar` | `citas` |
| `atenta_clientes_buscar` · `atenta_clientes_ficha` · `atenta_clientes_nota` · `atenta_clientes_seguimiento` · `atenta_clientes_etiquetar` | `clientes` |
| `atenta_reporte_mes` · `atenta_llamadas_historial` | `reportes` |
| `atenta_base_leer` · `atenta_base_proponer` (stays as a proposed correction; **approving is the owner's job, in the app**) · `atenta_menu_leer` · `atenta_menu_dia_hoy` | `base` |

Resources (with `base`): `atenta://base`, `atenta://menu-dia`, `atenta://catalogo`, `atenta://menu`, `atenta://horario`, `atenta://plan`. Prompts: `resumen_del_dia` (day's summary), `que_contesto_a_los_escalados` (what to reply to the escalated chats; it proposes, it doesn't send), `cobranza_de_hoy` (today's collections).

## 3. ChatGPT

The path that works with a token is a **custom GPT with Actions**: under "Actions", import the schema from `https://api.atenta.mx/v1/openapi.json`, authentication **API Key**, type **Bearer**, value `atk_...`. The schema already includes `servers`, `securitySchemes` and one operation per action (`chats_listar`, `pedidos_crear`, `citas_disponibles`…). Suggested instruction for the GPT: *"Use Atenta's actions to answer with real data from the business; never make up prices or hours; if an action fails, say the reason it returns".*

ChatGPT's custom MCP connectors authenticate with OAuth or with no authentication; Atenta authenticates with a token, so for ChatGPT the Actions route is the one to use.

## 4. Zapier, Make and n8n

Two directions.

**a) Atenta → your flow (webhooks).** They're created in **Settings → Integrations → Webhooks** (public `https` URL + events) or through the API with the `integraciones` permission:

```bash
curl -X POST https://api.atenta.mx/v1/webhooks \
  -H "Authorization: Bearer $ATENTA_TOKEN" -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.zapier.com/hooks/catch/123/abc","eventos":["pedido","cita","escalacion"]}'
```

The response includes `secreto` (`whsec_…`, **shown only once**). Each event arrives as a JSON `POST`:

```json
{ "evento": "pedido", "tenant": "mi-negocio", "ts": "2026-09-21T18:03:11.000Z",
  "datos": { "id": 812, "cliente_id": 41, "estado": "nuevo", "total": 250, "moneda": "MXN", "canal": "whatsapp", "nuevo": true } }
```

with the headers `X-Atenta-Evento`, `X-Atenta-Entrega` (unique id: use it to avoid processing twice) and `X-Atenta-Firma: t=<unix>,v1=<hmac>`. To verify: `v1 == HMAC_SHA256(secreto, t + "." + cuerpo_crudo)` and `|now − t| ≤ 300 s`. In Node:

```js
import { createHmac, timingSafeEqual } from "node:crypto";
export function firmaValida(secreto, cuerpoCrudo, firma) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(firma ?? ""); if (!m) return false;
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const esperado = createHmac("sha256", secreto).update(`${m[1]}.`).update(cuerpoCrudo).digest("hex");
  return esperado.length === m[2].length && timingSafeEqual(Buffer.from(esperado), Buffer.from(m[2]));
}
```

Events: `mensaje` (every message in or out, with the full message), `escalacion` (Atenta asked for a person), `pedido` (new or status change), `cita` (new, moved or cancelled), `pago` (link paid or installment recorded), `llamada` (AI call finished). Retries: if your URL doesn't answer 2xx, Atenta retries 3 times (1 s, 5 s, 25 s); after 20 failures in a row the webhook switches itself off and you see it in the app (with its latest deliveries). The "Test" button = `ping` event. Maximum 5 webhooks per business; `https` URLs only.

In Zapier: *Webhooks by Zapier → Catch Hook* gives you the URL; in Make: *Webhooks → Custom webhook*; in n8n: a *Webhook* node (POST) and a *Crypto* or *Code* node to validate `X-Atenta-Firma`.

**b) Your flow → Atenta (REST API).** Any route of the business works at `https://api.atenta.mx/v1/…` with `Authorization: Bearer atk_...` (*HTTP Request* / *Webhooks by Zapier → Custom Request* modules). Examples:

```bash
# who am I and what can I do
curl https://api.atenta.mx/v1/yo -H "Authorization: Bearer $ATENTA_TOKEN"
# new orders
curl "https://api.atenta.mx/v1/crm/pedidos?estado=nuevo&limite=20" -H "Authorization: Bearer $ATENTA_TOKEN"
# free slots to book
curl "https://api.atenta.mx/v1/citas/disponibles?n=5" -H "Authorization: Bearer $ATENTA_TOKEN"
# book
curl -X POST https://api.atenta.mx/v1/citas -H "Authorization: Bearer $ATENTA_TOKEN" -H "Content-Type: application/json" \
  -d '{"inicio":"2026-09-25T17:00:00-06:00","nombre":"Laura","motivo":"corte","estado":"confirmada"}'
# reply in a chat as a person from the business
curl -X POST https://api.atenta.mx/v1/conversaciones/5214421234567/mensajes -H "Authorization: Bearer $ATENTA_TOKEN" -H "Content-Type: application/json" \
  -d '{"texto":"Your order is on its way, it arrives in 20 minutes."}'
# monthly report
curl "https://api.atenta.mx/v1/reporte?mes=2026-09" -H "Authorization: Bearer $ATENTA_TOKEN"
```

Errors, always with a short `error` and a human-readable `motivo`: `401` invalid or revoked token · `403 sin_permiso` (says which permission is missing) · `403 solo_desde_la_app` (app only) · `404` doesn't exist in this business · `409` a state that doesn't allow it (`fuera_de_ventana`, `saldo_insuficiente`…) · `422` incomplete data (`campos`) · `429` more than 120 calls in a minute (`Retry-After`). `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers on every response.

## 5. Integrators (agencies)

An integrator token (`atp_…`, issued by Kalia Code) signs up businesses the same way the atenta.mx form does and reads their status; it never accesses a business's data (that's what the owner's token is for).

```bash
curl -X POST https://api.atenta.mx/v1/alta -H "Authorization: Bearer $ATENTA_INTEGRADOR" -H "Content-Type: application/json" \
  -d '{"negocio":"Taquería El Güero","giro":"fonda","whatsapp":"5214421234567","pais":"MX"}'
curl https://api.atenta.mx/v1/tenants -H "Authorization: Bearer $ATENTA_INTEGRADOR"
curl https://api.atenta.mx/v1/tenants/taqueria-el-guero/base -H "Authorization: Bearer $ATENTA_INTEGRADOR"
```

## 6. Security, in short

- The token only exists on your side: Atenta stores a hash. If it leaks, revoke it in the app and create another.
- Everything travels over HTTPS; responses never include secrets or customers' full phone numbers.
- Every write made with a token is recorded in the business's log with the token's name.
- External AIs read the business's data **only** with a token the owner created on purpose, with the permissions they chose and for as long as they don't revoke it. That AI's provider is a sub-processor chosen by the business itself, as the [privacy notice](https://atenta.mx/en/privacidad) says.
