Notificación IndexNOW (endpoint del integrador)
Cuando el contenido de Blog o Market se publica, actualiza, despublica o elimina, Mailoo puede hacer POST de identificadores a tu sitio. Tú construyes las URLs públicas de las páginas y las envías a IndexNow. Mailoo no llama a IndexNOW directamente y no envía URLs absolutas.
Las operaciones de publicación y escritura de catálogo en Mailoo siempre tienen éxito incluso si tu endpoint no existe o devuelve un error (fire-and-forget). Mailoo registra el resultado en la fila de contenido como indexNowStatus.
Relacionado: Blog CMS --- blog-headless-cms{.interpreted-text role="doc"}; catálogo de mercado --- market-catalog-external-api{.interpreted-text role="doc"}; ejemplo de blog con Next.js --- blog-nextjs-example{.interpreted-text role="doc"}; HMAC de webhook de entrega (misma fórmula de firma) --- transactional-email{.interpreted-text role="doc"}.
- Activador: Después de una escritura exitosa de artículo de Blog o producto de Market (o variante de producto publicado).
- Payload: IDs de proyecto/integración/contenido,
slugde blog opcional,localesyaction--- sin URLs de páginas. - Tu trabajo: Mapear ids → URLs públicas → API de IndexNOW con tu clave.
- Estado en el contenido:
PENDING→NOTIFIED|FAILED, oSKIPPEDcuandoindexNowno está configurado.nullsignifica nunca intentado (p. ej. solo borrador).
Por integración BLOG o MARKET:
- Panel de control → Conexión y ajustes → Notificación IndexNOW
- BLOG + MCP / API de gestión:
GET/PATCH /api/v1/blog/{projectUid}/integrations/{integrationId}/settings(campoindexNow; el GET saneado exponenotifyUrl+hasSecret). Herramienta MCPmanage_blog_integration_settings--- consultamailoo-mcp{.interpreted-text role="doc"}. - O
PATCH /api/v1/projects/{uid}/integrations/{id}con cuerpo:
{
"indexNow": {
"notifyUrl": "https://www.example.com/api/indexnow/notify",
"secret": "your-signing-secret"
}
}
Envía "indexNow": null para limpiar. Las respuestas saneadas de la API exponen notifyUrl y hasSecret (nunca el secreto sin procesar).
Si notifyUrl / secret no están establecidos, Mailoo establece indexNowStatus en ``SKIPPED`` y no hace llamada HTTP a tu sitio.
Método / URL: POST a la notifyUrl configurada.
Cabeceras:
Content-Type: application/jsonX-Mailoo-Event: indexnow.notifyX-Mailoo-Signature: t={unixSeconds},v1={hex}---v1es HMAC-SHA256 de"{timestamp}.{rawBody}"usandoindexNow.secret(misma fórmula que los webhooks de entrega transaccional).
Cuerpo (JSON):
{
"event": "indexnow.notify",
"kind": "blog_post",
"action": "published",
"projectUid": "…",
"integrationId": "…",
"contentId": "…",
"slug": "my-post",
"locales": ["en", "de"],
"timestamp": "2026-08-03T09:00:00.000Z"
}
Campos por tipo
kind---blog_postomarket_productcontentId--- id del artículo o id del productoslug--- slug del artículo (solo blog; omitido para market)locales--- blog:enmás claves del JSONlocalesdel artículo; market:defaultCatalogLocaleoenaction---published,updated,unpublishedodeleted
Cuándo dispara Mailoo
- Crear con
PUBLISHED→published - Actualizar que se convierte o permanece
PUBLISHED→published/updated - El estado deja
PUBLISHED→unpublished - Eliminar mientras está publicado →
deleted(sin actualización de fila de estado después) - Escrituras solo de borrador → sin notificación;
indexNowStatuspermanece ennull
Respuestas esperadas
- 2xx --- aceptado (puedes encolar IndexNOW de forma asíncrona). Mailoo establece
NOTIFIED. - 404 / 5xx / red / timeout --- Mailoo establece
FAILED. La publicación de contenido ya fue confirmada. - Configuración faltante ---
SKIPPEDsin solicitud.
PENDING--- notificación en cursoNOTIFIED--- el integrador devolvió 2xxSKIPPED--- sin configuraciónindexNowen la integraciónFAILED--- solicitud fallida o no-2xxnull--- no aplica / nunca intentado
Responsabilidades del integrador
- Verifica
X-Mailoo-Signature(rechaza firmas inválidas). - Resuelve URL(s) públicas desde
kind,contentId/slugylocales. - Llama a IndexNOW con tu clave (y archivo de clave del host según lo requiera IndexNOW).
- Devuelve 2xx rápidamente; haz trabajo pesado de forma asíncrona si es necesario.
Ejemplo con Next.js App Router
// app/api/indexnow/notify/route.ts
import { createHmac, timingSafeEqual } from 'node:crypto'
import { NextRequest, NextResponse } from 'next/server'
const SECRET = process.env.MAILOO_INDEXNOW_SECRET!
function verify(signatureHeader: string | null, body: string): boolean {
if (!signatureHeader) return false
const m = /^t=(\d+),v1=([a-f0-9]+)$/i.exec(signatureHeader.trim())
if (!m) return false
const [, t, v1] = m
const expected = createHmac('sha256', SECRET)
.update(`${t}.${body}`)
.digest('hex')
try {
return timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'))
} catch {
return false
}
}
export async function POST(req: NextRequest) {
const raw = await req.text()
if (!verify(req.headers.get('x-mailoo-signature'), raw)) {
return NextResponse.json({ error: 'invalid signature' }, { status: 401 })
}
const payload = JSON.parse(raw) as {
kind: string
action: string
contentId: string
slug?: string
locales: string[]
}
// Build URLs from payload, then POST to https://api.indexnow.org/indexnow
void payload
return NextResponse.json({ ok: true })
}
- Integración BLOG o MARKET
indexNow.notifyUrl+secretconfigurados (opcional; sin ello el estado esSKIPPED)- Tu sitio implementa el endpoint de notificación y el alojamiento de la clave IndexNOW
Ejemplo: mailoo.app como integrador
El sitio de marketing de Mailoo es tanto un consumidor de Blog como un integrador de IndexNOW para esa integración de Blog. Patrón que puedes replicar:
- Entorno solo del servidor en el sitio: secreto de firma compartido, clave IndexNOW y
SITE_URLpúblico. - Aloja el archivo de clave IndexNOW en
https://{your-domain}/{key}.txt. - Implementa
POST /api/indexnow/notifyque verifica HMAC, construye URLs públicas desde el payload y las envía ahttps://api.indexnow.org/indexnow. - En la integración de Blog, establece
notifyUrla ese endpoint ysecretal mismo secreto de firma (panel de control o MCPmanage_blog_integration_settings).