Documentación completa

Cómo integrarlo a tu proyecto

Referencia completa de Blog Listo: auth, kit embed, endpoints, JSON de respuesta, campos, errores y ejemplos por lenguaje. Todo en esta página, con el mismo diseño de la web informativa.

Inicio rápido

De cero a /blog

  1. 01

    Crea tu cuenta

    Regístrate gratis como empresa y copia tu API key en el panel.

  2. 02

    Elige API o embed

    Embed: pegas CSS + JS. API: pides JSON y lo renders con tu diseño.

  3. 03

    Publica contenido

    Crea categorías, tags y posts. Solo los publicados (y no programados a futuro) aparecen en la API.

  4. 04

    Prueba /blog

    Llama a GET /posts con tu key. Si ves data[], la conexión está bien.

Autenticación

Tu API key

Autenticación de la API pública con site key. Lectura de contenido + POST de newsletter y comentarios.

  • Header recomendado: X-Site-Key: TU_SITE_KEY
  • Query alternativa: ?site_key=TU_SITE_KEY
  • Base actual: https://platform-mi-blog-listo.tinguar.com/api/v1

La key la obtienes al crear tu cuenta o empresa en el panel. En los ejemplos usa TU_SITE_KEY hasta copiar la tuya.

Por lenguaje

Ejemplos listos para pegar

Elige tu stack. El código cambia al instante: HTML, JS, PHP, Python, WordPress, Astro, Laravel o React/Next.

blog.html html

Embed en cualquier HTML

Lo más rápido: pegas CSS + JS en tu página /blog.

<link rel="stylesheet" href="https://platform-mi-blog-listo.tinguar.com/embed/blog-templates.css" />
<div id="blog-root"></div>
<script src="https://platform-mi-blog-listo.tinguar.com/embed/blog-client.js"></script>
<script>
  BlogPlatform.create({
    apiBase: 'https://platform-mi-blog-listo.tinguar.com/api/v1',
    siteKey: 'TU_SITE_KEY',
    listEl: '#blog-root',
  }).bootFromQuery();
</script>

Kit embed

BlogPlatform.create

Archivos públicos del panel: https://platform-mi-blog-listo.tinguar.com/embed/blog-templates.css y https://platform-mi-blog-listo.tinguar.com/embed/blog-client.js. Ideal si quieres listado + detalle sin armar el front desde cero.

Opciones de create()

Opción¿Obligatoria?Descripción
apiBasesíBase de la API, ej. https://platform-mi-blog-listo.tinguar.com/api/v1
siteKeysíTu API key del panel
listElsíSelector o nodo del listado, ej. #blog-root
detailElnoSelector o nodo del detalle; si no hay, usa listEl

El kit incluye spinner, búsqueda, filtros, paginación, lightbox, compartir, tema oscuro, progreso de lectura, newsletter y comentarios con respuestas anidadas según settings. Las vistas del detalle (y UTM de la URL) se registran en el panel.

Métodos que devuelve

MétodoQué hace
bootFromQuery()Si la URL tiene ?slug=mi-post muestra detalle; si no, el listado (también lee featured, category, tag, q, page).
renderList()Listado con búsqueda, filtros (categoría/tag/destacados), paginación, newsletter y tema claro/oscuro.
renderPost(slug)Detalle: lightbox, compartir, progreso de lectura, comentarios, related. Registra vista + UTM.
api(path)Fetch interno a la API con tu site key; en detalle reenvía utm_* de location.search.

Detalle con query: /blog?slug=mi-primer-post → bootFromQuery() carga el artículo. Demo: https://platform-mi-blog-listo.tinguar.com/embed/example.html

API

Endpoints

Auth por site key. GET con caché/ETag salvo el detalle del post; POST con rate limit más bajo.

GET /api/v1/site

Datos del sitio, plantilla y settings públicos (hero, newsletter, comentarios).

Parámetros: Ninguno (solo auth). Cache + ETag ~45s.

Respuesta: data { name, slug, domain, template, settings }

GET /api/v1/posts

Listado paginado de posts publicados (no incluye programados a futuro).

Parámetros: page, per_page (1–50, default 12), category (slug), tag (slug), featured (1|true), q (búsqueda en título/subtítulo/extracto)

Respuesta: data[], meta { paginación + stats }, template. Cache + ETag.

GET /api/v1/posts/{slug}

Detalle publicado. Registra vista (únicos + UTM/referrer) y suma view_count.

Parámetros: slug; opcionales utm_source, utm_medium, utm_campaign, utm_content, utm_term (el embed los reenvía desde la URL de la página).

Respuesta: data (+ content, related[], comments[], tags[]), settings, template. Sin caché (para medir vistas).

GET /api/v1/categories

Categorías del sitio con conteo de posts.

Parámetros: Ninguno (solo auth). Cache + ETag.

Respuesta: data[] { name, slug, posts_count }

GET /api/v1/tags

Tags del sitio con conteo de posts.

Parámetros: Ninguno (solo auth). Cache + ETag.

Respuesta: data[] { name, slug, posts_count }

GET /api/v1/feed.xml

Feed RSS de posts publicados.

Parámetros: base_url (URL pública del blog del cliente, para links absolutos).

Respuesta: application/rss+xml

GET /api/v1/sitemap.xml

Sitemap XML de posts publicados.

Parámetros: base_url (URL pública del blog).

Respuesta: application/xml

GET /api/v1/posts/{slug}/comments

Árbol de comentarios visibles del post (si comments_enabled). Incluye respuestas anidadas.

Parámetros: slug. Cache + ETag.

Respuesta: data[] { id, parent_id, depth, author_name, body, is_featured, staff_label, created_at, created_label, replies[] }

POST /api/v1/posts/{slug}/comments

Publicar comentario o respuesta. Rate limit de escritura más estricto.

Parámetros: JSON: author_name, body; opcionales author_email, parent_id (respuesta, máx. 4 niveles), website (honeypot).

Respuesta: 201 + data { …, replies: [] } o pendiente si hay moderación

POST /api/v1/newsletter

Suscripción al newsletter del sitio (si newsletter_enabled).

Parámetros: JSON: email; opcionales source, website (honeypot).

Respuesta: 200 + mensaje de confirmación

Contrato JSON

Ejemplos de respuesta

Forma típica de GET /posts y GET /posts/{slug}.

GET /posts (listado)
{
  "data": [
    {
      "title": "Mi primer post",
      "subtitle": null,
      "slug": "mi-primer-post",
      "excerpt": "Resumen…",
      "cover_image": "https://…/cover.jpg",
      "cover_layout": "below",
      "cover_size": "full",
      "is_featured": false,
      "view_count": 42,
      "reading_minutes": 3,
      "published_at": "2026-08-10T12:00:00+00:00",
      "published_label": "10 ago 2026",
      "author": { "name": "Ana" },
      "category": { "name": "Noticias", "slug": "noticias" },
      "tags": [{ "name": "Guías", "slug": "guias" }]
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 12,
    "total": 1,
    "stats": { "articles": 1, "categories": 1, "featured": 0 }
  },
  "template": { "key": "classic", "name": "Classic", "config": {} }
}
GET /posts/{slug} (detalle)
{
  "data": {
    "title": "Mi primer post",
    "slug": "mi-primer-post",
    "excerpt": "Resumen…",
    "content": "<p>HTML del artículo…</p>",
    "meta_title": "Mi primer post",
    "meta_description": "Resumen…",
    "cover_image": "https://…/cover.jpg",
    "view_count": 43,
    "author": { "name": "Ana" },
    "category": { "name": "Noticias", "slug": "noticias" },
    "tags": [{ "name": "Guías", "slug": "guias" }],
    "related": [{ "title": "Otro post", "slug": "otro-post" }],
    "comments": [{
      "id": 12,
      "parent_id": null,
      "depth": 0,
      "author_name": "Luis",
      "body": "Muy útil",
      "is_featured": false,
      "staff_label": null,
      "created_label": "hace 2 horas",
      "replies": [{
        "id": 15,
        "parent_id": 12,
        "depth": 1,
        "author_name": "Ana",
        "body": "¡Gracias!",
        "is_featured": false,
        "staff_label": "Autor",
        "created_label": "hace 1 hora",
        "replies": []
      }]
    }]
  },
  "settings": {
    "newsletter_enabled": true,
    "comments_enabled": true,
    "blog_show_hero": true
  },
  "template": { "key": "classic", "name": "Classic", "config": {} }
}

Datos

Campos de un post

Todos los campos que expone la API hoy.

CampoDescripciónDónde
titleTítulolistado + detalle
subtitleSubtítulo opcionallistado + detalle
slugPara /blog/{slug} o ?slug=listado + detalle
excerptResumen cortolistado + detalle
contentHTML del cuerposolo detalle
cover_imageURL de portada (o null)listado + detalle
cover_layoutDisposición de portada (ej. below)listado + detalle
cover_sizeTamaño de portada (ej. full)listado + detalle
is_featuredtrue si está destacadolistado + detalle
view_countVisualizaciones del artículo (se incrementa al abrir el detalle)listado + detalle
reading_minutesMinutos estimados de lecturalistado + detalle
published_atFecha ISO 8601listado + detalle
published_labelFecha legible para UIlistado + detalle
author.nameNombre del autorlistado + detalle
category.nameNombre de categoríalistado + detalle
category.slugSlug de categoría (filtro)listado + detalle
tags[]Tags { name, slug }listado + detalle
related[]Hasta 3 posts relacionados (misma categoría)solo detalle
comments[]Árbol visible (replies[], is_featured, staff_label Empresa/Autor/Escritor)solo detalle
meta_titleTítulo SEOsolo detalle
meta_descriptionDescripción SEOsolo detalle

Panel

Funciones del CMS

Lo que configuras en el panel afecta la API y el embed.

  • Contenido

    • Categorías, tags, destacados y búsqueda pública (?q=).
    • Publicación programada con published_at (solo visibles cuando llega la fecha).
    • Autosave, revisiones y biblioteca de medios con compresión de imágenes.
    • Hero del embed editable u ocultable en Empresa → Ajustes.
  • Engagement

    • Newsletter y comentarios (moderación opcional) desde Ajustes.
    • Respuestas anidadas en el blog (hasta 4 niveles) y favoritos destacados.
    • Panel → Comentarios: hilos por artículo, aprobar/ocultar/favorito/responder y marcar leídos.
    • RSS y sitemap para SEO.
    • Embed: modo oscuro, progreso de lectura, compartir, lightbox con swipe/pinch.
  • Seguridad y equipo

    • Invitar writers por correo (enlace 7 días) desde Escritores.
    • Dueño ve todos los comentarios; escritor solo los de sus posts. Respuestas del equipo con badge público.
    • 2FA TOTP desde Perfil → Autenticación 2FA.
    • Audit log de acciones clave (posts, ajustes, API key, invites, 2FA).
  • Analítica

    • Vistas totales y diarias en el dashboard.
    • Únicos (7 días) y top fuentes UTM cuando el detalle se abre con ?utm_*.
    • El embed reenvía UTM de la URL de la página al GET /posts/{slug}.

Errores

Códigos habituales

HTTP 401 — Falta la key

{ "message": "Missing site_key. Send X-Site-Key header or site_key query param." }

HTTP 401 — Key inválida o sitio inactivo

{ "message": "Invalid or inactive site key." }

HTTP 404 — Post no existe, no está publicado o aún está programado

Respuesta estándar Laravel Not Found

HTTP 429 — Rate limit (lectura ~120/min; escritura newsletter/comentarios ~20/min por sitio+IP)

Too Many Attempts.

HTTP 304 — ETag válido en listados cacheados (If-None-Match)

Cuerpo vacío; reutiliza la respuesta anterior del cliente.

En tu sitio

Rutas recomendadas

  • Listado: tudominio.com/blog
  • Detalle con embed: tudominio.com/blog?slug=mi-post
  • Con UTM (analítica): tudominio.com/blog?slug=mi-post&utm_source=newsletter
  • Filtros de listado: ?category=noticias&tag=guias&q=netflix&page=2
  • Detalle con API propia: tudominio.com/blog/mi-post (tú haces el routing y pides /posts/mi-post)
  • Sin subdominio ni marca ajena: el blog vive bajo tu dominio.

Notas

Importante al integrar

  • Solo posts publicados con published_at ≤ ahora salen en la API. Borradores y programados no.
  • Lectura pública: GET con site key. Escritura pública limitada: newsletter y comentarios (POST).
  • Crear/editar posts, invites y 2FA se hacen en el panel (sesión autenticada).
  • Si recibes 401, revisa X-Site-Key o ?site_key= y que el sitio esté active.
  • Listados GET usan caché corta + ETag. El detalle /posts/{slug} no se cachea para medir vistas.
  • Rate limit: ~120 req/min lectura y ~20/min escritura por sitio+IP.
  • CORS: desde el navegador, tu dominio debe poder llamar a la API del panel.
  • Plantillas (classic, magazine, minimal): vienen en template.key; el embed aplica bp-{key}.
  • per_page máximo es 50. Por defecto son 12.
  • Filtros: featured, category, tag, q. Paginación: page.
  • UTM: pasa ?utm_source=… en la URL del blog; el embed los manda al detalle.
  • feed.xml y sitemap.xml necesitan base_url apuntando a la URL pública de tu /blog.
  • Si tu página ya tiene título (ej. “Noticias”), oculta el hero del embed en Empresa → Ajustes.
  • Comentarios: POST con parent_id para responder (máx. 4 niveles). Modera y marca leídos en panel → Comentarios.
  • Demo local del embed: https://platform-mi-blog-listo.tinguar.com/embed/example.html

¿Listo para probarlo?

Crea tu cuenta gratis, copia la API key y sigue el ejemplo de tu lenguaje. Si no puedes solo, hay ayuda a precio accesible.