Статьи на сайт Next.js без ручной работы
Добавьте роут app/api/seowriter/route.ts, проверьте в нём подпись и сохраните статью в свою базу. После сохранения вызовите revalidatePath, чтобы страница появилась сразу, а не после следующей сборки.
Публикация на произвольный сайт входит в тарифы от Starter.
Как это работает
Положите файл роута
app/api/seowriter/route.ts. Обязательно runtime nodejs — проверка подписи требует модуля crypto.
Добавьте ключ в переменные окружения
SEOWRITER_SECRET из настроек сайта. После добавления перезапустите приложение или контейнер.
Проверьте подключение
В настройках сайта нажмите «Проверить подключение», затем «Отправить пробную статью» — она придёт как настоящая, но не потратит статью из вашего лимита.
Обработчик для Next.js
Тело читается через req.text() до разбора JSON: подписаны именно эти байты. Подписывается строка «время.тело запроса», алгоритм HMAC-SHA256. Время внутри подписи не даёт переиграть перехваченный запрос: отвергайте всё старше пяти минут. Подписей в заголовке может быть несколько через запятую — это сутки после смены ключа, проверять нужно каждую.
import { createHmac, timingSafeEqual } from "crypto";
import { revalidatePath } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
export const runtime = "nodejs";
const SECRET = process.env.SEOWRITER_SECRET!;
export async function POST(req: NextRequest) {
const raw = await req.text();
const timestamp = req.headers.get("x-seowriter-timestamp") ?? "";
const header = req.headers.get("x-seowriter-signature") ?? "";
const event = req.headers.get("x-seowriter-event") ?? "";
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return NextResponse.json({ ok: false, error: "stale" }, { status: 401 });
}
const expected = Buffer.from(
createHmac("sha256", SECRET).update(`${timestamp}.${raw}`).digest("hex"),
);
const valid = header.split(",").map((p) => p.trim())
.filter((p) => p.startsWith("v1="))
.some((p) => {
const got = Buffer.from(p.slice(3));
return got.length === expected.length && timingSafeEqual(expected, got);
});
if (!valid) return NextResponse.json({ ok: false }, { status: 401 });
if (event === "ping") return NextResponse.json({ success: true, cms: "Next.js" });
const { article } = JSON.parse(raw);
// Ищем по article.id — повторная доставка обновит запись, а не создаст вторую
await db.article.upsert({
where: { seowriterId: article.id },
update: { title: article.h1, html: article.content_html, status: article.status },
create: {
seowriterId: article.id, slug: article.slug, title: article.h1,
html: article.content_html, cover: article.cover?.url ?? null,
},
});
revalidatePath("/blog");
revalidatePath(`/blog/${article.slug}`);
// Верните url — от него зависят внутренняя перелинковка и анонсы в соцсетях
return NextResponse.json({ ok: true, url: `https://site.ru/blog/${article.slug}` });
}Что приходит в каждой статье
Не только HTML: всё, что нужно странице, приходит отдельными полями — разбирать текст регулярками не придётся.
| article.id | Постоянный идентификатор статьи. Обновляйте запись по нему — тогда повторная доставка не создаст дубль |
| article.status | draft или publish — публиковать сразу или оставить черновиком |
| article.title, h1, slug | Заголовок, H1 и ЧПУ статьи |
| article.content_html | Готовый HTML статьи вместе с микроразметкой внутри |
| article.meta | Мета-заголовок и описание для тега title и meta description |
| article.cover | Обложка: адрес, alt и признак того, постоянная ли ссылка |
| article.images | Картинки из текста. У каждой помечено, лежит она у нас или на стороннем сервисе |
| article.faq | Вопросы и ответы — если рисуете свой блок FAQ, не разбирая HTML |
| article.json_ld | Блоки микроразметки Schema.org отдельными объектами |
| article.category | Категория, если она задана в настройках сайта |
Кому подходит доставка вебхуком
Разработчикам своих проектов
Сайт написан руками или собран на фреймворке, готовой интеграции для него не существует и не появится. Обработчик занимает тридцать строк и живёт в вашем репозитории вместе с остальным кодом.
Агентствам с разношёрстными клиентами
У одного клиента WordPress, у другого самописная админка, у третьего Next.js. Вебхук снимает вопрос «а поддерживаете ли вы такую CMS»: подходит любая, и все клиентские сайты ведутся из одного кабинета.
Headless-проектам и статике
Контент лежит в своей базе или в headless-CMS, а страницы собираются на деплое. Обработчик сохраняет статью и запускает пересборку — публикация появляется без ручного коммита.
Вручную и через вебхук
Статью всегда можно перенести руками — вопрос в том, сколько это стоит времени на дистанции.
| Вручную | Через вебхук | |
|---|---|---|
| Перенос статьи на сайт | Копирование текста, картинок и мета-тегов руками | Приходит готовым за секунды после генерации |
| Время на одну статью | 20–30 минут на перенос и вёрстку | Ноль: человек не участвует |
| Картинки и обложка | Скачать, загрузить, проставить alt | Приходят ссылками, обложка отдельным полем |
| Микроразметка и FAQ | Собирать вручную или забыть | Готовые блоки Schema.org отдельными полями |
| Публикация по расписанию | Нужен человек в нужный час | Идёт сама, хоть ночью |
| Если сайт лежал | Статья ждёт, пока про неё вспомнят | Шесть повторов за восемь часов и журнал доставок |
Безопасность
- Каждый запрос подписан ключом, который знаете только вы и мы.
- Время подписи не даёт переиграть перехваченный запрос: всё старше пяти минут отвергается.
- Отправляем только по https и не идём по переадресациям.
- Ключ можно перевыпустить: сутки после этого запросы подписаны старым и новым сразу, чтобы обработчик успел обновиться.
Поручите подключение своему ИИ
Скопируйте задание в Claude Code, Cursor или Codex прямо в своём проекте: в нём описан контракт без секретов, и ассистент встроит обработчик по правилам вашей кодовой базы.
В моём проекте нужен серверный обработчик вебхука от сервиса SEO Writer.
Добавь POST-эндпоинт по адресу /api/seowriter, используя текущий стек проекта, его роутинг и ORM.
Не создавай отдельный микросервис, если в проекте уже есть серверная часть.
Запрос приходит с заголовками:
X-SEOWriter-Event: article.published | article.updated | ping
X-SEOWriter-Timestamp: unix-время в секундах
X-SEOWriter-Signature: v1=<hex>[,v1=<hex>]
X-SEOWriter-Delivery: идентификатор доставки, одинаковый на всех повторах
Тело запроса — JSON вида:
{ "event": "article.published",
"article": { "id": "uuid", "status": "draft|publish", "title": "...", "h1": "...", "slug": "...",
"content_html": "...", "excerpt": "...", "meta": {"title": "...", "description": "..."},
"cover": {"url": "...", "alt": "..."}, "images": [{"url": "...", "alt": "..."}],
"faq": [{"question": "...", "answer": "..."}], "json_ld": [...] } }
Реализуй по шагам:
1. Прочитай тело как сырые байты или текст ДО разбора JSON.
2. Посчитай HMAC-SHA256 от строки "{timestamp}.{сырое тело}" с ключом из переменной окружения SEOWRITER_SECRET.
3. Заголовок подписи может содержать несколько значений v1= через запятую — проверь КАЖДОЕ, любое совпадение считается верным. Сравнивай безопасным сравнением, не обычным равенством.
4. Отклони запрос кодом 401, если подпись не сошлась или если время отличается от текущего больше чем на 300 секунд.
5. На событие ping ответь 200 и JSON {"success": true}.
6. На article.published и article.updated сохрани статью в существующую модель публикаций проекта, обновляя запись по article.id — повторная доставка не должна создавать дубль. Для тела страницы используй content_html как готовый HTML.
7. Ответь 200 и JSON {"ok": true, "url": "<адрес опубликованной статьи>"} — адрес нужен сервису для внутренней перелинковки.
8. Отвечай быстро: тяжёлую обработку выноси в фоновую задачу.
Ограничения:
- не хардкодь секрет, читай его из переменной окружения;
- не отключай проверку подписи даже временно;
- не отвечай переадресацией;
- принимай только POST.
В конце скажи, какой полный адрес эндпоинта получится после деплоя и куда добавить SEOWRITER_SECRET.Частые вопросы
Статика или серверный рендер — есть разница?
Нет. При статической генерации вызовите revalidatePath после сохранения — страница пересоберётся на лету. При серверном рендере ничего дополнительно не нужно.
Почему нельзя разобрать тело через req.json()?
Подпись считается от сырых байтов. Если разобрать JSON и собрать его обратно, порядок ключей или пробелы изменятся — подпись перестанет совпадать. Сначала req.text(), проверка, и только потом JSON.parse от той же строки.
Сайт задеплоен на Vercel — что-то меняется?
Только одно: укажите runtime nodejs, потому что в edge-среде нет модуля crypto. Всё остальное работает как есть, включая ревалидацию.
Что делать с картинками?
Они приходят ссылками и уже лежат на нашем хранилище — можно выводить как есть. Если хотите держать файлы у себя, скачайте их при сохранении: в поле images у каждой картинки помечено, лежит она у нас или на стороннем сервисе.
Что говорят пользователи
Реальные результаты от реальных людей
“Раньше платил 3–4 копирайтерам по 1 500 ₽ за статью и тратил по 2 дня на согласование. Сейчас загружаю 20 ключей в воскресенье — в понедельник все статьи уже в WordPress. Органический трафик вырос на 40% за три месяца.”
“Ведём 12 клиентских сайтов. Раньше контент был узким местом — теперь нет. SEO Writer генерирует тексты с правильной структурой H1/H2/H3, заполняет Yoast и внутренние ссылки. Клиенты довольны, мы успеваем брать новых.”
“Нам нужны были обзорные статьи под каждую категорию товаров. С SEO Writer закрыли 60 запросов за один месяц — с картинками, ссылками на карточки товаров и мета-описаниями. Такой объём с копирайтерами обошёлся бы в 90 000 ₽.”
Тарифы
Начните бесплатно, масштабируйтесь по мере роста
Starter
- 15 статей в месяц
- 2 сайта
- Системные API ключи
- Подбор тем: 4 в месяц
- Свои API ключи
- Планировщик
- Рерайт для сторонних площадок
Pro
- 50 статей в месяц
- 5 сайтов
- Системные API ключи
- Подбор тем: 10 в месяц
- Свои API ключи
- Планировщик
- Рерайт для сторонних площадок
- Генерация обложек
Agency
- 150 статей в месяц
- 20 сайтов
- Системные API ключи
- Подбор тем: 30 в месяц
- Свои API ключи
- Планировщик
- Рерайт для сторонних площадок
- Генерация обложек
- API доступ (скоро)
- Приоритетная поддержка
Оплата через ЮKassa · Карты, СБП, ЮMoney · Без автопродления · 30 дней доступа
Смотрите также
А если у вас популярная CMS
Для этих платформ обработчик писать не нужно — подключение занимает пару минут по логину и паролю.
Ваш сайт начнёт наполняться сам
Подключение занимает полчаса вместе с обработчиком. Дальше статьи приходят по расписанию, а вы видите каждую доставку в кабинете.
Создать аккаунт бесплатно →Полезные статьи
15 сентября 2026
Токены нейросети: что это и как считать расход
Токены нейросети: чем токен отличается от слова, почему русский текст дороже английского и как считать расход генерации.
Читать14 сентября 2026
Перелинковка сайта: схемы, анкоры и типичные ошибки
Как устроена перелинковка страниц сайта: зачем она нужна, какие бывают схемы, сколько ссылок ставить в статью, как подобрать анкоры и каких ошибок избежать.
Читать13 сентября 2026
Поведенческие факторы: что это и как их улучшить
Что относится к поведенческим факторам, как Яндекс их считает, что реально можно улучшить текстом и чем заканчивается накрутка.
Читать