Upgrid
Интеграции

Интеграция через 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:

  1. Upgrid конвертирует контент статьи в форматы HTML и Markdown
  2. Создается JSON payload с данными статьи (заголовок, контент, slug, изображения и т.д.)
  3. Payload отправляется через HTTP POST на ваш webhook URL
  4. Ваш endpoint получает данные и обрабатывает их (сохраняет в базу данных, публикует в CMS и т.д.)
  5. Ваш endpoint возвращает статус код 200 для подтверждения получения

Настройка вашего Webhook

Шаг 1: Настройте ваш Webhook Endpoint

  1. Перейдите в Settings → Integrations в панели управления Upgrid
  2. Нажмите Connect Webhook
  3. Введите ваш webhook URL (например, https://your-domain.com/api/webhook)
  4. Выберите тип аутентификации:
    • Signature-based (HMAC SHA-256) - Более безопасно, рекомендуется для production
    • Bearer Token - Проще, идеально для no-code платформ вроде Zapier

Шаг 2: Сохраните и скопируйте ваш секретный ключ

После сохранения конфигурации webhook:

  1. Upgrid генерирует уникальный секретный ключ для вашего webhook
  2. Скопируйте этот ключ немедленно - он понадобится вам для проверки входящих запросов
  3. Сохраните ключ надежно (например, как UPGRID_WEBHOOK_SECRET переменную окружения)

⚠️ Важно: Храните ваш секретный ключ в безопасности. Любой, у кого есть этот ключ, может отправлять запросы, которые будут выглядеть как от Upgrid.

Шаг 3: Проверьте подключение

  1. Нажмите Send Test, чтобы отправить тестовый payload на ваш endpoint
  2. Убедитесь, что ваш endpoint получает данные и возвращает статус 200
  3. Проверьте результат теста в 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

ПолеТипОписание
titlestringЗаголовок статьи
content_htmlstringКонтент статьи в формате HTML
content_markdownstringКонтент статьи в формате Markdown
slugstringURL-friendly slug для статьи
meta_descriptionstringSEO мета-описание (опционально)
statusstringВсегда "published" для webhooks
featured_imagestringURL обложки (опционально)
published_urlstringURL, где статья была/будет опубликована (опционально)
scheduled_datestringISO 8601 timestamp запланированной публикации (опционально)
published_atstringISO 8601 timestamp фактической публикации
is_republishbooleantrue если обновление существующей статьи, false для новой
testbooleantrue для тестовых 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

  1. Создайте новый Zap
  2. Выберите Webhooks by Zapier в качестве триггера
  3. Выберите Catch Hook
  4. Скопируйте предоставленный webhook URL
  5. В Upgrid вставьте этот URL и выберите Bearer Token аутентификацию
  6. Скопируйте ваш секретный ключ Upgrid в поле аутентификации Zapier
  7. Отправьте тест из Upgrid, чтобы заполнить примерные данные
  8. Подключите желаемое действие (Google Sheets, Airtable и т.д.)

Интеграция с Make.com

  1. Создайте новый сценарий
  2. Добавьте Webhook модуль
  3. Создайте новый webhook и скопируйте URL
  4. В Upgrid вставьте этот URL и выберите Bearer Token аутентификацию
  5. В Make.com добавьте валидацию заголовка:
    • Имя заголовка: Authorization
    • Ожидаемое значение: Bearer YOUR_SECRET_KEY
  6. Отправьте тест из Upgrid
  7. Подключите желаемые модули

Лучшие практики

Безопасность

  • Всегда проверяйте подписи или токены - Никогда не доверяйте входящим запросам без аутентификации
  • Используйте 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:

  1. Перейдите в настройки интеграции webhook
  2. Посмотрите время и статус Last Test
  3. Проверьте индикатор Connection Status
  4. Просмотрите сообщения об ошибках, если подключение не удалось

Логирование на стороне сервера

Добавьте подробное логирование в ваш 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

On this page

Мы используем cookie

Мы используем cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.