IllapaILLAPA
Documentación

Documentación

Todo lo que necesitas para integrar Illapa con tu sistema. Configura el webhook una vez y recibe el contenido generado directamente en tu CMS, base de datos o cualquier endpoint HTTP.

Integración

Integración por Webhook

Cuando un template está en modo Webhook automático, Illapa hace un POST a tu URL cada vez que se genera un post. El contenido llega estructurado y listo para insertar en tu base de datos.

Request
POST <tu-webhook-url>
Content-Type: application/json

Datos

JSON Payload

El body enviado a tu webhook siempre incluye los campos fijos más los campos personalizados que configuraste en el template.

POST · application/json
{
  "illapa_execution_id": "a1b2c3d4-5678-abcd-...",
  "title":        "Cómo optimizar tu blog para SEO en 2025",
  "slug":         "optimizar-blog-seo-2025",
  "description":  "Guía completa para posicionar tu blog en Google...",
  "keywords":     ["SEO para blogs", "posicionamiento web", "..."],
  "introduction": "Párrafo introductorio con hook y keyword principal...",
  "content":      "<h2>Por qué el SEO importa</h2><p>Contenido...</p>",

  "category_id":  5,
  "author_slug":  "juan",
  "status":       "draft"
}

Referencia

Campos del payload

Campos fijos

CampoTipoDescripción
illapa_execution_idstring (UUID)ID único de la ejecución. Útil para idempotencia y evitar duplicados.
titlestringTítulo del artículo generado.
slugstringSlug SEO-friendly: minúsculas, guiones, sin acentos, máx. 60 caracteres.
descriptionstringDescripción SEO corta (1-2 oraciones, ~150 caracteres).
keywordsstring[]Array de palabras clave relevantes para el artículo.
introductionstringPárrafo de introducción con la longitud configurada en el template.
contentstringCuerpo completo del artículo en el formato elegido (HTML, Markdown o texto).
imagesstring[]URLs de variantes de imagen (solo si el template tiene imágenes habilitadas).

Campos personalizados

Los campos que configures en el template se agregan al objeto raíz junto con los campos fijos. Tipos soportados: string, number, boolean.

{
  "illapa_execution_id": "...",
  "title": "...",
  "content": "...",
  "category_id": 5,
  "author_slug":  "juan",
  "status":       "draft",
  "featured":     true
}

Configuración

Formatos del campo "content"

Selecciona el formato en la configuración del template. El campo content llega en el formato elegido.

HTML (por defecto)

<h2>Subtítulo</h2>
<p>Párrafo del artículo.</p>
<ul>
  <li>Item de lista</li>
</ul>

Markdown

## Subtítulo

Párrafo del artículo.

- Item de lista

Texto plano

Subtítulo

Párrafo del artículo.

Item de lista

Protocolo

Respuesta esperada

Illapa evalúa la respuesta de tu webhook únicamente por código HTTP. El body de tu respuesta se almacena para depuración cuando hay errores.

Código HTTPResultado
2xx (200, 201, 204…)Ejecución marcada como entregada correctamente.
4xx o 5xxEjecución marcada como rechazada. El body de tu respuesta se guarda para depuración.
Timeout / sin respuestaEjecución marcada como rechazada.

Respuesta de éxito

HTTP/1.1 200 OK

{ "ok": true }

Respuesta de error

HTTP/1.1 422 Unprocessable Entity

{ "error": "El campo category_id es requerido" }

Implementación

Ejemplos de código

Next.js — App Router

app/api/webhook-illapa/route.ts
import { NextResponse } from 'next/server'

export async function POST(req: Request) {
  const {
    illapa_execution_id,
    title, slug, description,
    keywords, introduction, content,
  } = await req.json()

  // Guarda el post en tu base de datos
  // await db.insert(posts).values({ title, slug, content, description })

  return NextResponse.json({ ok: true }, { status: 200 })
}

Next.js — Pages Router

pages/api/webhook-illapa.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'POST') return res.status(405).end()

  const {
    illapa_execution_id,
    title, slug, content, description, keywords, introduction,
  } = req.body

  // Tu lógica aquí

  res.status(200).json({ ok: true })
}

Express (Node.js)

server.js
app.post('/api/webhook-illapa', async (req, res) => {
  const {
    illapa_execution_id,
    title, description, keywords, introduction, content,
  } = req.body

  // Idempotencia: verifica si ya procesaste esta ejecución
  // if (await alreadyProcessed(illapa_execution_id)) {
  //   return res.status(200).json({ ok: true, duplicate: true })
  // }

  // Guarda en tu base de datos, publica en tu CMS, etc.
  // await db.posts.create({ title, content })

  res.status(200).json({ ok: true })
})

PHP — Laravel

WebhookController.php
public function handle(Request $request)
{
    $data = $request->validate([
        'illapa_execution_id' => 'required|string',
        'title'               => 'required|string',
        'slug'                => 'required|string',
        'description'         => 'required|string',
        'keywords'            => 'required|array',
        'introduction'        => 'required|string',
        'content'             => 'required|string',
    ]);

    // Post::create($data);

    return response()->json(['ok' => true]);
}

Python — FastAPI

main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()

class PostPayload(BaseModel):
    illapa_execution_id: str
    title: str
    slug: str
    description: str
    keywords: List[str]
    introduction: str
    content: str

@app.post("/webhook-illapa")
async def receive_post(payload: PostPayload):
    # Guarda en DB, etc.
    return {"ok": True}

Desarrollo

Pruebas locales con ngrok

Si tu servidor corre en localhost, usa ngrok para exponerlo públicamente con una URL temporal.

ngrok http 3000
ngrok te da una URL del estilo https://abc123.ngrok.io. Pégala en el campo Webhook URL del template en Illapa. La URL cambia cada vez que reinicias ngrok (salvo en el plan pago).

Buenas prácticas

Idempotencia

Cada request incluye illapa_execution_id — un UUID único por ejecución. Si tu webhook puede recibir el mismo post más de una vez (reintentos manuales), guarda los IDs procesados para evitar duplicados.

Ejemplo con PostgreSQL
const existing = await db.query(
  'SELECT id FROM posts WHERE execution_id = $1',
  [illapa_execution_id]
)

if (existing.rows.length > 0) {
  return NextResponse.json({ ok: true, duplicate: true })
}

// Procesa el post normalmente...
await db.query(
  'INSERT INTO posts (execution_id, title, content) VALUES ($1, $2, $3)',
  [illapa_execution_id, title, content]
)

Resolución de problemas

Depuración

Item en estado Rechazado: cuando tu webhook devuelve un código de error (4xx, 5xx) o no responde, el item queda en estado Rechazado en el Planner. El dashboard muestra el body exacto que devolvió tu servidor — sin necesidad de revisar logs externos.

El webhook no recibe nada

  • Verifica que la URL sea accesible públicamente (no localhost).
  • Si estás en desarrollo, usa ngrok.
  • Revisa que el template esté en modo Webhook y verificado.

El post llega en formato incorrecto

  • Revisa el campo Formato de content en el template (HTML, Markdown, texto).
  • Los campos personalizados deben coincidir con los que espera tu backend.
  • Verifica el tab JSON Payload en la pantalla de generación.