/ راهنمای توسعهدهندگان
انتشار از طریق API روی هر وبسایتی.
چگونه endpoint خودتان را بسازید تا Orova مقالهها را روی سایتی که WordPress ندارد منتشر کند.
Orova مقالههای آماده را از ابتدا در WordPress منتشر میکند. اگر وبسایت شما WordPress نیست — یک CMS اختصاصی، ساختار headless، سایت ایستا یا بکاند خودتان — باز هم میتوانید با دریافت مقالهها از طریق یک API کوچک، انتشار خودکار را به Orova بسپارید. شما یک endpoint HTTP مینویسید؛ هر بار که نوشتن یک مقاله تمام شود، Orova آن را فراخوانی میکند. این راهنما برای توسعهدهندگان است و قرارداد کامل request را شرح میدهد.
/ نمای کلی
نحوهٔ کار
وقتی Orova نوشتن مقالهای برای یک کلیدواژه را تمام میکند، دقیقاً یک request HTTP POST به endpoint URL ثبتشدهٔ شما میفرستد. بدنهٔ request از نوع JSON است و کل مقاله را در بر دارد. endpoint شما نوشته را در سمت شما میسازد و URL عمومی نهایی مقاله را برمیگرداند. Orova آن URL را بهعنوان لینک منتشرشده ذخیره میکند، درست مانند نوشتهٔ WordPress. از این روش زمانی استفاده کنید که انتشار خودکار روی سایتی غیر از WordPress میخواهید یا ترجیح میدهید کنترل کامل نحوهٔ ذخیرهٔ نوشتهها در دست خودتان باشد.
/ راهاندازی
راهاندازی — شش گام
- 1
«اتصال از طریق API» را در پروژه روشن کنید. پروژهٔ خود را باز کنید (پروژهها ← پروژهٔ شما ← اتصالها). کنار کارت WordPress، کارت اتصال از طریق API قرار دارد. URL endpoint خود را آنجا وارد کنید. فیلد کلید محرمانه اختیاری است: اگر خالی بگذارید Orova یک کلید میسازد، یا کلیدی را که سایتتان از قبل استفاده میکند جایگذاری کنید. پس از ذخیره، کلید همراه دکمهٔ کپی روی کارت نمایش داده میشود. هر پروژه فقط در یک مقصد منتشر میکند — اتصال از طریق API دکمهٔ WordPress را قفل میکند و برعکس.
- 2
ساخت endpointای که POST میپذیرد. روی سایت یا سرور خودتان، مسیری بسازید که به HTTP POST با بدنهٔ JSON گوش دهد. این همان URL است که در صفحهٔ اتصالهای پروژه جایگذاری میکنید. Orova برای هر مقالهٔ تمامشده دقیقاً یکبار آن را فراخوانی میکند.
- 3
Bearer token را در هر request بررسی کنید. هر request از Orova یک هدر Authorization به شکل «Bearer <secret>» دارد که دقیقاً همان کلید محرمانهٔ نمایشدادهشده در پروژهٔ شماست. آن را با نسخهٔ ذخیرهشدهٔ خود مقایسه کنید و هر چیزی را که مطابقت ندارد رد کنید — تنها همین مانع میشود که دیگران روی سایت شما مطلب منتشر کنند.
- 4
ساخت مقاله از payload. بدنهٔ JSON را بخوانید و در CMS یا پایگاه دادهٔ خود نوشتهای بسازید: از title، slug و content_html برای خود مقاله، از excerpt بهعنوان meta description، از featured_image_url بهعنوان تصویر شاخص و از keyword / lang / published_at بهعنوان فراداده استفاده کنید.
- 5
URL زندهٔ مقاله را برگردانید. با HTTP 200 یا 201 و بدنهٔ JSON { "url": "https://yoursite.com/the-new-article" } پاسخ دهید. Orova آن URL را بهعنوان لینک منتشرشدهٔ مقاله ذخیره میکند. اگر کدی خارج از گروه 2xx برگردانید یا فیلد url را نفرستید، Orova آن انتشار را ناموفق در نظر میگیرد.
- 6
رسیدگی به درخواستهای بهروزرسانی از موتور بهینهسازی. وقتی بدنه action = "update" دارد، نوشتهٔ موجود را با target_url (یا slug) پیدا کنید و title، content_html و excerpt آن را در همان جا بازنویسی کنید — URL را تغییر ندهید. با 200 و { "url": ... } که به همان نوشته اشاره میکند پاسخ دهید. endpointهایی که هنوز این بخش را پیاده نکردهاند فقط باعث میشوند Orova بهینهسازی را ناموفق گزارش کند؛ انتشار مقالهٔ جدید مثل قبل کار میکند.
/ قرارداد
requestای که Orova میفرستد
هر مقالهٔ تمامشده بهصورت یک request با این شکل میرسد:
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./ مرجع
فیلدهای payload
فیلدهای یک request انتشار (مقالهٔ جدید). درخواستهای حذف و بهروزرسانی از همین endpoint با بدنههای کوچکتری استفاده میکنند که در بخشهای مربوط به خودشان در ادامه آمده است.
| فیلد | نوع | توضیحات |
|---|---|---|
| title | string | عنوان مقاله. |
| slug | string | URL-friendly identifier suggested for the post. |
| content_html | string | کل متن مقاله بهصورت HTML آمادهٔ انتشار. |
| excerpt | string | خلاصهای کوتاه، برای استفاده در meta description. |
| featured_image_url | string | null | URL تصویر شاخص، یا null اگر تصویری وجود ندارد. |
| keyword | string | کلیدواژهٔ SEO هدف که مقاله برای آن نوشته شده است. |
| lang | string | Language as an ISO code, e.g. "en" or "vi". |
| published_at | string | زمانی که Orova مقاله را فرستاد، با استاندارد ISO 8601 به وقت UTC و بدون پسوند منطقهٔ زمانی. |
| status | string | undefined | اختیاری. "draft" مقاله را منتشرنشده نگه میدارد؛ "publish" یا نبودِ مقدار، آن را منتشر میکند. |
| action | string | undefined | در مقالههای جدید وجود ندارد. "delete" از شما میخواهد نوشتهای را حذف کنید؛ "update" (ارسالی از موتور بهینهسازی) از شما میخواهد نوشتهٔ موجود را در همان جا بازنویسی کنید. |
| target_url | string | فقط همراه action = "update": URL زندهٔ نوشتهای که باید بازنویسی شود. این URL را تغییر ندهید. |
| id | number | undefined | Only sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`. |
فیلدهای اختیاری وقتی چیزی برای ارسال نباشد کاملاً از بدنه حذف میشوند — Orova هرگز null نمیفرستد، پس آنها را با احتیاط بخوانید. status هم اختیاری است: "draft" مقاله را بهصورت پیشنویس میسازد، و "publish" یا نبودِ status آن را فوراً منتشر میکند.
/ قرارداد
responseای که باید برگردانید
پس از ساختن نوشته، با کد HTTP 200 یا 201 و این بدنهٔ JSON پاسخ دهید:
HTTP 200 (or 201)
Content-Type: application/json
{
"url": "https://yoursite.com/published-article"
}Orova آن url را بهعنوان لینک منتشرشدهٔ مقاله ذخیره میکند. اگر endpoint شما هر کدی خارج از گروه 2xx برگرداند، یا بدنهای که یک object JSON با فیلد url نباشد، Orova انتشار را ناموفق در نظر میگیرد و مقاله را علامت میزند تا بتوانید دوباره اجرا کنید. ظرف 60 ثانیه پاسخ دهید — پس از آن Orova درخواست را قطع میکند (برای انتشار، بهروزرسانی و حذف یکسان است).
/ نمونه
نمونهکد
یک endpoint دریافت حداقلی با Node.js و Express. همین گامها — بررسی token، انشعاب بر اساس action (update / delete)، ساخت یا تغییر نوشته، برگرداندن URL — در هر زبان یا فریمورکی صدق میکند.
// 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);/ قرارداد — بهروزرسانی
بهروزرسانی مقالهها (بهینهسازی)
موتور بهینهسازی Orova مقالههایی را که از قبل منتشر شدهاند بازنویسی میکند — بهروزرسانی اطلاعات، عنوانهای بهتر، ادغام محتوا — و سپس نوشتهٔ موجود را در همان جا و با حفظ URL بازنویسی میکند. درخواست به همان endpoint URL میرود، بهصورت POST با همان 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.نوشته را با target_url پیدا کنید (در غیر این صورت با slug)، عنوان، متن و meta description آن را جایگزین کنید، URL را همانطور نگه دارید و سپس با 200 و { "url": "<same post URL>" } پاسخ دهید. هر کد خارج از گروه 2xx، یا بدنهای بدون url، باعث میشود Orova آن بهینهسازی را ناموفق علامت بزند (Orova هرگز نوشتهٔ تکراری نمیسازد). endpointهایی که هنوز این بخش را پیاده نکردهاند همچنان مقالههای جدید را بیتغییر منتشر میکنند — پیادهسازی update فقط وقتی لازم است که از صفحهٔ بهینهسازی استفاده کنید.
از 29 اوت، بدنهٔ بهروزرسانی میتواند id (پیش از target_url بر اساس آن تطبیق دهید)، featured_image_url برای تصویر شاخص جدید، و status با مقدار "publish" یا "draft" را هم داشته باشد. هر سه اختیاریاند؛ اگر خالی باشند از بدنه حذف میشوند و هرگز بهصورت null ارسال نمیشوند.
/ قرارداد — ویرایشگر بصری
خواندن مقالهها (الزامی)
Orova باید بتواند مقالههای موجود در سایت شما را بخواند — تا از موضوع تکراری پرهیز کند، لینک داخلی بگذارد و نوشتههای قدیمی را بهینه کند. دو action دیگر روی همان endpoint URL و با همان Bearer token اضافه کنید: list فهرست مقالهها را برمیگرداند و get یکی از آنها را بهطور کامل.
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 حداکثر 500 است و جدیدترین مقاله اول میآید. برای get، Orova id، target_url و slug را با هم میفرستد — به همین ترتیب با هر کدام که میشناسید تطبیق دهید. اگر چیزی پیدا نشد، با 404 و { "ok": false, "err": "not_found" } پاسخ دهید. excerpt در list اختیاری است (حداکثر 300 نویسه). الزامی: Orova برای دانستن مقالههای موجود list را فراخوانی میکند (برای پرهیز از تکرار، افزودن لینک داخلی و تحلیل کل سایت) و برای خواندن مقاله هنگام بهینهسازی get را. اگر سایت شما به این دو action پاسخ ندهد، مقالههای جدید لینک داخلی نمیگیرند و مقالههای قدیمی بهینه نمیشوند.
/ قرارداد — ویرایشگر بصری
بارگذاری تصویر (ویرایشگر بصری)
وقتی تصویری را در ویرایشگر بصری رها میکنید، Orova آن را به همان endpoint URL بهصورت base64 درون بدنهٔ JSON میفرستد — بدون multipart و بدون URL دومی که نیاز به ایمنسازی داشته باشد.
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
}فقط image/* را بپذیرید و هر چیزی بالاتر از 15 MB را رد کنید. فایل را ذخیره کنید و سپس URL مطلق تصویر را برگردانید: مسیر نسبی به محض نمایش مقاله در جای دیگر آن را خراب میکند. این action فقط برای ویرایشگر بصری لازم است.
/ قرارداد — حذف
حذف مقالهها
وقتی کاربری یک مقالهٔ منتشرشده را در خود Orova حذف میکند (از صفحهٔ نوشتن یا گزارشها)، Orova از سایت شما میخواهد آن را هم حذف کند. درخواست به همان endpoint URL میرود، بهصورت POST با همان Bearer token — تنها تفاوت در بدنه است:
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
}نوشته را با url پیدا کنید (در غیر این صورت با slug)، آن را حذف یا از انتشار خارج کنید و سپس با 200 و { "deleted": true } پاسخ دهید — یک 204 خالی هم کار میکند. اگر نوشته دیگر وجود ندارد، 404 برگردانید تا Orova آن را از قبل حذفشده در نظر بگیرد. برگرداندن { "deleted": false } به Orova میگوید نوشته حذف نشده است. درخواستهای انتشار عادی هرگز فیلد action ندارند، پس endpointهای موجود بیتغییر کار میکنند — پیادهسازی حذف اختیاری است اما توصیه میشود.
/ امنیت
نکات امنیتی
- همیشه Bearer token را بررسی کنید. endpoint شما یک URL عمومی است — هر کسی میتواند آن را فراخوانی کند. هدر Authorization تنها چیزی است که ثابت میکند درخواست واقعاً از Orova آمده است، پس هر درخواستی را که token آن دقیقاً با کلید محرمانهٔ ذخیرهشدهٔ شما مطابقت ندارد رد کنید.
- endpoint را روی HTTPS ارائه کنید. کلید محرمانه در یک هدر منتقل میشود. HTTPS مانع خوانده شدن آن در مسیر انتقال میشود.
- کلید محرمانه را بیرون از کد نگه دارید. آن را در یک متغیر محیطی یا مدیر اسرار نگه دارید و هرگز در فایلی که commit میکنید ننویسید. اگر روزی فاش شد، از پروژه ← اتصالها یک کلید جدید بسازید.
- content_html را محتوا بدانید، نه markup قابل اعتماد. آن را با همان دقتی ذخیره و نمایش دهید که هر متن مقالهای را در CMS خود.
/ جمعبندی
قرارداد همین است
وقتی endpoint فعال و در پروژهتان ثبت شده باشد، Orova بهطور خودکار در سایت شما منتشر میکند — دقیقاً همانطور که برای کاربران WordPress. هر مقالهٔ تمامشده به سرور شما میرسد، به یک نوشته تبدیل میشود و URL عمومی آن به گزارشها و تحلیل بازمیگردد تا عملکردش را پیگیری کنید.
/ کمک لازم دارید؟
در اتصال endpoint گیر کردهاید؟ بخش پشتیبانی را در فضای کاری خود باز کنید، یا برای ما پیام بفرستید.
