/ Guide för utvecklare
Publicering via API på vilken webbplats som helst.
Så bygger du en egen endpoint så att Orova kan publicera färdiga artiklar på en webbplats som inte kör WordPress.
Orova publicerar färdiga artiklar till WordPress direkt. Om din webbplats inte är WordPress — ett eget CMS, en headless-lösning, en statisk webbplats eller en egen backend — kan du ändå låta Orova publicera automatiskt genom att ta emot artiklar via ett litet API. Du skriver en HTTP-endpoint; Orova anropar den varje gång en artikel är klar. Guiden riktar sig till en utvecklare och beskriver hela förfrågningskontraktet.
/ Översikt
Så fungerar det
När Orova har skrivit klart en artikel för ett sökord skickar den en enda HTTP-förfrågan POST till den endpoint-URL du har registrerat. Förfrågans body är JSON och innehåller hela artikeln. Din endpoint skapar inlägget hos dig och svarar med artikelns slutliga, offentliga URL. Orova sparar den URL:en som publicerad länk, precis som för ett WordPress-inlägg. Använd detta när du vill ha automatisk publicering på en webbplats som inte är WordPress, eller när du vill ha full kontroll över hur inläggen lagras.
/ Konfiguration
Konfiguration — sex steg
- 1
Slå på ”Anslut via API” i ditt projekt. Öppna ditt projekt (Projekt → ditt projekt → Anslutningar). Bredvid WordPress-kortet finns kortet Anslut via API. Ange din endpoints URL där. Fältet för hemlig nyckel är valfritt: lämna det tomt så genererar Orova en, eller klistra in en nyckel som din webbplats redan använder. När du har sparat visas nyckeln på kortet med en Kopiera-knapp. Ett projekt publicerar bara till ett mål — anslutning via API låser WordPress-knappen, och tvärtom.
- 2
Bygg en endpoint som tar emot POST. På din egen webbplats eller server skapar du en route som lyssnar efter HTTP POST med en JSON-body. Det är den URL:en du klistrar in i projektets skärm Anslutningar. Orova anropar den en gång för varje artikel som blir klar.
- 3
Verifiera Bearer-token vid varje förfrågan. Varje förfrågan från Orova har ett Authorization-huvud i formen ”Bearer <secret>”, med exakt den hemliga nyckel som visas i ditt projekt. Jämför den med din sparade kopia och avvisa allt som inte stämmer — det är detta som hindrar andra från att publicera på din webbplats.
- 4
Skapa artikeln från payloaden. Läs JSON-bodyn och skapa ett inlägg i ditt CMS eller din databas: använd title, slug och content_html för själva artikeln, excerpt som metabeskrivning, featured_image_url som omslagsbild och keyword / lang / published_at som metadata.
- 5
Returnera artikelns publika URL. Svara med HTTP 200 eller 201 och en JSON-body med { "url": "https://yoursite.com/the-new-article" }. Orova sparar den URL:en som artikelns publicerade länk. Om du returnerar en status som inte är 2xx, eller utelämnar fältet url, behandlar Orova publiceringen som misslyckad.
- 6
Hantera uppdateringsförfrågningar från Optimize-motorn. När bodyn innehåller action = "update", leta upp det befintliga inlägget via target_url (eller slug) och skriv över dess title, content_html och excerpt på plats — behåll URL:en oförändrad. Svara 200 med { "url": ... } som pekar på samma inlägg. Endpoints som ännu inte hanterar detta gör bara att Orova rapporterar optimeringen som misslyckad; publicering av nya artiklar fungerar som förut.
/ Kontrakt
Förfrågan som Orova skickar
Varje färdig artikel kommer som en förfrågan med den här formen:
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./ Referens
Fält i payloaden
Fälten i en publiceringsförfrågan (ny artikel). Förfrågningar om borttagning och uppdatering använder samma endpoint med de mindre bodies som visas i respektive avsnitt nedan.
| Fält | Typ | Beskrivning |
|---|---|---|
| title | string | Artikelns rubrik. |
| slug | string | URL-friendly identifier suggested for the post. |
| content_html | string | Hela artikelns brödtext som publiceringsklar HTML. |
| excerpt | string | En kort sammanfattning, avsedd för metabeskrivningen. |
| featured_image_url | string | null | Omslagsbildens URL, eller null när det inte finns någon. |
| keyword | string | Det SEO-sökord som artikeln skrevs för. |
| lang | string | Language as an ISO code, e.g. "en" or "vi". |
| published_at | string | Tidpunkten då Orova skickade artikeln, ISO 8601 i UTC, utan tidszonssuffix. |
| status | string | undefined | Valfritt. "draft" håller artikeln opublicerad; "publish", eller inget värde alls, publicerar den. |
| action | string | undefined | Saknas för nya artiklar. "delete" ber dig ta bort ett inlägg; "update" (skickas av Optimize-motorn) ber dig skriva över ett befintligt inlägg på plats. |
| target_url | string | Endast med action = "update": publika URL:en till inlägget som ska skrivas över. Behåll URL:en oförändrad. |
| id | number | undefined | Only sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`. |
Valfria fält utelämnas helt ur bodyn när det inte finns något att skicka — Orova skickar aldrig null, så läs dem försiktigt. status är också valfritt: "draft" skapar artikeln som utkast, medan "publish" eller ingen status alls publicerar den direkt.
/ Kontrakt
Svaret du måste returnera
När du har skapat inlägget svarar du med HTTP-status 200 eller 201 och den här JSON-bodyn:
HTTP 200 (or 201)
Content-Type: application/json
{
"url": "https://yoursite.com/published-article"
}Orova sparar den url som artikelns publicerade länk. Om din endpoint returnerar en status som inte är 2xx, eller en body som inte är ett JSON-objekt med fältet url, behandlar Orova publiceringen som misslyckad och markerar artikeln därefter så att du kan försöka igen. Svara inom 60 sekunder — därefter avbryter Orova förfrågan (gäller publicering, uppdatering och borttagning).
/ Exempel
Kodexempel
En minimal mottagande endpoint i Node.js med Express. Samma steg — verifiera token, förgrena på action (update / delete), skapa eller ändra inlägget, returnera URL:en — gäller i alla språk och ramverk.
// 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 — uppdatering
Uppdatera artiklar (Optimize)
Orovas Optimize-motor skriver om artiklar som redan är publicerade — uppdaterade fakta, bättre titlar, sammanslaget innehåll — och skriver sedan över det befintliga inlägget på plats, med oförändrad URL. Förfrågan går till samma endpoint-URL, som en POST med samma 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.Leta upp inlägget via target_url (annars slug), ersätt titel, brödtext och metabeskrivning, behåll URL:en som den är och svara sedan 200 med { "url": "<same post URL>" }. En status som inte är 2xx, eller en body utan url, gör att Orova markerar optimeringen som misslyckad (den skapar aldrig ett dubblettinlägg). Endpoints som ännu inte har implementerat detta fortsätter publicera nya artiklar som vanligt — uppdatering behöver bara implementeras när du använder skärmen Optimera.
Sedan 29 augusti kan uppdateringsbodyn även innehålla id (matcha på det före target_url), featured_image_url för en ny omslagsbild och status med "publish" eller "draft". Alla tre är valfria; när de är tomma utelämnas de ur bodyn och skickas aldrig som null.
/ Kontrakt — Visual Editor
Läsa artiklar (obligatoriskt)
Orova behöver läsa artiklarna som redan finns på din webbplats — för att undvika dubbla ämnen, lägga till interna länkar och optimera äldre inlägg. Lägg till två actions till på samma endpoint-URL, med samma Bearer-token: list returnerar dina artiklar, get returnerar en av dem i sin helhet.
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 har ett tak på 500 och den nyaste artikeln kommer först. För get skickar Orova id, target_url och slug tillsammans — matcha på det du känner igen, i den ordningen. När inget matchar, svara 404 med { "ok": false, "err": "not_found" }. excerpt i list är valfritt (högst 300 tecken). Obligatoriskt: Orova anropar list för att ta reda på vilka artiklar som redan finns (för att undvika dubbletter, lägga till interna länkar och analysera hela webbplatsen) och anropar get för att läsa en artikel när den optimeras. Om din webbplats inte svarar på dessa två actions får nya artiklar inga interna länkar och äldre artiklar kan inte optimeras.
/ Kontrakt — Visual Editor
Ladda upp bilder (Visual Editor)
När du släpper en bild i Visual Editor skickar Orova den till samma endpoint-URL som base64 inuti JSON-bodyn — ingen multipart, ingen andra URL att säkra.
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
}Acceptera bara image/* och avvisa allt över 15 MB. Spara filen och svara sedan med bildens absoluta URL: en relativ sökväg förstör artikeln så fort den visas någon annanstans. Den här action behövs bara för Visual Editor.
/ Kontrakt — borttagning
Ta bort artiklar
När en användare tar bort en publicerad artikel i Orova (från skärmen Skriv eller Rapporter) ber Orova din webbplats att ta bort den också. Förfrågan går till samma endpoint-URL, som en POST med samma Bearer-token — den enda skillnaden är bodyn:
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
}Leta upp inlägget via url (annars slug), ta bort eller avpublicera det och svara sedan med 200 och { "deleted": true } — ett rent 204 fungerar också. Om inlägget inte finns längre, svara 404 så behandlar Orova det som redan borttaget. Att returnera { "deleted": false } talar om för Orova att inlägget inte togs bort. Vanliga publiceringsförfrågningar innehåller aldrig något action-fält, så befintliga endpoints fungerar oförändrat — att implementera borttagning är valfritt men rekommenderas.
/ Säkerhet
Säkerhetsanvisningar
- Verifiera alltid Bearer-token. Din endpoint är en offentlig URL — vem som helst kan anropa den. Authorization-huvudet är det enda som bevisar att en förfrågan verkligen kommer från Orova, så avvisa varje förfrågan vars token inte exakt matchar din sparade hemlighet.
- Servera endpointen via HTTPS. Hemligheten skickas i ett huvud. HTTPS hindrar att den läses under överföringen.
- Håll hemligheten borta från koden. Lagra den i en miljövariabel eller en hemlighetshanterare, aldrig hårdkodad i en fil du committar. Om den någonsin röjs, generera en ny under Projekt → Anslutningar.
- Behandla content_html som innehåll, inte som betrodd markup. Lagra och rendera den lika försiktigt som du gör med vilken artikeltext som helst i ditt CMS.
/ Sammanfattning
Det är hela kontraktet
När endpointen är igång och registrerad i ditt projekt publicerar Orova automatiskt på din webbplats — precis som för WordPress-användare. Varje artikel som blir klar landar på din server, blir ett inlägg, och dess offentliga URL förs tillbaka till Rapporter och Analys så att du kan följa hur den presterar.
/ Behöver du hjälp?
Fastnat när du kopplar upp din endpoint? Öppna avsnittet Support i din arbetsyta, eller skicka ett meddelande till oss.
