/ Przewodnik dla programistów
Publikowanie przez API na dowolnej stronie.
Jak zbudować własny endpoint, aby Orova mogła publikować gotowe artykuły na stronie, która nie działa na WordPress.
Orova od razu publikuje gotowe artykuły w WordPress. Jeśli Twoja strona nie działa na WordPress — własny CMS, rozwiązanie headless, strona statyczna lub własny backend — nadal możesz pozwolić Orova publikować automatycznie, przyjmując artykuły przez małe API. Piszesz jeden endpoint HTTP; Orova wywołuje go za każdym razem, gdy ukończy artykuł. Ten przewodnik jest dla programisty i opisuje pełny kontrakt żądania.
/ Przegląd
Jak to działa
Gdy Orova skończy pisać artykuł dla słowa kluczowego, wysyła jedno żądanie HTTP POST na zarejestrowany przez Ciebie URL endpointu. Treść żądania to JSON zawierający cały artykuł. Twój endpoint tworzy wpis po Twojej stronie i odpowiada końcowym, publicznym URL artykułu. Orova zapisuje ten URL jako link publikacji, tak samo jak dla wpisu WordPress. Użyj tego, gdy chcesz bezobsługowej publikacji na stronie, która nie działa na WordPress, lub gdy wolisz zachować pełną kontrolę nad sposobem przechowywania wpisów.
/ Konfiguracja
Konfiguracja — sześć kroków
- 1
Włącz „Połącz przez API” w swoim projekcie. Otwórz projekt (Projekty → Twój projekt → Połączenia). Obok karty WordPress znajduje się karta Połącz przez API. Wpisz tam URL swojego endpointu. Pole klucza tajnego jest opcjonalne: zostaw je puste, a Orova wygeneruje klucz, lub wklej klucz, którego Twoja strona już używa. Po zapisaniu klucz jest widoczny na karcie z przyciskiem Kopiuj. Projekt publikuje tylko do jednego celu — połączenie przez API blokuje przycisk WordPress i odwrotnie.
- 2
Zbuduj endpoint przyjmujący POST. Na własnej stronie lub serwerze utwórz trasę nasłuchującą żądań HTTP POST z treścią JSON. To ten URL wklejasz z powrotem na ekranie Połączenia projektu. Orova wywołuje go raz dla każdego ukończonego artykułu.
- 3
Weryfikuj token Bearer przy każdym żądaniu. Każde żądanie od Orova zawiera nagłówek Authorization w postaci „Bearer <secret>”, z dokładnie tym kluczem tajnym, który widać w projekcie. Porównaj go z zapisaną kopią i odrzuć wszystko, co się nie zgadza — to właśnie powstrzymuje innych przed publikowaniem na Twojej stronie.
- 4
Utwórz artykuł z payloadu. Odczytaj treść JSON i utwórz wpis w swoim CMS lub bazie danych: użyj title, slug i content_html dla samego artykułu, excerpt jako meta description, featured_image_url jako obrazu okładki, a keyword / lang / published_at jako metadanych.
- 5
Zwróć aktywny URL artykułu. Odpowiedz statusem HTTP 200 lub 201 i treścią JSON { "url": "https://yoursite.com/the-new-article" }. Orova zapisuje ten URL jako link publikacji artykułu. Jeśli zwrócisz status inny niż 2xx lub pominiesz pole url, Orova uzna publikację za nieudaną.
- 6
Obsługuj żądania aktualizacji z silnika Optymalizuj. Gdy treść zawiera action = "update", znajdź istniejący wpis po target_url (lub slug) i nadpisz na miejscu jego title, content_html i excerpt — URL pozostaje bez zmian. Odpowiedz 200 z { "url": ... } wskazującym ten sam wpis. Endpointy, które jeszcze tego nie obsługują, sprawią po prostu, że Orova zgłosi optymalizację jako nieudaną; publikowanie nowych artykułów działa jak dotąd.
/ Kontrakt
Żądanie wysyłane przez Orova
Każdy ukończony artykuł przychodzi jako jedno żądanie o takiej postaci:
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./ Dokumentacja
Pola payloadu
Pola żądania publikacji (nowego artykułu). Żądania usunięcia i aktualizacji używają tego samego endpointu z mniejszymi treściami, pokazanymi w osobnych sekcjach poniżej.
| Pole | Typ | Opis |
|---|---|---|
| title | string | Nagłówek artykułu. |
| slug | string | URL-friendly identifier suggested for the post. |
| content_html | string | Pełna treść artykułu jako HTML gotowy do publikacji. |
| excerpt | string | Krótkie podsumowanie, przeznaczone na meta description. |
| featured_image_url | string | null | URL obrazu okładki lub null, gdy go nie ma. |
| keyword | string | Docelowe słowo kluczowe SEO, dla którego napisano artykuł. |
| lang | string | Language as an ISO code, e.g. "en" or "vi". |
| published_at | string | Moment wysłania artykułu przez Orova, ISO 8601 w UTC, bez sufiksu strefy czasowej. |
| status | string | undefined | Opcjonalne. "draft" pozostawia artykuł nieopublikowany; "publish" lub brak wartości publikuje go. |
| action | string | undefined | Brak dla nowych artykułów. "delete" prosi o usunięcie wpisu; "update" (wysyłane przez silnik Optymalizuj) prosi o nadpisanie istniejącego wpisu na miejscu. |
| target_url | string | Tylko przy action = "update": aktywny URL wpisu do nadpisania. URL pozostaje bez zmian. |
| id | number | undefined | Only sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`. |
Pola opcjonalne są całkowicie pomijane w treści, gdy nie ma nic do wysłania — Orova nigdy nie wysyła null, więc odczytuj je ostrożnie. status także jest opcjonalny: "draft" tworzy artykuł jako szkic, a "publish" lub brak status publikuje go od razu.
/ Kontrakt
Odpowiedź, którą musisz zwrócić
Po utworzeniu wpisu odpowiedz statusem HTTP 200 lub 201 i taką treścią JSON:
HTTP 200 (or 201)
Content-Type: application/json
{
"url": "https://yoursite.com/published-article"
}Orova zapisuje ten url jako link publikacji artykułu. Jeśli endpoint zwróci status inny niż 2xx albo treść, która nie jest obiektem JSON z polem url, Orova uzna publikację za nieudaną i oznaczy artykuł, abyś mógł ponowić próbę. Odpowiedz w ciągu 60 sekund — po tym czasie Orova przerywa żądanie (dotyczy to publikacji, aktualizacji i usuwania).
/ Przykład
Przykład kodu
Minimalny endpoint odbierający w Node.js z Express. Te same kroki — weryfikacja tokenu, rozgałęzienie według action (update / delete), utworzenie lub zmiana wpisu, zwrócenie URL — działają w każdym języku i frameworku.
// 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);/ Kontrakt — aktualizacja
Aktualizacja artykułów (Optymalizuj)
Silnik Optymalizuj w Orova przepisuje artykuły, które są już opublikowane — odświeżone fakty, lepsze tytuły, scalone treści — a następnie nadpisuje istniejący wpis na miejscu, bez zmiany URL. Żądanie trafia na ten sam URL endpointu, jako POST z tym samym tokenem Bearer:
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.Znajdź wpis po target_url (awaryjnie po slug), zastąp jego tytuł, treść i meta description, zachowaj URL bez zmian, a następnie odpowiedz 200 z { "url": "<same post URL>" }. Każdy status inny niż 2xx lub treść bez url sprawia, że Orova oznacza optymalizację jako nieudaną (nigdy nie tworzy duplikatu wpisu). Endpointy, które jeszcze tego nie wdrożyły, nadal publikują nowe artykuły bez zmian — obsługa aktualizacji jest potrzebna dopiero, gdy używasz ekranu Optymalizuj.
Od 29 sierpnia treść aktualizacji może też zawierać id (dopasuj po nim przed target_url), featured_image_url dla nowego obrazu okładki oraz status z wartością "publish" lub "draft". Wszystkie trzy są opcjonalne; gdy są puste, są pomijane w treści, nigdy nie są wysyłane jako null.
/ Kontrakt — Visual Editor
Odczyt artykułów (wymagane)
Orova musi odczytywać artykuły, które już są na Twojej stronie — aby unikać powielania tematów, dodawać linki wewnętrzne i optymalizować starsze wpisy. Dodaj dwie kolejne akcje na tym samym URL endpointu, z tym samym tokenem Bearer: list zwraca Twoje artykuły, get zwraca jeden z nich w całości.
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 jest ograniczony do 500, a najnowszy artykuł jest pierwszy. Dla get Orova wysyła razem id, target_url i slug — dopasuj po tym, który rozpoznajesz, w tej kolejności. Gdy nic nie pasuje, odpowiedz 404 z { "ok": false, "err": "not_found" }. excerpt w list jest opcjonalny (maks. 300 znaków). Wymagane: Orova wywołuje list, aby wiedzieć, które artykuły już istnieją (by unikać duplikatów, dodawać linki wewnętrzne i analizować całą stronę), oraz get, aby odczytać artykuł podczas jego optymalizacji. Jeśli Twoja strona nie obsługuje tych dwóch akcji, nowe artykuły nie dostaną linków wewnętrznych, a starszych nie da się optymalizować.
/ Kontrakt — Visual Editor
Przesyłanie obrazów (Visual Editor)
Gdy upuszczasz obraz w Visual Editor, Orova wysyła go na ten sam URL endpointu jako base64 w treści JSON — bez multipart, bez drugiego URL do zabezpieczania.
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
}Przyjmuj tylko image/* i odrzucaj wszystko powyżej 15 MB. Zapisz plik, a następnie odpowiedz bezwzględnym URL obrazu: ścieżka względna psuje artykuł, gdy tylko zostanie wyświetlony gdzie indziej. Ta akcja jest potrzebna tylko dla Visual Editor.
/ Kontrakt — usuwanie
Usuwanie artykułów
Gdy użytkownik usuwa opublikowany artykuł w Orova (z ekranu Pisanie lub Raporty), Orova prosi Twoją stronę, aby też go usunęła. Żądanie trafia na ten sam URL endpointu, jako POST z tym samym tokenem Bearer — różni się tylko treść:
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
}Znajdź wpis po url (awaryjnie po slug), usuń go lub cofnij publikację, a następnie odpowiedz 200 i { "deleted": true } — sam 204 też działa. Jeśli wpis już nie istnieje, odpowiedz 404, a Orova uzna go za już usunięty. Zwrócenie { "deleted": false } informuje Orova, że wpis nie został usunięty. Zwykłe żądania publikacji nigdy nie zawierają pola action, więc istniejące endpointy działają bez zmian — obsługa usuwania jest opcjonalna, ale zalecana.
/ Bezpieczeństwo
Uwagi dotyczące bezpieczeństwa
- Zawsze weryfikuj token Bearer. Twój endpoint to publiczny URL — każdy może go wywołać. Nagłówek Authorization to jedyne, co dowodzi, że żądanie naprawdę pochodzi od Orova, więc odrzucaj każde żądanie, którego token nie zgadza się dokładnie z zapisanym kluczem tajnym.
- Udostępniaj endpoint przez HTTPS. Klucz tajny jest przesyłany w nagłówku. HTTPS chroni go przed odczytaniem w trakcie przesyłania.
- Trzymaj klucz tajny poza kodem. Przechowuj go w zmiennej środowiskowej lub menedżerze sekretów, nigdy na sztywno w pliku, który commitujesz. Jeśli kiedykolwiek wycieknie, wygeneruj nowy w Projekt → Połączenia.
- Traktuj content_html jak treść, a nie zaufany kod. Przechowuj i wyświetlaj go z taką samą ostrożnością jak treść każdego artykułu w swoim CMS.
/ Podsumowanie
To cały kontrakt
Gdy endpoint działa i jest zarejestrowany w projekcie, Orova publikuje na Twojej stronie automatycznie — dokładnie tak jak dla użytkowników WordPress. Każdy ukończony artykuł trafia na Twój serwer, staje się wpisem, a jego publiczny URL wraca do Raportów i Analizy, abyś mógł śledzić jego wyniki.
/ Potrzebujesz pomocy?
Masz problem z podłączeniem endpointu? Otwórz sekcję Pomoc w swoim obszarze roboczym lub wyślij nam wiadomość.
