Sendly MCP

Envía correos transaccionales, ejecuta campañas y gestiona contactos, listas y segmentos en Sendly. Servidor remoto con inicio de sesión OAuth.

Servidor MCP alojado

npx add-mcp 'https://app.sendly.now/api/mcp'

Se instala en Claude Code, Codex, Cursor y más

Documentación

Servidor MCP (/guides/mcp)

Sendly ejecuta un servidor remoto de Model Context Protocol. Conecta un cliente de IA compatible con MCP, elige lo que puede hacer, y el agente puede trabajar directamente en tu cuenta de Sendly: responder qué dominios están verificados, ordenar tus contactos, diagnosticar por qué un mensaje no llegó, crear y pausar tus automatizaciones, redactar la próxima campaña y, si tú lo autorizas, enviarla.

Los permisos que no se pueden deshacer (enviar correo, revocar una clave de API, eliminar una dirección de tu lista de supresión) **nunca se otorgan por defecto**. Aparecen sin marcar en la pantalla de consentimiento, cada uno con una frase sencilla que indica la consecuencia, y tú mismo los marcas. Todo lo demás es un permiso que puedes retirar más tarde sin desconectarte.

Endpoint [#endpoint]

https://app.sendly.now/api/mcp

El transporte es HTTP Streamable. La autorización es OAuth 2.1 con PKCE, o una clave secreta de Sendly.

Sendly aparece en el registro oficial de MCP como *now.sendly/sendly**. El listado es solo metadatos: el endpoint anterior, el transporte que utiliza y el encabezado opcional Authorization — porque Sendly es un servidor remoto: no hay nada que instalar ni ningún paquete que descargar. Un cliente que encuentre Sendly a través del registro se conecta exactamente a la URL que de otro modo pegarías manualmente.

Conectar [#connect]

Claude Code [#claude-code]

La primera llamada a una herramienta abre un navegador en Sendly, donde inicias sesión y eliges los permisos. Después de eso, la conexión persiste.

¿Configurando otro agente (Codex, Cursor, Windsurf, OpenCode o VS Code/Copilot)? Consulta Onboard your agent para ver el comando exacto por cliente, o pega el agent-setup prompt en el propio agente y deja que ejecute la configuración.

Claude (web y escritorio) [#claude-web-and-desktop]

Añade Sendly como conector personalizado en la configuración de Claude, usando la URL del endpoint anterior. Los conectores personalizados están disponibles en los planes de pago de Claude; la política de tu espacio de trabajo de Claude decide si los miembros pueden añadirlos.

Cualquier otro cliente MCP [#any-other-mcp-client]

Sendly funciona con cualquier cliente que hable HTTP Streamable y admita el flujo de código de autorización OAuth con PKCE. Dale al cliente la URL del endpoint: no hay ninguna aplicación que pre-registrar. El cliente descubre todo lo que necesita y se registra automáticamente:

Documento de descubrimientoURL
Metadatos de recurso protegido (RFC 9728)https://app.sendly.now/.well-known/oauth-protected-resource
Metadatos del servidor de autorización (RFC 8414)https://app.sendly.now/.well-known/oauth-authorization-server

El emisor es https://app.sendly.now/api/auth. El registro dinámico de clientes está abierto, por lo que un cliente que nunca haya hablado con Sendly puede registrarse e iniciar el flujo sin supervisión. También se sirven alias insertados en la ruta de ambos documentos, para que los clientes que deriven la URL de metadatos de cualquier manera puedan encontrarla.

El endpoint MCP acepta un token de acceso OAuth **o** una clave secreta de Sendly (`sk_…`). OAuth es adecuado para una persona que conecta un cliente de escritorio: apruebas permisos en una pantalla de consentimiento y gestionas la conexión en **Configuración → Aplicaciones conectadas**. Una clave secreta es adecuada para un agente sin interfaz o de CI: los permisos de la propia clave son lo que el agente puede hacer, y la gestionas en **Configuración → Claves de API**. Las claves de solo envío (`pk_…`) y las sesiones del panel siguen rechazándose: una clave `pk_` solo puede enviar, y una sesión es una credencial de navegador que nadie destinó a un agente.

Conectar con una clave de API [#connecting-with-an-api-key]

Coloca la clave en el encabezado Authorization y omite por completo el flujo OAuth:

Una conexión con clave se diferencia de una OAuth en tres aspectos que vale la pena conocer:

  • Los alcances de la propia clave son los permisos del agente. Lo que marcaste al crear la clave es lo que recibe el agente: no hay una segunda pantalla de consentimiento. Con una excepción documentada: algunas herramientas necesitan una persona con sesión iniciada en lugar de un proyecto, porque las rutas detrás de ellas identifican a un administrador del proyecto a partir del usuario. Esas nunca se ofrecen a una conexión con clave, sin importar lo que hayas marcado: create_project, create_mailbox, delete_mailbox y las cuatro herramientas de claves de API. Usa una conexión OAuth para esas.
  • Está vinculada a un solo proyecto. El proyecto de la clave es el destino de cada llamada, y un argumento projectId que no coincida se rechaza con PROJECT_FIXED en lugar de ignorarse silenciosamente. Para actuar en otro proyecto, usa una clave que pertenezca a él.
  • Es una credencial de proyecto, no personal. No aparece en Configuración → Aplicaciones conectadas, porque no hay ninguna fila de consentimiento detrás. La revocas desde Configuración → Claves de API, y la revocación detiene al agente en su siguiente llamada.
Una clave creada antes de que Sendly tuviera permisos por capacidad solo lleva la antigua configuración gruesa `FULL` / solo envío. Sus alcances se **infirieron** en lugar de elegirse, por lo que en este endpoint esa clave no recibe ninguna herramienta detrás de uno de los nueve permisos que necesitan aprobación explícita: nada de `send_email`, nada de `send_campaign`, nada de `update_workflow`, nada de `remove_suppression`, y así sucesivamente para el resto de esa lista. Eso no es un error y no hay ninguna casilla que volver a marcar: la solución es **crear una nueva clave en Configuración → Claves de API y marcar los permisos que quieres**. Todo lo demás que la clave siempre pudo hacer sigue funcionando, aquí y en la API REST, sin cambios.

Elegir lo que un agente puede hacer [#choosing-what-an-agent-can-do]

Ambas pantallas que te piden otorgar capacidades (la pantalla de consentimiento OAuth y el diálogo de claves de API) se abren con un ajuste predefinido con nombre y luego te permiten marcar casillas individuales. Un ajuste predefinido es solo una selección inicial; no se almacena nada excepto la lista resultante de permisos.

Ajuste predefinidoQué cubrePermisos¿Contiene algo irreversible?
Solo lecturaVer todo en este proyecto, no cambiar nada.18No
Acceso estándar* *(predeterminado)Gestionar contactos, plantillas, segmentos y borradores de campañas, y enviarte correos de prueba a ti mismo. No puede enviar correo a nadie más.29No
Envío y campañasTodo lo del acceso estándar, más enviar correo y ejecutar tus automatizaciones.32Sí — 3
Acceso completoTodo, incluidos buzones, nuevos proyectos y claves de API.38Sí — los 9

El acceso estándar es el predeterminado y es deliberadamente una concesión capaz: un agente que gestiona tus contactos, segmentos, plantillas y borradores de campañas, y que no puede poner correo en la bandeja de entrada de nadie. La mayoría de la gente quiere eso y nada más.

Una concesión sin permisos marcados es válida. Significa que el cliente puede iniciar sesión por ti y no aprender nada más.

Conectar un agente de solo lectura [#connecting-a-read-only-agent]

Solo lectura es el ajuste predefinido al que recurrir cuando un agente debe responder preguntas sobre un proyecto y no cambiar nada en él. Dos consecuencias vale la pena conocer antes de elegirlo, y el servidor aplica ambas en lugar de solo documentarlas.

  • La lista de herramientas es más corta. Las herramientas se filtran según los permisos de la conexión antes de mostrarle nada al agente, por lo que a una conexión de solo lectura nunca se le ofrecen send_campaign, create_contact ni edit_workflow. No puede llamar a lo que no puede ver, y no gasta un turno descubriendo un rechazo.
  • Se ofrece diagnose_delivery. La entregabilidad tiene un permiso de lectura propio, que es lo que permite que la pregunta que la gente más le hace a un agente —¿por qué no llegó este correo?— se pueda responder sin otorgar ni una sola escritura.

Dos permisos están deliberadamente fuera. api-keys:read enumera tus credenciales, por lo que no pertenece a ningún ajuste predefinido pre-marcado: un agente de solo lectura puede ver tus contactos, pero no lo que tus claves pueden hacer. Y emails:test está en Acceso estándar en su lugar, porque "solo lectura" es una promesa sobre el mundo más que sobre nuestra base de datos: un ajuste predefinido que envía correo, incluso a ti, ha roto la promesa que su nombre hace.

Permisos que necesitan aprobación explícita [#permissions-that-need-explicit-approval]

Nueve permisos pueden producir un efecto que nada en el panel de Sendly deshace. Se muestran sin marcar, con la advertencia siguiente junto a la casilla:

PermisoDe qué se te advierte
emails:sendEl correo enviado de esta manera llega a bandejas de entrada reales y no se puede recuperar.
campaigns:sendEsto envía una campaña a toda tu audiencia y no se puede recuperar.
workflows:writeUn flujo de trabajo habilitado sigue enviando por sí solo, mucho después de esta conversación.
suppression:writeEliminar una dirección permite que Sendly envíe correo a alguien que te pidió que te detuvieras.
projects:writeLos nuevos proyectos cuentan para tu plan y pueden facturarse.
api-keys:readRevela qué claves existen y qué puede hacer cada una.
api-keys:writeUna clave creada aquí sigue funcionando incluso después de que desconectes esta aplicación.
mailboxes:writeUn nuevo buzón comienza a recibir correo real en tu dominio, y eliminar uno borra todos los mensajes que contiene.
mailboxes:sendEl correo enviado de esta manera llega desde tu propia dirección de soporte y no se puede recuperar.

Dos de estos merecen una explicación, porque son los que la gente consulta. api-keys:read no cambia nada: está en la lista por lo que revela, ya que un mapa de qué credenciales existen y qué puede hacer cada una es un mapa de la superficie de ataque de tu cuenta. Y campaigns:write no está en la lista: redactar una campaña y enviarla por correo son permisos separados ahora, por lo que un agente puede crear una campaña para ti sin poder enviarla.

Modo Código (predeterminado) [#code-mode-default]

Una conexión ve exactamente dos herramientas, no una por operación: search_tools y execute_typescript. Cada capacidad en Full surface a continuación sigue existiendo detrás de ellas, accesible de la misma manera, bajo los mismos permisos: esto cambia cuántas herramientas lista un cliente, no lo que un agente puede hacer.

HerramientaQué hace
search_toolsEncuentra las herramientas a las que esta conexión puede acceder y devuelve cada una como una firma TypeScript declare function external_<name>(...), etiquetada como [read-only] o [write] con su título y descripción. Un argumento opcional query reduce el resultado a una coincidencia de subcadena sin distinción de mayúsculas y minúsculas contra el nombre, título o descripción de una herramienta: reduce lo que la conexión ya puede alcanzar y nunca lo amplía. Omítelo para listar todo lo accesible.
execute_typescriptEjecuta un programa TypeScript breve, escrito por el agente, en un entorno aislado. El programa llama a las funciones external_* declaradas search_tools —ya están en el ámbito— y debe return su resultado. await Promise.all([...]) ejecuta llamadas independientes en un solo viaje de ida y vuelta en lugar de varios.

Llamar a external_<name>(...) desde dentro del programa alcanza el mismo manejador que ejecutaría una llamada directa a esa herramienta: las mismas comprobaciones de autenticación, alcance, selección de proyecto, confirmación de envío masivo y auditoría, ya sea que el agente la haya llamado por nombre o mediante execute_typescript. Orquestar tres llamadas en un solo programa cuesta un viaje de ida y vuelta de MCP en lugar de tres; no hace nada que tres llamadas de herramienta separadas no pudieran hacer.

search_tools declara solo las herramientas a las que llegan los permisos de esta conexión. Cualquier otro nombre de external_* simplemente no está definido dentro del programa, por lo que llamar a uno lanza un ReferenceError en lugar de una negativa codificada.

Una negativa dentro del programa —un proyecto al que no puede dirigirse, una conexión revocada, un envío masivo sin confirmar, argumentos que no coinciden con la declaración— se manifiesta como un Error de JavaScript lanzado, no como una forma de resultado separada: el mensaje dice <CODE>: <details>, por ejemplo CONFIRMATION_REQUIRED: … o INVALID_ARGUMENTS: …, usando los mismos códigos que Solución de problemas más abajo. Cuando la negativa lleva campos estructurados (requiredScope, upstreamCode, upstreamStatus), estos siguen a los detalles como JSON final, por ejemplo TOOL_EXECUTION_FAILED: … {"upstreamCode":"CONFLICT","upstreamStatus":409}. El propio try/catch del programa lo ve como cualquier otra excepción, y la llamada a la herramienta externa no se marca como error: la llamada en sí tuvo éxito; el código que escribió el agente es lo que falló. Un programa que se ejecuta durante aproximadamente 30 segundos, incluido un bucle infinito, se detiene y la llamada aún responde normalmente, informando que no terminó en lugar de colgarse.

Si un cliente no puede orquestar un programa aislado en absoluto, Sendly puede servir la superficie de una herramienta por operación que se muestra a continuación directamente en su lugar (MCP_TOOL_SURFACE=full): un interruptor operativo, no algo que una conexión solicite por sí misma.

Superficie completa [#full-surface]

La superficie de una herramienta por operación que execute_typescript orquesta arriba, y la que Sendly también puede servir directamente. Un agente solo ve las herramientas que cubre su concesión aquí también: si apruebas analytics:read y nada más, view_analytics y list_projects son las únicas herramientas a continuación que aparecen: las demás nunca se registran, por lo que el agente ni siquiera puede intentarlas.

Tus proyectos [#your-projects]

HerramientaQué hacePermiso
list_projectsLista los proyectos en los que esta conexión puede actuar. Úsalo para elegir el projectId para otras herramientas.ninguno
get_projectLa configuración del proyecto activo: nombre, región de envío, modo de seguimiento de enlaces, si está deshabilitadoprojects:read
create_projectCrea un nuevo proyecto en tu cuenta. Solo conexiones OAuth: una clave API no tiene un usuario para agregar como miembroprojects:write

Contactos y segmentos [#contacts-and-segments]

HerramientaQué hacePermiso
list_contactsLista contactos, opcionalmente filtrados por correo electrónico o estado de suscripcióncontacts:read
get_contactUn contacto, con sus campos personalizadoscontacts:read
create_contactAgrega un contactocontacts:write
update_contactEdita los campos o el estado de suscripción de un contactocontacts:write
delete_contactElimina un contactocontacts:write
list_segmentsLista segmentossegments:read
get_segmentUn segmento y su condiciónsegments:read
list_segment_contactsQuién coincide actualmente con un segmentosegments:read
create_segmentCrea un segmentosegments:write
update_segmentEdita la condición de un segmentosegments:write
delete_segmentElimina un segmentosegments:write

Plantillas y dominios de envío [#templates-and-sending-domains]

HerramientaQué hacePermiso
list_templatesLista plantillas de correo electrónicotemplates:read
get_templateUna plantilla, con su cuerpotemplates:read
create_templateCrea una plantillatemplates:write
update_templateEdita una plantillatemplates:write
check_domainEstado de verificación de tus dominios de envíodomains:read
add_domainRegistra un dominio de envío y devuelve los registros DNS a publicardomains:write
verify_domainVuelve a verificar los registros DNS de un dominio y persiste el resultadodomains:write
start_domain_setupInicia la configuración DNS guiada y devuelve un enlace que abres para publicar los registros en tu registradordomains:write

Correo electrónico [#email]

HerramientaQué hacePermiso
list_emailsLos correos electrónicos que has enviado, con estado de entregaemails:read
get_emailUn correo electrónico y sus eventosemails:read
send_test_emailEnvía un mensaje de prueba desde la dirección de zona de pruebas del proyecto. Solo puede llegar al correo electrónico de cuenta verificado del propio propietario del proyecto: cualquier otro destinatario es rechazadoemails:test
send_emailEnvía un correo electrónico transaccional. Llega a una bandeja de entrada real y no se puede recuperaremails:send

send_test_email es cómo un agente demuestra que el envío funciona sin poder enviar correo a nadie. No toma ningún argumento from: el remitente es la dirección de zona de pruebas del proyecto, y la ruta rechaza un cuerpo que nombre uno— y hay un límite diario en los envíos de zona de pruebas. Es un miembro de Acceso estándar, por lo que ese ajuste preestablecido puede confirmar tu configuración de extremo a extremo mientras sigue sin poder poner correo en la bandeja de entrada de un extraño.

Campañas [#campaigns]

HerramientaQué hacePermiso
list_campaignsLista campañascampaigns:read
get_campaignUna campañacampaigns:read
get_campaign_statsLos números de entrega y participación de una campañacampaigns:read
create_campaignCrea un borrador de campañacampaigns:write
update_campaignEdita el contenido o la audiencia de un borrador o campaña programadacampaigns:write
manage_campaignCancela, pausa o reanuda una campañacampaigns:write
delete_campaignElimina una campañacampaigns:write
send_campaignEnvía o programa una campaña a su audiencia completa. No se puede recuperar una vez que comienza el envíocampaigns:send
Cuando el proyecto tiene más de **1,000 contactos**, la primera llamada a `send_campaign` es rechazada con `CONFIRMATION_REQUIRED`, y la negativa le dice al agente que indique a cuántas personas llega la campaña y qué dice, luego llame de nuevo con `confirm: true` solo si estás de acuerdo. Nada se envía con la llamada rechazada: la verificación se ejecuta antes de que se toque la propia API de Sendly.

La protección es del lado del servidor por una razón. Sendly puede pedirle a tu cliente que confirme a través del protocolo, pero este endpoint no mantiene sesión, por lo que esa solicitud no llega a nadie; una protección cuya única aplicación vive en el cliente no es una protección. Requerir una segunda llamada que lleve un argumento adicional es algo que un servidor sin estado puede realmente aplicar. El umbral se mide contra los contactos del proyecto, no contra la audiencia de la campaña. El recuento de audiencia es un número almacenado en caché que un trabajo en segundo plano actualiza, por lo que está desactualizado exactamente cuando importa: una lista recién construida que aún muestra cero. Cada audiencia es un subconjunto de los contactos del proyecto, por lo que este límite no puede ser incorrecto en la dirección peligrosa. Sí pide de más: un envío a un segmento de tres personas dentro de un proyecto grande aún requiere la confirmación, que es la forma correcta de equivocarse en una pregunta cuya respuesta no se puede recordar.

send_email no está cubierto y no necesita estarlo. Acepta un destinatario, por lo que enviar un correo a mil personas a través de él son mil llamadas visibles en lugar de la única llamada cuyo radio de explosión el agente nunca tuvo que declarar.

Flujos de trabajo [#workflows]

HerramientaQué hacePermiso
list_workflowsListar flujos de trabajo de automatizaciónworkflows:read
get_workflowUn flujo de trabajo y sus pasosworkflows:read
get_workflow_statusEl estado actual de un flujo de trabajo: habilitado o no, qué lo activa, cuántos pasos tiene, y cómo han ido sus ejecuciones: el total y el recuento de las que están en curso, en espera, completadas, fallidas y canceladasworkflows:read
list_workflow_executionsLas ejecuciones de un flujo de trabajo, una fila por contactoworkflows:read
create_workflowConstruir un flujo de trabajo a partir de una especificación: un disparador, su configuración y una lista ordenada de pasos. Se inicia deshabilitado a menos que se indique lo contrario, para que puedas revisarlo antes de que se ejecuteworkflows:write
edit_workflowCambiar el nombre, la descripción, el estado habilitado o el disparador de un flujo de trabajo y, opcionalmente, reemplazar toda su secuencia de pasos en la misma llamadaworkflows:write
clone_workflowCopiar un flujo de trabajo, pasos incluidos, como un nuevo borrador. La copia siempre se inicia deshabilitada; el original no se modificaworkflows:write
manage_workflowPausar o reanudar un flujo de trabajo. Pausar detiene nuevas ejecuciones y cancela las que ya están en curso, informando cuántas terminóworkflows:write
update_workflowEditar solo metadatos: nombre, descripción, evento disparador, estado habilitado, reentrada, límite por hora. No puede crear ni reemplazar el grafo de pasos: edit_workflow es la herramienta que puede hacerloworkflows:write
delete_workflowEliminar un flujo de trabajo y su historial de ejecuciones. Se rechaza con un 409 mientras alguna de sus ejecuciones siga en cursoworkflows:write

manage_workflow tiene exactamente dos acciones, pause y resume. Leer el estado de un flujo de trabajo solía ser una tercera y ahora es get_workflow_status, lo cual es un hecho de permisos más que una reorganización: una herramienta declara un permiso y debe poder alcanzar todo lo que hace; pausar necesita workflows:write, y leer los recuentos de ejecuciones solo necesita workflows:read. Si se dejara como una sola herramienta, la acción de lectura se rechazaría precisamente para las conexiones a las que se les otorgó escritura sin lectura. Dividida, un agente que puede mirar pero no tocar aún puede responder "¿está esta automatización en ejecución y cuántos contactos hay dentro?".

Establecer `enabled` en false — mediante `update_workflow` o `edit_workflow` — detiene que el flujo de trabajo sea *disparado* de nuevo y deja que cada contacto que ya está a mitad de camino recorra los pasos. El siguiente retraso aún expira y el siguiente correo aún se envía.

manage_workflow con pause hace ambas cosas. Detiene nuevas ejecuciones y cancela las ejecuciones ya en curso, e informa cuántas canceló, para que el agente pueda distinguir "no había nada en ejecución" de "acabo de detener cuatrocientas jornadas". Cancelar es terminal: resume reabre el flujo de trabajo a nuevas ejecuciones, no devuelve a los contactos cancelados a donde estaban.

Este es el borde práctico de la advertencia en workflows:write: un flujo de trabajo habilitado sigue enviando por sí solo, mucho después de la conversación que lo habilitó.

Construir los pasos de un flujo de trabajo [#building-a-workflows-steps]

create_workflow y edit_workflow aceptan una lista lineal de pasos, que es lo que casi toda automatización es. El paso disparador se antepone por ti: no lo incluyas, y pasar steps a edit_workflow reemplaza cada paso existente en lugar de fusionar, que es por lo que esa herramienta está marcada como destructiva.

Cualquier cosa con una rama es un grafo en lugar de una secuencia, y los grafos se leen y escriben completos en GET y PUT /api/v1/workflows/{id}/graph — workflows:read y workflows:write respectivamente. Una respuesta de GET se acepta textualmente por PUT en la misma ruta, por lo que el viaje de ida y vuelta es: leer el documento, cambiar un paso, enviarlo todo de vuelta.

{
  "workflow_id": "8f1c4d2e-5a7b-4a1e-9c3f-2b6d8e0a1f42",
  "version": 7,
  "steps": [
    {
      "id": "1b9f6a30-2c44-4f8e-9a01-77c2d1b5e903",
      "type": "TRIGGER",
      "name": "Signed up",
      "position": { "x": 0, "y": 0 },
      "config": { "eventName": "user.signup" },
      "template_id": null
    },
    {
      "id": "3d2e7c81-9b05-4a26-8f13-0c5a4e9d6712",
      "type": "DELAY",
      "name": "Wait a day",
      "position": { "x": 0, "y": 160 },
      "config": { "amount": 1, "unit": "days" },
      "template_id": null
    },
    {
      "id": "5a4b8e12-6d37-4c90-b2e5-1f8c3a70d954",
      "type": "SEND_EMAIL",
      "name": "Day 1: getting started",
      "position": { "x": 0, "y": 320 },
      "config": {},
      "template_id": "c7e1a904-3b62-4d58-8a17-9e05f2d6b481"
    }
  ],
  "transitions": [
    {
      "id": "9c0d5f73-1a86-42be-9d47-6b3e8a15c027",
      "from_step_id": "1b9f6a30-2c44-4f8e-9a01-77c2d1b5e903",
      "to_step_id": "3d2e7c81-9b05-4a26-8f13-0c5a4e9d6712",
      "condition": null,
      "priority": 0
    },
    {
      "id": "2e6a1b48-7f39-4c05-a8d2-53b90c7e6f18",
      "from_step_id": "3d2e7c81-9b05-4a26-8f13-0c5a4e9d6712",
      "to_step_id": "5a4b8e12-6d37-4c90-b2e5-1f8c3a70d954",
      "condition": null,
      "priority": 0
    }
  ]
}

Envía ese mismo documento de vuelta a PUT para mantener el flujo de trabajo como está, o cámbialo primero. Las reglas que sigue el documento:

  • Los ids de los pasos son tuyos. Envía de vuelta un id que leíste para conservar ese paso y su historial de ejecuciones, un UUID nuevo para añadir un paso, y omite un paso por completo para eliminarlo junto con su historial.
  • Exactamente un paso es el TRIGGER — el único nodo de entrada del grafo.
  • config se tipa por tipo de paso, entre los nueve tipos TRIGGER, SEND_EMAIL, DELAY, WAIT_FOR_EVENT, CONDITION, EXIT, WEBHOOK, UPDATE_CONTACT y SEND_AT_OPTIMAL_TIME. Debido a que el contrato discrimina en type, un SDK generado reduce config desde el tipo que ya conoces. Una clave que el contrato no nombra se pasa tal cual en lugar de eliminarse; una clave cuyo valor es incorrecto — unit: "fortnights", un objetivo de webhook que no es una URL — se rechaza.
  • Cada borde permanece dentro del documento. from_step_id y to_step_id deben nombrar pasos en el mismo payload, y un paso no puede apuntarse a sí mismo. priority ordena los bordes que salen de un paso, de menor a mayor. Los bordes de un paso CONDITION llevan { "branch": "yes" } y { "branch": "no" }, o el id de la rama en la forma de múltiples ramas.
  • Como máximo 200 pasos y 400 transiciones.
  • version avanza en cada escritura estructural, y cada escritura toma una instantánea del grafo, por lo que un número diferente entre dos lecturas significa que alguien lo editó en el medio.
  • Una escritura de grafo se rechaza con un 409 mientras el flujo de trabajo tenga ejecuciones en curso, porque esos contactos están parados en los pasos que se están reemplazando. POST /api/v1/workflows/{id}/pause los despeja primero.

Para una secuencia lineal no necesitas estos endpoints en absoluto: POST /api/v1/workflows y PATCH /api/v1/workflows/{id} ambos aceptan un sequence, que es exactamente lo que create_workflow y edit_workflow envían, por lo que las dos credenciales construyen el mismo grafo en lugar de grafos similares.

POST /api/v1/workflows/{id}/clone — la ruta detrás de clone_workflow — copia un flujo de trabajo y todo su grafo en el lado del servidor, desde una lectura consistente. Eso no es lo mismo que leer un grafo y escribirlo en un nuevo flujo de trabajo: una copia ensamblada a partir de dos solicitudes puede capturar una edición que ocurrió entre ellas y materializar un flujo de trabajo que nunca existió.

Analítica y uso [#analytics-and-usage]

HerramientaQué hacePermiso
view_analyticsRecuentos de enviados, entregados, abiertos y rebotadosanalytics:read
get_usageRecuentos de envíos de este mes y de hoy frente a tus límites aplicadosusage:read

Entregabilidad [#deliverability]

HerramientaQué hacePermiso
diagnose_deliveryPor qué el correo de uno de tus dominios de envío no llega: el estado DKIM, SPF, DMARC y MX del dominio, los contadores recientes de rebotes y quejas del proyecto y — si nombras un destinatario — si esa dirección está suprimidadeliverability:read

Cada señal en la respuesta ya era legible un endpoint a la vez. Lo que ningún endpoint hacía era decir qué significa la combinación, que es la parte en la que una conversación de soporte realmente se centra: "verificado, pero SPF fallando y 6% de rebotes" es un problema diferente de "no verificado", y distinguirlos a partir de tres payloads separados era un juicio que quien llamaba tenía que hacer sin ayuda.

Entonces la respuesta comienza con findings — peor primero, cada uno con un code estable, una severidad de blocking, degraded o info, qué está mal y la solución. Un agente se ramifica en el código, nunca en la redacción. Los códigos son domain_not_registered, domain_not_verified, recipient_suppressed, spf_failing, dmarc_missing, custom_mail_from_failed, bounce_rate_critical, bounce_rate_elevated, complaint_rate_critical, complaint_rate_elevated, no_recent_sends, sample_too_small_for_rates y dns_never_checked.

Dos cosas para leer las señales crudas. Los estados DNS son los resultados en caché de el trabajo de verificación de Sendly en lugar de una consulta en vivo, y identity.last_checked_at dice cuándo se completaron. Los contadores de entrega son del proyecto, en una ventana de 1 a 30 días que por defecto es 7, porque un registro de correo no almacena qué dominio lo envió.

Tiene un permiso propio en lugar de apoyarse en domains:read, porque lee el estado DNS, los contadores de entrega y la supresión juntos, y una clave a la que se le otorgó "ver tus dominios de envío" no aceptaba la última de esas. Nada bajo él escribe nada ni revela nada que un miembro del proyecto no pueda ver ya en el panel, por lo que está pre-marcado desde Solo lectura hacia arriba.

Limpiar una lista [#cleaning-a-list]

HerramientaQué hacePermiso
validate_emailsComprueba hasta 50 direcciones en una sola llamadavalidation:write
clean_listInicia una ejecución en segundo plano sobre cada dirección de una listavalidation:write
get_validation_runCuánto ha avanzado una ejecución y qué ha encontradovalidation:read
list_validation_resultsUna página de los veredictos por dirección de una ejecución, filtrable por veredictovalidation:read

Estas se facturan por dirección comprobada, por eso el permiso de escritura es una casilla propia en lugar de parte de contacts:write: un agente autorizado para gestionar tus contactos no debería poder gastar tu dinero recorriendo tu lista. Leer una ejecución terminada no cuesta nada y se encuentra en Solo lectura.

Cada respuesta incluye un verdict, y es el campo sobre el que decidir. deliverable es seguro para enviar por correo. undeliverable significa que el dominio no existe o no publica registros MX. risky significa un proveedor de buzón desechable. unknown significa que el DNS no respondió, por lo que esa dirección no fue comprobada — es un valor separado de undeliverable a propósito, porque un agente que fusionara ambos eliminaría contactos activos por un fallo de red.

Los otros campos describen la dirección en lugar de juzgarla. is_personal (un proveedor gratuito de consumo) y is_role_address (support@, info@) son información de calidad de lista, no problemas: los clientes reales usan Gmail y las empresas reales responden a su dirección de soporte. is_disposable es la única marca que baja un veredicto.

clean_list no limpia nada por sí mismo. Valida y se detiene — no se da de baja ninguna suscripción ni se elimina ningún contacto. Actuar sobre un hallazgo es una llamada separada bajo un permiso diferente, que es lo que evita que una consulta DNS que puede responder unknown reduzca silenciosamente una audiencia.

Listas de suscriptores [#subscriber-lists]

HerramientaQué hacePermiso
list_listsLas listas de suscriptores que mantiene este proyecto, con sus tamañoslists:read
get_listUna lista, con su tamaño y su configuración de doble opt-inlists:read
create_listCrear una lista vacíalists:write
update_listRenombrar una lista o cambiar su configuraciónlists:write
delete_listEliminar una lista y cada suscripción en ellalists:write

Una lista es una suscripción estática: personas que se añadieron y permanecen hasta que se van. Eso es lo que la separa de un segmento, que es una condición dinámica sobre campos de contacto, y de un tema, que es una decisión permanente sobre un asunto. member_count cuenta suscripciones en cada estado — una invitación pendiente y una exclusión voluntaria son ambas suscripciones — por lo que es el tamaño de la tabla de suscripciones, no el número de personas que un envío alcanzaría.

Añadir personas a una lista no está en esta superficie. La suscripción es donde comienza el doble opt-in: crea la suscripción como pendiente y genera un token de confirmación cuya entrega es tu trabajo. Un agente que pusiera a alguien en una lista estaría afirmando un consentimiento del que no tiene evidencia, por lo que las herramientas se detienen en la lista misma.

delete_list elimina el registro de consentimiento. Las suscripciones se van con la lista, y una suscripción dada de baja es la evidencia de que alguien optó por no participar — recrear la lista y reimportar las mismas direcciones no encontrará sus exclusiones esperando. El correo ya enviado no se ve afectado.

Temas y consentimiento [#topics-and-consent]

HerramientaQué hacePermiso
list_topicsLos asuntos sobre los que este proyecto envía correo, y cuántas personas respondieron a cada unotopics:read
get_contact_topic_preferencesTodo lo que un contacto ha dicho que quieretopics:read
create_topicAñadir un asunto al que las personas pueden suscribirsetopics:write
update_topicRenombrar, re-describir, cambiar el valor predeterminado o retirar un tematopics:write
set_topic_subscriptionRegistrar lo que un contacto quiere sobre un tematopics:write

Un tema es un asunto sobre el que envías correo — un resumen semanal, un registro de cambios, un aviso de facturación — y la respuesta de un contacto a uno es una decisión permanente en lugar de un filtro de audiencia. Se aplica a cualquier audiencia que seleccione una campaña, por lo que elegir una audiencia diferente no es una forma de evitarlo. Esa es la diferencia entre un centro de preferencias y una casilla que nadie respeta.

set_topic_subscription no puede suscribir a nadie. Pedirle que suscriba deja al contacto en pending y devuelve un enlace de confirmación; no se envía nada sobre ese tema hasta que una persona lo abre, y no hay parámetro para omitir el paso. Un agente que afirma que alguien quiere correo no es evidencia de que lo quiera, y la reputación que cuesta el error es tuya. Sendly no envía ese correo de confirmación — lo haces tú, desde tu propio dominio verificado. La exclusión es al revés y tiene efecto de inmediato: retirar el consentimiento nunca debe ser más difícil que darlo.

default_opt_in decide qué significa el SILENCIO. Si se deja como verdadero, un contacto que nunca ha respondido cuenta como suscrito — que es la lectura honesta para un tema introducido sobre una lista que ya tienes, ya que esas personas consintieron en recibir noticias tuyas. Si se establece como falso, la ausencia significa "no preguntado" y solo cuenta un sí explícito. subscribed_count informa solo respuestas explícitas, por lo que se lee bajo en un tema default_opt_in; así es cuántas personas respondieron, no cuántas recibirían el correo.

No hay eliminación. archived: true retira un tema — lo deja fuera del centro de preferencias y deja de ser enviable — y cada exclusión registrada contra él sobrevive, porque eliminar el tema eliminaría las elecciones que las personas hicieron sobre él.

Eventos y webhooks [#events-and-webhooks]

HerramientaQué hacePermiso
list_eventsLos eventos personalizados que tu aplicación ha registradoevents:read
record_eventRegistrar un evento personalizado contra un contacto existenteevents:write
list_webhooksListar endpoints de webhookwebhooks:read
create_webhookAñadir un endpoint de webhookwebhooks:write
update_webhookEditar un endpoint de webhookwebhooks:write
delete_webhookEliminar un endpoint de webhookwebhooks:write

Lista de supresión [#suppression-list]

HerramientaQué hacePermiso
list_suppressionsLas direcciones que Sendly se niega a enviarsuppression:read
add_suppressionBloquear una direcciónsuppression:write
remove_suppressionDesbloquear una dirección, re-habilitando el envío a ellasuppression:write

Buzones [#mailboxes]

Los buzones son el lado conversacional: una bandeja de entrada real detrás de una dirección como support@yourdomain.com, por lo que el correo enviado allí llega a Sendly — y, con su propio permiso, un agente puede escribir y enviar un nuevo mensaje desde esa dirección.

HerramientaQué hacePermiso
list_mailboxesLos buzones en los dominios de este proyecto, con el estado y dominio de cada unomailboxes:read
get_mailboxUn buzón, más el host, puerto y nombre de usuario IMAP y SMTP para conectar un cliente de correo. La contraseña no está incluida y no se puede leer de vueltamailboxes:read
create_mailboxCrear un buzón en un dominio verificadomailboxes:write
delete_mailboxEliminar permanentemente un buzón y cada mensaje que contienemailboxes:write
compose_mailbox_emailEscribir un correo para un buzón: redactar uno desde una breve instrucción, reescribir un borrador que ya tienes, o sugerir líneas de asunto. Devuelve texto y no envía nada — el resultado siempre dice sent: falsemailboxes:read
send_mailbox_emailEnviar un correo nuevo de texto plano desde la propia dirección de un buzón, a hasta veinte destinatarios. El destinatario puede responder, se enhebra en las conversaciones de ese buzón, y no se puede recuperar. Los destinatarios suprimidos y el correo que el escáner de contenido rechaza son rechazados; un buzón puede enviar 60 mensajes por hora de esta maneramailboxes:send

Cinco datos sobre estos que son más fáciles de saber ahora que de descubrir después:

  • Ninguna herramienta del agente lee los mensajes de un buzón. El correo que recibe un buzón es correspondencia de terceros, y ningún permiso del vocabulario cubre su lectura. get_mailbox devuelve la configuración de conexión, nunca el contenido.
  • Un buzón no se puede modificar después de crearse. No hay ruta de actualización ni herramienta de actualización, a propósito. Para cambiar una dirección, elimina el buzón y crea el que quieras.
  • No se admiten cuotas. Los buzones se crean sin límite de almacenamiento y no hay forma de añadir uno después. La herramienta no ofrece el argumento, y la ruta lo rechaza en lugar de aceptar un valor que nunca aplicaría.
  • El dominio debe verificarse primero, y un proyecto puede tener como máximo diez buzones. Ambos casos se rechazan con un 409, no se solucionan silenciosamente.
  • Redactar y enviar son permisos diferentes. compose_mailbox_email solo necesita mailboxes:read porque no cambia nada: entrega al agente texto para mostrarte. Poner ese texto en la bandeja de entrada de alguien como tu dirección de soporte es send_mailbox_email bajo mailboxes:send, que está separado de emails:send a propósito: un agente autorizado para enviar un recibo desde tu dominio verificado no ha sido autorizado por ello a abrir una conversación como tu mesa de soporte. Llega sin verificar, y un agente bien educado te muestra el borrador y confirma los destinatarios antes de llamarlo.
`create_mailbox` y `delete_mailbox` requieren una conexión con sesión iniciada cuyo usuario sea un **admin** del proyecto. Una clave API no lleva ningún usuario, por lo que esas dos herramientas nunca se ofrecen a una conexión con clave: están ausentes de su lista de herramientas en lugar de fallar cuando lo intenta. `list_mailboxes` y `get_mailbox` funcionan normalmente con una clave.

La eliminación es la herramienta más afilada de esta superficie: cada mensaje que contiene el buzón se borra, Sendly no guarda otra copia, y ni el panel ni el soporte pueden recuperarlo. El correo enviado a la dirección después se rechaza. Por eso mailboxes:write llega sin verificar.

Claves API [#api-keys]

HerramientaQué hacePermiso
list_api_keysQué claves existen, qué puede hacer cada una, cuándo se usó por última vez. No se devuelve ningún secreto: solo los últimos cuatro caracteres existen en algún lugarapi-keys:read
create_api_keyCrear una nueva clave. El secreto no se devuelve al agente: el resultado lleva un enlace de un solo uso que solo tú puedes abrirapi-keys:write
rotate_api_keyReemplazar el secreto de una clave en su lugar, conservando su nombre y permisos. Mismo enlace de un solo uso; el secreto antiguo deja de funcionar inmediatamenteapi-keys:write
revoke_api_keyRevocar permanentemente una clave. Cualquier cosa que aún la use falla en su próxima solicitud, y no se puede restaurarapi-keys:write

Un agente puede crear y rotar claves, y aun así nunca ve un secreto: consulta Herramientas que te entregan un enlace a continuación para ver cómo funciona. Dos límites merecen expresarse claramente:

  • Una clave nueva no puede ser más amplia que la conexión que la creó. create_api_key requiere una lista explícita de permisos, y cualquier permiso que la conexión no tenga por sí misma se rechaza con SCOPE_ESCALATION. La solicitud se rechaza por completo, nunca se recorta silenciosamente para que encaje, por lo que un agente no puede usar api-keys:write para fabricar una capacidad que nunca le concediste. Solo tu propia sesión del panel está exenta, porque un admin de proyecto ya tiene autoridad total sobre su propio proyecto.

  • Cada herramienta de esta sección necesita una conexión OAuth. Las cuatro rutas de claves API identifican a un admin de proyecto a partir del usuario con sesión iniciada, y una clave API no lleva ningún usuario, por lo que una conexión con clave nunca podría llamar a una. Por lo tanto, no se ofrecen a una conexión con clave en absoluto: ausentes de su lista de herramientas en lugar de fallar cuando lo intenta. Esta es una propiedad diseñada, no un modo de fallo: una herramienta que un agente no puede usar no debería estar en su lista. Un agente conectado con una clave no puede listar, crear, rotar ni revocar claves.

    list_api_keys es la que parece fuera de lugar, por lo que vale la pena decir por qué está aquí. Esta regla trata sobre la forma de la ruta, no sobre el peligro: api-keys:read no es uno de los permisos que requieren aprobación explícita, y simplemente leer no es irreversible, pero la ruta aún requiere un usuario con sesión iniciada y, por lo tanto, la herramienta aún no puede ser utilizada por una clave. Cada otra herramienta retenida en esta guía se retiene por lo que puede hacer; esta se retiene por quién necesita ser.

Esta es la única capacidad que escapa a su propia revocación. Una clave emitida aquí es una credencial separada: no consulta ninguna fila de consentimiento, sigue funcionando después de que desconectes la aplicación que la solicitó, y la revocas desde **Configuración → Claves API** en lugar de desde **Configuración → Aplicaciones conectadas**. Esa es toda la razón por la que `api-keys:write` está desmarcada por defecto y lleva una advertencia.

Herramientas que te entregan un enlace [#tools-that-hand-you-a-link]

Tres herramientas no pueden terminar dentro del agente, y responden con status: "action_required" y una URL para que abras en lugar de un resultado:

HerramientaQué hace el enlaceCuánto dura
create_api_keyMuestra el secreto de la nueva clave, una vez5 minutos
rotate_api_keyMuestra el nuevo secreto de la clave rotada, una vez5 minutos
start_domain_setupConfiguración DNS guiada en tu registrador, luego te devuelve a SendlyDe corta duración, establecida por la sesión

Para las dos herramientas de claves, la razón es que un secreto nunca debe entrar en un resultado de herramienta. Cualquier cosa que reciba un agente se escribe en el contexto del modelo, la transcripción del cliente y cada registro por el que pase esa transcripción: una credencial permanente en los tres. Así que el secreto no cruza el límite en absoluto. En su lugar, el enlace es un ticket de un solo uso de cinco minutos, y abrirlo requiere una sesión de Sendly con sesión iniciada que pertenezca a un admin del proyecto: una credencial que ningún agente tiene, y que ni un token OAuth ni una clave API pueden producir. El agente que solicitó la clave no puede leerla, incluido el que acaba de romper tu implementación al rotarla. Tener la URL no es suficiente por sí solo, y un enlace abierto por la persona equivocada se gasta en lugar de honrarse, así que si eso sucede, rota de nuevo.

start_domain_setup usa la misma forma por una razón diferente: publicar registros DKIM, SPF y MX ocurre en tu registrador, donde Sendly no tiene credenciales y nunca las tendrá.

Transmítelo y di que se abre una vez. Un cliente bien educado también puede ofrecer abrir la ventana por ti: eso es un extra opcional, no el mecanismo. **La herramienta está completa de cualquier manera**, porque la URL está en el resultado que el modelo puede leer. Si tu cliente nunca ofrece abrir nada, nada está roto y nada se ha omitido.

Permisos y consentimiento [#permissions-and-consent]

Durante el flujo OAuth, Sendly te muestra una pantalla de consentimiento que enumera exactamente lo que el cliente está solicitando, con una casilla de verificación para cada uno. Estas son las descripciones que leerás:

PermisoTexto de la pantalla de consentimientoRequiere aprobación explícita
emails:sendEnviar correos electrónicos desde tus dominios verificadosSí
emails:readVer los correos electrónicos que has enviado y su estado de entregaNo
contacts:readVer tus contactos y sus campos personalizadosNo
contacts:writeCrear, actualizar y eliminar tus contactosNo
campaigns:readVer tus campañas y su rendimientoNo
campaigns:writeCrear, editar y organizar tus campañasNo
segments:readVer tus segmentos y quién pertenece a ellosNo
segments:writeCrear, editar y eliminar tus segmentosNo
workflows:readVer tus flujos de automatización y sus ejecucionesNo
workflows:writeCrear, editar, habilitar y eliminar tus flujos de automatizaciónSí
templates:readVer tus plantillas de correo electrónicoNo
templates:writeCrear, editar y eliminar tus plantillas de correo electrónicoNo
domains:readVer tus dominios de envío y su estado de verificaciónNo
domains:writeAñadir y eliminar dominios de envío, y activar la verificaciónNo
webhooks:readVer tus endpoints de webhook y su historial de entregaNo
webhooks:writeCrear, editar y eliminar tus endpoints de webhookNo
suppression:readVer las direcciones en tu lista de supresiónNo
suppression:writeAñadir y eliminar direcciones en tu lista de supresiónSí
analytics:readVer tus análisis de envío y métricas de participaciónNo
usage:readVer tus totales de uso y límites de facturaciónNo
events:readVer los eventos personalizados que tu aplicación ha registradoNo
events:writeRegistrar eventos personalizados para tus contactosNo
projects:readVer tus proyectos y sus configuracionesNo
projects:writeCrear nuevos proyectos en tu cuentaSí
api-keys:readVer qué claves API existen, incluido lo que cada una puede hacerSí
api-keys:writeCrear, rotar y revocar claves API — estas siguen funcionando incluso después de desconectar esta aplicaciónSí
campaigns:sendEnviar o programar tus campañas a su audienciaSí
mailboxes:readVer los buzones en tus dominios y sus configuracionesNo
mailboxes:writeCrear y eliminar buzones en tus dominios verificadosSí
emails:testEnviar correos electrónicos de prueba a tu propia dirección desde el sandbox de SendlyNo
deliverability:readComprobar por qué el correo de uno de tus dominios no llegaNo
mailboxes:sendEscribir y enviar nuevos correos electrónicos desde tus buzones alojados, como esa direcciónSí
validation:readVer tus ejecuciones de validación de correo electrónico y sus resultadosNo
validation:writeComprobar si las direcciones de correo electrónico pueden recibir correo — esto se factura por direcciónNo
topics:readVer los temas sobre los que envías correo y quién está suscrito a cada unoNo
topics:writeCrear y editar temas, y cambiar a qué están suscritos tus contactos — esto decide a quién llegan tus campañasNo
lists:readVer tus listas de suscriptores y quién está en ellasNo
lists:writeCrear, renombrar y eliminar tus listas de suscriptoresNo

Nada sucede en tu cuenta hasta que lo apruebas, y ninguna herramienta se ejecuta nunca bajo un permiso que no hayas otorgado.

Cada permiso en esta tabla tiene al menos un endpoint detrás, así que nada de lo que otorgues aquí es inerte. Dos de ellos no tienen aún una HERRAMIENTA DE AGENTE: lists:read y lists:write gobiernan los endpoints de /api/v1/lists, que una clave API o un token OAuth pueden llamar directamente, y la superficie MCP no ofrece una herramienta de listado. Otorgarlos a una conexión de agente hoy por tanto amplía lo que un token podría hacer por HTTP y no cambia nada a lo que el propio agente puede alcanzar — lo cual vale la pena saber antes de marcarlos.

Elegir un proyecto [#choosing-a-project]

Cada herramienta excepto list_projects opera en un proyecto.

  • Una conexión OAuth cubre cada proyecto al que perteneces, y cada herramienta toma un projectId opcional. ¿Perteneces a exactamente uno? Omítelo; Sendly lo infiere. ¿Perteneces a varios? El agente debe pasar uno, o la llamada se rechaza con PROJECT_REQUIRED y se le indica que llame a list_projects primero. Los agentes bien comportados hacen esto por su cuenta. Un projectId que no sea uno de tus proyectos recibe el mismo PROJECT_REQUIRED antes de que la herramienta haga algo, redactado de manera idéntica tanto si ese proyecto existe como si no.
  • Una conexión de clave API está vinculada al proyecto propio de la clave. El argumento no se ofrece en absoluto, y un cliente que lo envíe de todos modos es rechazado con PROJECT_FIXED.

Sendly rechaza en lugar de adivinar en ambos casos a propósito: elegir silenciosamente "el primer" proyecto, o ignorar silenciosamente un projectId que el agente creía estar usando, significaría actuar sobre los datos del inquilino equivocado.

Cambiar de opinión [#changing-your-mind]

Para retirar un permiso, ve a Configuración → Aplicaciones conectadas y desmárcalo. La conexión permanece; el agente conserva todo lo demás.

Para terminar la conexión por completo, elige Desconectar en la misma página. Para una conexión de clave, revoca la clave en Configuración → Claves API en su lugar.

Cada llamada de herramienta vuelve a comprobar tu concesión en vivo antes de ejecutarse, así que un permiso retirado comienza a ser rechazado **de inmediato** — incluso aunque el token de acceso del agente no haya expirado — y desconectar detiene al agente por completo en su siguiente llamada.

La lista de herramientas se actualiza poco después. El listado de un agente se construye a partir del token que tiene, así que una herramienta retirada puede seguir apareciendo allí hasta que ese token sea reemplazado: cada llamada a ella es rechazada con SCOPE_MISSING mientras tanto, así que ninguna autoridad sobrevive al cambio. La lectura del token usa la misma concesión en vivo que la puerta, lo que significa que la herramienta retirada desaparece del listado tan pronto como el agente se actualiza — dentro de una hora, ya que los tokens de acceso duran ese tiempo — e inmediatamente si se reconecta. Una actualización presentada después de una desconexión completa es rechazada directamente en lugar de ser honrada.

Solución de problemas [#troubleshooting]

Los fallos vuelven al agente como resultados de herramienta que llevan un código, no como errores de transporte, así que el agente puede explicártelos en lugar de simplemente soltar la conexión.

Lo que vesQué significaSolución
401 desde el endpointNo se presentó una credencial válidaEjecuta el flujo OAuth, o coloca una clave sk_… válida en el encabezado Authorization. Una clave pk_… o una sesión de navegador no serán aceptadas
SCOPE_MISSINGA esta conexión nunca se le otorgó ese permiso, o fue retirado — o la clave es anterior a los permisos por capacidad y la herramienta es irreversibleVuelve a aprobarla en Configuración → Aplicaciones conectadas, o crea una nueva clave en Configuración → Claves API con los permisos marcados
CONSENT_REVOKEDLa conexión fue desconectada de tu cuenta de SendlyReconecta la aplicación desde Configuración → Aplicaciones conectadas
KEY_REVOKEDLa clave API que usa esta conexión fue revocada o rotadaReconecta con una clave actual desde Configuración → Claves API
PROJECT_REQUIREDTu cuenta tiene más de un proyecto (o ninguno), por lo que el destino es ambiguo — o el projectId pasado no es uno de tus proyectosHaz que el agente llame a list_projects y pase el id elegido como projectId
PROJECT_FIXEDA una conexión con clave API se le dio un projectId, pero una clave está vinculada a un solo proyectoElimina el argumento, o usa una clave que pertenezca al otro proyecto
USER_DECLINEDTu cliente te pidió confirmar una acción irreversible y dijiste que no. No se hizo nada — la negativa ocurre antes de que se llame a Sendly en absolutoNada que solucionar. Dile al agente qué te gustaría en su lugar
CONFIRMATION_REQUIREDSe llamó a send_campaign en un proyecto con más de 1,000 contactos sin confirm: true. No se envió nadaHaz que el agente te diga a quién llega la campaña y qué dice, luego llama de nuevo con confirm: true
INVALID_ARGUMENTSLos argumentos no pueden producir una llamada — una regla de campos cruzados de la propia herramienta los rechazó, por ejemplo send_email dado un fromName sin from. No se hizo nadaEl mensaje dice qué cambiar. No reintentes los mismos argumentos: producen la misma negativa
SCOPE_ESCALATIONcreate_api_key solicitó un permiso que esta conexión no posee por sí mismaSolicita una clave cuyos permisos sean un subconjunto de lo que otorgaste al agente, o crea la clave tú mismo en Configuración → Claves API
TOOL_EXECUTION_FAILEDLa llamada llegó a Sendly pero no pudo completarseReintenta. Si persiste, contacta a support@sendly.now
El aviso de aprobación propio de tu cliente MCP es lo que te pregunta antes de que una herramienta irreversible se ejecute, y pregunta debido a su propia política — la mayoría de los clientes confirman cada llamada de herramienta, o cada llamada que no hayas aprobado ya para la sesión. Sendly no lo activa.

La anotación destructive que publica Sendly marca una cosa más específica, y vale la pena saber cuál: una herramienta que borra o sobrescribe algo que ya existía — delete_contact, edit_workflow (cuyos steps reemplazan cada paso existente), revoke_api_key (que invalida un secreto activo), delete_list (que lleva consigo las membresías). Un envío no está marcado como destructivo, porque no destruye nada; es irreversible en la otra dirección, y ninguna anotación captura eso. No leas una herramienta no marcada como una segura.

Sendly también puede preguntar a través del protocolo, y USER_DECLINED es la respuesta que produce una negativa, pero solo los clientes en un transporte que lleva una conversación con ámbito de sesión lo mostrarán; en el endpoint actual esa solicitud no puede entregarse, por lo que no aparece ningún aviso propio de Sendly. Nada depende de ello: el permiso que marcaste en la pantalla de consentimiento es la concesión, y cada herramienta interactiva se completa devolviendo un enlace.

La única confirmación que Sendly impone por sí mismo es la protección de envío masivo: por encima de 1,000 contactos, send_campaign requiere una segunda llamada que lleve confirm: true. Esa funciona en este transporte precisamente porque pide un argumento en lugar de una conversación.

Cómo se relaciona con el resto de la plataforma [#how-it-relates-to-the-rest-of-the-platform]

El servidor MCP no es un backend separado. Cada llamada de herramienta sale a través de la propia API REST pública de Sendly llevando la misma credencial con la que te conectaste, por lo que las mismas verificaciones de alcance, reglas de membresía y reglas de proyecto deshabilitado se aplican a un agente como a curl o los SDK. No hay atajo privilegiado.

La [referencia de API](/api-reference/overview) se genera a partir del contrato OpenAPI de Sendly, por lo que cada ruta nombrada en esta página tiene su propia página allí, con los esquemas completos de solicitud y respuesta. Los SDK de `sendly-js` y `sendly-python` se generan a partir de ese mismo contrato en su propio ritmo de lanzamiento, por lo que una ruta agregada desde su último lanzamiento llega a ellos en el siguiente. Hasta entonces es accesible de la manera en que todo aquí lo es: sobre HTTP con tu clave, o a través de la herramienta MCP que la envuelve. Comandos de configuración por cliente, y un aviso que un agente puede obtener y ejecutar por sí mismo Credenciales de servidor a servidor — para tu propio código, y para conectar un agente sin interfaz La superficie REST por la que pasa cada llamada de herramienta MCP