artículos · · 8 min de lectura

Conectar Holded con Claude por MCP, API y webhooks

Cómo conectar Holded con Claude por MCP o con tu propio código, qué eventos de webhook existen y qué límites de la API conviene conocer (nov. 2026).

Hay tres formas de conectar Holded con Claude, de la más sencilla a la más a medida. Si no tienes a nadie que programe, empieza por la primera con el perfil Analista, que solo lee, como recomienda Holded.

1. Conector MCP oficial. MCP es un estándar abierto que permite a Claude leer los datos de otra aplicación y, si se lo permites, escribir en ella. Holded publica su servidor en https://mcp.holded.com/mcp. En Claude.ai o Claude Desktop lo añades como conector personalizado. En Claude Code, con claude mcp add --transport http holded https://mcp.holded.com/mcp. Inicias sesión en Holded, eliges un perfil de permisos y preguntas por facturas, contactos o stock en una conversación. Es la vía para preguntar y revisar tus datos. Escribir en Holded también se puede, con los límites que verás abajo.

2. Tu propio código contra la API. La API v2 está en https://api.holded.com y se autentica con una clave API que viaja en la cabecera de cada petición (Bearer token). Tu código lee o escribe en Holded y, si hace falta, llama a Claude para leer un documento o redactar un texto. Encaja con tareas que se repiten cada semana.

3. Webhooks. Holded avisa a una URL tuya cuando se crea o cambia un documento. Un programa tuyo recibe el aviso y llama a Claude solo cuando hace falta, de modo que una factura aprobada o un contacto nuevo ponen en marcha un proceso sin que nadie lo pida. El aviso trae un resumen del documento y el resto se pide a la API. Las dos páginas oficiales de Holded sobre webhooks se contradicen en varios detalles, y los marco más abajo.

Revisé la documentación de Holded con Claude el 2 de noviembre de 2026. Todavía no lo he probado en una cuenta real.

El conector MCP, paso a paso

Necesitas una cuenta de Holded con el acceso a la API activado (ayuda de Holded).

  • Claude.ai: perfil, Personalizar, Connectors, "+", "Añadir conector personalizado", pega la URL, Conectar y autoriza en Holded.
  • Claude Desktop: Ajustes, Connectors, "Añadir conector personalizado".
  • Claude Code: el comando de arriba. El navegador se abre en el primer uso.

En el plan gratuito de Claude solo cabe un conector personalizado. En Team y Enterprise, el propietario de la organización lo añade primero y después cada miembro conecta el suyo.

El asistente actúa con tu usuario de Holded y solo con los permisos del perfil que elijas al autorizar: Analista (solo lectura), Operativo (lectura y escritura) o Personalizado. Holded recomienda empezar por Analista, y el asistente nunca puede más de lo que ya puede tu usuario. El conector expone 366 herramientas: 153 de lectura y 213 de escritura. Según Holded, Claude te pide confirmación antes de cada acción que escribe.

Aquí las páginas de Holded no coinciden. La página para desarrolladores dice que el perfil Operativo permite crear facturas y registrar pagos. El artículo de ayuda dice que las facturas y los documentos fiscales quedan en solo lectura en los perfiles recomendados, Operativo incluido, y que con el perfil Personalizado se puede activar la escritura por áreas, bajo tu responsabilidad. Si tu caso depende de esto, pruébalo antes en una cuenta de prueba (la prueba gratuita de Holded dura 14 días).

API y webhooks

La clave se genera en Ajustes, API, "Generar nueva clave", y se muestra una sola vez. Los endpoints documentan el permiso que necesitan, por ejemplo sales:invoices.read, y sin él recibes un 403 (autenticación). Usa permisos mínimos y guarda la clave en una variable de entorno.

Un webhook se crea en Ajustes, Webhooks, o por API. Los eventos tienen la forma área.acción, con las acciones create, update y delete, y approve en los documentos que se aprueban. Estos son los grupos de la guía de webhooks de Holded. La tabla deja fuera pagos, servicios y almacenes, porque la ayuda dice que no emiten webhooks (más abajo):

Documento Eventos
Facturas invoice.*
Facturas rectificativas creditnote.*
Facturas proforma proform.*
Recibos de venta salesreceipt.*
Facturas de proveedor y gastos purchase.*
Reembolsos de compra purchaserefund.*
Recibos receipt.*
Pedidos de compra purchaseorder.*
Albaranes de entrada receiptnote.*
Pedidos de venta salesorder.*
Albaranes de venta waybill.*
Presupuestos estimate.*
Contactos contact.*
Productos product.*

Además existe stock.update. La proforma no tiene approve.

Las listas de eventos también difieren entre las dos páginas. La guía añade eventos de pagos, servicios y almacenes. El artículo de ayuda dice que pagos, servicios, almacenes, proyectos, tareas y facturas recurrentes no emiten webhooks, y que product.delete no existe, aunque la guía lo lista. Según la ayuda, registrar un pago dispara invoice.update o purchase.update y el aviso no trae el estado de pago: hay que pedir el documento a la API. Si tu caso depende de los pagos, consulta el endpoint de eventos y haz una prueba real con tu cuenta.

Para quien lo programe

Por API, un webhook se crea con POST /api/v2/webhooks (permiso developers:webhooks.write). Envías url, events, version (hoy v1) y description. La respuesta incluye un secret con prefijo whsec_ que no vuelve a mostrarse (referencia). GET /api/v2/webhooks/events devuelve los tipos de evento soportados, agrupados por dominio.

Cada aviso lleva seis cabeceras: x-holded-webhook-event, -date, -signature, -id, -account-id y -version. La firma es un HMAC-SHA256 en hexadecimal con prefijo sha256=.

Las dos páginas de Holded también describen la firma de forma distinta. La guía dice que se firma el cuerpo en bruto. La ayuda dice que se firma la fecha de la cabecera x-holded-webhook-date, un punto y el cuerpo: fecha + "." + cuerpo. Mientras Holded no lo aclare, acepta los dos esquemas y anota en tu registro cuál coincide:

import hashlib, hmac

def firma_valida(secreto: str, cuerpo: bytes, fecha: str, cabecera: str) -> str | None:
    """Devuelve el esquema que coincide ("cuerpo" o "fecha.cuerpo"), o None."""
    recibida = cabecera.removeprefix("sha256=").encode()
    esquemas = {
        "cuerpo": cuerpo,
        "fecha.cuerpo": fecha.encode() + b"." + cuerpo,
    }
    for nombre, mensaje in esquemas.items():
        esperada = hmac.new(secreto.encode(), mensaje, hashlib.sha256).hexdigest().encode()
        if hmac.compare_digest(esperada, recibida):
            return nombre
    return None

Usa siempre el cuerpo en bruto, antes de convertirlo a JSON. Si la firma es válida, responde 2xx enseguida y procesa el aviso en segundo plano. Si tu servicio responde con un error o tarda demasiado, Holded lo cuenta como entrega fallida y reintenta (hasta 5 veces, según la ayuda). Guarda x-holded-webhook-id y descarta los avisos repetidos.

El aviso de una factura incluye id, documentNumber, date, dueDate, contactId, contactName, subtotal, total e isDraft. Para ver las líneas haces un GET con ese id.

Ideas de automatización

Cuatro ideas que encajan con estos eventos (no las he probado todavía):

  • invoice.approve: tu código compara líneas e importes con el presupuesto de origen y Claude redacta el aviso si no cuadran.
  • purchase.create: tu código cruza la factura del proveedor con el pedido de compra y el albarán de entrada. El método está en el artículo sobre cotejar albarán, pedido y factura.
  • contact.create: tu código busca contactos duplicados por NIF.
  • stock.update: tu código avisa cuando un producto baja del mínimo que tú fijes.

Mi regla de diseño: Claude lee y propone, y tu servicio escribe en Holded solo lo que una persona ha aprobado.

Límites y trampas

La API tiene un tope por minuto y una cuota mensual, ambos por cuenta (límites):

Plan Peticiones por minuto Llamadas al mes
Starter 60 500
Growth y Basic 60 2.000
Standard 120 7.500
Advanced 120 30.000
Premium 600 100.000

Los nombres de plan son los de la página de límites. En la página de precios figuran hoy Light (gratuito), Starter, Basic, Standard, Advanced y Premium. "Growth" solo aparece en la página de límites, y Light no aparece en ella. Los planes inferiores devuelven un 429 al llegar a la cuota mensual, y Premium factura el exceso.

Con 500 llamadas al mes salen unas 16 al día, y un GET por cada aviso las agota con poco movimiento. La página de límites recomienda usar webhooks en lugar de consultar la API a cada rato y dice que así recibes los cambios "sin consumir tu cuota". Los GET que haces después de cada aviso son llamadas normales a la API, así que, por lo que entiendo, cuentan en la cuota mensual; Holded no lo dice expresamente. La especificación OpenAPI de Holded menciona otra cifra por minuto (100 por clave). Usa las cabeceras Retry-After y X-RateLimit-* de las respuestas 429 en lugar de adivinar los tiempos de espera.

Al crear facturas por API, no pongas en number el formato de una serie activa. La serie lleva su propio contador y no se entera, así que al llegar a ese número la petición falla y se pierde la correlatividad. Si necesitas una numeración propia, usa un prefijo que no coincida con ninguna serie activa, o crea todos los documentos de esa serie con number_line_id (referencia).

Todo lo que Claude lee sale de Holded hacia un proveedor de IA. Si entre tus contactos hay personas físicas (autónomos, por ejemplo), su NIF y su dirección son datos personales: decide qué datos le llegan a Claude y revisa con tu gestor qué contrato de tratamiento de datos tienes con el proveedor. La clave de la API no se pega nunca en un chat.

Si prefieres que lo montemos

Soy Artem Baranov y en tastare monto sistemas de IA para negocios pequeños. Tengo publicado un servidor MCP propio, de código abierto (licencia MIT), que construí con Claude. Se llama callsense-mcp y puntúa llamadas de ventas; el repositorio conserva el nombre anterior del producto, hoy Oyendo. Parte de una idea parecida a mi regla de diseño de arriba: Claude propone y otra pieza comprueba. Aquí el servidor solo acepta la puntuación si la cita coincide palabra por palabra con lo que dijo la persona correcta, y el veredicto lo calcula el código. Con Holded todavía no he probado nada en una cuenta real.

Sobre tu cuenta de Holded puedo montarte una de estas dos cosas: dejarte configurado el conector oficial en modo solo lectura, con una lista de preguntas que probamos con tus datos antes de entregarla, o un servicio que escucha un evento (por ejemplo purchase.create) y deja a Claude preparar un aviso que revisa una persona. En una llamada de 30 minutos me cuentas qué documento quieres vigilar y te digo qué vía encaja (MCP, API o webhook) y qué habría que programar. Reserva en cal.com/salta.team o escribe a artem@tastare.com.

empieza por una tarea

nos cuentas la tarea que más te pesa. definimos el sistema más pequeño que la resuelve y te damos un precio cerrado en la llamada o en un día.

reserva una llamada de 30 min