Blog (CMS headless) --- Integración

Última actualización: Aug 31, 2026Sección: Integraciones

Referencia completa de la API: https://api.mailoo.app/docs/v1

Usa Mailoo como un CMS headless centralizado para tu blog: crea y gestiona artículos en el panel de control y entrégalos mediante una API externa por proyecto. Los sitios externos (o el tuyo propio) pueden consumir la misma API.

Resumen

La integración de Blog funciona como otras integraciones de Mailoo (p. ej. Form): añades una integración Blog a un proyecto. Todos los artículos de ese blog pertenecen a esa integración. La API no proporciona acceso a artículos de blog sin autorización. API de lectura: lista artículos y obtiene uno por slug; requiere X-API-Key con alcance blog.external-read; el acceso se determina por UID del proyecto e ID de integración. API de gestión: crea, actualiza y elimina artículos con X-API-Key y alcance blog.manage (mismo prefijo de ruta /api/v1/blog/.../articles). Los agentes pueden usar el servidor MCP de Mailoo con un token MCP personal creado en Perfil → Seguridad; consulta mailoo-mcp{.interpreted-text role="doc"}.

Capacidades:

  • Crear, editar y eliminar artículos en el panel de control de Mailoo (por integración), o mediante la API de gestión / MCP con blog.manage
  • Publicar como borrador o publicado; extracto opcional, metadatos SEO (metaTitle, metaDescription, ogImageMediaIdogImageUrl pública), clasificadores (temas, intenciones, audiencias, clusters SEO), etiquetas
  • API de lectura (X-API-Key obligatoria): lista artículos publicados y obtiene uno por slug, por UID del proyecto e ID de integración
  • API de gestión (X-API-Key + blog.manage): POST/GET/PUT/DELETE …/blog/{projectUid}/integrations/{integrationId}/articles; lista valores de clasificador con blog.external-read o blog.manage
  • Respuesta de artículo (lista): id, title, slug, excerpt, content (Markdown, fuente de verdad), htmlContent (HTML generado a partir de content por la API; usar para visualización), status, publishedAt, createdAt, updatedAt, author (id, name, email?, avatar, bio --- localizado cuando se establece locale), category (tema principal por compatibilidad: primer tema por slug, o null), themes, intents, audiences, seoClusters (cada uno un array de { id, slug, name }), metaTitle, metaDescription (null cuando no se establece --- los consumidores pueden recurrir a title / excerpt), ogImageUrl (null cuando no se establece), canonicalUrl (URL absoluta cuando la integración tiene plantilla canónica configurada; de lo contrario null), seoWords ([{ slug, word }] --- unión de entradas del catálogo de palabras SEO de la integración vinculadas a los clusters SEO del artículo; localizado cuando se establece locale; array vacío cuando no hay), featured, readTimeMinutes, tags (array, por defecto []). La paginación incluye page, limit, total, totalPages, hasNextPage, hasPrevPage. Las imágenes en línea usan URLs de la API de imágenes de Mailoo (consulta images{.interpreted-text role="doc"}); no hay un campo image de nivel superior separado en el artículo.
  • Respuesta de artículo (uno por slug): mismos campos que el elemento de lista, más linkedLinks opcionales --- un array ordenado (máx. 10 por artículo) de enlaces curados: id, type (internal_article | update_announcement | external_resource), label, url, intro (nullable), date (nullable ISO datetime), sortOrder. Los enlaces internos usan una ruta del mismo sitio /blog/{targetSlug} en url; prefija con tu segmento de idioma al construir el href público (p. ej. /{locale}/blog/...). Los editores gestionan los enlaces en el panel de control; las filas internal_article pueden autocompletar label/date/intro del artículo destino con anulaciones opcionales.
  • JSON-LD (opcional): GET …/slug/{slug}/json-ld?locale= devuelve Schema.org BlogPosting. Cuando la plantilla canónica de la integración está configurada, url / mainEntityOfPage / inLanguage / nombre del publisher son absolutos (o rellenados con locale). Cuando no está configurada, los marcadores {{canonicalUrl}}, {{origin}}, {{locale}} permanecen para que el integrador los reemplace. No se incluye en la respuesta por defecto de lista/slug. BFF del mismo origen: GET /api/v1/blog/slug/{slug}/json-ld.

SEO de página para consumidores: Mapea metaTitle ?? title → título del documento / OG, metaDescription ?? excerpt → descripción, ogImageUrl (sino primera imagen del cuerpo) → imagen OG/Twitter, canonicalUrl (o slug + tu prefijo de idioma y origen del sitio cuando es null) → canónica / rel=canonical. seoWords / tags opcionales pueden alimentar <meta name="keywords"> o enlaces internos. Las canónicas absolutas no se almacenan por artículo; configura una plantilla canónica por integración en Conexión y ajustes (publicBaseUrl + patrón de ruta con {locale} / {slug}).

Perfiles de autor (panel de control)

Cada cuenta puede mantener perfiles de autor propiedad del usuario (nombre para mostrar, slug, correo público opcional, URL de avatar, bio, anulaciones opcionales por idioma). Los artículos almacenan una referencia activa al perfil seleccionado para que las actualizaciones se propaguen. Se admiten seudónimos (isAlias). Un perfil puede marcarse como tu predeterminado global; también puedes establecer un predeterminado por integración de blog (tu preferencia solamente --- no se almacena en la configuración compartida de la integración).

  • Pestaña Autores en la integración de Blog: lista y crea/edita perfiles.
  • Pestaña Conexión y ajustes: Autor predeterminado guarda la anulación por integración (recurre al predeterminado global cuando no está establecido). Plantilla de URL canónica almacena config.canonicalTemplate (publicBaseUrl, pathPattern) para que las lecturas externas puedan devolver canonicalUrl. Notificación IndexNOW (config.indexNow opcional) hace POST de identificadores de artículos a tu sitio al publicar/cambiar --- tú llamas a IndexNOW (consulta indexnow-notify{.interpreted-text role="doc"}). Fotos de stock almacena claves API de Unsplash / Pexels por integración (cifradas) para que los editores puedan buscar e importar imágenes de stock en los artículos; las fotos importadas se convierten en embeds normales de UserMedia de Mailoo (consulta images{.interpreted-text role="doc"}). Los agentes pueden leer/actualizar la misma configuración saneada mediante GET/PATCH /api/v1/blog/.../settings o la herramienta MCP manage_blog_integration_settings (consulta mailoo-mcp{.interpreted-text role="doc"}).
  • Nuevo/Editar artículo: elige un perfil de autor o deja predeterminado de integración / global para que la API resuelva el autor automáticamente.

Crear una integración de Blog

  1. Ve a Panel de control → Proyectos → [Tu proyecto]
  2. Haz clic en Crear nueva integración
  3. Selecciona Blog (CMS headless)
  4. Establece un nombre y estado (p. ej. Activo)
  5. Después de la creación, abre la integración para ver la lista de Artículos y el bloque de Conexión

Gestión de artículos

En la página de la integración puedes:

  • Tabla de artículos: Título, slug, resumen de clasificadores, estado, fecha y Editar para cada artículo; filtros rápidos por tema, intención, audiencia y cluster SEO; Exportar informe Markdown (con o sin texto completo del artículo) para planificación de contenido asistida por IA
  • Crear artículo: Abre el formulario de nuevo artículo (título, extracto, metadatos SEO, contenido, estado). El contenido se escribe en Markdown (encabezados, listas, negrita, enlaces, imágenes mediante el panel de imágenes del panel de control --- incluyendo la pestaña Stock para Unsplash/Pexels cuando las claves están configuradas --- o URLs de embed públicas); la API lo convierte a HTML al guardar.
  • Editar: Abre el formulario de edición para ese artículo (guardar como borrador, publicar o archivar). El mismo editor Markdown para el idioma por defecto y para cada traducción (locales).
  • Enlaces relacionados: Al crear/editar, puedes adjuntar hasta 10 enlaces vinculados curados (tipo, URL o artículo destino interno, label/intro/date opcionales). La API externa de obtener por slug los expone como linkedLinks para frontends headless (p. ej. notas de versión en una página de proyecto de portafolio).

Formato de contenido: El cuerpo del artículo se almacena como content en Markdown. La API genera htmlContent a partir de él. Los consumidores deben usar htmlContent para la visualización para obtener estructura y tipografía correctas.

Enlaces en htmlContent: Las URLs absolutas http:// / https:// y los enlaces relativos al protocolo //… incluyen target="_blank" y rel="noopener noreferrer". El comportamiento de misma pestaña aplica a rutas raíz del sitio (/path), enlaces relativos explícitos (./…, ../…), anclas de página (#section) y URLs solo de consulta (?q=1).

Enlaces del mismo sitio --- usa una barra inicial. En Markdown, [label](support/docs) se convierte en HTML href="support/docs". Esa es una URL relativa a la ruta: los navegadores la resuelven según RFC 3986 contra el directorio de la página que muestra el artículo, no contra la raíz del sitio. Por ejemplo, en una publicación en https://example.com/en/blog/my-post, support/docs resuelve a /en/blog/support/docs. Para apuntar a una sección del sitio desde la raíz, escribe [label](/support/docs) (o la URL https://… completa). Los enlaces de un solo segmento sin barra (p. ej. [other](other-slug)) también son relativos a la ruta, así que permanecen bajo el mismo directorio que la URL de la publicación --- útil para publicaciones hermanas solo cuando tus URLs públicas de publicaciones comparten ese prefijo de ruta.

Mermaid en htmlContent: Los bloques de código con etiqueta mermaid se convierten a SVG estático en línea (dentro de <figure class="mailoo-mermaid">). No se requiere runtime de Mermaid en el navegador; el mismo HTML es adecuado para renderizado tipo correo electrónico. La sintaxis Mermaid inválida hace que la API rechace el guardado (artículos, contenido localizado, campañas, etc.).

Bloques de resumen imprimible en htmlContent: Los bloques de código con etiqueta mailoo-print se convierten en una sección imprimible autodescriptiva. Los autores escriben:

```mailoo-print Short summary title

## Key points

- First **takeaway**
- Second [link](/docs)
```

La API lo convierte a HTML similar a:

<section class="mailoo-printable" data-mailoo-print="true" data-print-title="Short summary title">
  <div class="mailoo-printable-body">…parsed Markdown as HTML…</div>
</section>

El título opcional después de mailoo-print en la línea de información del bloque se convierte en data-print-title. El contenido interior admite Markdown completo (encabezados, listas, enlaces, imágenes, bloques mermaid anidados). Los consumidores (tu sitio o el blog de este repositorio) deben detectar [data-mailoo-print], inyectar un botón de impresión en el navegador e imprimir solo .mailoo-printable-body --- la API no embebe controladores onclick (seguro para CSP). Consulta blog-nextjs-example{.interpreted-text role="doc"} para un patrón de mejora del cliente.

Gestión de temas (categorías) y clasificadores

El contenido del blog se organiza en cuatro ejes de clasificadores, cada uno con vínculos muchos-a-muchos con artículos:

  • THEME (reemplaza el antiguo modelo de "categoría"): temas pilares; el bloque Categorías del panel de control sigue gestionando valores de tema para esta integración (mismas rutas CRUD que antes: …/categories).
  • INTENT, AUDIENCE, SEO_CLUSTER: ejes opcionales para alineación editorial y SEO (p. ej. intención informativa vs transaccional, ICP, clusters de consultas). Gestiona valores bajo GET/POST …/blog-classifier-values?type=… (panel de control / API Bearer).

Palabras SEO (frases localizadas) --- opcionales por integración: frases cortas para planificación SEO, no almacenadas en artículos. Cada palabra SEO tiene un slug tipo URL, una word canónica en inglés, anulaciones locales opcionales (misma idea de estructura JSON que las traducciones de artículos) y un vínculo muchos-a-muchos con valores de SEO_CLUSTER únicamente. Pestaña Productos del panel de control: GET/POST /api/v1/projects/{projectUid}/integrations/{integrationId}/blog-seo-words y PUT/DELETE …/blog-seo-words/{wordId} (Bearer). La lista admite ?clusterId= opcional (CUID de un valor de cluster SEO). Al crear o editar un artículo, el formulario muestra en modo lectura la unión de palabras SEO vinculadas a los clusters SEO seleccionados en ese artículo.

Al crear o editar un artículo, selecciona cualquier cantidad de valores por eje. Los filtros de lista externa admiten category / categoryId para temas más intentId, audienceId, seoClusterId (CUID de un valor de clasificador).

Descripción del tema (opcional): Para un valor de tema, elige un artículo como descriptionPostId (igual que la antigua descripción de categoría). El endpoint externo GET …/categories/slug/{slug}/description sigue resolviendo slugs de tema.

Temas del sistema (about / portfolio)

Mailoo reserva dos slugs de tema por integración de Blog: about y portfolio. Úsalos para páginas independientes que no deben aparecer en la lista pública predeterminada de artículos.

  • Aprovisionamiento: Crear una integración Blog crea automáticamente los valores de tema About (slug about) y Portfolio (slug portfolio).
  • Lista predeterminada: GET …/blog/{projectUid}/integrations/{integrationId} sin category y sin categoryId omite los artículos publicados vinculados a un tema cuyo slug sea about o portfolio. Los artículos sin clasificadores de tema pero con un string category legado que coincida con esos slugs (sin distinguir mayúsculas) también se omiten.
  • Filtro explícito: Pasa category (slug de tema o string legado) o categoryId (id del valor de tema) para incluir esos artículos.
  • La lista de artículos del panel de control muestra todos los artículos a menos que apliques filtros.

El parámetro de consulta category filtra por BlogClassifierValue de tema (tipo THEME) slug o el string category legado de la publicación. Si tanto category como categoryId están establecidos, ambas condiciones aplican (AND lógico).

Informe Markdown del panel de control

Los editores autenticados del proyecto pueden descargar un informe Markdown estructurado para una integración de Blog:

GET /api/v1/projects/{projectUid}/integrations/{integrationId}/blog/report?includeFullText=true|false&format=md&publishedFrom=YYYY-MM-DD&publishedTo=YYYY-MM-DD

Parámetros de consulta:

  • includeFullText --- cuando es true, cada sección de artículo incluye el cuerpo Markdown completo.
  • format --- solo md está soportado.
  • publishedFrom / publishedTo (opcionales, inclusivos, fechas calendario UTC YYYY-MM-DD) --- filtra por publishedAt. Cuando cualquiera está establecido, solo se incluyen publicaciones con publishedAt no nulo (se excluyen borradores sin fecha de publicación). Si ambos están establecidos, publishedFrom debe ser igual o anterior a publishedTo.

El flujo de Exportar informe del panel abre una página dedicada (navegación por migas de pan) para elegir mes, trimestre o un rango personalizado, previsualizar el Markdown y guardarlo en un archivo.

El archivo incluye metadatos, agregados por eje de clasificador, una matriz tema×intención y una sección por artículo (opcionalmente con cuerpos Markdown completos). Destinado a alimentar LLMs externos para planificación de contenido.

Conexión (API externa)

El bloque de Conexión en la página de la integración muestra los valores de entorno para copiar (URL base de la API, UID del proyecto, ID de integración) y un fragmento .env listo para pegar. Para aplicaciones web, el único enfoque recomendado es BFF (clave API en el servidor); las llamadas directas a la API con X-API-Key son para uso servidor-a-servidor. Las rutas completas de endpoints están documentadas abajo; no se duplican en el panel de control. Para Next.js, consulta blog-nextjs-example{.interpreted-text role="doc"}.

Rutas BFF (Next.js, mismo origen) --- usa las variables de entorno MAILOO_BLOG_PROJECT_UID, MAILOO_BLOG_INTEGRATION_ID, MAILOO_BLOG_API, MAILOO_BLOG_API_KEY:

  • Listar artículos publicados: GET /api/v1/blog Consulta: page, limit, category, categoryId, intentId, audienceId, seoClusterId, search, locale, featured, tag (opcionales); los filtros se combinan con AND lógico. Sin category ni categoryId, los artículos en slugs de tema reservados about y portfolio se excluyen (consulta Temas del sistema arriba). tag es una coincidencia exacta con un valor en el array tags del artículo (sensible a mayúsculas tal como se almacena). Devuelve { success, data: [...], pagination } con hasNextPage, hasPrevPage. createMailooBlogClient().listPosts del SDK de Next.js mantiene data, pagination e imagePublicEmbedBaseUrl (consulta nextjs-packages{.interpreted-text role="doc"}).

  • Obtener un artículo por slug: GET /api/v1/blog/slug/{slug} Consulta: locale (opcional). Devuelve { success, data: article }. Solo se devuelven artículos publicados.

  • JSON-LD por slug: GET /api/v1/blog/slug/{slug}/json-ld Consulta: locale (opcional). Devuelve { success, data: { jsonLd } } (BlogPosting con marcadores). Next.js: monta createBlogJsonLdHandler(); RSC/sitemap debe llamar a createMailooBlogClient(config).getPostJsonLd (API directa, sin loopback al BFF). Consulta nextjs-packages{.interpreted-text role="doc"}.

  • Listar categorías (temas): GET /api/v1/blog/categories Devuelve { success, data: Category[] } (id, slug, name) --- solo valores de clasificador de tema (nombre de ruta compatible con versiones anteriores).

  • Obtener descripción de categoría por slug: GET /api/v1/blog/categories/slug/{slug}/description Consulta: locale (opcional). Devuelve { success, data: { description, descriptionHtml } }. Si la categoría no tiene artículo de descripción, ambos campos son cadenas vacías. Si el slug de categoría no existe, devuelve 404.

  • Perfil público de autor (por id o slug): no se requiere BFF del mismo origen; llama a la API directamente con X-API-Key:

    GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/authors/{authorSlugOrId}

    Consulta: locale (opcional). Devuelve name, bio, avatar y email localizados solo cuando el perfil expone un correo público (los seudónimos no filtran el correo de la cuenta).

Llamadas directas a la API --- requieren cabecera X-API-Key y alcance blog.external-read:

  • Lista: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId} Consulta opcional: page, limit, category, categoryId, intentId, audienceId, seoClusterId, search, locale, featured, tag (misma semántica que la ruta de lista BFF arriba).
  • Individual: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/slug/{slug} Consulta: locale (opcional).
  • JSON-LD: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/slug/{slug}/json-ld Consulta: locale (opcional).
  • Categorías: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/categories
  • Descripción de categoría: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/categories/slug/{slug}/description Consulta: locale (opcional). Mismo comportamiento que el endpoint BFF arriba.
  • Perfil de autor: GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/authors/{authorSlugOrId} Consulta: locale (opcional). Lectura pública del perfil de autor del propietario del proyecto para firmas y páginas de autor.

API de gestión --- requiere X-API-Key y alcance blog.manage (las claves FULL también funcionan):

  • Lista (todos los estados): GET {baseUrl}/api/v1/blog/{projectUid}/integrations/{integrationId}/articles
  • Crear: POST …/articles --- el cuerpo coincide con la creación del panel (title, content en Markdown, locales opcionales incluyendo metaTitle / metaDescription por idioma, themeValueIds / intentValueIds / audienceValueIds / seoClusterValueIds, tags, metaTitle, metaDescription, ogImageMediaId, status, slug, authorProfileId, ...). El autor por defecto es el perfil predeterminado del propietario de la clave cuando se omite; 400 si no existe ninguno.
  • Obtener / actualizar / eliminar: GET|PUT|DELETE …/articles/{articleId}
  • Valores de clasificador (lectura o gestión): GET …/classifier-values?type=THEME|INTENT|AUDIENCE|SEO_CLUSTER --- requiere blog.external-read o blog.manage

Descubrimiento de agentes (cualquier clave API válida; solo proyectos propiedad del usuario de la clave):

  • GET {baseUrl}/api/v1/agent/projects
  • GET {baseUrl}/api/v1/agent/projects/{uid}/integrations?type=BLOG

Para la configuración de MCP, consulta mailoo-mcp{.interpreted-text role="doc"}.

La URL base es tu API de Mailoo (p. ej. https://api.mailoo.app). projectUid e integrationId se muestran en el bloque de Conexión. Las imágenes en los cuerpos de los artículos usan la API de subida de imágenes de Mailoo y URLs de embed públicas dentro de content / htmlContent (consulta images{.interpreted-text role="doc"} para almacenamiento, alcances y cómo leer bytes con Bearer o X-API-Key). Las etiquetas se devuelven como un array; array vacío cuando no hay. Los artículos usados como descripciones de categoría se excluyen de la lista externa de artículos, para que no se dupliquen en los resultados del feed regular. La misma lista predeterminada también excluye artículos de las categorías about y portfolio a menos que pases un filtro de categoría explícito. Para los campos de respuesta completos y códigos de error, consulta la documentación de la API arriba.

Para un ejemplo completo con Next.js, consulta blog-nextjs-example{.interpreted-text role="doc"}.