/ Vodnik za razvijalce
Objava prek API na kateri koli spletni strani.
Kako zgraditi lastno končno točko, da lahko Orova dokončane članke objavi na strani, ki ne teče na WordPressu.
Orova dokončane članke privzeto objavlja v WordPress. Če vaša spletna stran ni WordPress — CMS po meri, headless postavitev, statična stran ali lasten zaledni sistem — lahko Orovi vseeno omogočite samodejno objavo tako, da članke sprejemate prek majhnega API. Napišete eno končno točko HTTP; Orova jo pokliče vsakič, ko dokonča članek. Ta vodnik je namenjen razvijalcu in opisuje celotno pogodbo zahtevka.
/ Pregled
Kako deluje
Ko Orova dokonča članek za ključno besedo, pošlje en sam zahtevek HTTP POST na URL končne točke, ki ste ga registrirali. Telo zahtevka je JSON in vsebuje celoten članek. Vaša končna točka ustvari objavo na vaši strani in odgovori s končnim, javnim URL članka. Orova ta URL shrani kot objavljeno povezavo, tako kot pri objavi v WordPressu. To uporabite, če želite samodejno objavo na strani, ki ni WordPress, ali če želite ohraniti popoln nadzor nad shranjevanjem objav.
/ Nastavitev
Nastavitev — šest korakov
- 1
V projektu vklopite »Poveži prek API«. Odprite projekt (Projekti → vaš projekt → Povezave). Ob kartici WordPress je kartica Poveži prek API. Tam vnesite URL svoje končne točke. Polje skrivnega ključa ni obvezno: pustite ga prazno in Orova ga ustvari, ali prilepite ključ, ki ga vaša stran že uporablja. Po shranjevanju je ključ prikazan na kartici z gumbom Kopiraj. Projekt objavlja le na en cilj — povezava prek API zaklene gumb WordPress in obratno.
- 2
Zgradite končno točko, ki sprejema POST. Na svoji strani ali strežniku ustvarite pot, ki posluša HTTP POST s telesom JSON. To je URL, ki ga prilepite nazaj na zaslon Povezave v projektu. Orova jo pokliče enkrat za vsak dokončan članek.
- 3
Pri vsakem zahtevku preverite žeton Bearer. Vsak zahtevek iz Orove nosi glavo Authorization v obliki »Bearer <secret>« z natanko tistim skrivnim ključem, ki je prikazan v vašem projektu. Primerjajte ga s shranjeno kopijo in zavrnite vse, kar se ne ujema — tako preprečite, da bi kdo drug objavljal na vaši strani.
- 4
Ustvarite članek iz vsebine. Preberite telo JSON in ustvarite objavo v svojem CMS ali bazi podatkov: za sam članek uporabite title, slug in content_html, excerpt kot meta opis, featured_image_url kot naslovno sliko ter keyword / lang / published_at kot metapodatke.
- 5
Vrnite javni URL članka. Odgovorite s HTTP 200 ali 201 in telesom JSON { "url": "https://yoursite.com/the-new-article" }. Orova ta URL shrani kot objavljeno povezavo članka. Če vrnete stanje, ki ni 2xx, ali izpustite polje url, Orova objavo šteje za neuspešno.
- 6
Obravnavajte zahtevke za posodobitev iz pogona Optimize. Ko telo vsebuje action = "update", poiščite obstoječo objavo po target_url (ali slug) in na mestu prepišite njen title, content_html in excerpt — URL ostane nespremenjen. Odgovorite z 200 in { "url": ... }, ki kaže na isto objavo. Končne točke, ki tega še ne podpirajo, bodo povzročile le, da Orova optimizacijo javi kot neuspešno; objavljanje novih člankov deluje kot prej.
/ Pogodba
Zahtevek, ki ga pošlje Orova
Vsak dokončan članek prispe kot en zahtevek takšne oblike:
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./ Referenca
Polja vsebine
Polja zahtevka za objavo (nov članek). Zahtevki za brisanje in posodobitev uporabljajo isto končno točko z manjšimi telesi, prikazanimi v ustreznih razdelkih spodaj.
| Polje | Vrsta | Opis |
|---|---|---|
| title | string | Naslov članka. |
| slug | string | URL-friendly identifier suggested for the post. |
| content_html | string | Celotno besedilo članka kot HTML, pripravljen za objavo. |
| excerpt | string | Kratek povzetek, namenjen meta opisu. |
| featured_image_url | string | null | URL naslovne slike ali null, če je ni. |
| keyword | string | Ciljna ključna beseda SEO, za katero je bil članek napisan. |
| lang | string | Language as an ISO code, e.g. "en" or "vi". |
| published_at | string | Trenutek, ko je Orova poslala članek, ISO 8601 v UTC, brez pripone časovnega pasu. |
| status | string | undefined | Neobvezno. "draft" pusti članek neobjavljen; "publish" ali brez vrednosti ga objavi. |
| action | string | undefined | Ni prisotno pri novih člankih. "delete" zahteva odstranitev objave; "update" (pošlje ga pogon Optimize) zahteva prepis obstoječe objave na mestu. |
| target_url | string | Le pri action = "update": javni URL objave za prepis. URL ostane nespremenjen. |
| id | number | undefined | Only sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`. |
Neobvezna polja so povsem izpuščena iz telesa, kadar ni česa poslati — Orova nikoli ne pošlje null, zato jih berite previdno. Tudi status je neobvezen: "draft" ustvari članek kot osnutek, "publish" ali brez status pa ga takoj objavi.
/ Pogodba
Odgovor, ki ga morate vrniti
Ko ustvarite objavo, odgovorite s stanjem HTTP 200 ali 201 in tem telesom JSON:
HTTP 200 (or 201)
Content-Type: application/json
{
"url": "https://yoursite.com/published-article"
}Orova ta url shrani kot objavljeno povezavo članka. Če vaša končna točka vrne katero koli stanje, ki ni 2xx, ali telo, ki ni objekt JSON s poljem url, Orova objavo šteje za neuspešno in članek ustrezno označi, da ga lahko poskusite znova. Odgovorite v 60 sekundah — Orova nato zahtevek prekine (velja enako za objavo, posodobitev in brisanje).
/ Primer
Primer kode
Minimalna sprejemna končna točka v Node.js z Expressom. Isti koraki — preverite žeton, razvejite po action (update / delete), ustvarite ali spremenite objavo, vrnite URL — veljajo v katerem koli jeziku ali ogrodju.
// 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);/ Pogodba — posodobitev
Posodabljanje člankov (Optimize)
Pogon Optimize v Orovi prepisuje članke, ki so že objavljeni — osveženi podatki, boljši naslovi, združena vsebina — in nato prepiše obstoječo objavo na mestu, z nespremenjenim URL. Zahtevek gre na isti URL končne točke kot POST z istim žetonom 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.Poiščite objavo po target_url (sicer po slug), zamenjajte naslov, besedilo in meta opis, URL pustite, kot je, nato odgovorite z 200 in { "url": "<same post URL>" }. Vsako stanje, ki ni 2xx, ali telo brez url povzroči, da Orova optimizacijo označi kot neuspešno (nikoli ne ustvari podvojene objave). Končne točke, ki tega še niso uvedle, še naprej objavljajo nove članke brez sprememb — posodobitev je treba uvesti šele, ko uporabljate zaslon Optimize.
Od 29. avgusta lahko telo posodobitve vsebuje tudi id (ujemanje po njem pred target_url), featured_image_url za novo naslovno sliko in status z vrednostjo "publish" ali "draft". Vsa tri so neobvezna; prazna so izpuščena iz telesa, nikoli poslana kot null.
/ Pogodba — Vizualni urejevalnik
Branje člankov (obvezno)
Orova mora prebrati članke, ki so že na vaši strani — da se izogne podvojenim temam, doda notranje povezave in optimizira starejše objave. Na istem URL končne točke z istim žetonom Bearer dodajte še dve dejanji: list vrne vaše članke, get vrne enega v celoti.
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 je omejen na 500, najnovejši članek je prvi. Pri get Orova pošlje id, target_url in slug skupaj — ujemajte po tistem, ki ga prepoznate, v tem vrstnem redu. Če se nič ne ujema, odgovorite z 404 in { "ok": false, "err": "not_found" }. excerpt v list ni obvezen (največ 300 znakov). Obvezno: Orova pokliče list, da izve, kateri članki že obstajajo (da se izogne podvojitvam, doda notranje povezave in analizira celotno stran), in get, da prebere članek pri optimizaciji. Če vaša stran na ti dve dejanji ne odgovori, novi članki ne dobijo notranjih povezav, starejših pa ni mogoče optimizirati.
/ Pogodba — Vizualni urejevalnik
Nalaganje slik (Vizualni urejevalnik)
Ko sliko spustite v Vizualni urejevalnik, jo Orova pošlje na isti URL končne točke kot base64 v telesu JSON — brez multipart, brez drugega URL za zaščito.
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
}Sprejmite le image/* in zavrnite vse nad 15 MB. Shranite datoteko in odgovorite z absolutnim URL slike: relativna pot pokvari članek, takoj ko se prikaže drugje. To dejanje je potrebno le za Vizualni urejevalnik.
/ Pogodba — brisanje
Brisanje člankov
Ko uporabnik v Orovi odstrani objavljen članek (z zaslona Napiši ali Poročila), Orova zahteva, da ga izbriše tudi vaša stran. Zahtevek gre na isti URL končne točke kot POST z istim žetonom Bearer — razlika je le v telesu:
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
}Poiščite objavo po url (sicer po slug), jo odstranite ali umaknite iz objave, nato odgovorite z 200 in { "deleted": true } — deluje tudi gol 204. Če objava ne obstaja več, odgovorite z 404 in Orova jo šteje za že izbrisano. Vrnitev { "deleted": false } Orovi pove, da objava ni bila odstranjena. Običajni zahtevki za objavo nikoli ne vsebujejo polja action, zato obstoječe končne točke delujejo nespremenjeno — uvedba brisanja ni obvezna, je pa priporočljiva.
/ Varnost
Varnostni napotki
- Vedno preverite žeton Bearer. Vaša končna točka je javni URL — pokliče jo lahko kdor koli. Glava Authorization je edino, kar dokazuje, da zahtevek res prihaja iz Orove, zato zavrnite vsak zahtevek, katerega žeton se ne ujema natanko s shranjeno skrivnostjo.
- Končno točko strežite prek HTTPS. Skrivnost potuje v glavi. HTTPS prepreči, da bi jo kdo prebral med prenosom.
- Skrivnosti ne hranite v kodi. Shranite jo v okoljsko spremenljivko ali upravitelja skrivnosti, nikoli je ne vpišite neposredno v datoteko, ki jo potrdite v repozitorij. Če kdaj uide, ustvarite novo v Projekt → Povezave.
- content_html obravnavajte kot vsebino, ne kot zaupanja vredno oznako. Shranjujte in prikazujte ga enako previdno kot katero koli besedilo članka v vašem CMS.
/ Za konec
To je celotna pogodba
Ko je končna točka aktivna in registrirana v projektu, Orova samodejno objavlja na vaši strani — natanko tako kot za uporabnike WordPressa. Vsak dokončan članek pristane na vašem strežniku, postane objava, njegov javni URL pa se vrne v Poročila in Analizo, da lahko spremljate njegovo uspešnost.
/ Potrebujete pomoč?
Vam povezava končne točke ne uspe? Odprite razdelek Podpora v svojem delovnem prostoru ali nam pošljite sporočilo.
