OROVA.VN — BIZ AI AGENT

/ Veiledning for utviklere

Publisering via API på et hvilket som helst nettsted.

Slik bygger du ditt eget endepunkt så Orova kan publisere ferdige artikler til et nettsted som ikke kjører WordPress.

Orova publiserer ferdige artikler til WordPress rett ut av boksen. Hvis nettstedet ditt ikke er WordPress — et eget CMS, et headless-oppsett, et statisk nettsted eller din egen backend — kan du likevel la Orova publisere automatisk ved å ta imot artikler via et lite API. Du skriver ett HTTP-endepunkt; Orova kaller det hver gang en artikkel er ferdig. Denne veiledningen er for utviklere og beskriver hele forespørselskontrakten.

/ Oversikt

Slik fungerer det

Når Orova er ferdig med å skrive en artikkel for et søkeord, sender den én HTTP POST-forespørsel til endepunkt-URL-en du registrerte. Forespørselens body er JSON og inneholder hele artikkelen. Endepunktet ditt oppretter innlegget hos deg og svarer med den endelige, offentlige URL-en til artikkelen. Orova lagrer den URL-en som publisert lenke, akkurat som for et WordPress-innlegg. Bruk dette når du vil ha publisering uten manuelt arbeid på et nettsted som ikke er WordPress, eller når du vil ha full kontroll over hvordan innlegg lagres.

/ Oppsett

Oppsett — seks steg

  • 1

    Slå på «Koble til via API» i prosjektet ditt. Åpne prosjektet ditt (Prosjekter → ditt prosjekt → Tilkoblinger). Ved siden av WordPress-kortet finnes kortet Koble til via API. Skriv inn URL-en til endepunktet ditt der. Feltet for hemmelig nøkkel er valgfritt: la det stå tomt, så genererer Orova en, eller lim inn en nøkkel nettstedet ditt allerede bruker. Når det er lagret, vises nøkkelen på kortet med en Kopier-knapp. Et prosjekt publiserer bare til ett mål — tilkobling via API låser WordPress-knappen, og omvendt.

  • 2

    Bygg et endepunkt som tar imot POST. På ditt eget nettsted eller server oppretter du en rute som lytter etter HTTP POST med JSON-body. Dette er URL-en du limer inn på skjermen Tilkoblinger i prosjektet. Orova kaller den én gang for hver ferdige artikkel.

  • 3

    Verifiser Bearer-tokenet på hver forespørsel. Hver forespørsel fra Orova har en Authorization-header på formen «Bearer <secret>», med nøyaktig den hemmelige nøkkelen som vises i prosjektet ditt. Sammenlign den med din lagrede kopi og avvis alt som ikke stemmer — det er dette som hindrer andre i å poste på nettstedet ditt.

  • 4

    Opprett artikkelen fra payloaden. Les JSON-bodyen og opprett et innlegg i CMS-et eller databasen din: bruk title, slug og content_html til selve artikkelen, excerpt som metabeskrivelse, featured_image_url som forsidebilde og keyword / lang / published_at som metadata.

  • 5

    Returner den publiserte URL-en til artikkelen. Svar med HTTP 200 eller 201 og en JSON-body { "url": "https://yoursite.com/the-new-article" }. Orova lagrer den URL-en som publisert lenke for artikkelen. Returnerer du en status utenom 2xx, eller utelater url-feltet, regner Orova publiseringen som mislykket.

  • 6

    Håndter oppdateringsforespørsler fra Optimaliser-motoren. Når bodyen har action = "update", finner du det eksisterende innlegget via target_url (eller slug) og overskriver title, content_html og excerpt på stedet — URL-en beholdes uendret. Svar 200 med { "url": ... } som peker til samme innlegg. Endepunkter som ikke håndterer dette ennå, gjør bare at Orova rapporterer optimaliseringen som mislykket; publisering av nye artikler fungerer som før.

/ Kontrakt

Forespørselen Orova sender

Hver ferdige artikkel kommer som én forespørsel med denne 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.

/ Referanse

Felter i payloaden

Feltene i en publiseringsforespørsel (ny artikkel). Slette- og oppdateringsforespørsler bruker samme endepunkt med de mindre bodyene som vises i egne deler nedenfor.

FeltTypeBeskrivelse
titlestringOverskriften på artikkelen.
slugstringURL-friendly identifier suggested for the post.
content_htmlstringHele artikkelteksten som publiseringsklar HTML.
excerptstringEt kort sammendrag, ment som metabeskrivelse.
featured_image_urlstring | nullURL til forsidebildet, eller null når det ikke finnes.
keywordstringSEO-søkeordet artikkelen ble skrevet for.
langstringLanguage as an ISO code, e.g. "en" or "vi".
published_atstringTidspunktet Orova sendte artikkelen, ISO 8601 i UTC, uten tidssonesuffiks.
statusstring | undefinedValgfritt. "draft" holder artikkelen upublisert; "publish", eller ingen verdi, publiserer den.
actionstring | undefinedMangler for nye artikler. "delete" ber deg fjerne et innlegg; "update" (sendt av Optimaliser-motoren) ber deg overskrive et eksisterende innlegg på stedet.
target_urlstringBare med action = "update": den publiserte URL-en til innlegget som skal overskrives. Behold URL-en uendret.
idnumber | undefinedOnly sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`.

Valgfrie felter utelates helt fra bodyen når det ikke er noe å sende — Orova sender aldri null, så les dem defensivt. status er også valgfritt: "draft" oppretter artikkelen som utkast, mens "publish" eller ingen status publiserer den med en gang.

/ Kontrakt

Svaret du må returnere

Når du har opprettet innlegget, svarer du med HTTP-status 200 eller 201 og denne JSON-bodyen:

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

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

Orova lagrer denne url som publisert lenke for artikkelen. Hvis endepunktet returnerer en status utenom 2xx, eller en body som ikke er et JSON-objekt med et url-felt, regner Orova publiseringen som mislykket og merker artikkelen deretter, så du kan prøve igjen. Svar innen 60 seconds — Orova avbryter forespørselen etter det (gjelder publisering, oppdatering og sletting likt).

/ Eksempel

Kodeeksempel

Et minimalt mottaksendepunkt i Node.js med Express. De samme stegene — verifiser tokenet, forgren på action (update / delete), opprett eller endre innlegget, returner URL-en — gjelder i alle språk og rammeverk.

// 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 — oppdatering

Oppdatere artikler (Optimaliser)

Orovas Optimaliser-motor skriver om artikler som allerede er publisert — oppdaterte fakta, bedre titler, sammenslått innhold — og overskriver deretter det eksisterende innlegget på stedet, med uendret URL. Forespørselen går til samme endepunkt-URL, som en POST med samme 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.

Finn innlegget via target_url (ellers slug), erstatt tittel, brødtekst og metabeskrivelse, behold URL-en som den er, og svar 200 med { "url": "<same post URL>" }. En status utenom 2xx, eller en body uten url, gjør at Orova merker optimaliseringen som mislykket (den oppretter aldri et duplisert innlegg). Endepunkter som ikke har implementert dette ennå, fortsetter å publisere nye artikler som før — oppdatering trengs først når du bruker skjermen Optimaliser.

Siden 29. august kan oppdateringsbodyen også inneholde id (match på den før target_url), featured_image_url for et nytt forsidebilde og status med "publish" eller "draft". Alle tre er valgfrie; når de er tomme, utelates de fra bodyen og sendes aldri som null.

/ Kontrakt — Visuell redigering

Lese artikler (påkrevd)

Orova må kunne lese artiklene som allerede ligger på nettstedet ditt — for å unngå dupliserte emner, legge til interne lenker og optimalisere eldre innlegg. Legg til to handlinger til på samme endepunkt-URL, med samme Bearer-token: list returnerer artiklene dine, get returnerer én 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 er maks 500, og nyeste artikkel kommer først. For get sender Orova id, target_url og slug samtidig — match på det du kjenner igjen, i den rekkefølgen. Når ingenting stemmer, svar 404 med { "ok": false, "err": "not_found" }. excerpt i list er valgfritt (maks 300 tegn). Påkrevd: Orova kaller list for å vite hvilke artikler som finnes (for å unngå duplikater, legge til interne lenker og analysere hele nettstedet) og kaller get for å lese en artikkel når den optimaliseres. Svarer ikke nettstedet ditt på disse to handlingene, får nye artikler ingen interne lenker, og eldre artikler kan ikke optimaliseres.

/ Kontrakt — Visuell redigering

Laste opp bilder (Visuell redigering)

Når du slipper et bilde i Visuell redigering, sender Orova det til samme endepunkt-URL som base64 i JSON-bodyen — ingen multipart, ingen ekstra URL å sikre.

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
  }

Godta bare image/* og avvis alt over 15 MB. Lagre filen og svar med den absolutte URL-en til bildet: en relativ sti ødelegger artikkelen så snart den vises et annet sted. Denne handlingen trengs bare for Visuell redigering.

/ Kontrakt — sletting

Slette artikler

Når en bruker fjerner en publisert artikkel i Orova (fra skjermen Skriv eller Rapporter), ber Orova nettstedet ditt om å slette den også. Forespørselen går til samme endepunkt-URL, som en POST med samme Bearer-token — den eneste forskjellen er bodyen:

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
  }

Slå opp innlegget via url (ellers slug), fjern det eller avpubliser det, og svar med 200 og { "deleted": true } — en ren 204 fungerer også. Finnes ikke innlegget lenger, svar 404, så regner Orova det som allerede slettet. Returnerer du { "deleted": false }, forteller du Orova at innlegget ikke ble fjernet. Vanlige publiseringsforespørsler har aldri et action-felt, så eksisterende endepunkter fungerer uendret — sletting er valgfritt, men anbefalt.

/ Sikkerhet

Sikkerhetsråd

  • Verifiser alltid Bearer-tokenet. Endepunktet ditt er en offentlig URL — hvem som helst kan kalle det. Authorization-headeren er det eneste som beviser at en forespørsel faktisk kom fra Orova, så avvis alle forespørsler der tokenet ikke stemmer nøyaktig med den lagrede hemmeligheten.
  • Kjør endepunktet over HTTPS. Hemmeligheten sendes i en header. HTTPS hindrer at den leses underveis.
  • Hold hemmeligheten utenfor koden. Lagre den i en miljøvariabel eller en hemmelighetsbehandler, aldri hardkodet i en fil du committer. Hvis den noen gang lekker, genererer du en ny under Prosjekt → Tilkoblinger.
  • Behandle content_html som innhold, ikke som pålitelig markering. Lagre og vis den like forsiktig som enhver annen artikkeltekst i CMS-et ditt.

/ Oppsummering

Det er hele kontrakten

Når endepunktet er live og registrert i prosjektet, publiserer Orova til nettstedet ditt automatisk — akkurat som for WordPress-brukere. Hver ferdige artikkel havner på serveren din, blir et innlegg, og den offentlige URL-en går tilbake til Rapporter og Analyse, så du kan følge hvordan den presterer.

← Tilbake til veiledningsbiblioteket

/ Trenger du hjelp?

Står du fast med å koble opp endepunktet? Åpne Støtte i arbeidsområdet ditt, eller send oss en melding.