Интеграция через Webhook
Отправляйте данные статьи на любой URL для кастомных интеграций с вашей CMS, платформой автоматизации или serverless функцией
Как интегрировать Upgrid с Webhooks
Webhooks позволяют Upgrid отправлять данные статьи в реальном времени на любой URL endpoint, когда статья публикуется. Этот мощный метод интеграции позволяет подключать Upgrid к кастомным CMS платформам, инструментам автоматизации, serverless функциям или любому сервису, который принимает HTTP POST запросы.
Распространенные сценарии использования
Webhooks идеально подходят для:
- Интеграции с кастомными CMS - Next.js, Hugo, Jekyll или самописные платформы
- Платформ автоматизации - Zapier, Make.com, n8n или аналогичные инструменты
- Serverless функций - AWS Lambda, Vercel Functions, Netlify Functions
- Систем уведомлений - Slack, Discord, email уведомления
- Кастомных рабочих процессов - Любой сервис, принимающий HTTP POST запросы
Требования
Перед началом вам понадобится:
- Публично доступный URL endpoint, принимающий POST запросы
- Возможность добавить server-side код для проверки подписи webhook (рекомендуется для безопасности)
- HTTPS endpoint (рекомендуется для production использования)
Как работают Webhooks
Когда статья публикуется в Upgrid:
- Upgrid конвертирует контент статьи в форматы HTML и Markdown
- Создается JSON payload с данными статьи (заголовок, контент, slug, изображения и т.д.)
- Payload отправляется через HTTP POST на ваш webhook URL
- Ваш endpoint получает данные и обрабатывает их (сохраняет в базу данных, публикует в CMS и т.д.)
- Ваш endpoint возвращает статус код 200 для подтверждения получения
Настройка вашего Webhook
Шаг 1: Настройте ваш Webhook Endpoint
- Перейдите в Settings → Integrations в панели управления Upgrid
- Нажмите Connect Webhook
- Введите ваш webhook URL (например,
https://your-domain.com/api/webhook) - Выберите тип аутентификации:
- Signature-based (HMAC SHA-256) - Более безопасно, рекомендуется для production
- Bearer Token - Проще, идеально для no-code платформ вроде Zapier
Шаг 2: Сохраните и скопируйте ваш секретный ключ
После сохранения конфигурации webhook:
- Upgrid генерирует уникальный секретный ключ для вашего webhook
- Скопируйте этот ключ немедленно - он понадобится вам для проверки входящих запросов
- Сохраните ключ надежно (например, как
UPGRID_WEBHOOK_SECRETпеременную окружения)
⚠️ Важно: Храните ваш секретный ключ в безопасности. Любой, у кого есть этот ключ, может отправлять запросы, которые будут выглядеть как от Upgrid.
Шаг 3: Проверьте подключение
- Нажмите Send Test, чтобы отправить тестовый payload на ваш endpoint
- Убедитесь, что ваш endpoint получает данные и возвращает статус 200
- Проверьте результат теста в Upgrid, чтобы подтвердить статус подключения
После подключения и проверки ваш webhook готов к автоматическому приему данных статей.
Методы аутентификации
Вариант 1: Signature-based Authentication (Рекомендуется)
Аутентификация на основе подписи использует HMAC SHA-256 для криптографической проверки того, что запросы исходят от Upgrid. Это наиболее безопасный вариант.
Как это работает:
- Upgrid создает подпись payload запроса, используя ваш секретный ключ
- Подпись отправляется в заголовке
X-Upgrid-Signature - Ваш endpoint пересчитывает подпись и сравнивает её для проверки подлинности
Пример реализации (Next.js):
// app/api/webhook/route.ts
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
export async function POST(req: NextRequest) {
try {
// Get the raw body
const body = await req.text()
// Get signature from header
const signature = req.headers.get('X-Upgrid-Signature')
if (!signature) {
return NextResponse.json(
{ error: 'Missing signature' },
{ status: 401 }
)
}
// Verify signature
const secret = process.env.UPGRID_WEBHOOK_SECRET!
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex')
if (signature !== expectedSignature) {
return NextResponse.json(
{ error: 'Invalid signature' },
{ status: 401 }
)
}
// Parse the verified payload
const payload = JSON.parse(body)
// Process the article data
await processArticle(payload)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Webhook error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
async function processArticle(payload: any) {
// Skip test payloads if you don't want to save them
if (payload.test) {
console.log('Received test webhook')
return
}
// Your logic to save/publish the article
// For example, save to database, publish to CMS, etc.
}Вариант 2: Bearer Token Authentication
Аутентификация с Bearer токеном проще и отлично подходит для no-code платформ, таких как Zapier или Make.com.
Как это работает:
- Upgrid отправляет ваш секретный ключ в заголовке
Authorization: Bearer <token> - Ваш endpoint проверяет, что токен совпадает с вашим сохраненным секретом
Пример реализации:
// app/api/webhook/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
try {
// Get authorization header
const authHeader = req.headers.get('Authorization')
if (!authHeader?.startsWith('Bearer ')) {
return NextResponse.json(
{ error: 'Missing or invalid authorization' },
{ status: 401 }
)
}
const token = authHeader.substring(7) // Remove 'Bearer ' prefix
const expectedToken = process.env.UPGRID_WEBHOOK_SECRET!
if (token !== expectedToken) {
return NextResponse.json(
{ error: 'Invalid token' },
{ status: 401 }
)
}
// Parse payload
const payload = await req.json()
// Process the article data
await processArticle(payload)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Webhook error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}Структура Payload Webhook
Upgrid отправляет JSON payload со следующей структурой:
{
"title": "Your Article Title",
"content_html": "<h1>Your Article Title</h1><p>Article content...</p>",
"content_markdown": "# Your Article Title\n\nArticle content...",
"slug": "your-article-title",
"meta_description": "Brief description for SEO",
"status": "published",
"featured_image": "https://cdn.upgrid.com/images/abc123.jpg",
"published_url": "https://your-site.com/blog/your-article-title",
"scheduled_date": "2025-01-15T10:00:00.000Z",
"published_at": "2025-01-15T10:00:00.000Z",
"is_republish": false,
"test": false
}Поля Payload
| Поле | Тип | Описание |
|---|---|---|
title | string | Заголовок статьи |
content_html | string | Контент статьи в формате HTML |
content_markdown | string | Контент статьи в формате Markdown |
slug | string | URL-friendly slug для статьи |
meta_description | string | SEO мета-описание (опционально) |
status | string | Всегда "published" для webhooks |
featured_image | string | URL обложки (опционально) |
published_url | string | URL, где статья была/будет опубликована (опционально) |
scheduled_date | string | ISO 8601 timestamp запланированной публикации (опционально) |
published_at | string | ISO 8601 timestamp фактической публикации |
is_republish | boolean | true если обновление существующей статьи, false для новой |
test | boolean | true для тестовых payloads, false для реальных статей |
Примеры полной реализации
Next.js с Upsert в базу данных
Этот пример показывает, как обрабатывать как новые статьи, так и обновления, используя паттерн upsert:
// app/api/webhook/route.ts
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
import { db } from '@/lib/db'
export async function POST(req: NextRequest) {
try {
// Verify signature (signature-based auth)
const body = await req.text()
const signature = req.headers.get('X-Upgrid-Signature')
if (!signature) {
return NextResponse.json({ error: 'Missing signature' }, { status: 401 })
}
const expectedSignature = crypto
.createHmac('sha256', process.env.UPGRID_WEBHOOK_SECRET!)
.update(body)
.digest('hex')
if (signature !== expectedSignature) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
// Parse verified payload
const payload = JSON.parse(body)
// Skip test webhooks
if (payload.test) {
console.log('Test webhook received successfully')
return NextResponse.json({ success: true, message: 'Test webhook received' })
}
// Upsert article to database
const article = await db.article.upsert({
where: { slug: payload.slug },
update: {
title: payload.title,
contentHtml: payload.content_html,
contentMarkdown: payload.content_markdown,
metaDescription: payload.meta_description,
featuredImage: payload.featured_image,
updatedAt: new Date(),
},
create: {
slug: payload.slug,
title: payload.title,
contentHtml: payload.content_html,
contentMarkdown: payload.content_markdown,
metaDescription: payload.meta_description,
featuredImage: payload.featured_image,
status: 'published',
publishedAt: new Date(payload.published_at),
},
})
console.log(
payload.is_republish
? `Updated article: ${article.slug}`
: `Created article: ${article.slug}`
)
return NextResponse.json({
success: true,
articleId: article.id,
action: payload.is_republish ? 'updated' : 'created',
})
} catch (error) {
console.error('Webhook processing error:', error)
return NextResponse.json(
{ error: 'Failed to process webhook' },
{ status: 500 }
)
}
}Vercel Serverless Function
// api/webhook.ts
import type { VercelRequest, VercelResponse } from '@vercel/node'
import crypto from 'crypto'
export default async function handler(
req: VercelRequest,
res: VercelResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' })
}
try {
// Get raw body as string
const body = JSON.stringify(req.body)
const signature = req.headers['x-upgrid-signature'] as string
// Verify signature
const expectedSignature = crypto
.createHmac('sha256', process.env.UPGRID_WEBHOOK_SECRET!)
.update(body)
.digest('hex')
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' })
}
const payload = req.body
// Process article
if (!payload.test) {
// Your logic here
console.log('Processing article:', payload.title)
}
return res.status(200).json({ success: true })
} catch (error) {
console.error('Webhook error:', error)
return res.status(500).json({ error: 'Internal server error' })
}
}Express.js Endpoint
// server.js
const express = require('express')
const crypto = require('crypto')
const app = express()
// Important: Use raw body for signature verification
app.use('/api/webhook', express.raw({ type: 'application/json' }))
app.post('/api/webhook', async (req, res) => {
try {
// Get signature
const signature = req.headers['x-upgrid-signature']
if (!signature) {
return res.status(401).json({ error: 'Missing signature' })
}
// Verify signature using raw body
const expectedSignature = crypto
.createHmac('sha256', process.env.UPGRID_WEBHOOK_SECRET)
.update(req.body)
.digest('hex')
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' })
}
// Parse the verified payload
const payload = JSON.parse(req.body.toString())
// Skip test webhooks
if (payload.test) {
console.log('Test webhook received')
return res.json({ success: true })
}
// Process article
await processArticle(payload)
res.json({ success: true })
} catch (error) {
console.error('Webhook error:', error)
res.status(500).json({ error: 'Internal server error' })
}
})
async function processArticle(payload) {
// Your article processing logic
console.log('Processing article:', payload.title)
}
app.listen(3000, () => console.log('Server running on port 3000'))Обработка обновлений vs Новые статьи
Используйте поле is_republish, чтобы определить, нужно ли создавать новую статью или обновлять существующую:
async function handleArticle(payload: any) {
if (payload.is_republish) {
// Update existing article
await updateArticle({
slug: payload.slug,
title: payload.title,
content: payload.content_html,
// ... other fields
})
} else {
// Create new article
await createArticle({
slug: payload.slug,
title: payload.title,
content: payload.content_html,
// ... other fields
})
}
}Лучшая практика: Используйте операцию upsert (update or insert) на основе поля slug. Это обрабатывает оба случая корректно и предотвращает дубликаты:
// Prisma example
await prisma.article.upsert({
where: { slug: payload.slug },
update: { /* updated fields */ },
create: { /* all fields */ },
})
// MongoDB example
await Article.findOneAndUpdate(
{ slug: payload.slug },
{ $set: { /* updated fields */ } },
{ upsert: true, new: true }
)Интеграция с Zapier / Make.com
Интеграция с Zapier
- Создайте новый Zap
- Выберите Webhooks by Zapier в качестве триггера
- Выберите Catch Hook
- Скопируйте предоставленный webhook URL
- В Upgrid вставьте этот URL и выберите Bearer Token аутентификацию
- Скопируйте ваш секретный ключ Upgrid в поле аутентификации Zapier
- Отправьте тест из Upgrid, чтобы заполнить примерные данные
- Подключите желаемое действие (Google Sheets, Airtable и т.д.)
Интеграция с Make.com
- Создайте новый сценарий
- Добавьте Webhook модуль
- Создайте новый webhook и скопируйте URL
- В Upgrid вставьте этот URL и выберите Bearer Token аутентификацию
- В Make.com добавьте валидацию заголовка:
- Имя заголовка:
Authorization - Ожидаемое значение:
Bearer YOUR_SECRET_KEY
- Имя заголовка:
- Отправьте тест из Upgrid
- Подключите желаемые модули
Лучшие практики
Безопасность
- Всегда проверяйте подписи или токены - Никогда не доверяйте входящим запросам без аутентификации
- Используйте HTTPS endpoints - Нешифрованный HTTP раскрывает ваш секретный ключ
- Храните секреты надежно - Используйте переменные окружения, никогда не хардкодьте секреты
- Реализуйте ограничение частоты запросов (rate limiting) - Защититесь от злоупотреблений
- Возвращайте 200 быстро - Обрабатывайте данные асинхронно, если нужно
Надежность
- Возвращайте статус код 200 - Upgrid считает ответы не-200 как ошибки
- Обрабатывайте тестовые webhooks - Проверяйте
test: trueи пропускайте операции с базой данных - Используйте upsert операции - Предотвращайте дублирование статей при обработке обновлений
- Реализуйте логику повторных попыток - Для внешних вызовов API в вашей обработке
- Логируйте ошибки правильно - Помогает при отладке проблем
Производительность
- Отвечайте быстро - Не заставляйте ваш webhook ждать долгих операций
- Обрабатывайте асинхронно - Ставьте в очередь тяжелые задачи (обработка изображений, внешние API)
- Валидируйте payload рано - Проверяйте обязательные поля перед обработкой
- Используйте транзакции базы данных - Обеспечивайте целостность данных
Устранение неполадок
Ошибка теста подключения
Проблема: Тестовый webhook возвращает ошибку или тайм-аут
Решения:
- Проверьте, что ваш endpoint публично доступен (не localhost)
- Проверьте логи сервера на ошибки
- Убедитесь, что endpoint возвращает статус код 200
- Проверьте валидность HTTPS сертификата (если используется HTTPS)
- Проверьте, что firewall не блокирует запросы Upgrid
Ошибки аутентификации
Проблема: Ошибки "Invalid signature" или "Invalid token"
Решения:
- Проверьте, что вы скопировали весь секретный ключ правильно
- Проверьте лишние пробелы или переносы строк в переменной окружения
- Для signature auth: убедитесь, что вы хешируете сырое тело запроса (raw request body)
- Для bearer auth: проверьте формат заголовка Authorization:
Bearer <token> - Не модифицируйте тело запроса перед проверкой
Создаются дубликаты статей
Проблема: Обновления создают новые статьи вместо обновления существующих
Решения:
- Используйте upsert операции на основе поля
slug - Проверяйте поле
is_republish, чтобы обрабатывать обновления иначе - Убедитесь, что у вас есть уникальное ограничение (unique constraint) на поле
slug
Отсутствуют заголовки Webhook
Проблема: Не удается найти заголовок X-Upgrid-Signature или Authorization
Решения:
- Некоторые фреймворки приводят имена заголовков к нижнему регистру
- Попробуйте получить доступ как
x-upgrid-signatureили используйте нечувствительный к регистру поиск - Проверьте, не удаляет ли ваш фреймворк или прокси заголовки
- Проверьте логи сервера, чтобы увидеть заголовки во входящих запросах
Тестовые Webhooks сохраняются в базу
Проблема: Тестовые payloads сохраняются как реальные статьи
Решения:
// Always check for test flag
if (payload.test) {
console.log('Test webhook - skipping database save')
return NextResponse.json({ success: true })
}
// Only process real articles
await saveArticle(payload)Мониторинг и отладка
Проверка логов Webhook
В панели управления Upgrid:
- Перейдите в настройки интеграции webhook
- Посмотрите время и статус Last Test
- Проверьте индикатор Connection Status
- Просмотрите сообщения об ошибках, если подключение не удалось
Логирование на стороне сервера
Добавьте подробное логирование в ваш endpoint:
export async function POST(req: NextRequest) {
console.log('[Webhook] Received request')
console.log('[Webhook] Headers:', Object.fromEntries(req.headers))
try {
const body = await req.text()
console.log('[Webhook] Body length:', body.length)
// ... verification logic
const payload = JSON.parse(body)
console.log('[Webhook] Payload:', {
title: payload.title,
slug: payload.slug,
is_republish: payload.is_republish,
test: payload.test,
})
// ... processing logic
console.log('[Webhook] Successfully processed')
return NextResponse.json({ success: true })
} catch (error) {
console.error('[Webhook] Error:', error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
}Часто задаваемые вопросы
В: Могу ли я использовать webhooks с localhost для разработки? О: Нет, webhook URL должны быть публично доступны. Используйте инструменты типа ngrok, localtunnel или Cloudflare Tunnel, чтобы открыть локальный сервер во время разработки.
В: Что произойдет, если мой endpoint недоступен, когда статья публикуется? О: Upgrid пометит публикацию как неудачную. Вы можете повторить попытку публикации со страницы управления статьей в панели управления.
В: Могу ли я иметь несколько webhook URL? О: В настоящее время каждый сайт может иметь один webhook URL. Если вам нужно отправлять данные в несколько сервисов, реализуйте логику fan-out в вашем webhook endpoint.
В: Как мигрировать с Bearer Token на Signature authentication? О: Просто обновите настройки webhook в Upgrid, выбрав Signature authentication. Будет сгенерирован новый секретный ключ. Обновите код вашего endpoint для проверки подписей вместо токенов.
В: Есть ли лимит на webhooks? О: Upgrid не накладывает специфичных для webhooks лимитов, но соблюдайте общие лимиты API, если вы делаете запросы обратно к Upgrid из вашего endpoint.
В: Могу ли я валидировать структуру payload webhook? О: Да, реализуйте валидацию схемы, используя библиотеки типа Zod:
import { z } from 'zod'
const webhookSchema = z.object({
title: z.string(),
content_html: z.string(),
content_markdown: z.string(),
slug: z.string(),
meta_description: z.string().optional(),
status: z.literal('published'),
featured_image: z.string().url().optional(),
published_at: z.string(),
is_republish: z.boolean(),
test: z.boolean().optional(),
})
// In your handler
const payload = webhookSchema.parse(JSON.parse(body))В: Как протестировать мой webhook локально? О: Используйте ngrok для создания публичного URL для вашего локального сервера:
# Start your local server on port 3000
npm run dev
# In another terminal, start ngrok
ngrok http 3000
# Use the provided ngrok URL (e.g., https://abc123.ngrok.io/api/webhook) in Upgrid