OROVA.VN — BIZ AI AGENT

/ מדריך למפתחים

פרסום דרך API בכל אתר.

איך לבנות נקודת קצה משלך כדי ש-Orova תוכל לפרסם מאמרים מוכנים לאתר שלא פועל על WordPress.

Orova מפרסמת מאמרים מוכנים ל-WordPress מהקופסה. אם האתר שלך אינו WordPress — CMS מותאם, מבנה headless, אתר סטטי או backend משלך — עדיין אפשר לתת ל-Orova לפרסם אוטומטית על ידי קבלת מאמרים דרך API קטן. אתה כותב נקודת קצה HTTP אחת; Orova קוראת לה בכל פעם שהיא מסיימת מאמר. המדריך הזה מיועד למפתח ומתאר את חוזה הבקשה המלא.

/ סקירה כללית

איך זה עובד

כש-Orova מסיימת לכתוב מאמר עבור מילת מפתח, היא שולחת בקשת HTTP POST אחת לכתובת נקודת הקצה שרשמת. גוף הבקשה הוא JSON ומכיל את המאמר כולו. נקודת הקצה שלך יוצרת את הפוסט אצלך ומשיבה עם כתובת ה-URL הסופית והציבורית של המאמר. Orova שומרת את הכתובת הזו כקישור המפורסם, בדיוק כמו בפוסט WordPress. השתמש בזה כשאתה רוצה פרסום ללא מגע ידני באתר שאינו WordPress, או כשאתה מעדיף לשמור שליטה מלאה על אופן אחסון הפוסטים.

/ הגדרה

ההגדרה — שישה צעדים

  • 1

    הפעל “התחברות דרך API” בפרויקט שלך. פתח את הפרויקט (פרויקטים ← הפרויקט שלך ← חיבורים). ליד כרטיס ה-WordPress יש כרטיס התחברות דרך API. הזן שם את כתובת נקודת הקצה שלך. שדה המפתח הסודי אופציונלי: השאר אותו ריק ו-Orova תיצור מפתח, או הדבק מפתח שהאתר שלך כבר משתמש בו. לאחר השמירה, המפתח מוצג בכרטיס עם כפתור העתקה. פרויקט מפרסם ליעד אחד בלבד — חיבור דרך API נועל את כפתור ה-WordPress, וגם להפך.

  • 2

    בנה נקודת קצה שמקבלת POST. באתר או בשרת שלך, צור נתיב שמאזין ל-HTTP POST עם גוף JSON. זו הכתובת שאתה מדביק בחזרה במסך החיבורים של הפרויקט. Orova קוראת לה פעם אחת עבור כל מאמר שהיא מסיימת.

  • 3

    אמת את ה-Bearer token בכל בקשה. כל בקשה מ-Orova נושאת כותרת Authorization בצורה “Bearer <secret>”, עם המפתח הסודי המדויק שמוצג בפרויקט שלך. השווה אותו לעותק השמור אצלך ודחה כל מה שלא תואם — זה מה שמונע מאחרים לפרסם באתר שלך.

  • 4

    צור את המאמר מתוך ה-payload. קרא את גוף ה-JSON וצור פוסט ב-CMS או במסד הנתונים שלך: השתמש ב-title, slug ו-content_html למאמר עצמו, ב-excerpt כתיאור המטא, ב-featured_image_url כתמונת השער, וב-keyword / lang / published_at כמטא-דאטה.

  • 5

    החזר את הכתובת החיה של המאמר. השב עם HTTP 200 או 201 וגוף JSON של { "url": "https://yoursite.com/the-new-article" }. Orova שומרת את הכתובת הזו כקישור המפורסם של המאמר. אם תחזיר סטטוס שאינו 2xx, או תשמיט את השדה url, Orova תתייחס לפרסום כנכשל.

  • 6

    טפל בבקשות עדכון ממנוע האופטימיזציה. כשהגוף נושא action = "update", מצא את הפוסט הקיים לפי target_url (או slug) ודרוס את ה-title, content_html וה-excerpt שלו במקום — השאר את הכתובת ללא שינוי. השב 200 עם { "url": ... } שמצביע על אותו פוסט. נקודות קצה שעדיין לא מטפלות בזה פשוט יגרמו ל-Orova לדווח על האופטימיזציה כנכשלת; פרסום מאמרים חדשים ממשיך לעבוד כרגיל.

/ חוזה

הבקשה ש-Orova שולחת

כל מאמר מוכן מגיע כבקשה אחת במבנה הזה:

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

שדות של בקשת פרסום (מאמר חדש). בקשות מחיקה ועדכון משתמשות באותה נקודת קצה עם גופים קטנים יותר, המוצגים בסעיפים שלהן למטה.

שדהסוגתיאור
titlestringכותרת המאמר.
slugstringURL-friendly identifier suggested for the post.
content_htmlstringגוף המאמר המלא כ-HTML מוכן לפרסום.
excerptstringסיכום קצר, מיועד לתיאור המטא.
featured_image_urlstring | nullכתובת תמונת השער, או null כשאין.
keywordstringמילת המפתח של ה-SEO שעבורה נכתב המאמר.
langstringLanguage as an ISO code, e.g. "en" or "vi".
published_atstringהרגע שבו Orova שלחה את המאמר, ISO 8601 ב-UTC, ללא סיומת אזור זמן.
statusstring | undefinedאופציונלי. "draft" שומר את המאמר כלא מפורסם; "publish", או ללא ערך כלל, מפרסם אותו.
actionstring | undefinedלא קיים במאמרים חדשים. "delete" מבקש ממך להסיר פוסט; "update" (נשלח ממנוע האופטימיזציה) מבקש ממך לדרוס פוסט קיים במקום.
target_urlstringרק עם action = "update": הכתובת החיה של הפוסט לדריסה. השאר את הכתובת ללא שינוי.
idnumber | undefinedOnly sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`.

שדות אופציונליים מושמטים לגמרי מהגוף כשאין מה לשלוח — Orova לעולם לא שולחת null, אז קרא אותם בזהירות. גם status אופציונלי: "draft" יוצר את המאמר כטיוטה, ואילו "publish" או היעדר status מפרסמים אותו מיד.

/ חוזה

התגובה שעליך להחזיר

לאחר שיצרת את הפוסט, השב עם סטטוס HTTP 200 או 201 וגוף ה-JSON הזה:

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

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

Orova שומרת את ה-url הזה כקישור המפורסם של המאמר. אם נקודת הקצה שלך מחזירה סטטוס כלשהו שאינו 2xx, או גוף שאינו אובייקט JSON עם שדה url, Orova מתייחסת לפרסום כנכשל ומסמנת את המאמר בהתאם כדי שתוכל לנסות שוב. השב תוך 60 שניות — Orova מפסיקה להמתין לבקשה אחרי זה (חל על פרסום, עדכון ומחיקה כאחד).

/ דוגמה

דוגמת קוד

נקודת קצה מינימלית לקבלה ב-Node.js עם Express. אותם צעדים — אימות הטוקן, פיצול לפי action (update / delete), יצירה או שינוי של הפוסט, החזרת הכתובת — חלים בכל שפה או 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);

/ חוזה — עדכון

עדכון מאמרים (אופטימיזציה)

מנוע האופטימיזציה של Orova כותב מחדש מאמרים שכבר באוויר — עובדות מעודכנות, כותרות טובות יותר, תוכן ממוזג — ואז דורס את הפוסט הקיים במקום, תוך שמירה על הכתובת ללא שינוי. הבקשה נשלחת לאותה כתובת נקודת קצה, כ-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), החלף את הכותרת, הגוף ותיאור המטא שלו, השאר את הכתובת כפי שהיא, ואז השב 200 עם { "url": "<same post URL>" }. כל סטטוס שאינו 2xx, או גוף ללא url, גורם ל-Orova לסמן את האופטימיזציה כנכשלת (היא לעולם לא יוצרת פוסט כפול). נקודות קצה שעדיין לא מימשו זאת ממשיכות לפרסם מאמרים חדשים ללא שינוי — מימוש העדכון נדרש רק כשמשתמשים במסך האופטימיזציה.

מאז 29 באוגוסט גוף העדכון יכול לשאת גם id (התאם לפיו לפני target_url), featured_image_url לתמונת שער חדשה, ו-status עם "publish" או "draft". שלושתם אופציונליים; כשהם ריקים הם מושמטים מהגוף, ולעולם לא נשלחים כ-null.

/ חוזה — Visual Editor

קריאת מאמרים (חובה)

Orova צריכה לקרוא את המאמרים שכבר קיימים באתר שלך — כדי להימנע מנושאים כפולים, להוסיף קישורים פנימיים ולבצע אופטימיזציה לפוסטים ישנים. הוסף עוד שתי פעולות באותה כתובת נקודת קצה, עם אותו 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 כדי לקרוא מאמר בעת האופטימיזציה שלו. אם האתר שלך לא עונה לשתי הפעולות האלה, מאמרים חדשים לא יקבלו קישורים פנימיים ולא ניתן יהיה לבצע אופטימיזציה למאמרים ישנים.

/ חוזה — Visual Editor

העלאת תמונות (Visual Editor)

כשאתה גורר תמונה לתוך ה-Visual Editor, Orova שולחת אותה לאותה כתובת נקודת קצה כ-base64 בתוך גוף ה-JSON — בלי multipart, בלי כתובת שנייה לאבטח.

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 המוחלטת של התמונה: נתיב יחסי שובר את המאמר ברגע שהוא מוצג במקום אחר. פעולה זו נדרשת רק עבור ה-Visual Editor.

/ חוזה — מחיקה

מחיקת מאמרים

כשמשתמש מסיר מאמר מפורסם בתוך Orova (ממסך הכתיבה או הדוחות), Orova מבקשת מהאתר שלך למחוק אותו גם כן. הבקשה נשלחת לאותה כתובת נקודת קצה, כ-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, כך שנקודות קצה קיימות ממשיכות לעבוד ללא שינוי — מימוש המחיקה אופציונלי אך מומלץ.

/ אבטחה

הערות אבטחה

  • תמיד אמת את ה-Bearer token. נקודת הקצה שלך היא כתובת ציבורית — כל אחד יכול לקרוא לה. כותרת ה-Authorization היא הדבר היחיד שמוכיח שבקשה באמת הגיעה מ-Orova, לכן דחה כל בקשה שהטוקן שלה לא תואם בדיוק למפתח הסודי השמור אצלך.
  • הגש את נקודת הקצה ב-HTTPS. המפתח הסודי עובר בכותרת. HTTPS מונע את קריאתו בזמן המעבר.
  • שמור את המפתח הסודי מחוץ לקוד. אחסן אותו במשתנה סביבה או במנהל סודות, לעולם לא מקודד בקובץ שאתה מעלה למאגר. אם הוא נחשף אי פעם, צור מפתח חדש מפרויקט ← חיבורים.
  • התייחס ל-content_html כתוכן, לא כ-markup מהימן. אחסן והצג אותו באותה זהירות שבה היית מטפלים בכל גוף מאמר ב-CMS שלך.

/ לסיכום

זה כל החוזה

כשנקודת הקצה באוויר ורשומה בפרויקט שלך, Orova מפרסמת לאתר שלך אוטומטית — בדיוק כמו עבור משתמשי WordPress. כל מאמר שהיא מסיימת מגיע לשרת שלך, הופך לפוסט, והכתובת הציבורית שלו חוזרת אל הדוחות והניתוח כדי שתוכל לעקוב אחר הביצועים שלו.

→ חזרה לספריית המדריכים

/ צריך עזרה?

נתקעת בחיבור נקודת הקצה? פתח את אזור התמיכה בתוך סביבת העבודה שלך, או שלח לנו הודעה.