/ Guia per a desenvolupadors
Publicació via API a qualsevol web.
Com crear el vostre propi endpoint perquè Orova pugui publicar articles acabats en un web que no funciona amb WordPress.
Orova publica articles acabats a WordPress de sèrie. Si el vostre web no és WordPress (un CMS propi, una configuració headless, un web estàtic o el vostre propi backend), igualment podeu deixar que Orova publiqui automàticament acceptant articles a través d'una petita API. Escriviu un endpoint HTTP i Orova el crida cada vegada que acaba un article. Aquesta guia és per a desenvolupadors i descriu el contracte complet de la sol·licitud.
/ Visió general
Com funciona
Quan Orova acaba d'escriure un article per a una paraula clau, envieu una sola sol·licitud HTTP POST a l'URL de l'endpoint que heu registrat. El cos de la sol·licitud és JSON i conté l'article sencer. El vostre endpoint crea l'article al vostre costat i respon amb l'URL públic definitiu de l'article. Orova desa aquest URL com a enllaç publicat, igual que ho faria amb un article de WordPress. Feu-ho servir quan vulgueu publicar sense intervenir en un web que no és WordPress, o quan preferiu mantenir el control total de com es desen els articles.
/ Configuració
Configuració en sis passos
- 1
Activeu “Connect via API” al vostre projecte. Obriu el vostre projecte (Projectes → el vostre projecte → Connexions). Al costat de la targeta de WordPress hi ha una targeta Connect via API. Introduïu-hi l'URL del vostre endpoint. El camp de la clau secreta és opcional: deixa'l en blanc i Orova en generarà una, o enganxa una clau que el vostre web ja faci servir. Un cop desat, la clau es mostra a la targeta amb un botó Copiar. Un projecte només publica a un destí: connectar via API bloqueja el botó de WordPress, i a l'inrevés.
- 2
Creeu un endpoint que accepti POST. Al vostre web o servidor, creeu una ruta que escolti HTTP POST amb un cos JSON. Aquest és l'URL que enganxeu a la pantalla Connexions del projecte. Orova el crida una vegada per cada article que acaba.
- 3
Verifiqueu el token Bearer a cada sol·licitud. Cada sol·licitud d'Orova porta una capçalera Authorization en el format “Bearer <secret>”, amb la clau secreta exacta que es mostra al vostre projecte. Compareu-la amb la còpia que teniu desada i rebutgeu tot el que no coincideixi: això és el que impedeix que qualsevol altra persona publiqui al vostre web.
- 4
Creeu l'article a partir del payload. Llegiu el cos JSON i creeu un article al vostre CMS o base de dades: feu servir title, slug i content_html per a l'article en si, excerpt com a meta description, featured_image_url com a imatge de portada, i keyword / lang / published_at com a metadades.
- 5
Retorna l'URL públic de l'article. Responeu amb HTTP 200 o 201 i un cos JSON { "url": "https://yoursite.com/the-new-article" }. Orova desa aquest URL com a enllaç publicat de l'article. Si retorneu un estat que no és 2xx, o omets el camp url, Orova considera que la publicació ha fallat.
- 6
Gestiona les sol·licituds d'actualització del motor Optimize. Quan el cos porta action = "update", troba l'article existent per target_url (o slug) i sobreescriu-ne el title, el content_html i l'excerpt al mateix lloc, sense canviar l'URL. Respon 200 amb { "url": ... } apuntant al mateix article. Els endpoints que encara no ho gestionen simplement faran que Orova informi que l'optimització ha fallat; la publicació d'articles nous continua funcionant com abans.
/ Contracte
La sol·licitud que envia Orova
Cada article acabat arriba com una sol·licitud amb aquesta forma:
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./ Referència
Camps del payload
Camps d'una sol·licitud de publicació (article nou). Les sol·licituds d'eliminació i d'actualització fan servir el mateix endpoint amb els cossos més petits que es mostren a les seves seccions més avall.
| Camp | Tipus | Descripció |
|---|---|---|
| title | string | El titular de l'article. |
| slug | string | URL-friendly identifier suggested for the post. |
| content_html | string | El cos complet de l'article en HTML a punt per publicar. |
| excerpt | string | Un resum breu, pensat per a la meta description. |
| featured_image_url | string | null | URL de la imatge de portada, o null si no n'hi ha. |
| keyword | string | La paraula clau SEO objectiu per a la qual es va escriure l'article. |
| lang | string | Language as an ISO code, e.g. "en" or "vi". |
| published_at | string | El moment en què Orova va enviar l'article, en ISO 8601 i UTC, sense sufix de zona horària. |
| status | string | undefined | Opcional. "draft" manté l'article sense publicar; "publish", o cap valor, el publica. |
| action | string | undefined | No hi és en els articles nous. "delete" us demana que elimineu un article; "update" (enviat pel motor Optimize) us demana que sobreescrigueu un article existent al mateix lloc. |
| target_url | string | Només amb action = "update": l'URL públic de l'article que cal sobreescriure. No canvieu l'URL. |
| id | number | undefined | Only sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`. |
Els camps opcionals no s'inclouen gens al cos quan no hi ha res a enviar: Orova mai no envia null, així que llegiu-los amb precaució. status també és opcional: "draft" crea l'article com a esborrany, mentre que "publish" o cap status el publica immediatament.
/ Contracte
La resposta que heu de retornar
Un cop creat l'article, responeu amb un estat HTTP 200 o 201 i aquest cos JSON:
HTTP 200 (or 201)
Content-Type: application/json
{
"url": "https://yoursite.com/published-article"
}Orova desa aquest url com a enllaç publicat de l'article. Si el vostre endpoint retorna qualsevol estat que no sigui 2xx, o un cos que no sigui un objecte JSON amb un camp url, Orova considera que la publicació ha fallat i marqueu l'article en conseqüència perquè pugueu tornar-ho a provar. Responeu en menys de 60 seconds: passat aquest temps Orova talla la sol·licitud (s'aplica igual a publicar, actualitzar i eliminar).
/ Exemple
Exemple de codi
Un endpoint receptor mínim en Node.js amb Express. Els mateixos passos (verificar el token, bifurcar segons action (update / delete), crear o canviar l'article, retornar l'URL) s'apliquen en qualsevol llenguatge o 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);/ Contracte — update
Actualitzar articles (Optimize)
El motor Optimize d'Orova reescriu articles que ja estan publicats (dades actualitzades, títols millors, contingut fusionat) i després sobreescriu l'article existent al mateix lloc, sense canviar l'URL. La sol·licitud va al mateix URL de l'endpoint, com a POST amb el mateix token 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.Trobeu l'article per target_url (si no, per slug), substituiu-ne el títol, el cos i la meta description, deixeu l'URL tal com és i responeu 200 amb { "url": "<same post URL>" }. Qualsevol estat que no sigui 2xx, o un cos sense url, fa que Orova marqui aquella optimització com a fallida (mai no crea un article duplicat). Els endpoints que encara no ho han implementat continuen publicant articles nous sense canvis: implementar update només cal quan feu servir la pantalla Optimize.
Des del 29 d'agost, el cos d'actualització també pot portar id (feu-hi la coincidència abans que amb target_url), featured_image_url per a una imatge de portada nova i status amb "publish" o "draft". Tots tres són opcionals; quan són buits s'eliminen del cos i mai no s'envien com a null.
/ Contracte — Visual Editor
Llegir articles (obligatori)
Orova ha de llegir els articles que ja hi ha al vostre web per evitar temes duplicats, afegir enllaços interns i optimitzar articles antics. Afegiu dues accions més al mateix URL de l'endpoint, amb el mateix token Bearer: list retorna els vostres articles i get en retorna un de sencer.
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 té un màxim de 500 i l'article més nou va primer. Per a get, Orova envia id, target_url i slug alhora: feu la coincidència amb el que reconegueu, en aquest ordre. Quan no coincideixi res, responeu 404 amb { "ok": false, "err": "not_found" }. excerpt a list és opcional (màxim 300 caràcters). Obligatori: Orova crida list per saber quins articles ja existeixen (per evitar duplicats, afegir enllaços interns i analitzar tot el web) i crida get per llegir un article quan l'optimitza. Si el vostre web no respon aquestes dues accions, els articles nous no tindran enllaços interns i els antics no es podran optimitzar.
/ Contracte — Visual Editor
Pujar imatges (Visual Editor)
Quan deixeu anar una imatge al Visual Editor, Orova l'envia al mateix URL de l'endpoint en base64 dins del cos JSON: sense multipart i sense un segon URL per protegir.
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
}Accepteu només image/* i rebutgeu qualsevol cosa de més de 15 MB. Deseu el fitxer i responeu amb l'URL absolut de la imatge: un camí relatiu trenca l'article en el moment que es mostra en un altre lloc. Aquesta acció només cal per al Visual Editor.
/ Contracte — delete
Eliminar articles
Quan un usuari elimina un article publicat dins d'Orova (des de la pantalla Escriure o Informes), Orova demana al vostre web que també l'elimini. La sol·licitud va al mateix URL de l'endpoint, com a POST amb el mateix token Bearer; l'única diferència és el cos:
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
}Busqueu l'article per url (si no, per slug), elimineu-lo o despublica'l i responeu amb 200 i { "deleted": true }; un 204 sol també funciona. Si l'article ja no existeix, responeu 404 i Orova el considera ja eliminat. Retornar { "deleted": false } indica a Orova que l'article no s'ha eliminat. Les sol·licituds de publicació normals mai no contenen cap camp action, així que els endpoints existents continuen funcionant sense canvis: implementar delete és opcional però recomanable.
/ Seguretat
Notes de seguretat
- Verifica sempre el token Bearer. El vostre endpoint és un URL públic: qualsevol el podria cridar. La capçalera Authorization és l'única cosa que demostra que una sol·licitud ve realment d'Orova, així que rebutja totes les sol·licituds el token de les quals no coincideixi exactament amb el secret que teniu desat.
- Serveix l'endpoint per HTTPS. El secret viatja en una capçalera. HTTPS evita que es pugui llegir pel camí.
- Mantingueu el secret fora del vostre codi. Desa'l en una variable d'entorn o en un gestor de secrets, mai escrit directament en un fitxer que feu commit. Si mai queda exposat, genereu-ne un de nou des de Projecte → Connexions.
- Tracta content_html com a contingut, no com a marcatge de confiança. Desa'l i mostra'l amb la mateixa cura que qualsevol cos d'article del vostre CMS.
/ Per acabar
Aquest és tot el contracte
Amb l'endpoint en marxa i registrat al vostre projecte, Orova publica al vostre web automàticament, exactament com ho fa per als usuaris de WordPress. Cada article que acaba arriba al vostre servidor, es converteix en una entrada i el seu URL públic torna a Informes i Anàlisi perquè pugueu seguir-ne el rendiment.
/ Necessiteu ajuda?
Us heu encallat connectant el vostre endpoint? Obriu la secció Assistència dins del vostre espai de treball, o envia'ns un missatge.
