IndexNOW-уведомление (эндпоинт интегратора)

Обновлено: Aug 31, 2026Раздел: Интеграции

Когда контент Blog или Market публикуется, обновляется, снимается с публикации или удаляется, Mailoo может отправить POST с идентификаторами на ваш сайт. Вы формируете публичные URL страниц и отправляете их в IndexNow. Mailoo не вызывает IndexNOW напрямую и не отправляет абсолютные URL.

Публикация и запись данных каталога в Mailoo всегда завершаются успешно, даже если ваш эндпоинт недоступен или возвращает ошибку (fire-and-forget). Mailoo записывает результат в поле indexNowStatus строки контента.

Связанные материалы: блог CMS --- blog-headless-cms{.interpreted-text role="doc"}; каталог товаров --- market-catalog-external-api{.interpreted-text role="doc"}; пример Next.js для блога --- blog-nextjs-example{.interpreted-text role="doc"}; HMAC вебхуков доставки (та же формула подписи) --- transactional-email{.interpreted-text role="doc"}.

Архитектура

  • Триггер: После успешной записи статьи блога или товара каталога (или варианта опубликованного товара).
  • Полезная нагрузка: Идентификаторы проекта/интеграции/контента, опциональный slug блога, locales и action --- без URL страниц.
  • Ваша задача: Сопоставить идентификаторы → публичные URL → API IndexNOW с вашим ключом.
  • Статус контента: PENDINGNOTIFIED | FAILED, или SKIPPED если indexNow не настроен. null означает «не предпринималось» (например, только черновик).

Настройка

Для каждой интеграции BLOG или MARKET:

  • Панель управления → Подключение и настройки → IndexNOW-уведомление
  • BLOG + MCP / API управления: GET/PATCH /api/v1/blog/{projectUid}/integrations/{integrationId}/settings (поле indexNow; санитизированный GET возвращает notifyUrl + hasSecret). MCP-инструмент manage_blog_integration_settings --- см. mailoo-mcp{.interpreted-text role="doc"}.
  • Или PATCH /api/v1/projects/{uid}/integrations/{id} с телом:
{
  "indexNow": {
    "notifyUrl": "https://www.example.com/api/indexnow/notify",
    "secret": "your-signing-secret"
  }
}

Отправьте "indexNow": null для сброса. Санитизированные ответы API содержат notifyUrl и hasSecret (секрет в открытом виде никогда не возвращается).

Если notifyUrl / secret не заданы, Mailoo устанавливает indexNowStatus в ``SKIPPED`` и не выполняет HTTP-вызов вашего сайта.

Контракт эндпоинта

Метод / URL: POST на настроенный notifyUrl.

Заголовки:

  • Content-Type: application/json
  • X-Mailoo-Event: indexnow.notify
  • X-Mailoo-Signature: t={unixSeconds},v1={hex} --- v1 --- HMAC-SHA256 строки "{timestamp}.{rawBody}" с использованием indexNow.secret (та же формула, что и у вебхуков доставки транзакционных писем).

Тело (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"
}

Поля по типу

  • kind --- blog_post или market_product
  • contentId --- id статьи или id товара
  • slug --- slug статьи (только для блога; отсутствует для каталога)
  • locales --- блог: en плюс ключи из JSON locales статьи; каталог: defaultCatalogLocale или en
  • action --- published, updated, unpublished или deleted

Когда Mailoo отправляет уведомление

  • Создание со статусом PUBLISHEDpublished
  • Обновление, при котором статус становится или остаётся PUBLISHEDpublished / updated
  • Статус покидает PUBLISHEDunpublished
  • Удаление в опубликованном состоянии → deleted (обновления строки статуса после этого нет)
  • Запись только черновика → уведомление не отправляется; indexNowStatus остаётся null

Ожидаемые ответы

  • 2xx --- принято (вы можете поставить IndexNOW в очередь асинхронно). Mailoo устанавливает NOTIFIED.
  • 404 / 5xx / сеть / таймаут --- Mailoo устанавливает FAILED. Публикация контента уже зафиксирована.
  • Отсутствующая конфигурация --- SKIPPED без запроса.

Значения indexNowStatus

  • PENDING --- уведомление в процессе
  • NOTIFIED --- интегратор вернул 2xx
  • SKIPPED --- нет конфигурации indexNow на интеграции
  • FAILED --- запрос не удался или не-2xx ответ
  • null --- не применимо / не предпринималось

Обязанности интегратора

  1. Проверить X-Mailoo-Signature (отклонять невалидные подписи).
  2. Сформировать публичный URL из kind, contentId / slug и locales.
  3. Вызвать IndexNOW с вашим ключом (и файлом ключа хоста, как требует IndexNOW).
  4. Вернуть 2xx быстро; выполнять тяжёлые операции асинхронно.

Набросок для 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 })
}

Предварительные требования

  • Интеграция BLOG или MARKET
  • indexNow.notifyUrl + secret настроены (необязательно; без них статус SKIPPED)
  • Ваш сайт реализует эндпоинт уведомлений и хостинг ключа IndexNOW

Пример: mailoo.app как интегратор

Маркетинговый сайт Mailoo является одновременно потребителем блога и интегратором IndexNOW для этой интеграции Blog. Паттерн, который вы можете повторить:

  1. Серверные переменные окружения на сайте: общий секрет подписи, ключ IndexNOW и публичный SITE_URL.
  2. Разместите файл ключа IndexNOW по адресу https://{your-domain}/{key}.txt.
  3. Реализуйте POST /api/indexnow/notify, который проверяет HMAC, формирует публичные URL из полезной нагрузки и отправляет их в https://api.indexnow.org/indexnow.
  4. На интеграции Blog укажите notifyUrl на этот эндпоинт и secret равный тому же секрету подписи (панель управления или MCP manage_blog_integration_settings).