Інтеграція Custom API

Підключіть будь-який сайт до Seonix

Реалізуйте один REST-ендпоінт у своєму сайті — Seonix публікуватиме згенеровані статті напряму на нього: оновлення, завантаження медіа та опційне видалення з коробки. Працює з будь-яким стеком: Node, Python, PHP, Go, Rails чи генератор статичних сайтів.

Як це працює

Seonix — клієнт. Ваш сайт надає ендпоінт. Ви генеруєте, редагуєте й оцінюєте статті в Seonix, тиснете Publish — Seonix надсилає статтю на ваш ендпоінт із Bearer-токеном. Повторні публікації використовують той самий external_id, тому дублікатів не буде.

1. Реалізуйте ендпоінт

Приймайте JSON POST, перевіряйте Bearer-токен проти власного секрету та робіть upsert за external_id. Достатньо кількох рядків у будь-якому фреймворку — див. приклади клієнта нижче.

2. Підключіть канал у Seonix

У Channels проєкту додайте Custom API канал із URL ендпоінта і токеном. Seonix перевіряє з'єднання при збереженні, потім надсилає кожну статтю і зберігає id, який ви повернули.

3. Оновлення та видалення синхронні

Кожна повторна публікація — той самий POST із тим же external_id, upsert на вашому боці. Якщо налаштуєте delete URL template, видалення статті в Seonix викличе DELETE з id, який повернув ваш ендпоінт.

Налаштування підключення

Для підключення обов'язкові два поля — URL ендпоінта і токен. Додайте також media upload URL: саме він тримає ваші зображення на вашому хості, а не лишає опубліковані сторінки з посиланнями на Seonix (див. Медіа). Вставити й зберегти їх можна у вкладці Channels всередині Seonix, не виходячи з додатку.

  1. 1

    Оберіть спільний секрет

    Згенеруйте довгий випадковий рядок (32+ символи). Це Bearer-токен, який Seonix надсилає в кожному запиті. Тримайте його на сервері; не зашивайте у клієнтський код.

  2. 2

    Реалізуйте POST /your-endpoint

    Перевіряйте заголовок Authorization проти вашого секрету, робіть upsert статті за external_id та повертайте { "id": "…", "url": "…" }. У разі помилки відповідайте 4xx/5xx із JSON-тілом.

  3. 3

    Реалізуйте ендпоінт завантаження медіа

    Приймайте multipart POST із полем `file` і повертайте { "url": "…" }. Обов'язково для продакшену — саме це кладе зображення на ваш хост, а не лишає сторінки з посиланнями на Seonix. Хешуйте байти та зберігайте за контент-адресою, повертаючи вже наявний URL для відомих байтів: те саме зображення надсилається повторно під час правок і переопублікацій, і саме контент-адресація зупиняє дублікати.

  4. 4

    (Опційно) Реалізуйте DELETE /your-endpoint/:id

    Якщо хочете, щоб статті зникали з сайту при видаленні в Seonix, обробляйте DELETE з тим самим Bearer-токеном. Повертайте 200 у разі успіху; 404 ми трактуємо як вже видалений запис.

  5. 5

    Додайте канал у Seonix

    Відкрийте Channels у проєкті → Custom API → Connect. Вставте URL ендпоінта і токен, опційно — delete URL template, media upload URL, локаль та автора. Seonix перевіряє з'єднання одразу після збереження.

  6. 6

    Публікуйте з редактора

    Відкрийте статтю, натисніть Publish, оберіть свій Custom API канал у списку — і тисніть Publish now. Seonix збереже повернутий id, щоб усі подальші оновлення й видалення йшли в той самий запис.

Конфіг каналу

Канал Custom API зберігає URL ендпоінта, bearer-токен і опційні поля нижче. Секрети залишаються всередині проєкту Seonix — їх читають лише для побудови вихідних запитів. Мапа `headers` — розширена опція, що задається через API каналу; решту покриває форма в дашборді.

json
{
  "api_url":             "https://your-site.example/api/articles",
  "api_token":           "YOUR_SHARED_SECRET",
  "delete_url_template": "https://your-site.example/api/articles/{id}",
  "media_upload_url":    "https://your-site.example/api/media",
  "lang":                "en",
  "author":              "Your team",
  "headers":             { "X-Custom": "advanced, via channel API" }
}

Усі поля конфіга зберігаються всередині проєкту Seonix, доступні лише для серверної частини Seonix і ніколи не залишають движок. Змінити конфіг можна в будь-який момент через ту саму модалку Channels → Custom API → Manage.

Автентифікація

Оберіть будь-який bearer-токен — це спільний секрет між вашим сайтом і Seonix. Seonix надсилає його в заголовку Authorization кожного запиту: публікації, завантаження медіа, видалення та верифікаційні проби. Ротація — просто оновіть конфіг каналу, рестарт не потрібен жодній стороні.

  • Authorization:Bearer <your shared secret>
  • X-Seonix-Contract: 1

Порівнюйте токен за константний час (hash_equals / timingSafeEqual / hmac.compare_digest — див. приклади нижче) і тримайте його лише на сервері. X-Seonix-Contract — версія контракту: вона зміниться лише при breaking-змінах, нові опційні поля її не піднімають.

Перевірка з'єднання

При збереженні каналу Seonix надсилає пробний запит, щоб неправильний URL чи токен спливли одразу, а не на першій публікації. Проба — звичайний POST із порожнім JSON-об'єктом і заголовком X-Seonix-Verify: 1. Не створюйте для неї запис: відхилити порожній payload з 400 — очікувана відповідь.

POST/ваш-ендпоінт

Проба з'єднання: порожній JSON + X-Seonix-Verify: 1.

http
POST https://your-site.example/api/articles
Authorization: Bearer YOUR_SHARED_SECRET
X-Seonix-Verify: 1
X-Seonix-Contract: 1
Content-Type: application/json

{}

# How Seonix reads your answer:
#   401 / 403            -> token rejected, channel save fails
#   404                  -> endpoint not found, channel save fails
#   non-JSON content     -> wrong URL (a marketing page?), save fails
#   2xx or 4xx with JSON -> connection verified

Публікація статті

Seonix надсилає JSON POST на ваш налаштований URL і для створення, і для оновлення. external_id — стабільний id статті всередині Seonix (UUID), він не змінюється між публікаціями. Робіть upsert за ним — повторне надсилання того ж id оновлює існуючий запис замість створення дубліката.

POST/ваш-ендпоінт

Створення або оновлення (upsert).

http
POST https://your-site.example/api/articles
Authorization: Bearer YOUR_SHARED_SECRET
X-Seonix-Contract: 1
Content-Type: application/json

{
  "external_id":         "9b2f6e0a-4b1d-4c3a-9a56-170b63f6d2c1",
  "translation_key":     "c1d2e3f4-…",
  "slug":                "how-to-automate-seo-content",
  "lang":                "en",
  "title":               "How to automate SEO content in 2026",
  "excerpt":             "Short summary shown on list pages and meta tags.",
  "content_html":        "<h2 id=\"intro\">Intro</h2><p>Body copy…</p>",
  "category":            "Automation",
  "key_takeaways":       ["Point one", "Point two"],
  "key_takeaways_title": "Key takeaways",
  "author":              "Your team",
  "cover_url":           "https://your-site.example/media/abc.webp",
  "cover_alt":           "Dashboard with rising organic traffic",
  "og_image":            "https://your-site.example/media/abc.webp",
  "seo_description":     "Short meta description.",
  "published_at":        "2026-04-16T09:00:00Z",
  "schema_jsonld":       "{\"@context\":\"https://schema.org\",\"@graph\":[…]}"
}
http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id":  "42",
  "url": "https://your-site.example/blog/how-to-automate-seo-content"
}

Оновлення — це той самий POST з тим же external_id. Опційні поля, порожні в Seonix, у payload не потрапляють — збережені значення для них залишайте як є.

http
# Updates reuse the same POST with the same external_id —
# your endpoint should upsert on it. Optional fields that are
# empty in Seonix are omitted from the payload.

POST https://your-site.example/api/articles
Authorization: Bearer YOUR_SHARED_SECRET

{
  "external_id":  "9b2f6e0a-4b1d-4c3a-9a56-170b63f6d2c1",
  "slug":         "how-to-automate-seo-content",
  "lang":         "en",
  "title":        "How to automate SEO content in 2026 (refined)",
  "excerpt":      "Updated summary.",
  "content_html": "<p>Updated body…</p>"
}

Медіа: зберігайте зображення в себе

Кожне зображення має опинитися на вашій інфраструктурі. Seonix — не CDN: опублікована сторінка не повинна залишатися з посиланням на URL Seonix. Налаштуйте media upload URL — і Seonix надсилатиме кожне зображення одразу, щойно його додали в редакторі: multipart/form-data, поле `file`, той самий Bearer-токен. Поверніть URL, який ви призначили (root-relative теж підходить — він розгортається відносно origin вашого ендпоінта), і до моменту публікації кожен img src та обкладинка вже вказують на ваш хост. Дедуплікуйте за вмістом: захешуйте байти, зберігайте файл під цим хешем і повертайте вже наявний URL, коли хеш відомий — Seonix іменує файли за контент-хешем і надсилає те саме зображення повторно під час правок, рефайнменту та переопублікацій, тож саме контент-адресація лишає одну копію замість нового запису на кожну публікацію. Без media upload URL у content_html і cover_url будуть тимчасові URL доставки Seonix: завантажте їх і перехостіть у себе перед рендером. Це механізм передавання, а не гарантія хостингу.

POST/ваш-медіа-ендпоінт

multipart/form-data, поле `file`. Той самий Bearer-токен.

http
POST https://your-site.example/api/media
Authorization: Bearer YOUR_SHARED_SECRET
Content-Type: multipart/form-data; boundary=…

--…
Content-Disposition: form-data; name="file"; filename="cover.webp"
Content-Type: image/webp

<binary image bytes>
--…--

# Expected response — absolute or root-relative URL:
HTTP/1.1 200 OK
Content-Type: application/json

{ "url": "https://your-site.example/media/ab12cd.webp" }

Приймайте зображення (jpeg/png/webp/gif) розміром щонайменше до 12 МБ. Дедуплікуйте за вмістом: візьміть SHA-256 від байтів, зберігайте файл під цим хешем і повертайте вже наявний URL, коли хеш відомий. Seonix надсилає те саме зображення повторно під час правок, рефайнменту та переопублікацій — саме контент-адресація лишає одну копію в медіатеці замість нового запису на кожну публікацію.

Видалення статті

Налаштуйте delete_url_template у каналі — Seonix підставить в {id} той id, який ваш ендпоінт повернув при публікації (або external_id статті, якщо ви його не повернули), і зробить DELETE із тим же Bearer-токеном. Пропустіть template, щоб ігнорувати видалення; 404 трактується як вже видалений запис.

DELETE/ваш-ендпоінт/:id

Видалення статті. Використовується той самий Bearer-токен.

http
# {id} in delete_url_template is substituted with the id your
# endpoint returned at publish time ("42" in the example above);
# if you returned none, the article's external_id is used instead.
DELETE https://your-site.example/api/articles/42
Authorization: Bearer YOUR_SHARED_SECRET

Поля payload

Саме таку форму надсилає Seonix — поля, позначені як опційні, пропускаються, коли порожні. Мапте їх на свою схему на сервері та ігноруйте непотрібне. Вважайте content_html недовіреним вводом і санітизуйте власним allowlist перед рендером.

ПолеТипОпис
external_idstringСтабільний UUID статті в Seonix — ключ upsert. Присутній завжди і не змінюється між публікаціями.
translation_keystring?Спільний ключ мовних версій однієї статті. Надсилається, коли стаття має переклади, — використовуйте для крос-лінків між локалями.
slugstringМалими літерами, через дефіс (^[a-z0-9]+(?:-[a-z0-9]+)*$). Seonix санітизує його перед надсиланням. Однаковий slug у різних локалях вирівнює URL-адреси.
langstringISO-639-1 код мови статті: "en", "ua", "de", "fr", … Seonix підтримує 50+ мов, тож не обмежуйте валідацію двома значеннями. Українська нормалізується uk → ua.
titlestringЗаголовок статті. Значення за замовчуванням для <h1> та <title>.
excerptstringКороткий опис для сторінок списків і запасний варіант meta description. Присутній завжди, але може бути порожнім рядком — майте власний фолбек.
content_htmlstringHTML-тіло статті (заголовки h2/h3, абзаци, списки, таблиці, зображення, код). Вважайте недовіреним і санітизуйте на своєму боці — але лишіть у allowlist <img>, <figure>, <figcaption> та атрибути src, alt, width, height, loading: інакше зображення зникнуть, а сторінку смикатиме (CLS).
categorystring?Довільний рядок — категорія статті так, як вона названа у проєкті Seonix. Фіксованого словника немає; мапте на свою таксономію.
key_takeawaysstring[]?Маркований список для блоку-резюме вгорі сторінки статті. 4–6 самодостатніх речень, впорядкованих за корисністю (перше — пряма відповідь на запит). Див. примітку та розмітку під таблицею.
key_takeaways_titlestring?Заголовок блоку key takeaways мовою статті — рендерте разом зі списком, щоб не потрібен був власний переклад.
authorstringАвтор. За замовчуванням 'Seonix' або значення з конфіга каналу.
cover_urlstring?Абсолютний URL обкладинки 16/9. З налаштованим media upload URL він уже вказує на ваш хост. Без нього це тимчасовий URL доставки Seonix — завантажте файл і перехостіть у себе, не рендерте його напряму.
cover_altstring?Alt-текст обкладинки — використовуйте для <img alt> та og:image:alt.
og_imagestring?Зображення Open Graph. Наразі збігається з cover_url.
seo_descriptionstring?Meta description статті.
published_atISO 8601?Дата першої публікації (RFC 3339). Не змінюється при оновленнях — використовуйте як канонічну позначку часу і не «омолоджуйте» статтю.
schema_jsonldstring?Серіалізований schema.org @graph статті (Article, WebPage, BreadcrumbList, FAQPage, HowTo, …), згенерований з контенту. Рендерте у <script type="application/ld+json"> — див. примітку під таблицею.

Про key_takeaways: рендерте блок як звичайні заголовок + список НАД тілом статті — саме цю форму пошуковики підіймають у list-сніпети, а AI-асистенти цитують дослівно. key_takeaways_title використовуйте як заголовок без змін (він уже мовою статті); якщо він порожній — покажіть список без заголовка. Елементи — чистий текст без HTML: екрануйте їх і зберігайте порядок (він за корисністю, не за позицією в тексті). Якщо масив відсутній або порожній — сховайте блок повністю. Дублювання не буде: коли key_takeaways присутній, content_html не містить власної секції takeaways — Seonix прибирає її під час публікації, структуроване поле є єдиним джерелом.

html
<!-- Recommended markup — a plain heading + list ABOVE the body. -->
<!-- This exact shape is what search engines lift into list snippets -->
<!-- and what AI assistants (ChatGPT, Perplexity, AI Overviews) quote. -->

<section class="key-takeaways">
  <h2>{key_takeaways_title}</h2>   <!-- already in the article's language -->
  <ul>
    <li>{key_takeaways[0]}</li>    <!-- plain text: HTML-escape each item -->
    <li>{key_takeaways[1]}</li>
    …
  </ul>
</section>

{content_html}

Про schema_jsonld: @graph генерується до того, як стане відомий фінальний URL сторінки, тому URL-залежні вузли (Article, WebPage, BreadcrumbList) розраховані від домену проєкту. Якщо шлях ваших статей інший (наприклад /blog/…), рендерте з графа лише доповнювальні типи — FAQPage / HowTo — поруч із власними Article та BreadcrumbList, перезаписавши їхній @id на реальний URL сторінки. Саме так робить блог Seonix. Дублювати Article двома графами не варто: конкурентні графи пошуковики ігнорують.

Очікувана відповідь

У відповіді поверніть JSON із id запису та публічним URL сторінки. Seonix зберігає цей id і використовує його для видалення; оновлення в будь-якому разі йдуть за тим самим external_id. Обгортки { "external_id": … } та { "data": { … } } теж приймаються; все інше у відповіді ігнорується.

json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id":  "42",
  "url": "https://your-site.example/blog/how-to-automate-seo-content"
}

Помилки й повтори

Seonix чекає до 30 секунд на запит і трактує вашу відповідь так, як описано нижче. Невдалі публікації повторюються до 3 разів (через 15 хвилин, 45 хвилин і 2 години), після чого публікація позначається failed і оператор отримує сповіщення. JSON-тіло помилки з коротким людським повідомленням показується оператору як є — зробіть його корисним.

Ваша відповідьЯк трактує Seonix
2xx + JSONУспіх. Зчитуються id та url; решта ігнорується.
404Запис не знайдено. На повторній публікації Seonix створить свіжу публікацію; для DELETE це «вже видалено».
інший 4xx / 5xx / таймаутПомилка публікації: до 3 повторів через 15 хв → 45 хв → 2 год, далі failed + сповіщення оператору. message з JSON-тіла показується оператору.
не-JSON відповідьПомилка конфігурації — api_url, схоже, вказує на звичайну сторінку, а не на API. Верифікація каналу це також ловить.

Формат тіла помилки: { "error": { "code": "VALIDATION", "message": "…" } }. Порожнє чи довільне тіло теж прийнятне — тоді оператор побачить лише HTTP-статус.

Ліміти й гарантії

На що ваш ендпоінт може розраховувати і до чого має бути готовим.

  • Таймаут запиту — 30 секунд; відповідайте швидко і виносьте повільну роботу у фон.
  • Тіла статей зазвичай десятки–сотні КБ; приймайте JSON щонайменше до 5 МБ, щоб мати запас.
  • Робіть upsert атомарним та ідемпотентним: повтори й позачергові доставки можливі, тож той самий payload може прийти двічі.
  • Вважайте пару (lang, slug) унікальною на своєму боці; конфлікт зі старим записом розв'язуйте на користь свіжого external_id.
  • Транспортна безпека — HTTPS + статичний Bearer-токен. Підпису тіла немає, тож тримайте секрет довгим і ротуйте його через конфіг каналу.
  • Контракт версіонується заголовком X-Seonix-Contract (зараз 1): нові опційні поля можуть з'являтися без зміни версії — ігноруйте незнайомі поля.

Приклади клієнта

Мінімальні приймачі, які можна вставити в будь-який бекенд. Вони валідують bearer-токен за константний час, роблять upsert статті та повертають відповідь, яку очікує Seonix.

bash
# Minimal receiver test — simulate what Seonix sends you
curl -X POST "$YOUR_ENDPOINT" \
  -H "Authorization: Bearer $YOUR_SHARED_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id":  "9b2f6e0a-4b1d-4c3a-9a56-170b63f6d2c1",
    "slug":         "how-to-automate-seo-content",
    "lang":         "en",
    "title":        "How to automate SEO content in 2026",
    "excerpt":      "Short summary.",
    "content_html": "<p>Body…</p>",
    "category":     "Automation",
    "published_at": "2026-04-16T09:00:00Z"
  }'

Готові підключитися?

Створіть проєкт у Seonix, додайте канал блогу й опублікуйте першу статтю за десять хвилин. Один токен відкриває write, медіа і delete.

Почати безкоштовно