OROVA.VN — BIZ AI AGENT

/ Ръководство за разработчици

Публикуване чрез API на всеки уебсайт.

Как да изградите собствен endpoint, така че Orova да публикува готови статии в сайт, който не работи с WordPress.

Orova публикува готови статии в WordPress още от самото начало. Ако сайтът Ви не е WordPress — собствена CMS, headless система, статичен сайт или Ваш backend — пак можете да оставите Orova да публикува автоматично, като приемате статиите чрез малко API. Пишете един HTTP endpoint; Orova го извиква всеки път, когато завърши статия. Това ръководство е за разработчици и описва пълния договор на заявката.

/ Общ преглед

Как работи

Когато Orova завърши статия за ключова дума, изпраща една HTTP заявка POST към регистрирания от Вас endpoint URL. Тялото на заявката е JSON и съдържа цялата статия. Вашият endpoint създава публикацията при Вас и връща крайния публичен URL на статията. Orova записва този URL като публикуван линк, точно както за публикация в WordPress. Използвайте това, когато искате автоматично публикуване в сайт, който не е WordPress, или искате сами да контролирате как се съхраняват публикациите.

/ Настройка

Настройка — шест стъпки

  • 1

    Включете „Свързване чрез API“ в проекта. Отворете проекта си (Проекти → Вашият проект → Връзки). До картата WordPress има карта Свързване чрез API. Въведете там URL на Вашия endpoint. Полето за таен ключ е по избор: оставете го празно и Orova ще генерира ключ, или поставете ключ, който сайтът Ви вече използва. След запазване ключът се показва на картата с бутон Копирай. Един проект публикува само на едно място — свързването чрез API заключва бутона за WordPress и обратно.

  • 2

    Изградете endpoint, който приема POST. На своя сайт или сървър създайте маршрут, който слуша за HTTP POST с JSON тяло. Това е URL, който поставяте обратно в екрана Връзки на проекта. Orova го извиква по веднъж за всяка завършена статия.

  • 3

    Проверявайте Bearer token при всяка заявка. Всяка заявка от Orova носи заглавка Authorization във вида „Bearer <secret>“ с точния таен ключ, показан в проекта Ви. Сравнете го със съхранения екземпляр и отхвърляйте всичко, което не съвпада — само това пречи на чужди хора да публикуват в сайта Ви.

  • 4

    Създайте статията от payload. Прочетете JSON тялото и създайте публикация във Вашата CMS или база данни: използвайте title, slug и content_html за самата статия, excerpt като мета описание, featured_image_url като корица, а keyword / lang / published_at като метаданни.

  • 5

    Върнете реалния URL на статията. Отговорете с HTTP 200 или 201 и JSON тяло { "url": "https://yoursite.com/the-new-article" }. Orova записва този URL като публикуван линк на статията. Ако върнете код извън 2xx или пропуснете полето url, Orova счита публикуването за неуспешно.

  • 6

    Обработвайте заявките за обновяване от engine Оптимизиране. Когато тялото съдържа action = "update", намерете съществуващата публикация по target_url (или slug) и презапишете нейните title, content_html и excerpt на място — без да променяте URL. Отговорете с 200 и { "url": ... }, сочещ към същата публикация. Endpoint, който още не обработва това, просто ще накара Orova да отчете оптимизацията като неуспешна; публикуването на нови статии продължава да работи както досега.

/ Договор

Заявката, която Orova изпраща

Всяка завършена статия пристига като една заявка в този вид:

POST <your endpoint URL>

Headers:
  Content-Type: application/json
  Authorization: Bearer <secret>     # the secret key shown in Project -> Connections
  User-Agent: Orova-SEO

Body (JSON):
  {
    "title": "...",                  # article headline
    "slug": "...",                   # URL-friendly identifier
    "content_html": "...",           # full article HTML
    "excerpt": "...",                # meta description
    "featured_image_url": "..." | null,
    "keyword": "...",                # target keyword
    "lang": "en",                    # ISO language code
    "published_at": "2026-05-17T09:00:00.482913",  # ISO 8601, UTC, no offset suffix
    "status": "draft"                # optional: "draft" keeps it unpublished
  }
  # Optional fields are left out of the body when empty — never sent as null.

/ Справка

Полета на payload

Полета на заявка за публикуване (нова статия). Заявките за изтриване и обновяване използват същия endpoint с по-кратки тела, показани в собствените им раздели по-долу.

ПолеТипОписание
titlestringЗаглавието на статията.
slugstringURL-friendly identifier suggested for the post.
content_htmlstringЦялото тяло на статията като HTML, готов за публикуване.
excerptstringКратко резюме, предназначено за мета описанието.
featured_image_urlstring | nullURL на корицата или null, когато няма такава.
keywordstringЦелевата SEO ключова дума, за която е написана статията.
langstringLanguage as an ISO code, e.g. "en" or "vi".
published_atstringМоментът, в който Orova е изпратила статията, ISO 8601 в UTC, без суфикс за часова зона.
statusstring | undefinedПо избор. "draft" оставя статията непубликувана; "publish" или липса на стойност я публикува.
actionstring | undefinedЛипсва при нови статии. "delete" Ви моли да премахнете публикация; "update" (изпраща се от engine Оптимизиране) Ви моли да презапишете съществуваща публикация на място.
target_urlstringСамо при action = "update": реалният URL на публикацията за презаписване. Не променяйте URL.
idnumber | undefinedOnly sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`.

Полетата по избор изобщо се пропускат от тялото, когато няма какво да се изпрати — Orova никога не изпраща null, затова ги четете внимателно. status също е по избор: "draft" създава статията като чернова, а "publish" или липса на status я публикува веднага.

/ Договор

Отговорът, който трябва да върнете

След като създадете публикацията, отговорете с HTTP статус 200 или 201 и това JSON тяло:

HTTP 200 (or 201)
Content-Type: application/json

{
  "url": "https://yoursite.com/published-article"
}

Orova записва този url като публикуван линк на статията. Ако endpoint върне статус извън 2xx или тяло, което не е JSON обект с поле url, Orova счита публикуването за неуспешно и маркира статията, за да можете да опитате отново. Отговорете в рамките на 60 секунди — след това Orova прекъсва заявката (важи за публикуване, обновяване и изтриване).

/ Пример

Примерен код

Минимален приемащ endpoint на Node.js с Express. Същите стъпки — проверка на token, разклоняване по action (update / delete), създаване или промяна на публикацията, връщане на URL — важат за всеки език или framework.

// Node.js / Express — a minimal receiving endpoint
import express from "express";

const app = express();
app.use(express.json({ limit: "5mb" }));

// The secret Orova generated for this project.
const OROVA_SECRET = process.env.OROVA_SECRET;

app.post("/orova/publish", async (req, res) => {
  // 1. Verify the Bearer token.
  const auth = req.get("authorization") || "";
  if (auth !== "Bearer " + OROVA_SECRET) {
    return res.status(401).json({ error: "unauthorized" });
  }

  // 2. Required: list your articles (duplicate check, internal links). action = "list".
  if (req.body.action === "list") {
    const posts = await listPosts(req.body.limit || 500); // newest first, 500 max
    // each post: { id, title, slug, link, date, modified, status, author, excerpt? }
    return res.status(200).json({ ok: true, posts });
  }

  // 3. Required: read one article in full (used when optimizing it). action = "get".
  if (req.body.action === "get") {
    const post = await findPost(req.body.id, req.body.target_url, req.body.slug);
    if (!post) return res.status(404).json({ ok: false, err: "not_found" });
    return res.status(200).json({ ok: true, post });
  }

  // 4. Visual Editor: store an image, answer with its absolute URL.
  if (req.body.action === "upload_image") {
    const { filename, mime, data_base64 } = req.body;
    if (!String(mime || "").startsWith("image/")) {
      return res.status(422).json({ error: "image files only" });
    }
    const bytes = Buffer.from(data_base64, "base64");
    if (bytes.length > 15 * 1024 * 1024) {
      return res.status(413).json({ error: "image too large" });
    }
    return res.status(200).json({ ok: true, url: await saveImage(filename, bytes) });
  }

  // 5. Delete requests: body carries action = "delete".
  if (req.body.action === "delete") {
    const removed = await deletePostByUrl(req.body.url, req.body.slug);
    if (!removed) return res.status(404).json({ deleted: true }); // already gone
    return res.status(200).json({ deleted: true });
  }

  // 6. Update requests: action = "update". Optional fields may be absent.
  if (req.body.action === "update") {
    const updated = await updatePost(req.body.id, req.body.target_url, {
      title: req.body.title,
      html: req.body.content_html,
      metaDescription: req.body.excerpt,
      coverImage: req.body.featured_image_url,  // optional
      status: req.body.status,                  // optional: "publish" | "draft"
    });
    if (!updated) return res.status(404).json({ error: "post not found" });
    return res.status(200).json({ url: updated.url });
  }

  // 7. New article: read the payload Orova sent.
  const {
    title, slug, content_html, excerpt,
    featured_image_url, keyword, lang, published_at, status,
  } = req.body;

  // 8. Create the article in your own CMS or database.
  const post = await createPost({
    title,
    slug,
    html: content_html,
    metaDescription: excerpt,
    coverImage: featured_image_url,
    keyword,
    lang,
    publishedAt: published_at,
    draft: status === "draft",   // status is optional; absent means publish now
  });

  // 9. Return 200/201 with the live URL.
  return res.status(201).json({
    url: "https://yoursite.com/" + post.slug,
  });
});

app.listen(3000);

/ Договор — обновяване

Обновяване на статии (Оптимизиране)

Engine Оптимизиране на Orova пренаписва вече публикувани статии — обновени факти, по-добри заглавия, обединено съдържание — и след това презаписва съществуващата публикация на място, без да променя URL. Заявката отива към същия endpoint URL, като POST със същия Bearer token:

POST <your endpoint URL>

Headers:
  Content-Type: application/json
  Authorization: Bearer <secret>
  User-Agent: Orova-SEO

Body (JSON):
  {
    "action": "update",          # always the literal string "update"
    "id": 123,                   # optional: post id, match on this first
    "target_url": "...",         # public URL of the post to overwrite (match next)
    "title": "...",              # new headline
    "slug": "...",               # slug fallback for matching, may be empty
    "content_html": "...",       # full new article body as HTML
    "excerpt": "...",            # new meta description
    "featured_image_url": "...", # optional: new cover image
    "status": "publish"          # optional: "publish" or "draft"
  }
  # Optional fields are left out of the body when empty — never sent as null.

Намерете публикацията по target_url (при неуспех — по slug), заменете заглавието, тялото и мета описанието, запазете URL и отговорете с 200 и { "url": "<same post URL>" }. Всеки статус извън 2xx или тяло без url кара Orova да маркира оптимизацията като неуспешна (никога не създава дублирана публикация). Endpoint, който още не поддържа това, продължава да публикува нови статии без промяна — обновяването е нужно едва когато използвате екрана Оптимизиране.

От 29 август тялото за обновяване може да съдържа и id (търсете първо по него, преди target_url), featured_image_url за нова корица и status със стойност "publish" или "draft". И трите са по избор; когато са празни, се пропускат от тялото и никога не се изпращат като null.

/ Договор — Visual Editor

Четене на статии (задължително)

Orova трябва да чете статиите, които вече са в сайта Ви — за да избягва дублирани теми, да добавя вътрешни връзки и да оптимизира по-стари публикации. Добавете още две action на същия endpoint URL със същия Bearer token: list връща статиите Ви, get връща една от тях изцяло.

POST <your endpoint URL>     # same endpoint, same Bearer secret

Body (JSON):
  {
    "action": "list",            # always the literal string "list"
    "limit": 500                 # newest article first, 500 maximum
  }

Response — HTTP 200:
  {
    "ok": true,
    "posts": [
      {
        "id": 123,               # your own post id, reused by "get" and "update"
        "title": "...",
        "slug": "...",
        "link": "https://yoursite.com/the-article",
        "date": "2026-08-29",    # published date, YYYY-MM-DD
        "modified": "2026-08-29",
        "status": "publish",     # "publish" or "draft"
        "author": "...",
        "excerpt": "..."         # optional: short summary, 300 characters max
      }
    ]
  }
  # Required: Orova reads this list to avoid duplicate topics,
  # add internal links and analyze the whole site.
POST <your endpoint URL>

Body (JSON):
  {
    "action": "get",             # always the literal string "get"
    "id": 123,                   # match on this first
    "target_url": "...",         # then the public URL
    "slug": "..."                # then the slug
  }

Response — HTTP 200:
  {
    "ok": true,
    "post": {
      "id": 123,
      "title": "...",
      "slug": "...",
      "link": "https://yoursite.com/the-article",
      "content_html": "...",     # full article body as HTML
      "excerpt": "...",
      "featured_image_url": "...",
      "status": "publish"        # "publish" or "draft"
    }
  }

No such post — HTTP 404:
  { "ok": false, "err": "not_found" }

limit е най-много 500, а най-новата статия е първа. За get Orova изпраща едновременно id, target_url и slug — търсете по това, което разпознаете, в този ред. Ако нищо не съвпада, отговорете с 404 и { "ok": false, "err": "not_found" }. excerpt в list е по избор (най-много 300 знака). Задължително: Orova извиква list, за да разбере кои статии вече съществуват (за избягване на дубликати, вътрешни връзки и анализ на целия сайт), и get, за да прочете статия при оптимизиране. Ако сайтът Ви не отговаря на тези две action, новите статии няма да имат вътрешни връзки, а старите не могат да се оптимизират.

/ Договор — Visual Editor

Качване на изображения (Visual Editor)

Когато пуснете изображение във Visual Editor, Orova го изпраща към същия endpoint URL като base64 в JSON тялото — без multipart, без втори URL за защита.

POST <your endpoint URL>

Body (JSON):
  {
    "action": "upload_image",    # always the literal string "upload_image"
    "filename": "cover.png",     # original file name, extension included
    "mime": "image/png",         # image/* only
    "data_base64": "iVBORw0KGgo..."   # file bytes, base64, 15 MB maximum
  }

Response — HTTP 200 (or 201):
  {
    "ok": true,
    "url": "https://yoursite.com/uploads/cover.png"   # absolute URL
  }

Приемайте само image/* и отхвърляйте всичко над 15 MB. Запазете файла и отговорете с абсолютния URL на изображението: относителният път чупи статията веднага щом се покаже другаде. Тази action е нужна само за Visual Editor.

/ Договор — изтриване

Изтриване на статии

Когато потребител премахне публикувана статия в Orova (от екрана Писане или Отчети), Orova моли сайта Ви също да я изтрие. Заявката отива към същия endpoint URL, като POST със същия Bearer token — разликата е само в тялото:

POST <your endpoint URL>

Headers:
  Content-Type: application/json
  Authorization: Bearer <secret>
  User-Agent: Orova-SEO

Body (JSON):
  {
    "action": "delete",          # always the literal string "delete"
    "url": "...",                # public URL of the article to remove (match on this first)
    "slug": "...",               # slug fallback, may be empty
    "keyword": "..."             # the article's target keyword, for your logs
  }

Намерете публикацията по url (при неуспех — по slug), премахнете я или я скрийте от публикуване и отговорете с 200 и { "deleted": true } — работи и празен 204. Ако публикацията вече не съществува, отговорете с 404 и Orova ще я счита за изтрита. Отговор { "deleted": false } казва на Orova, че публикацията не е премахната. Обикновените заявки за публикуване никога нямат поле action, затова съществуващите endpoint продължават да работят — изтриването е по избор, но е препоръчително.

/ Сигурност

Бележки за сигурността

  • Винаги проверявайте Bearer token. Вашият endpoint е публичен URL — всеки може да го извика. Заглавката Authorization е единственото доказателство, че заявката наистина идва от Orova, затова отхвърляйте всяка заявка, чийто token не съвпада точно със съхранения таен ключ.
  • Обслужвайте endpoint през HTTPS. Тайният ключ пътува в заглавка. HTTPS не позволява да бъде прочетен по пътя.
  • Дръжте тайния ключ извън кода. Пазете го в променлива на средата или в мениджър на тайни, никога твърдо записан във файл, който качвате с commit. Ако някога изтече, генерирайте нов от Проект → Връзки.
  • Третирайте content_html като съдържание, а не като доверен markup. Съхранявайте и показвайте го със същото внимание като всяко друго тяло на статия във Вашата CMS.

/ Обобщение

Това е целият договор

Щом endpoint работи и е регистриран в проекта Ви, Orova публикува в сайта Ви автоматично — точно както за потребителите на WordPress. Всяка завършена статия пристига на Вашия сървър, става публикация, а публичният ѝ URL се връща в Отчети и Анализ, за да следите резултатите ѝ.

← Към библиотеката с ръководства

/ Нужна Ви е помощ?

Не успявате да свържете endpoint? Отворете раздела Поддръжка в работното си пространство или ни изпратете съобщение.