OXIRANKER DEVELOPER DOCS

OxiRanker dokumentasiýasy

Web saýtyňyzy OxiRanker hyzmatlaryna birikdirmek üçin arassa dokumentasiýa merkezi. WordPress integrasiýasy ýa-da API integrasiýasy bilen başlaň, soňra gollanmany ädimme-ädim ýerine ýetiriň.

Resmi dokumentasiýaFull-feature V1Saýt eýeleri, developerler we integrasiýa toparlary üçin
Dokumentasiýa menýusy
Menýudan gollanma saýlaň. Bu sahypa OxiRanker-iň esasy dokumentasiýa merkezidir.
API hyzmaty

API hyzmaty

Custom websites, custom CMS platformalary, Next.js, Laravel, Node.js, PHP ýa-da Python backendleri üçin Export API we Webhook ulanyň.

Domain
Tassyklanan
Content
Taýýar
API
Webhook
Saklama barada möhüm duýduryş

OxiRanker destination website üçin permanent hosting däldir

OxiRanker destination website content, images ýa-da exported files üçin permanent hosting kepilligini bermeýär. OxiRanker user-content hosting service däldir we generated content, cover images, Open Graph images ýa-da output files OxiRanker infrastructure-da 90 günden köp galman biler.

Şonuň üçin output alnandan soň destination website final article, images, SEO data, Open Graph data we Schema data-ny öz database, server, storage ýa-da CDN içinde store etmeli we public display üçin OxiRanker URLs-e bagly bolmaly däldir.

Okuw wideosy

Birikme okuw wideosy

WordPress-i birikdirmezden ýa-da API integrasiýasyny implement etmezden öň bu wideony wizual walkthrough hökmünde ulanyň.

API Output

Output flow-y ädimme-ädim implement ediň

Ilki Export Token we Webhook Secret-i backend-iňizde store ediň, soňra Webhook receiver, database, UPSERT logic, Image Mirroring we daily sync-i tamamlaň.

ÄDIM 01

OxiRanker-de hasaba durmak we domain döretmek

Ilki OxiRanker-de hasaba duruň, account-yňyza sign in ediň we Domains bölüminden Add Domain-e basyň.

Dogry domain formaty

Domain name meýdanyna diňe root domain giriziň. Salgyda http, https ýa-da www bolmaly däl.

example.com

Nädogry formatlar

Bu formatlary domain meýdanyna girizmäň.

https://example.com
http://example.com
www.example.com
Domain döredilenden soň
Eger domain formaty valid bolsa, OxiRanker domain döredýär we sizi domain ownership verification ädimine geçirýär.
ÄDIM 02

Domain eýeçiligini tassyklamak

Bu ädimde domain size degişlidigini ýa-da ony dolandyrmak üçin access bardygyny subut etmeli.

DNS TXT

Maslahat berilýär

Domain DNS-e TXT record goşuň. Cloudflare üçin bu adatça iň aňsat method bolýar.

DNS Name:
_yourdomain-verification.example.com

TXT Value:
acb1****29803

HTTP File

OxiRanker barlap biler ýaly web saýtyňyzyň .well-known path içinde text file ýerleşdiriň.

File URL:
https://example.com/.well-known/yourdomain-verification.txt

File Content:
220daaa37****42730f27

Meta Tag

Web saýtyňyzyň homepage head section içinde meta tag ýerleşdiriň.

<meta name="yourdomain-verification" content="190208d****d275b65" />
Möhüm DNS belligi
DNS record goşanyňyzdan soň, esasanam Cloudflare-de, 2-den 3 minuda çenli garaşmaly bolmagyňyz mümkin. Soňra şol OxiRanker page-de Confirm basyň.
Verification token möhleti
Domain verification values üçin time limit bar. Häzirki wagtda verification values adatça 1440 minutes dowamynda valid bolýar.
ÄDIM 03

Birinji domain tassyklanylandan soň mugt tokenleri almak

Birinji domain verify edilenden soň, OxiRanker initial testing üçin account-yňyza 1000 free tokens goşýar.

Diňe birinji domain üçin
Bu sowgat diňe birinji verified domain üçin goşulýar we soňky domains üçin gaýtadan goşulmaýar.
ÄDIM 04

Dilleri we Blog URL-ni sazlamak

Blog URLs bölüminde web saýtyňyzyň single-language ýa-da multilingual bolandygyny we generated articles üçin haýsy languages ulanylmalydygyny kesgitläň.

Dilleri saýlamak

OxiRanker 24 languages goldaýar. Web saýtyňyzyň gurluşyna görä bir ýa-da birnäçe languages saýlap bilersiňiz.

Blog URL girizmek

Her language üçin şol language blog URL-ni giriziň. Eger web saýtyňyz multilingual bolsa, her language path-y aýratyn giriziň.

https://example.com/fa/blog
https://example.com/en/blog
URL gurluşy
Eger web saýtyňyzyň gurluşy başga bolsa, web saýtyňyzyň hakyky blog URL-ni giriziň. Esasy zat final URL articles publish edilmeli path bolmalydyr.
ÄDIM 05

Her language üçin keywords goşmak

Keywords tab içinde her language üçin azyndan 3 we iň köp 50 keywords goşuň.

Esasy düzgün

Her language üçin keywords şol language-da girizilmeli. Eger article language English bolsa, keywords hem English bolmaly.

Keywords nädip goşulýar

Her keyword ýazanyňyzdan soň, ony goşmak üçin Enter basyň. Ähli keywords tamamlanandan soň Save basyň.

Köp duş gelýän ýalňyşlyk
Eger English language üçin Turkish keywords girizseňiz, English content içinde Turkish phrases peýda bolup biler. Keyword language article language bilen gabat gelmeli.
ÄDIM 06

Domain Article Profile doldurmak

Article Profile OxiRanker-e brand-iňiziň nämedigini, kim üçin ýazýandygyny, haýsy tone ulanylmalydygyny we haýsy topics-den gaça durmalydygyny aýdýar.

Industry

Market ýa-da business category kesgitleýär. Mysal: Dubai-de real estate consulting we property buying and selling.

Business description

Business näme satýandygyny, kime hyzmat edýändigini, nähili işleýändigini we näme bilen tapawutlanýandygyny düşündirýär.

Audience

Content kime ýüzlenmelidigini kesgitleýär. Her audience aýratyn save edilip bilner.

Tone

Generated content-iň style we voice bölegini dolandyrýar, meselem formal, educational, simple, technical ýa-da sales-oriented.

Content goal

Articles näme gazanmalydygyny kesgitleýär, meselem customers çekmek, traffic artdyrmak ýa-da users öwretmek.

Gadagan topics

Brand-iň gürrüň etmeli däl ýa-da baglanyşdyrylmaly däl ähli zatlary şu ýere girizilýär.

Optional CTA

CTA reader article okandan soň etmeli action-yňyzy aňladýar. Haýsy text suitable bolýandygyna ynamly bolmasaňyz, bu field-i empty goýmak gowudyr.

Multilingual profile
Eger web saýtyňyz multilingual bolsa, profile-i diňe bir language-da doldurmak ýeterlik. OxiRanker şol profile esasynda beýleki languages üçin zerur versions taýýarlap biler.
ÄDIM 07

Içki we daşarky linkler

OxiRanker articles içinde internal links we external links-i intelligent görnüşde ulanyp biler.

Internal links

Bu option default ýagdaýda enabled we enabled saklamak has gowudyr. Internal linking search engines-e web saýtyňyzyň content structure-yny has gowy düşünmäge kömek edýär.

Birinji article içinde internal links adatça döredilmeýär, sebäbi entek previous article ýok. Ikinji article-den başlap system internal linking edip biler.

External links

External links official we trusted sources-e goşulýar we SEO authority unintended geçmez ýaly nofollow hökmünde marked edilýär.

ÄDIM 08

AI bilen profile quality barlamak

Article Profile doldurylandan soň, OxiRanker profile quality-ni review edýär.

85+
Save etmek üçin required minimum score
95
Improvements-den soň adatça ýetilýän excellent score
AI
Her field üçin professional suggestions
Eger score 85-den pes bolsa
System final saving rugsat bermeýär we her problematic field üçin explanations hem-de improvement suggestions görkezýär. Suggestions apply edip, täzeden save edip bilersiňiz.
ÄDIM 09

Article summaries üçin Telegram birikdirmek

Profile üstünlikli save edilenden soň, article summaries-i Telegram channel-a ibermegi enable edip bilersiňiz.

1

Bot goşmak

@Oxiranker_Bot-y Telegram channel-yňyza goşuň we ony Admin ediň.

2

Channel username girizmek

Channel name-i @ bilen giriziň. Mysal: @my_channel

3

Summary delivery enable etmek

Valid channel bellige alnandan soň article summary delivery enable ediň.

Telegram peýdasy
Eger enabled bolsa, content generation-den soň article summary image we link bilen Telegram channel-a iberilýär. Bu web saýtyňyza traffic paths güýçlendirip biler.
ÄDIM 10

Automatic article engine sazlamak

Automatic article generation tab içinde automatic article generation engine-i dolandyryp bilersiňiz.

Smart topic selection

System suitable, unique we publishable topics saýlamak üçin keywords we Article Profile ulanýar.

Brand-aware generation

Article generation wagtynda tone, audience, content goal we brand restrictions berjaý edilýär.

Scheduled publishing

Monthly capacity aýyň dowamynda paýlanýar, şonuň üçin content publishing has natural we consistent görünýär.

Monthly capacity

7 articles / month
10 articles / month
15 articles / month
30 articles / month

30 articles diýenimizde takmynan her gün bir article. 15 articles diýenimizde takmynan iki günden bir. 10 articles diýenimizde takmynan her iki-üç günden bir. 7 articles diýenimizde takmynan her üç-dört günden bir.

Article length we image

Article length Short, Standard ýa-da Long bolup biler. Standard köp web saýtlar üçin balanced choice.

Article images enabled ýa-da disabled edilip bilner, image theme bolsa Light ýa-da Dark saýlanyp bilner.

API websites üçin bellik
Automatic engine articles-i OxiRanker içinde generate edýär. Generation-den soň her article-i custom website-e geçirmek üçin Export API, Webhook, database storage we Image Mirroring implement etmeli.
ÄDIM 11

Generate-den birinji article döretmek

Domain taýýarlanylandan soň Generate menu açyň, domain saýlaň we New Content ýa-da New Article basyň.

1

Language

Her content request üçin diňe bir language saýlanýar.

2

Topic

Açyk topic ýazyň. Source URL optional.

3

Image

Image enable ýa-da disable ediň we Light ýa-da Dark theme saýlaň.

4

Payment we generation

Cost wallet-den deducted edilýär we article generation başlanýar.

Generation time
Article we image generation adatça takmynan 2-den 3 minuda çenli dowam edýär. Tamamlanandan soň article OxiRanker içinde visible bolýar.
Source URL
Eger Source URL girizilse, OxiRanker topic-i has gowy düşünmek üçin ony ulanýar, ýöne copied content generate etmeýär. System unique content döretmek üçin designed.
ÄDIM 12

Preview, SEO Report we JSON Output review etmek

Article generated bolandan soň, quality review we publishing preparation üçin birnäçe important outputs elýeterli bolýar.

Preview

Article text we generated image görkezýär. Bu section publishing-den öň final quality review etmek üçin peýdaly.

SEO Report

SEO Score, title, meta description, canonical, keywords, schema, headings, links, images we OpenGraph öz içine alýan understandable SEO report görkezýär.

JSON Output

Technical users üçin full article output görkezýär, şol sanda slug, html_body, schema, image URLs, tags, category, reading time we created/updated timestamps.

Outputs manysy
Bu outputs article diňe plain text däldigini görkezýär. Ol WordPress, CMS, API we publishing systems üçin taýýarlanan structured, SEO-ready content-dir.
ÄDIM 13

Export API we Webhook takyk näme edýär?

OxiRanker içinde article generated bolandan soň, destination website ony Export API bilen read edip biler ýa-da article ready bolanda Webhook arkaly notified bolup biler.

Method 1: Export API

Destination website zerur wagty Export Token bilen OxiRanker-den articles read edip biler. Bu method manual sync, daily sync, article list pages we full article JSON fetching üçin ulanylýar.

GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json

Method 2: Webhook

New article created bolanda ýa-da article updated bolanda, OxiRanker destination website endpoint-e notify edýär. Webhook full article ibermeýär. Ol diňe article identifier iberýär, şonuň bilen destination website Export JSON API-den full article fetch edip biler.

https://example.com/api/oxiranker/webhook
{
  "event": "article.created",
  "domain_id": "1",
  "article_id": "5",
  "request_id": "5",
  "lang": "fa",
  "slug": "smart-internal-links",
  "occurred_at": "2026-05-20T19:35:33.000Z"
}
Örän möhüm note
Webhook diňe lightweight message. Destination website Webhook içinde full article expect etmeli däl. domain_id we article_id alandan soň, Export JSON API-den complete article fetch edip, öz database içinde store etmeli.
ÄDIM 14

Content storage responsibility we Image Mirroring

Destination website final article we images-i öz infrastructure-da store etmeli.

Destination website nämeleri store etmeli

Destination website database içindäki full article text
Destination server, storage ýa-da CDN-däki main article image
Destination server, storage ýa-da CDN-däki Open Graph image
SEO, OpenGraph we Article Schema data
Original output preserve etmek üçin raw_detail_payload we raw_list_item

Dogry image path mysaly

OxiRanker image URLs-i directly we permanently ulanmak dogry däl. Image download edilmeli we article URLs destination website internal URLs bilen replace edilmeli.

https://oxiranker.com/uploads/articles/3_5/cover.webp
https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp
OxiRanker destination website üçin permanent hosting däl
OxiRanker destination website üçin output content we images-i permanently storing etmekden jogapkär däl. OxiRanker user-content hosting service däl we generated content, cover images, Open Graph images ýa-da output files OxiRanker infrastructure-da 90 days-den köp galman biler. Şonuň üçin destination website final article, images, SEO data, OpenGraph we Schema-ny öz database, server, storage ýa-da CDN içinde store etmeli.
ÄDIM 15

Export Token we ready endpoints almak

Export Token we ready-to-use endpoints article page, Outputs tab we API Output section arkaly elýeterli.

Export Token nireden alynýar

OxiRanker-de birinji article create ediň.
Şol article page-i open ediň.
Outputs tab open ediň.
API Output section open ediň.
Token management-den token create ediň.
Full token diňe bir gezek shown edilýär.
Token-i destination website backend ýa-da .env içinde store ediň.
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}

Ready-to-use endpoints

Şol article page içinde, API Output section-da, Ready-to-use endpoints prepared URLs görkezýär.

https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0
https://oxiranker.com/api/v1/users/domains/1/articles/1/export/json
https://oxiranker.com/api/v1/users/domains/1/articles/1/export/html?canonical_mode=absolute&include_css=0
Rotating Token
Eger Export Token OxiRanker panel-de rotated edilse, previous token expires bolýar. New value destination website .env ýa-da backend environment içindäki old value-ni immediately replace etmeli we destination backend restarted ýa-da redeployed edilmeli.
ÄDIM 16

Webhook Secret we destination endpoint döretmek

Webhook Secret hem article page, Outputs tab we API Output section arkaly döredilýär.

Webhook Secret nirede döredilýär?

Article page open ediň.
Outputs tab open ediň.
API Output section open ediň.
Webhook Secret section içinde secret create ediň.
Full secret diňe bir gezek shown edilýär.
Secret-i destination website .env içinde store ediň.

Fixed destination endpoint

OxiRanker destination Webhook üçin diňe aşakdaky fixed path kabul edýär. Destination website bu endpoint-i backend içinde create etmeli.

POST /api/oxiranker/webhook

https://example.com/api/oxiranker/webhook

Bu endpoint diňe POST accept etmeli, raw body preserve etmeli, timestamp we signature verify etmeli we webhook.test üçin successful response return etmeli.

Active events
Current version içinde events fixed: article.created we article.updated. Connection testing üçin event value webhook.test iberilýär.
ÄDIM 17

Destination website üçin required .env values

OxiRanker destination website files ýa-da .env-e access etmeýär. Destination website developer bu values-i öz backend içinde store etmeli.

OXIRANKER_EXPORT_API_BASE_URL=https://oxiranker.com
OXIRANKER_EXPORT_API_TOKEN=YOUR_EXPORT_TOKEN
OXIRANKER_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET
OXIRANKER_WEBHOOK_MAX_SKEW_MS=300000
OXIRANKER_SOURCE_PROVIDER=oxiranker
OXIRANKER_SOURCE_DOMAIN_IDS=1

SITE_ARTICLE_IMAGE_UPLOAD_DIR=/var/www/example.com/uploads/site-articles
SITE_ARTICLE_IMAGE_MAX_BYTES=10485760

PUBLIC_BASE_URL=https://example.com

OXIRANKER_SYNC_ENABLED=true
OXIRANKER_SYNC_HOUR=3
OXIRANKER_SYNC_MINUTE=10
OXIRANKER_SYNC_PAGE_LIMIT=20
OXIRANKER_SYNC_MAX_PAGES=50

Security values

OXIRANKER_EXPORT_API_TOKEN
Export API token diňe backend içinde stored bolmaly.
OXIRANKER_WEBHOOK_SECRET
Webhook Secret diňe backend içinde stored bolmaly.
OXIRANKER_WEBHOOK_MAX_SKEW_MS
Webhook üçin maximum allowed time difference. 300000 5 minutes diýmekdir.

Storage values

SITE_ARTICLE_IMAGE_UPLOAD_DIR
Destination server-de mirrored images storing üçin physical path.
SITE_ARTICLE_IMAGE_MAX_BYTES
Download we storage üçin maximum allowed image size.
PUBLIC_BASE_URL
Final image we article URLs create etmek üçin ulanylýan public destination website URL.
Frontend security
Export Token we Webhook Secret frontend code, user-visible JavaScript, localStorage ýa-da window object içinde ýerleşdirilmeli däl.
ÄDIM 18

Export API: List, JSON, HTML we ZIP

Destination website Export API ulanyp article list ýa-da her article-iň full output-y receive edip biler.

Domain üçin list of articles almak

GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json

limit number of articles kesgitleýär we häzirki wagtda 1 bilen 50 arasynda limited. offset pagination üçin ulanylýar. lang optional we fa, en ýa-da tr ýaly specific language articles fetch etmek üçin ulanylýar.

Full article-i JSON hökmünde almak

GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json

Bu destination website üçin iň important output. Destination website full article JSON-y öz database içinde store etmeli we public display üçin öz database-den read etmeli.

Ready HTML almak

GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/html?canonical_mode=absolute&include_css=0
Authorization: Bearer YOUR_EXPORT_TOKEN

HTML ready output isleýän websites üçin suitable, ýöne has professional approach JSON store edip, destination website-iň öz template-i bilen render etmekdir.

ZIP output almak

GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/zip
Authorization: Bearer YOUR_EXPORT_TOKEN

ZIP output article.json, meta.json, assets.json, schema.jsonld, index.html, version.txt we README.txt öz içine alýar.

ÄDIM 19

Webhook Signature we secure Webhook receiving

Destination website original raw body ulanyp Webhook signature verify etmeli.

Webhook headers

content-type: application/json
user-agent: Oxiranker-Webhooks/1.0
x-oxiranker-event: article.created
x-oxiranker-domain-id: 1
x-oxiranker-timestamp: 2026-05-20T19:35:33.000Z
x-oxiranker-signature: sha256=...

Signature formula

payload_to_sign = timestamp + "." + rawBody
signature = "sha256=" + HMAC_SHA256(webhook_secret, payload_to_sign)

Eger destination website ilki JSON parse edip, soňra täzeden stringify etse, order ýa-da spacing üýtgäp biler we signature invalid bolup biler. Signature verification üçin original raw body ulanylmaly.

Timing-safe comparison
Signatures-i expected === received bilen deňeşdirmäň. Node.js-de crypto.timingSafeEqual ulanmak has gowudyr.
ÄDIM 20

Webhook receiving üçin simple Node.js code sample

Bu Express sample raw body, timestamp, signature, webhook.test we article events handle edýär.

import express from "express";
import crypto from "crypto";

const app = express();

app.use(
  express.json({
    verify: (req, _res, buf) => {
      (req as any).rawBody = Buffer.from(buf);
    },
  })
);

function getHeader(req: express.Request, name: string): string {
  const value = req.headers[name.toLowerCase()];
  if (Array.isArray(value)) return String(value[0] || "").trim();
  return String(value || "").trim();
}

function getRawBody(req: express.Request): string {
  const raw = (req as any).rawBody;
  if (Buffer.isBuffer(raw)) return raw.toString("utf8");
  return JSON.stringify(req.body || {});
}

function createSignature(secret: string, timestamp: string, rawBody: string): string {
  const payload = `${timestamp}.${rawBody}`;
  const digest = crypto
    .createHmac("sha256", secret)
    .update(payload, "utf8")
    .digest("hex");

  return `sha256=${digest}`;
}

function timingSafeEqualText(a: string, b: string): boolean {
  const ab = Buffer.from(a, "utf8");
  const bb = Buffer.from(b, "utf8");

  if (ab.length !== bb.length) return false;
  return crypto.timingSafeEqual(ab, bb);
}

function isTimestampValid(timestamp: string): boolean {
  const maxSkewMs = Number(process.env.OXIRANKER_WEBHOOK_MAX_SKEW_MS || 300000);
  const date = new Date(timestamp);

  if (Number.isNaN(date.getTime())) return false;

  const diff = Math.abs(Date.now() - date.getTime());
  return diff <= maxSkewMs;
}

app.post("/api/oxiranker/webhook", async (req, res) => {
  const secret = String(process.env.OXIRANKER_WEBHOOK_SECRET || "").trim();

  if (!secret) {
    return res.status(500).json({
      error: "webhook_secret_missing",
      message: "Webhook secret is not configured.",
    });
  }

  const eventHeader = getHeader(req, "x-oxiranker-event");
  const domainIdHeader = getHeader(req, "x-oxiranker-domain-id");
  const timestamp = getHeader(req, "x-oxiranker-timestamp");
  const receivedSignature = getHeader(req, "x-oxiranker-signature");
  const rawBody = getRawBody(req);

  if (!eventHeader) {
    return res.status(400).json({
      error: "missing_webhook_event",
      message: "Webhook event header is required.",
    });
  }

  if (!domainIdHeader) {
    return res.status(400).json({
      error: "missing_webhook_domain_id",
      message: "Webhook domain id header is required.",
    });
  }

  if (!timestamp) {
    return res.status(400).json({
      error: "missing_webhook_timestamp",
      message: "Webhook timestamp header is required.",
    });
  }

  if (!receivedSignature) {
    return res.status(400).json({
      error: "missing_webhook_signature",
      message: "Webhook signature header is required.",
    });
  }

  if (!isTimestampValid(timestamp)) {
    return res.status(401).json({
      error: "invalid_webhook_timestamp",
      message: "Webhook timestamp is expired or invalid.",
    });
  }

  const expectedSignature = createSignature(secret, timestamp, rawBody);

  if (!timingSafeEqualText(expectedSignature, receivedSignature)) {
    return res.status(401).json({
      error: "invalid_webhook_signature",
      message: "Invalid webhook signature.",
    });
  }

  const payload = req.body || {};
  const event = String(payload.event || eventHeader || "").trim();

  if (event === "webhook.test") {
    const sourceDomainId = Number(payload.domain_id || domainIdHeader);

    return res.status(200).json({
      ok: true,
      message: "Webhook test received successfully.",
      event,
      source_domain_id:
        Number.isFinite(sourceDomainId) && sourceDomainId > 0 ? sourceDomainId : null,
      test: true,
    });
  }

  if (event !== "article.created" && event !== "article.updated") {
    return res.status(400).json({
      error: "unsupported_webhook_event",
      message: "Webhook event is not supported.",
    });
  }

  const sourceDomainId = Number(payload.domain_id || domainIdHeader);
  const sourceArticleId = Number(payload.article_id);

  if (!Number.isFinite(sourceDomainId) || sourceDomainId <= 0) {
    return res.status(400).json({
      error: "invalid_domain_id",
      message: "Webhook domain id is invalid.",
    });
  }

  if (!Number.isFinite(sourceArticleId) || sourceArticleId <= 0) {
    return res.status(400).json({
      error: "invalid_article_id",
      message: "Webhook article id is invalid.",
    });
  }

  // Fetch the full article from Export API and store it in the database here.
  // await syncArticleFromOxiranker(sourceDomainId, sourceArticleId);

  return res.status(200).json({
    ok: true,
    message: "Webhook processed successfully.",
    event,
    source_domain_id: sourceDomainId,
    source_article_id: sourceArticleId,
  });
});
ÄDIM 21

Recommended destination website database

Destination website articles-i öz database içinde store etmeli we unique constraint bilen duplicates prevent etmeli.

CREATE TABLE public.site_articles (
  id BIGSERIAL PRIMARY KEY,

  source_provider VARCHAR NOT NULL DEFAULT 'oxiranker',
  source_domain_id BIGINT NOT NULL,
  source_article_id BIGINT NOT NULL,
  source_request_id BIGINT NULL,

  lang VARCHAR NOT NULL,
  lang_label VARCHAR NULL,
  dir VARCHAR NOT NULL,
  is_rtl BOOLEAN NULL,

  length VARCHAR NULL,
  topic_text TEXT NULL,

  slug VARCHAR NOT NULL,
  canonical_path VARCHAR NULL,
  effective_canonical_url TEXT NULL,

  title TEXT NOT NULL,
  meta_description TEXT NULL,
  html_body TEXT NULL,

  focus_keywords JSONB NOT NULL DEFAULT '[]'::jsonb,
  category VARCHAR NULL,
  tags JSONB NOT NULL DEFAULT '[]'::jsonb,

  reading_time_minutes INTEGER NULL,

  og_title TEXT NULL,
  og_description TEXT NULL,

  cover_image_url TEXT NULL,
  og_image_url TEXT NULL,

  article_schema JSONB NOT NULL DEFAULT '{}'::jsonb,

  raw_list_item JSONB NOT NULL DEFAULT '{}'::jsonb,
  raw_detail_payload JSONB NOT NULL DEFAULT '{}'::jsonb,

  source_created_at TIMESTAMPTZ NULL,
  source_updated_at TIMESTAMPTZ NULL,

  sync_status VARCHAR NOT NULL DEFAULT 'synced',
  last_synced_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  last_sync_error TEXT NULL,

  publish_status VARCHAR NOT NULL DEFAULT 'draft',
  published_at TIMESTAMPTZ NULL,
  deleted_at TIMESTAMPTZ NULL,

  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),

  source_cover_image_url TEXT NULL,
  source_og_image_url TEXT NULL,

  image_mirror_status VARCHAR NOT NULL DEFAULT 'pending',
  image_mirror_error TEXT NULL,
  image_mirrored_at TIMESTAMPTZ NULL,

  CONSTRAINT site_articles_dir_check
    CHECK (dir IN ('ltr', 'rtl')),

  CONSTRAINT site_articles_reading_time_check
    CHECK (reading_time_minutes IS NULL OR reading_time_minutes >= 0),

  CONSTRAINT site_articles_sync_status_check
    CHECK (sync_status IN ('pending', 'syncing', 'synced', 'failed')),

  CONSTRAINT site_articles_publish_status_check
    CHECK (publish_status IN ('draft', 'published', 'archived')),

  CONSTRAINT site_articles_image_mirror_status_check
    CHECK (image_mirror_status IN ('pending', 'not_needed', 'mirrored', 'failed')),

  CONSTRAINT site_articles_source_unique
    UNIQUE (source_provider, source_domain_id, source_article_id)
);
CREATE INDEX site_articles_source_lookup_idx
ON public.site_articles (source_provider, source_domain_id, source_article_id);

CREATE INDEX site_articles_source_updated_at_idx
ON public.site_articles (source_updated_at DESC);

CREATE INDEX site_articles_slug_idx
ON public.site_articles (slug)
WHERE deleted_at IS NULL;

CREATE UNIQUE INDEX site_articles_lang_slug_active_unique
ON public.site_articles (lang, slug)
WHERE deleted_at IS NULL;

CREATE INDEX site_articles_lang_publish_idx
ON public.site_articles (lang, publish_status)
WHERE deleted_at IS NULL;

CREATE INDEX site_articles_published_at_idx
ON public.site_articles (published_at DESC)
WHERE deleted_at IS NULL;

CREATE INDEX site_articles_focus_keywords_gin_idx
ON public.site_articles USING gin (focus_keywords);

CREATE INDEX site_articles_tags_gin_idx
ON public.site_articles USING gin (tags);

CREATE INDEX site_articles_article_schema_gin_idx
ON public.site_articles USING gin (article_schema);
PostgreSQL note
PostgreSQL-de Persian ýa-da Unicode text üçin simple quotes ulanylýar. N'...' ýaly zat gerek däl we ulanylmaly däl.
ÄDIM 22

UPSERT bilen duplicates prevent etmek

Article her Webhook üçin täzeden inserted edilmeli däl. Eger ol eýýäm exists bolsa, updated edilmeli.

Duplicates preventing üçin main key
Duplicates preventing üçin iň important constraint: UNIQUE (source_provider, source_domain_id, source_article_id)
INSERT INTO public.site_articles (
  source_provider,
  source_domain_id,
  source_article_id,
  source_request_id,
  lang,
  dir,
  slug,
  title,
  meta_description,
  html_body,
  focus_keywords,
  category,
  tags,
  reading_time_minutes,
  cover_image_url,
  og_image_url,
  article_schema,
  source_created_at,
  source_updated_at,
  sync_status,
  last_synced_at,
  publish_status,
  published_at,
  created_at,
  updated_at
)
VALUES (
  'oxiranker',
  1,
  5,
  5,
  'fa',
  'rtl',
  'smart-internal-links',
  'Article title',
  'Meta description',
  '<p>HTML body</p>',
  '[]'::jsonb,
  'SEO',
  '[]'::jsonb,
  7,
  'https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp',
  'https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp',
  '{}'::jsonb,
  '2026-05-20T19:33:23.618Z',
  '2026-05-20T19:34:09.629Z',
  'synced',
  now(),
  'published',
  now(),
  now(),
  now()
)
ON CONFLICT (source_provider, source_domain_id, source_article_id)
DO UPDATE SET
  source_request_id = EXCLUDED.source_request_id,
  lang = EXCLUDED.lang,
  dir = EXCLUDED.dir,
  slug = EXCLUDED.slug,
  title = EXCLUDED.title,
  meta_description = EXCLUDED.meta_description,
  html_body = EXCLUDED.html_body,
  focus_keywords = EXCLUDED.focus_keywords,
  category = EXCLUDED.category,
  tags = EXCLUDED.tags,
  reading_time_minutes = EXCLUDED.reading_time_minutes,
  cover_image_url = EXCLUDED.cover_image_url,
  og_image_url = EXCLUDED.og_image_url,
  article_schema = EXCLUDED.article_schema,
  source_created_at = EXCLUDED.source_created_at,
  source_updated_at = EXCLUDED.source_updated_at,
  sync_status = 'synced',
  last_synced_at = now(),
  last_sync_error = NULL,
  publish_status = CASE
    WHEN public.site_articles.publish_status = 'archived'
    THEN public.site_articles.publish_status
    ELSE 'published'
  END,
  published_at = COALESCE(public.site_articles.published_at, EXCLUDED.published_at, now()),
  deleted_at = NULL,
  updated_at = now();
ÄDIM 23

Webhook-dan soň full article fetch we sync etmek

Webhook gelende, destination website Export API-den full article fetch etmeli, images mirror etmeli we article UPSERT etmeli.

async function fetchOxirankerArticleDetail(args: {
  baseUrl: string;
  token: string;
  domainId: number;
  articleId: number;
}) {
  const url = `${args.baseUrl.replace(/\/+$/, "")}/api/v1/users/domains/${args.domainId}/articles/${args.articleId}/export/json`;

  const response = await fetch(url, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${args.token}`,
      Accept: "application/json",
      "User-Agent": "My-Site-Oxiranker-Sync/1.0",
    },
  });

  const bodyText = await response.text();

  if (response.status < 200 || response.status > 299) {
    throw new Error(
      `Export API request failed with HTTP status ${response.status}. Response: ${bodyText.slice(0, 1000)}`
    );
  }

  try {
    return JSON.parse(bodyText);
  } catch {
    throw new Error("Export API returned invalid JSON.");
  }
}
async function syncArticleFromWebhook(payload: any) {
  const sourceDomainId = Number(payload.domain_id);
  const sourceArticleId = Number(payload.article_id);

  if (!Number.isFinite(sourceDomainId) || sourceDomainId <= 0) {
    throw new Error("Webhook domain id is invalid.");
  }

  if (!Number.isFinite(sourceArticleId) || sourceArticleId <= 0) {
    throw new Error("Webhook article id is invalid.");
  }

  const article = await fetchOxirankerArticleDetail({
    baseUrl: process.env.OXIRANKER_EXPORT_API_BASE_URL!,
    token: process.env.OXIRANKER_EXPORT_API_TOKEN!,
    domainId: sourceDomainId,
    articleId: sourceArticleId,
  });

  const mirroredImages = await mirrorArticleImages(article);

  const finalHtmlBody = replaceImageUrls(article.html_body, mirroredImages);
  const finalArticleSchema = replaceImageUrlsInJson(article.article_schema, mirroredImages);

  await upsertSiteArticle({
    source_provider: process.env.OXIRANKER_SOURCE_PROVIDER || "oxiranker",
    source_domain_id: sourceDomainId,
    source_article_id: sourceArticleId,
    source_request_id: article.request_id,
    lang: article.lang,
    dir: article.dir,
    slug: article.slug,
    title: article.title,
    meta_description: article.meta_description,
    html_body: finalHtmlBody,
    focus_keywords: article.focus_keywords,
    category: article.category,
    tags: article.tags,
    reading_time_minutes: article.reading_time_minutes,
    cover_image_url: mirroredImages.cover_image_url,
    og_image_url: mirroredImages.og_image_url,
    article_schema: finalArticleSchema,
    raw_detail_payload: article,
    source_created_at: article.created_at,
    source_updated_at: article.updated_at,
  });
}
ÄDIM 24

Image Mirroring we image storage rules

Destination website images download etmeli, öz infrastructure-da store etmeli we article URLs replace etmeli.

Image Mirroring rules

Diňe HTTPS URLs accepted edilmeli.
File hakykatdan image bolmaly.
content-type image/ bilen başlamaly.
Empty files stored edilmeli däl.
File size limit-den geçmeli däl.
Eger cover we og birmeňzeş bolsa, diňe bir gezek download ediň.
Mirroring-den soň html_body we article_schema içindäki URLs replace ediň.

Recommended storage path

/uploads/site-articles/oxiranker/1/5/cover.webp
/uploads/site-articles/oxiranker/1/5/og.webp
https://example.com/uploads/site-articles/oxiranker/1/5/cover.webp
ÄDIM 25

Reliability üçin daily sync

Webhook alone ýeterlik däl. Destination website missed articles recover etmek üçin daily sync hem bolmaly.

Daily sync näme üçin gerek?

Destination website temporarily down bolup biler.
Webhook timeout bolup biler.
Deployment ýa-da network issues bolup biler.
Article generated bolup biler, ýöne onuň Webhook-y missed bolup biler.

Daily sync algorithm

offset-i 0-dan start ediň.
limit 20 ýa-da 50 ulanyň.
Export List API-den article list fetch ediň.
Her item üçin şol article-iň Export JSON-y fetch ediň.
Images mirror ediň.
Article-i UPSERT bilen store ediň.
offset increase ediň we total completed bolýança continue ediň.
ÄDIM 26

Destination website-de articles görkezmek

Articles site_articles içinde storing edilenden soň, destination website her public display üçin OxiRanker-e connect etmeli däl. Ol öz database-den read etmeli.

Blog list page

SELECT
  id,
  lang,
  lang_label,
  dir,
  is_rtl,
  slug,
  title,
  meta_description,
  category,
  tags,
  reading_time_minutes,
  cover_image_url,
  og_image_url,
  source_created_at,
  published_at,
  created_at
FROM public.site_articles
WHERE source_provider = 'oxiranker'
  AND deleted_at IS NULL
  AND sync_status = 'synced'
  AND publish_status = 'published'
ORDER BY COALESCE(source_created_at, published_at, created_at) DESC, id DESC
LIMIT 20 OFFSET 0;

Article detail page

SELECT *
FROM public.site_articles
WHERE source_provider = 'oxiranker'
  AND lang = 'fa'
  AND slug = 'smart-internal-links'
  AND deleted_at IS NULL
  AND sync_status = 'synced'
  AND publish_status = 'published'
LIMIT 1;
Recommended article URL
Multilingual websites üçin /{lang}/blog/{slug} pattern ulanyp bilersiňiz. Mysal: /fa/blog/smart-internal-links
ÄDIM 27

SEO, Schema, RTL we HTML rendering

Destination website SEO data-ny öz database-den read edip, article page-de render etmeli.

SEO fields

page title üçin title
meta description üçin meta_description
canonical üçin effective_canonical_url
Open Graph üçin og_title we og_description
social preview image üçin og_image_url ýa-da cover_image_url
JSON-LD üçin article_schema

HTML rendering

<div
  className="article-content"
  dangerouslySetInnerHTML={{ __html: article.html_body }}
/>

Eger destination website strict security policy bar bolsa, HTML-i saving-den öň ýa-da displaying-den öň sanitize edip biler.

RTL we LTR

Her article-de lang, dir we is_rtl fields bar. Eger dir rtl bolsa, article page right-to-left rendered edilmeli.

<article dir="rtl">
  ...
</article>
ÄDIM 28

Common errors we olaryň manysy

Destination website errors English, clear we understandable bolmaly, şeýdip troubleshooting tiz ýerine ýetirilip bilner.

Export API errors

Domain export token ýok.
Export API token iberilmedi. Authorization: Bearer YOUR_EXPORT_TOKEN iberilmeli.
Domain export token invalid.
Token wrong, rotated, bu domain bilen bagly däl ýa-da domainId wrong.
Token required scope ýok.
Token required scope ýok. New tokens articles:read we exports:read bolmaly.
lang invalid.
Query parameter içindäki lang value incorrectly iberildi.

Webhook errors

Webhook URL HTTPS ulanmaly.
Webhook URL https:// bilen başlamaly.
Webhook signature invalid.
Webhook Secret wrong, secret rotated, raw body changed ýa-da signature incorrectly calculated.
Webhook timestamp expired ýa-da invalid.
Timestamp old, future içinde ýa-da invalid. Server clock we NTP barlaň.
Webhook test delivery şowsuz boldy.
Destination website successful status 200-den 299-a çenli return etmedi, timed out boldy ýa-da webhook.test correctly handle etmedi.
ÄDIM 29

Final API we Webhook testing checklist

Implementation-den soň bu tests connection-i beginning-den end-e çenli verify edýär.

Export Token we JSON test

GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=2&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/json

Webhook test

Eger destination Webhook-y Postman bilen signature bolmazdan call etseňiz, security error receive etmeli. Bu endpoint active we checks security diýmekdir.

{
  "error": "missing_webhook_signature",
  "message": "Webhook signature header is required."
}

Soňra OxiRanker içinde Test Webhook basyň. Eger receiver webhook.test supports edýän bolsa, response_status 200 bolmaly.

Rotation-den soň
Eger Webhook Secret ýa-da Export Token rotated edilse, new value derrew copy ediň, destination website .env içinde replace ediň we destination backend restart ýa-da redeploy ediň.
ÄDIM 30

Correct implementation üçin important rules

System secure, stable we maintainable bolmagy üçin API integration içinde bu rules ýerine ýetirilmeli.

Export Token diňe backend içinde stored edilmeli.
Webhook Secret diňe backend içinde stored edilmeli.
Destination Webhook endpoint takyk /api/oxiranker/webhook bolmaly.
Webhook URL HTTPS ulanmaly.
Webhook signature we timestamp hemişe verified edilmeli.
Articles simple insert däl-de, UPSERT bilen stored edilmeli.
Duplicates prevent etmek üçin unique constraint required.
Images OxiRanker-den downloaded edilip, destination website-de stored edilmeli.
Destination website OxiRanker image URLs-e permanently depend etmeli däl.
Image URLs html_body we article_schema içinde replaced edilmeli.
Eger Webhook fails bolsa, daily sync missed article recover etmeli.
Users ýa-da logs üçin shown edilýän errors English we understandable bolmaly.
Eger article archived bolsa, automatic sync ony täzeden publish etmeli däl.
Original output data ýitmez ýaly raw_detail_payload we raw_list_item preserved edilmeli.
Eger Export Token ýa-da Webhook Secret rotated edilse, new value .env içinde replaced edilmeli we backend restarted ýa-da redeployed edilmeli.
ÄDIM 31

Complete API flow summary

Bu steps tamamlanandan soň custom website OxiRanker-de generated articles-i securely receive, store, mirror we publish edip biler.

OxiRanker-de hasaba durmak
Domain goşmak
Domain ownership verify etmek
Languages saýlamak we Blog URL goşmak
Her language üçin keywords goşmak
Article Profile doldurmak
Zerur bolsa Telegram sazlamak
Automatic article engine sazlamak
Generate-den birinji article create etmek
Preview we SEO Report review etmek
API Output-dan Export Token almak
Ready Export API endpoints almak
Webhook Secret almak
Destination backend içinde fixed /api/oxiranker/webhook endpoint create etmek
Token we Secret-i destination website .env içinde store etmek
site_articles table we unique constraint create etmek
Export JSON-dan full article fetching implement etmek
Image Mirroring implement etmek
UPSERT implement etmek
Daily sync implement etmek
Destination database-den article, SEO, OG we Schema render etmek
Final Export API we Webhook tests run etmek