OROVA.VN — BIZ AI AGENT

/ Geliştirici kılavuzu

API ile yayınlama her web sitesinde.

Orova'nın bitmiş makaleleri WordPress çalıştırmayan bir siteye yayınlayabilmesi için kendi endpoint'inizi nasıl oluşturacağınız.

Orova bitmiş makaleleri doğrudan WordPress'e yayınlar. Web siteniz WordPress değilse — özel bir CMS, headless kurulum, statik site veya kendi backend'iniz — makaleleri küçük bir API üzerinden kabul ederek Orova'nın otomatik yayınlamasına yine izin verebilirsiniz. Tek bir HTTP endpoint yazarsınız; Orova her makaleyi bitirdiğinde onu çağırır. Bu kılavuz bir geliştirici içindir ve tam istek sözleşmesini açıklar.

/ Genel bakış

Nasıl çalışır

Orova bir anahtar kelime için makale yazmayı bitirdiğinde, kaydettiğiniz endpoint URL'sine tek bir HTTP POST isteği gönderir. İstek gövdesi JSON'dur ve makalenin tamamını içerir. Endpoint'iniz yazıyı kendi tarafınızda oluşturur ve makalenin nihai, herkese açık URL'siyle yanıt verir. Orova bu URL'yi, bir WordPress yazısında olduğu gibi yayınlanmış link olarak kaydeder. WordPress olmayan bir sitede elle uğraşmadan yayınlama istediğinizde veya yazıların nasıl saklanacağını tamamen kontrol etmeyi tercih ettiğinizde bunu kullanın.

/ Kurulum

Kurulum — altı adım

  • 1

    Projenizde “API ile bağlan”ı açın. Projenizi açın (Projeler → projeniz → Bağlantılar). WordPress kartının yanında bir API ile bağlan kartı var. Endpoint'inizin URL'sini oraya girin. Gizli anahtar alanı isteğe bağlıdır: boş bırakırsanız Orova bir tane oluşturur veya sitenizin zaten kullandığı bir anahtarı yapıştırabilirsiniz. Kaydedildikten sonra anahtar kartta bir Kopyala düğmesiyle gösterilir. Bir proje yalnızca tek bir hedefe yayınlar — API ile bağlanmak WordPress düğmesini kilitler, tersi de geçerlidir.

  • 2

    POST kabul eden bir endpoint oluşturun. Kendi sitenizde veya sunucunuzda, JSON gövdeli HTTP POST dinleyen bir route oluşturun. Bu, proje Bağlantılar ekranına geri yapıştırdığınız URL'dir. Orova bitirdiği her makale için onu bir kez çağırır.

  • 3

    Her istekte Bearer token'ı doğrulayın. Orova'dan gelen her istek, projenizde gösterilen gizli anahtarın aynısını kullanarak “Bearer <secret>” biçiminde bir Authorization başlığı taşır. Bunu sakladığınız kopyayla karşılaştırın ve eşleşmeyen her şeyi reddedin — başkalarının sitenize yazı göndermesini engelleyen budur.

  • 4

    Payload'dan makaleyi oluşturun. JSON gövdesini okuyun ve CMS'inizde veya veritabanınızda bir yazı oluşturun: makalenin kendisi için title, slug ve content_html, meta açıklama olarak excerpt, kapak görseli olarak featured_image_url ve meta veri olarak keyword / lang / published_at kullanın.

  • 5

    Makalenin canlı URL'sini döndürün. HTTP 200 veya 201 ve { "url": "https://yoursite.com/the-new-article" } JSON gövdesiyle yanıt verin. Orova bu URL'yi makalenin yayınlanmış linki olarak saklar. 2xx olmayan bir durum döndürürseniz veya url alanını atlarsanız, Orova yayınlamayı başarısız sayar.

  • 6

    Optimize motorundan gelen güncelleme isteklerini işleyin. Gövde action = "update" taşıdığında, mevcut yazıyı target_url (veya slug) ile bulun ve title, content_html ve excerpt alanlarını yerinde üzerine yazın — URL'yi değiştirmeyin. Aynı yazıyı gösteren { "url": ... } ile 200 yanıtı verin. Bunu henüz işlemeyen endpoint'ler yalnızca Orova'nın optimizasyonu başarısız olarak raporlamasına neden olur; yeni makale yayınlama eskisi gibi çalışmaya devam eder.

/ Sözleşme

Orova'nın gönderdiği istek

Her bitmiş makale şu biçimde tek bir istek olarak gelir:

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.

/ Referans

Payload alanları

Bir yayınlama (yeni makale) isteğinin alanları. Silme ve güncelleme istekleri aynı endpoint'i, aşağıda kendi bölümlerinde gösterilen daha küçük gövdelerle kullanır.

AlanTürAçıklama
titlestringMakale başlığı.
slugstringURL-friendly identifier suggested for the post.
content_htmlstringYayınlamaya hazır HTML olarak makalenin tam gövdesi.
excerptstringMeta açıklama için kısa bir özet.
featured_image_urlstring | nullKapak görselinin URL'si veya yoksa null.
keywordstringMakalenin yazıldığı hedef SEO anahtar kelimesi.
langstringLanguage as an ISO code, e.g. "en" or "vi".
published_atstringOrova'nın makaleyi gönderdiği an; UTC'de ISO 8601, saat dilimi eki olmadan.
statusstring | undefinedİsteğe bağlı. "draft" makaleyi yayınlanmamış tutar; "publish" veya hiç değer olmaması onu yayınlar.
actionstring | undefinedYeni makalelerde yoktur. "delete" bir yazıyı kaldırmanızı ister; "update" (Optimize motoru tarafından gönderilir) mevcut bir yazının yerinde üzerine yazmanızı ister.
target_urlstringYalnızca action = "update" ile: üzerine yazılacak yazının canlı URL'si. URL'yi değiştirmeyin.
idnumber | undefinedOnly sent when Orova knows your post id (from `list` or `get`): match on it before `target_url`.

İsteğe bağlı alanlar, gönderilecek bir şey olmadığında gövdeden tamamen çıkarılır — Orova asla null göndermez, bu yüzden bunları dikkatli okuyun. status da isteğe bağlıdır: "draft" makaleyi taslak olarak oluşturur, "publish" veya hiç status olmaması ise hemen yayınlar.

/ Sözleşme

Döndürmeniz gereken yanıt

Yazıyı oluşturduktan sonra HTTP 200 veya 201 durumu ve şu JSON gövdesiyle yanıt verin:

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

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

Orova bu url'yi makalenin yayınlanmış linki olarak saklar. Endpoint'iniz 2xx olmayan herhangi bir durum veya url alanı olan bir JSON nesnesi olmayan bir gövde döndürürse, Orova yayınlamayı başarısız sayar ve makaleyi buna göre işaretler, böylece yeniden deneyebilirsiniz. 60 saniye içinde yanıt verin — Orova bundan sonra isteği zaman aşımına uğratır (bu yayınlama, güncelleme ve silme için aynı şekilde geçerlidir).

/ Örnek

Kod örneği

Node.js ve Express ile minimal bir alıcı endpoint. Aynı adımlar — token'ı doğrulamak, action'a göre dallanmak (update / delete), yazıyı oluşturmak veya değiştirmek, URL'yi döndürmek — her dilde veya framework'te geçerlidir.

// 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);

/ Sözleşme — update

Makaleleri güncelleme (Optimize)

Orova'nın Optimize motoru zaten yayında olan makaleleri yeniden yazar — güncellenmiş bilgiler, daha iyi başlıklar, birleştirilmiş içerik — ve ardından mevcut yazının yerinde, URL'yi değiştirmeden üzerine yazar. İstek, aynı Bearer token ile bir POST olarak aynı endpoint URL'sine gider:

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.

Yazıyı target_url ile bulun (yoksa slug), başlığını, gövdesini ve meta açıklamasını değiştirin, URL'yi olduğu gibi bırakın, ardından { "url": "<same post URL>" } ile 200 yanıtı verin. 2xx olmayan herhangi bir durum veya url içermeyen bir gövde, Orova'nın o optimizasyonu başarısız olarak işaretlemesine neden olur (asla kopya yazı oluşturmaz). Bunu henüz uygulamamış endpoint'ler yeni makaleleri değişmeden yayınlamaya devam eder — update'i uygulamak yalnızca Optimize ekranını kullandığınızda gerekir.

29 Ağustos'tan itibaren update gövdesi ayrıca id (target_url'den önce bununla eşleştirin), yeni kapak görseli için featured_image_url ve "publish" veya "draft" değerini taşıyan status içerebilir. Üçü de isteğe bağlıdır; boş olduklarında gövdeden çıkarılır, asla null olarak gönderilmez.

/ Sözleşme — Visual Editor

Makaleleri okuma (zorunlu)

Orova'nın sitenizdeki mevcut makaleleri okuması gerekir — tekrar eden konulardan kaçınmak, iç link eklemek ve eski yazıları optimize etmek için. Aynı Bearer token ile aynı endpoint URL'sine iki action daha ekleyin: list makalelerinizi döndürür, get bunlardan birini tam olarak döndürür.

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 en fazla 500'dür ve en yeni makale önce gelir. get için Orova id, target_url ve slug değerlerini birlikte gönderir — tanıdığınız hangisiyse, bu sırayla eşleştirin. Hiçbiri eşleşmediğinde { "ok": false, "err": "not_found" } ile 404 yanıtı verin. list içindeki excerpt isteğe bağlıdır (en fazla 300 karakter). Zorunlu: Orova hangi makalelerin zaten var olduğunu öğrenmek için list'i (kopyalardan kaçınmak, iç link eklemek ve tüm siteyi analiz etmek için), bir makaleyi optimize ederken okumak için de get'i çağırır. Siteniz bu iki action'a yanıt vermezse yeni makaleler iç link almaz ve eski makaleler optimize edilemez.

/ Sözleşme — Visual Editor

Görsel yükleme (Visual Editor)

Visual Editor'a bir görsel bıraktığınızda Orova onu JSON gövdesi içinde base64 olarak aynı endpoint URL'sine gönderir — multipart yok, güvence altına alınacak ikinci bir URL yok.

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
  }

Yalnızca image/* kabul edin ve 15 MB üzerindeki her şeyi reddedin. Dosyayı saklayın, ardından görselin mutlak URL'siyle yanıt verin: göreli bir yol, makale başka bir yerde görüntülendiği anda onu bozar. Bu action yalnızca Visual Editor için gereklidir.

/ Sözleşme — delete

Makaleleri silme

Bir kullanıcı Orova içinde yayınlanmış bir makaleyi kaldırdığında (Yaz veya Raporlar ekranından), Orova sitenizden de onu silmesini ister. İstek, aynı Bearer token ile bir POST olarak aynı endpoint URL'sine gider — tek fark gövdedir:

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
  }

Yazıyı url ile bulun (yoksa slug), kaldırın veya yayından çekin, ardından 200 ve { "deleted": true } ile yanıt verin — yalın bir 204 de çalışır. Yazı artık yoksa 404 yanıtı verin, Orova onu zaten silinmiş sayar. { "deleted": false } döndürmek Orova'ya yazının kaldırılmadığını söyler. Normal yayınlama istekleri asla action alanı içermez, bu yüzden mevcut endpoint'ler değişmeden çalışmaya devam eder — delete'i uygulamak isteğe bağlıdır ama önerilir.

/ Güvenlik

Güvenlik notları

  • Bearer token'ı her zaman doğrulayın. Endpoint'iniz herkese açık bir URL'dir — herkes onu çağırabilir. Authorization başlığı, bir isteğin gerçekten Orova'dan geldiğini kanıtlayan tek şeydir; bu yüzden token'ı sakladığınız gizli anahtarla tam eşleşmeyen her isteği reddedin.
  • Endpoint'i HTTPS üzerinden sunun. Gizli anahtar bir başlıkta taşınır. HTTPS, aktarım sırasında okunmasını engeller.
  • Gizli anahtarı kodunuzun dışında tutun. Bir ortam değişkeninde veya gizli anahtar yöneticisinde saklayın, commit ettiğiniz bir dosyaya asla doğrudan yazmayın. Açığa çıkarsa Proje → Bağlantılar bölümünden yenisini oluşturun.
  • content_html'i güvenilir işaretleme değil, içerik olarak ele alın. CMS'inizdeki herhangi bir makale gövdesine gösterdiğiniz özenle saklayın ve görüntüleyin.

/ Toparlarsak

Sözleşmenin tamamı bu

Endpoint yayında ve projenize kayıtlı olduğunda Orova sitenize otomatik olarak yayınlar — tıpkı WordPress kullanıcıları için yaptığı gibi. Bitirdiği her makale sunucunuza ulaşır, bir yazıya dönüşür ve herkese açık URL'si Raporlar ve Analiz'e geri akar; böylece performansını takip edebilirsiniz.

← Kılavuz kütüphanesine dön

/ Yardıma mı ihtiyacınız var?

Endpoint'inizi bağlarken takıldınız mı? Çalışma alanınızdaki Destek bölümünü açın veya bize mesaj gönderin.