# Documentación de la API y MCP de Circles

> Esta página está escrita para agentes de IA que quieren saber qué es Circles y cómo usarlo: las herramientas, la forma de cada respuesta, cómo funciona el inicio de sesión, la API HTTP y cada límite. Las personas que configuran Circles tienen las guías en [usecircles.app/es/docs](https://usecircles.app/es/docs).

**Circles entrega a los agentes de una persona los chats de WhatsApp que esa persona abre**, por un servidor MCP remoto en `https://app.usecircles.app/api/mcp` (OAuth 2.1 o un token bearer) y una API HTTP en `https://app.usecircles.app/api/v1/agent` (OpenAPI 3.1 en `https://app.usecircles.app/api/openapi.json`). Hay 9 herramientas, cada rechazo es una respuesta que nombra lo que sí está abierto, y todas las horas están en UTC. A octubre de 2026.

Fuente: https://usecircles.app/es/api-mcp-docs · Actualizado el 2026-10-31

## Empieza aquí

Actúas por una persona, dentro de los chats que esa persona abrió a los agentes. Nada más de su WhatsApp existe para ti.

- **Conoce tu mundo.** `allowed_chats` lista cada chat abierto para ti y si puedes enviar ahí. Un chat que no está en la lista no se puede leer ni buscar. La única forma de salir de la lista es un chat nuevo: cuando el valor por defecto del número es lectura y envío, `send` puede empezar un chat con alguien nuevo, por número de teléfono o por el nombre exacto de un contacto guardado (`GET /recipients?to=` por HTTP).
- **“¿Qué me perdí?”** Llama primero a `activity` con un momento `since` (solo cantidades, barato), y después a `catch_up` para los chats que se movieron. Los audios llegan como texto en `media.transcript` cuando hay transcripción (hebreo e inglés).
- **Un rechazo es una respuesta.** Llega con `isError: false` y nombra lo que SÍ está abierto o permitido. Pásaselo a la persona; no lo reintentes.
- **Enviar es hablar como la persona.** Muéstrale a la persona el texto exacto y el chat exacto, y llama a `send` con `confirmed: true` solo cuando acepte. Un envío nunca se reintenta: si la entrega es incierta, lee el chat antes de decidir nada.
- **Las horas están en UTC.** Circles no sabe dónde está la persona; convierte tú las horas.
- **¿Todavía no estás conectado?** La persona vincula WhatsApp y te conecta siguiendo las guías en [usecircles.app/es/docs](https://usecircles.app/es/docs). Circles nunca te pide su contraseña ni un código de WhatsApp.

## Direcciones

| Qué | Dónde |
| --- | --- |
| Servidor MCP (HTTP, sin estado) | `https://app.usecircles.app/api/mcp` |
| API HTTP | `https://app.usecircles.app/api/v1/agent` |
| OpenAPI 3.1 | `https://app.usecircles.app/api/openapi.json`; una referencia legible en `https://app.usecircles.app/api` |
| Metadatos OAuth | `https://app.usecircles.app/.well-known/oauth-protected-resource`, `https://app.usecircles.app/.well-known/oauth-authorization-server` |
| Tarjeta del servidor MCP | `https://usecircles.app/.well-known/mcp/server-card.json` |
| Esta página en Markdown | `https://usecircles.app/es/api-mcp-docs.md`; todas las páginas del sitio están en `https://usecircles.app/llms.txt` |

## Las 9 herramientas MCP

Las herramientas se listan según quién llama: `send` aparece solo con el scope `chats:send` y algún chat donde enviar, `get_media` solo con `media:read`. Una herramienta que falta responde igual que una que no existe.

**`allowed_chats`**: sin parámetros. Los chats abiertos a los agentes, cada uno con `name`, `kind`, `access` (`read only` o `read and send`) y `archived` cuando corresponde. Es todo el mundo: un chat que no está aquí no se puede leer, buscar ni recibir envíos. También trae `connection` (`not_linked`, `subscription_ended`) y `subscription.ends_at` cuando un plan cancelado está por terminar.

**`activity`**: `since` (ISO 8601, obligatorio). Qué chats abiertos se movieron: cantidad de mensajes, `last_at` / `last_at_iso`, `last_from`. Solo cantidades, sin texto. La primera llamada barata para “¿qué me perdí?”.

**`catch_up`**: `since` (obligatorio), `chat`, `limit`, `per_chat`, `kind`. Mensajes desde un momento, del más antiguo al más nuevo, remitentes por nombre. Con `chat`: ese chat, `limit` por defecto 50, máximo 200, se conservan los más nuevos. Sin él: cada chat abierto que se movió, el chat más reciente primero, `per_chat` por defecto 20, máximo 50, dentro de un total de 150; un chat con más dice `more`. `kind` es `voice`, `audio` o `recording` (cualquiera de los dos). Los adjuntos solo se anuncian. Trae uno con `get_media`.

**`search`**: `query` (obligatorio), `chat`, `days` (1 a 36.500; por defecto, todo el historial guardado), `kind`. Búsqueda de texto sin distinguir mayúsculas en un chat abierto o en todos, que incluye lo escrito y lo que dicen las transcripciones de audios. Cada resultado nombra su chat cuando se busca en todos.

**`people`**: `since` (obligatorio), `limit` (por defecto 60, máximo 150), `min_messages` (por defecto 3). Con quién habla la persona en los chats abiertos: cantidades en chats individuales, grupos compartidos, mensajes por grupo. Devuelve solo nombres, sin números y sin texto.

**`group_members`**: `chat` (obligatorio). Los miembros de un grupo abierto según la lista de WhatsApp, incluidos los que nunca escribieron. Nombre guardado, o si no el nombre con el que escriben, o su nombre de WhatsApp marcado con `~`. Administradores marcados. Como máximo 300 nombres; los miembros sin nombre conocido se cuentan en `unnamed`. Nunca números de teléfono.

**`documents`**: `since` (obligatorio). Cada PDF, archivo de Word y hoja de cálculo enviados en los chats abiertos: nombre del archivo, fecha, los chats a los que se envió (una entrada por archivo), `ref` y `state`. Solo nombres y fechas.

**`get_media`**: `ref` (obligatorio). Un adjunto, en base64 en `bytes_base64`, con `mime`, `filename` y `size_bytes`. Hasta 50 MB por archivo. Para un archivo `on_request` pide al celular que lo vuelva a subir y espera unos 25 segundos. Una ref por llamada. El agente lee el archivo. Circles no hace OCR, no describe imágenes ni extrae el texto de los PDF.

**`send`**: `chat`, `text`, `confirmed` (todos obligatorios), `file` (`mime`, `data_base64`, `filename`), `from`. Un mensaje a un chat abierto para envío: texto de hasta 65.536 caracteres, o un archivo de hasta unos 3 MB con el texto como pie (hasta 4.096). Un JPEG, PNG o WebP sale como foto, un MP4 o 3GP como video, y cualquier otra cosa como documento con nombre. Cuando el valor por defecto del número es lectura y envío, `chat` puede ser un número de teléfono con código de país o el nombre exacto de un contacto guardado, para empezar un chat nuevo; `from` elige el número cuando podría ser más de uno. Con límite diario, nunca se reintenta. `confirmed` es la afirmación del agente de que la persona aceptó este texto exacto en este chat exacto; Circles no puede verificarla y no la toma como permiso.

## Respuestas, rechazos y archivos

**El envoltorio.** Cada herramienta responde con un elemento de texto. En una respuesta, `content[0].text` es JSON (con formato). En un rechazo, es una oración simple. En los dos casos `isError` es `false`: lee el rechazo y pásalo. No lo reintentes.

**Los rechazos nombran lo que SÍ está permitido.** “No chat named X is open to agents. What IS open: A, B.” o “X is open for reading only.” Un chat oculto nunca se nombra, así que un rechazo no le dice a un agente si un chat existe. “No hay mensajes” y “no puedes ver ese chat” nunca se confunden.

**Cobertura.** Cuando una solicitud llega más atrás de lo que se guarda, la respuesta trae `coverage`, que dice dónde empieza el historial guardado y por qué, y termina con “Nothing was hidden from you.” Si no hay `coverage`, no falta nada en la ventana.

**Las horas** están en UTC: `at` en milisegundos desde 1970 y `at_iso` en ISO 8601 terminado en Z (igual `last_at` / `last_at_iso`). Circles no sabe dónde está la persona y no convierte nada.

**Audios.** `media.kind` es `voice` para un audio de WhatsApp y `audio` para un archivo de audio. Los dos traen `transcript` y `transcript_state`: `pending` (todavía se está transcribiendo), `done`, `empty` (no se dijo nada) o `failed`; y `duration_seconds` cuando WhatsApp lo da. La transcripción es automática, en hebreo e inglés; la de audios en español todavía no está verificada. Un audio de más de unos 25 días al vincular se transcribe cuando se trae.

**Estados de los adjuntos.** Cada archivo anunciado tiene un `state`:

- `available`: `get_media` lo devuelve ahora.
- `downloading`: está llegando solo, normalmente en menos de un minuto. Vuelve a pedirlo en un rato.
- `on_request`: WhatsApp ya no lo tiene; `get_media` le pide al celular de la persona que lo vuelva a subir (el celular tiene que estar en línea) y espera unos 25 segundos.
- `fetching`: el celular todavía no respondió. NO se perdió: vuelve a pedirlo con la misma ref en un minuto; una repetición se suma a la solicitud que ya está corriendo.
- `gone`: el celular no pudo volver a subirlo. Dilo; no lo reintentes.
- `too_large`: supera 50 MB, el límite por llamada.
- `failed`: todavía no se pudo descargar. La persona no tiene que hacer nada.

**También en los mensajes:** `reactions` (emoji, quién, cuándo) cuando alguien reaccionó, desde el 2026-10-27. Las ediciones en WhatsApp actualizan el texto guardado; “eliminar para todos” quita el mensaje. Las ubicaciones, tarjetas de contacto y encuestas se guardan como mensajes vacíos para que un chat no tenga huecos. Las vistas previas de enlaces y el contexto de mensajes citados no se incluyen.

Un ejemplo abreviado, con personas inventadas (las respuestas de la API están en inglés):

```
// An answer: content[0].text is JSON, isError is false
{
  "chat": "Oak Street parents",
  "coverage": {
    "days": 30,
    "note": "This account keeps the last 30 days ... Nothing was hidden from you."
  },
  "messages": [
    {
      "id": "G7",
      "from": "Dana",
      "fromMe": false,
      "at": 1792995120000,
      "at_iso": "2026-10-26T06:12:00.000Z",
      "body": "",
      "media": {
        "kind": "voice",
        "duration_seconds": 41,
        "transcript_state": "done",
        "transcript": "The trip moved to Thursday, buses leave at 8:15."
      }
    },
    {
      "id": "G8",
      "from": "Yossi",
      "fromMe": false,
      "at": 1792998180000,
      "at_iso": "2026-10-26T07:03:00.000Z",
      "body": "Permission slip attached",
      "media": {
        "kind": "document",
        "filename": "trip-permission.pdf",
        "mime": "application/pdf",
        "size": "212 KB",
        "state": "on_request",
        "fetch_with": "get_media",
        "ref": "r1_..."
      }
    }
  ]
}

// A refusal: content[0].text is the sentence itself, isError is still false
No chat named "Book club" is open to agents. What IS open: Oak Street parents, Building committee. Only the person can open another, in Circles.
```

## Autenticación: OAuth 2.1 y tokens

|  | Valor |
| --- | --- |
| Metadatos del recurso protegido | `https://app.usecircles.app/.well-known/oauth-protected-resource` |
| Metadatos del servidor de autorización | `https://app.usecircles.app/.well-known/oauth-authorization-server` (emisor `https://app.usecircles.app`) |
| Direcciones | `/oauth/authorize`, `/oauth/token`, `/oauth/register` (registro dinámico de clientes), `/oauth/revoke` |
| Flujo | Código de autorización con PKCE (solo S256). Sin flujo implícito, sin secretos de cliente (`token_endpoint_auth_methods_supported: none`). |
| 401 | `WWW-Authenticate: Bearer resource_metadata=...` apuntando a los metadatos del recurso protegido |
| URI de redirección | `https` en cualquier lugar, o `http` en localhost, 127.0.0.1 o [::1]. Los esquemas personalizados se rechazan. |
| Scopes | `chats:read` (leer los chats abiertos), `media:read` (abrir sus archivos), `chats:send` (enviar donde la persona lo permitió) |
| Aprobación | En la página de Circles. El cliente recibe solo lo que la persona deja marcado ahí, pida lo que pida. Para los clientes que se registran por su cuenta, lectura y archivos empiezan marcados y el envío desmarcado. Los clientes propios de Circles pueden empezar con el envío marcado. Revisa los permisos antes de aprobar. |
| Duración | Token de acceso 1 hora. Token de renovación 30 días, rotado en cada uso, con detección de reutilización. |
| Tokens creados | Se crean en la página de agentes (Nuevo token). Prefijo `circ_`, se muestran una vez, solo se guarda un hash. Duración: sin límite (por defecto), 90 días o 1 año. Sin renovación. |
| Código de conexión de un solo uso | `circ_code_...`, se canjea en `POST /api/v1/connect/exchange` con `{"code": "..."}` y sin Authorization. Un solo uso, 1 hora, no abre nada por sí mismo, solo hash, 10 intentos por minuto por dirección. |
| Revocar | Desconectar en la página de agentes revoca al instante los permisos OAuth y los tokens creados |

Un scope restringe; nunca amplía. Un token con todos los scopes sigue llegando solo a los chats que la persona abrió, porque los niveles de los chats se vuelven a leer en cada llamada.

## API HTTP

Las mismas operaciones que MCP, como JSON por HTTP, para agentes y herramientas que llaman a una API. OpenAPI 3.1 (versión 2.0.0) en `https://app.usecircles.app/api/openapi.json`; una referencia de solo lectura en `https://app.usecircles.app/api`.

Cada solicitud lleva `Authorization: Bearer `: una clave `circ_` creada o el token OAuth de un cliente MCP. No se acepta nada más, ni cookies.

- **Lecturas:** `GET https://app.usecircles.app/api/v1/agent/allowed_chats`, `/activity`, `/people`, `/catch_up`, `/search`, `/documents`, `/group_members`, con los mismos parámetros que las herramientas.
- **Detalles:** `GET /chats/{chat_ref}` y `GET /messages/{message_ref}` devuelven lo permitido (`capabilities`, con motivos y notas) y las `actions` completas.
- **Enviar:** `POST /chats/{chat_ref}/messages` (texto, o un archivo de hasta unos 3 MB con un pie opcional).
- **Responder:** `POST /messages/{message_ref}/replies`, una respuesta citada nativa de WhatsApp. Una respuesta que no se puede citar se rechaza.
- **Reaccionar:** `POST /messages/{message_ref}/reactions`, una reacción con emoji.
- **Chat nuevo:** `GET /recipients?to=` con un número de teléfono (con código de país, codificado para URL) o el nombre exacto de un contacto guardado; responde como los detalles de un chat.
- **Comprobante de escritura:** `GET /writes/{submission_ref}`.
- **Archivos:** `POST /get_media` (puede pedir el archivo al celular), `GET /media/{ref}/status`, `/content` y `/view` (nunca se lo piden al celular).
- **Código de conexión:** `POST https://app.usecircles.app/api/v1/connect/exchange`.

**Actuar.** Para enviar, responder o reaccionar, haz GET de los detalles del chat o del mensaje y ejecuta una de sus `actions` tal como viene: una solicitud completa (método, URL, encabezados, cuerpo) a la que el agente solo agrega su encabezado Authorization y el texto.

**Escrituras idempotentes.** Cada escritura lleva una referencia de envío emitida por el servidor y atada a su destino. Reutilizarla no puede volver a enviar ni ocupar otro lugar del límite diario, durante 24 horas. Una entrega incierta devuelve `delivery_unknown` y nunca se reintenta: lee el comprobante y el chat antes de hacer un envío nuevo y deliberado.

**Paginación.** Las respuestas en lista traen `pagination.next_url` y `has_more` en el nivel superior. Sigue `next_url` hasta que sea null; cada página vuelve a verificar la autorización. Las páginas de `catch_up` van de los mensajes más nuevos hacia atrás.

**Errores.** Un 401 significa que la clave ya no funciona. Un 403 `scope_required` significa que la persona no dio ese permiso. Un argumento inválido nombra el campo y su rango permitido.

No se admite: votar en encuestas, quitar una reacción, citar un mensaje que no es de texto.

```
# Read: which chats moved since 06:00 UTC
curl -H "Authorization: Bearer circ_..." \
  "https://app.usecircles.app/api/v1/agent/activity?since=2026-10-27T06:00:00Z"

# Act: GET a chat's details, then perform one of its actions as given
curl -H "Authorization: Bearer circ_..." "https://app.usecircles.app/api/v1/agent/chats/<chat_ref>"
# -> "actions": [{ "method": "POST", "url": ".../chats/<chat_ref>/messages",
#                  "headers": {...}, "body": { ... "submission_ref": "..." } }]

# An agent handed a one-time code (Instinct) swaps it for its key, no Authorization
curl -X POST -H "Content-Type: application/json" \
  -d '{"code":"circ_code_..."}' https://app.usecircles.app/api/v1/connect/exchange
```

## Límites

| Qué | Límite |
| --- | --- |
| Texto por mensaje | 65.536 caracteres (el límite de WhatsApp); un pie, 4.096 |
| Archivo para enviar | Unos 3 MB |
| Archivo para traer | 50 MB por archivo; más grande es `too_large` |
| Envíos por día | Por defecto 5 por número, sumando todos los agentes; 5, 10, 20 o sin límite; contados por día UTC |
| `catch_up`, un chat | Por defecto 50, máximo 200 mensajes |
| `catch_up`, todos los chats | Por defecto 20, máximo 50 por chat; 150 en total |
| `people` | Por defecto 60, máximo 150 personas; `min_messages` por defecto 3 |
| `group_members` | Como máximo 300 nombres |
| `search` | Hasta 36.500 días atrás, o la ventana de la cuenta |
| `get_media` | Una ref por llamada; espera unos 25 segundos a que el celular vuelva a subir el archivo |
| Descarga de archivos en segundo plano | Dentro de unos 25 días desde el mensaje; los más antiguos, a pedido |
| Historial | Los agentes ven el último mes en Free y los últimos 3 meses en Starter; en Pro, todo el historial, o los últimos 90, 60 o 30 días |
| Chats abiertos a los agentes | 2 en Free; hasta 5 en Starter; todos los que la persona elija en Pro |
| Envío | Solo en Pro |
| Transcripción de audios | Automática, en hebreo e inglés; en español todavía no está verificada |
| Tokens OAuth | Acceso 1 hora, renovación 30 días |
| Código de conexión de un solo uso | Un solo uso, 1 hora, 10 intentos por minuto por dirección |
| Idempotencia de escrituras | 24 horas por referencia de envío |
| Enlace de inicio de sesión | Un solo uso, 10 minutos |
| Números de WhatsApp | Uno por cuenta, en todos los planes |

## Registro de cambios

Cambios recientes que notaría un agente o un integrador.

- **2026-10-27**: Instinct se conecta con un código de un solo uso enviado desde WhatsApp, sin mostrar ni pegar ninguna clave.
- **2026-10-27**: Un mensaje puede ser tan largo como permite WhatsApp: 65.536 caracteres.
- **2026-10-27**: Las reacciones se guardan en su mensaje y aparecen en cada lectura.
- **2026-10-27**: Los chats individuales se abren para lectura por defecto en un número nuevo.
- **2026-10-27**: API HTTP 2.0.0: se quitaron los agentes de navegador; la API de agentes acepta solo una clave Bearer.
- **2026-10-26**: Una suscripción cancelada sigue funcionando hasta el final del período pagado, y `allowed_chats` le dice la fecha al agente.
- **2026-10-23**: Los agentes pueden escribirle a alguien con quien todavía no hay chat, por número de teléfono o nombre de contacto guardado.
- **2026-10-19**: Los agentes pueden enviar un archivo de cualquier tipo: un PDF, un documento, un video o una imagen.
- **2026-10-19**: Grabaciones: filtros `kind=voice|audio|recording` y `duration_seconds`; cada hora se indica en UTC.
- **2026-10-19**: Claude y Codex se conectan por dirección, con aprobación en la página de Circles.
- **2026-10-18**: Los audios se transcriben automáticamente y los agentes leen la transcripción en el mensaje.
- **2026-10-18**: Todo el historial es el valor por defecto, para números nuevos y existentes.
- **2026-10-16**: API HTTP con respuestas y reacciones nativas; se aplican las ediciones y eliminaciones hechas en WhatsApp.

## Preguntas y respuestas

### ¿Cuál es la URL del servidor MCP de Circles?

`https://app.usecircles.app/api/mcp`. Es un servidor MCP remoto por HTTP que acepta OAuth 2.1 (con descubrimiento y registro dinámico) o un token bearer que creas en Circles.

### ¿Circles admite streaming o SSE?

Circles responde cada solicitud MCP con una sola respuesta JSON por HTTP POST, sin estado. No hay un flujo de eventos que mantener abierto, así que cualquier cliente que hable MCP por HTTP puede usarlo.

### ¿Por qué mi agente recibió un rechazo en lugar de un error?

Circles devuelve los rechazos como respuestas (`isError: false`) para que los agentes los lean y no los reintenten. Cada uno nombra lo que SÍ está abierto o permitido.

### ¿Por qué un archivo está en “fetching”?

Circles le pidió al celular de la persona que vuelva a subir un archivo que WhatsApp ya no tiene. No se perdió: vuelve a pedirlo con la misma ref en un minuto, con el celular en línea.

### ¿Puedo responder o reaccionar a un mensaje?

Sí, por la API HTTP de Circles: respuestas citadas nativas y reacciones con emoji. Las herramientas MCP solo envían mensajes y archivos simples.

## Sigue leyendo

- [Las guías, para personas](https://usecircles.app/es/docs): Vincular WhatsApp, elegir chats, conectar un agente.
- [WhatsApp MCP, de un vistazo](https://usecircles.app/es/whatsapp-mcp): Qué es Circles y en qué se diferencia de tener tu propio servidor.
- [Seguridad y control](https://usecircles.app/es/security): Niveles por chat, aprobación, registro de actividad y los riesgos.

---

14 días de Pro gratis, sin tarjeta. Vincula tu número, elige los chats y conecta la IA que ya usas: Instinct con un toque, Claude, ChatGPT y Codex con una dirección. Empieza aquí: https://usecircles.app/start?lang=es
