Ҳуҷҷатҳои OxiRanker
Маркази тозаву равшани ҳуҷҷатҳо барои пайваст кардани вебсайти шумо ба сервисҳои OxiRanker. Аз интегратсияи WordPress ё интегратсияи API оғоз кунед ва баъд роҳнаморо қадам ба қадам пайравӣ кунед.
Сервиси API
Export API ва Webhook-ро барои сайтҳои фармоишӣ, custom CMS platforms, Next.js, Laravel, Node.js, PHP ё backend-ҳои Python истифода баред.
OxiRanker барои destination website permanent hosting нест
OxiRanker барои content, images ё exported files-и destination website кафолати permanent hosting намедиҳад. OxiRanker user-content hosting service нест ва generated content, cover images, Open Graph images ё output files метавонанд дар infrastructure-и OxiRanker зиёда аз 90 рӯз боқӣ намонанд.
Бинобар ин, баъд аз гирифтани output, destination website бояд final article, images, SEO data, Open Graph data ва Schema data-ро дар database, server, storage ё CDN-и худ store кунад ва барои public display ба OxiRanker URLs вобаста набошад.
Видеои омӯзишии пайвастшавӣ
Пеш аз пайваст кардани WordPress ё implement кардани интегратсияи API, ин видеоро ҳамчун роҳнамои визуалӣ истифода баред.
Output flow-ро қадам ба қадам implement кунед
Аввал Export Token ва Webhook Secret-ро дар backend-и худ store кунед, баъд Webhook receiver, database, UPSERT logic, Image Mirroring ва daily sync-ро пурра кунед.
Сабти ном ва сохтани domain дар OxiRanker
Аввал дар OxiRanker сабти ном кунед, ба account-и худ sign in кунед ва аз бахши Domains ба Add Domain клик кунед.
Формати дурусти domain
Дар майдони Domain name танҳо root domain-ро ворид кунед. Суроға набояд http, https ё www дошта бошад.
example.comФорматҳои нодуруст
Ин форматҳоро дар майдони domain ворид накунед.
https://example.com
http://example.com
www.example.comТасдиқи моликияти domain
Дар ин қадам бояд исбот кунед, ки domain ба шумо тааллуқ дорад ё шумо барои идоракунии он access доред.
DNS TXT
ТавсияшудаБа DNS-и domain як TXT record илова кунед. Барои Cloudflare, ин одатан содатарин method аст.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
Як text file-ро дар path-и .well-known-и вебсайти худ ҷойгир кунед, то OxiRanker онро verify карда тавонад.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
Як meta tag-ро дар head section-и homepage-и вебсайти худ ҷойгир кунед.
<meta name="yourdomain-verification" content="190208d****d275b65" />Гирифтани token-ҳои ройгон баъд аз тасдиқи domain-и аввал
Баъд аз verify кардани domain-и аввал, OxiRanker барои initial testing ба account-и шумо 1000 free tokens илова мекунад.
Танзими забонҳо ва Blog URL
Дар бахши Blog URLs муайян кунед, ки вебсайти шумо single-language аст ё multilingual ва кадом languages бояд барои generated articles истифода шаванд.
Интихоби забонҳо
OxiRanker 24 languages-ро дастгирӣ мекунад. Шумо метавонед вобаста ба сохтори вебсайти худ як ё якчанд languages интихоб кунед.
Ворид кардани Blog URL
Барои ҳар language, blog URL-и ҳамон language-ро ворид кунед. Агар вебсайти шумо multilingual бошад, ҳар language path-ро ҷудогона ворид кунед.
https://example.com/fa/blog
https://example.com/en/blogИлова кардани keywords барои ҳар забон
Дар tab-и Keywords, барои ҳар language ҳадди ақал 3 ва то 50 keywords илова кунед.
Қоидаи асосӣ
Keywords-и ҳар language бояд бо ҳамон language ворид шаванд. Агар article language English бошад, keywords ҳам бояд English бошанд.
Чӣ тавр keywords илова карда мешаванд
Баъд аз навиштани ҳар keyword, барои илова кардани он Enter-ро пахш кунед. Баъд аз пур кардани ҳамаи keywords, ба Save клик кунед.
Пур кардани Domain Article Profile
Article Profile ба OxiRanker мегӯяд, ки brand-и шумо чист, барои кӣ менависад, кадом tone-ро бояд истифода барад ва аз кадом topics бояд дурӣ кунад.
Industry
Market ё business category-ро муайян мекунад. Мисол: real estate consulting ва property buying and selling дар Dubai.
Business description
Шарҳ медиҳад, ки business чӣ мефурӯшад, ба кӣ хизмат мекунад, чӣ гуна кор мекунад ва чӣ чиз онро фарқ мекунад.
Audience
Муайян мекунад, ки content бояд бо кӣ сухан гӯяд. Ҳар audience метавонад ҷудогона save шавад.
Tone
Style ва voice-и generated content-ро идора мекунад, монанди formal, educational, simple, technical ё sales-oriented.
Content goal
Муайян мекунад, ки articles бояд ба чӣ ноил шаванд, монанди ҷалби customers, зиёд кардани traffic ё омӯзонидани users.
Мавзӯъҳои манъшуда
Ҳар чизе, ки brand набояд дар бораи он сухан гӯяд ё набояд бо он алоқаманд шавад, дар ин ҷо ворид мешавад.
Optional CTA
CTA маънои action-еро дорад, ки мехоҳед reader баъд аз хондани article анҷом диҳад. Агар мутмаин набошед, ки кадом text suitable аст, беҳтар аст ин field-ро empty гузоред.
Линкҳои дохилӣ ва берунӣ
OxiRanker метавонад internal links ва external links-ро ба таври intelligent дар articles татбиқ кунад.
Internal links
Ин option ба таври default enabled аст ва беҳтар аст enabled боқӣ монад. Internal linking ба search engines кӯмак мекунад, ки content structure-и вебсайти шуморо беҳтар фаҳманд.
Дар article-и аввал, internal links одатан сохта намешаванд, зеро ҳанӯз previous article вуҷуд надорад. Аз article-и дуюм ба баъд, system метавонад internal linking иҷро кунад.
External links
External links ба official ва trusted sources илова мешаванд ва ҳамчун nofollow marked мешаванд, то SEO authority нохоста интиқол наёбад.
Санҷидани profile quality бо AI
Баъд аз пур кардани Article Profile, OxiRanker profile quality-ро review мекунад.
Пайваст кардани Telegram барои article summaries
Баъд аз profile бомуваффақият save шуд, шумо метавонед фиристодани article summaries ба Telegram channel-ро enable кунед.
Илова кардани bot
@Oxiranker_Bot-ро ба Telegram channel-и худ илова кунед ва онро Admin кунед.
Ворид кардани channel username
Channel name-ро бо @ ворид кунед. Мисол: @my_channel
Enable summary delivery
Баъд аз сабти channel-и valid, article summary delivery-ро enable кунед.
Танзими automatic article engine
Дар tab-и Automatic article generation, шумо метавонед automatic article generation engine-ро идора кунед.
Smart topic selection
System аз keywords ва Article Profile истифода мебарад, то topics-и suitable, unique ва publishable интихоб кунад.
Brand-aware generation
Tone, audience, content goal ва brand restrictions ҳангоми article generation риоя мешаванд.
Scheduled publishing
Monthly capacity дар тӯли моҳ тақсим мешавад, то content publishing табиӣтар ва consistent ба назар расад.
Monthly capacity
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 articles маънои тақрибан як article дар рӯзро дорад. 15 articles маънои тақрибан як рӯз дар миёнро дорад. 10 articles маънои тақрибан ҳар ду-се рӯзро дорад. 7 articles маънои тақрибан ҳар се-чор рӯзро дорад.
Article length ва image
Article length метавонад Short, Standard ё Long бошад. Standard барои аксари вебсайтҳо balanced choice аст.
Article images метавонанд enabled ё disabled шаванд ва image theme метавонад Light ё Dark бошад.
Сохтани article-и аввал аз Generate
Баъд аз омода кардани domain, Generate menu-ро кушоед, domain-ро интихоб кунед ва ба New Content ё New Article клик кунед.
Language
Барои ҳар content request танҳо як language интихоб мешавад.
Topic
Як topic-и равшан нависед. Source URL optional аст.
Image
Image-ро enable ё disable кунед ва theme-и Light ё Dark интихоб кунед.
Payment ва generation
Cost аз wallet deducted мешавад ва article generation оғоз мешавад.
Review кардани Preview, SEO Report ва JSON Output
Баъд аз generated шудани article, якчанд output-и муҳим барои quality review ва publishing preparation дастрас мешавад.
Preview
Article text ва generated image-ро нишон медиҳад. Ин section барои review кардани final quality пеш аз publishing муфид аст.
SEO Report
Як SEO report-и understandable нишон медиҳад, ки SEO Score, title, meta description, canonical, keywords, schema, headings, links, images ва OpenGraph-ро дар бар мегирад.
JSON Output
Full article output-ро барои technical users нишон медиҳад, аз ҷумла slug, html_body, schema, image URLs, tags, category, reading time ва created/updated timestamps.
Export API ва Webhook дақиқ чӣ кор мекунанд?
Баъд аз generated шудани article дар OxiRanker, destination website метавонад онро бо Export API read кунад ё ҳангоми ready шудани article тавассути Webhook notified шавад.
Method 1: Export API
Destination website метавонад articles-ро аз OxiRanker бо Export Token ҳар вақте лозим бошад read кунад. Ин method барои manual sync, daily sync, article list pages ва fetching full article JSON истифода мешавад.
GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonGET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonMethod 2: Webhook
Вақте new article created мешавад ё article updated мешавад, OxiRanker destination website endpoint-ро notify мекунад. Webhook full article намефиристад. Он танҳо article identifier мефиристад, то destination website метавонад full article-ро аз Export JSON API fetch кунад.
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"
}Content storage responsibility ва Image Mirroring
Destination website бояд final article ва images-ро дар infrastructure-и худ store кунад.
Destination website бояд чиро store кунад
Мисоли дурусти image path
Истифодаи OxiRanker image URLs ба таври directly ва permanently дуруст нест. Image бояд download шавад ва article URLs бояд бо internal URLs-и destination website replace шаванд.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpГирифтани Export Token ва ready endpoints
Export Token ва ready-to-use endpoints аз article page, Outputs tab ва API Output section дастрасанд.
Export Token аз куҷо гирифта мешавад
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Ready-to-use endpoints
Дар ҳамон article page, дар дохили API Output section, Ready-to-use endpoints prepared URLs-ро нишон медиҳад.
https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=20&offset=0https://oxiranker.com/api/v1/users/domains/1/articles/1/export/jsonhttps://oxiranker.com/api/v1/users/domains/1/articles/1/export/html?canonical_mode=absolute&include_css=0Сохтани Webhook Secret ва destination endpoint
Webhook Secret ҳам аз article page, Outputs tab ва API Output section сохта мешавад.
Webhook Secret дар куҷо сохта мешавад?
Fixed destination endpoint
OxiRanker барои destination Webhook танҳо fixed path-и зеринро қабул мекунад. Destination website бояд ин endpoint-ро дар backend-и худ create кунад.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookИн endpoint бояд танҳо POST-ро accept кунад, raw body-ро preserve кунад, timestamp ва signature-ро verify кунад ва барои webhook.test successful response return кунад.
Required .env values барои destination website
OxiRanker ба destination website files ё .env access надорад. Destination website developer бояд ин values-ро дар backend-и худ store кунад.
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=50Security values
Storage values
Export API: List, JSON, HTML ва ZIP
Destination website метавонад Export API-ро барои receive кардани article list ё full output-и ҳар article истифода барад.
Гирифтани list of articles барои domain
GET https://oxiranker.com/api/v1/users/domains/:domainId/export/articles?limit=20&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonlimit number of articles-ро муайян мекунад ва айни ҳол аз 1 то 50 limited аст. offset барои pagination истифода мешавад. lang optional аст ва барои fetch кардани articles-и specific language мисли fa, en ё tr истифода мешавад.
Гирифтани full article ҳамчун JSON
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonИн муҳимтарин output барои destination website аст. Destination website бояд full article JSON-ро дар database-и худ store кунад ва барои public display аз database-и худ read кунад.
Гирифтани ready HTML
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/html?canonical_mode=absolute&include_css=0
Authorization: Bearer YOUR_EXPORT_TOKENHTML барои websites, ки ready output мехоҳанд, suitable аст, аммо approach-и professional-тар ин аст, ки JSON store шавад ва бо template-и худи destination website render шавад.
Гирифтани ZIP output
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/zip
Authorization: Bearer YOUR_EXPORT_TOKENZIP output article.json, meta.json, assets.json, schema.jsonld, index.html, version.txt ва README.txt-ро дар бар мегирад.
Webhook Signature ва secure Webhook receiving
Destination website бояд Webhook signature-ро бо истифода аз original raw body verify кунад.
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)Агар destination website аввал JSON-ро parse кунад ва баъд онро дубора stringify кунад, order ё spacing метавонад тағйир ёбад ва signature invalid шавад. Барои signature verification бояд original raw body истифода шавад.
Simple Node.js code sample барои receiving Webhook
Ин Express sample raw body, timestamp, signature, webhook.test ва article events-ро handle мекунад.
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,
});
});Recommended destination website database
Destination website бояд articles-ро дар database-и худ store кунад ва бо unique constraint duplicates-ро prevent кунад.
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);Prevent duplicates бо UPSERT
Article набояд барои ҳар Webhook дубора inserted шавад. Агар он аллакай exists бошад, бояд updated шавад.
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();Fetch кардани full article ва sync баъд аз Webhook
Вақте Webhook мерасад, destination website бояд full article-ро аз Export API fetch кунад, images-ро mirror кунад ва article-ро UPSERT кунад.
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,
});
}Image Mirroring ва image storage rules
Destination website бояд images-ро download кунад, дар infrastructure-и худ store кунад ва article URLs-ро replace кунад.
Image Mirroring rules
Recommended storage path
/uploads/site-articles/oxiranker/1/5/cover.webp
/uploads/site-articles/oxiranker/1/5/og.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpDaily sync барои reliability
Webhook alone кофӣ нест. Destination website бояд daily sync ҳам дошта бошад, то missed articles-ро recover кунад.
Чаро daily sync лозим аст?
Daily sync algorithm
Намоиши articles дар destination website
Баъд аз storing articles дар site_articles, destination website набояд барои ҳар public display ба OxiRanker connect шавад. Он бояд аз database-и худ read кунад.
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;SEO, Schema, RTL ва HTML rendering
Destination website бояд SEO data-ро аз database-и худ read кунад ва онро дар article page render кунад.
SEO fields
HTML rendering
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>Агар destination website strict security policy дошта бошад, метавонад HTML-ро пеш аз saving ё пеш аз displaying sanitize кунад.
RTL ва LTR
Ҳар article fields-и lang, dir ва is_rtl дорад. Агар dir rtl бошад, article page бояд right-to-left rendered шавад.
<article dir="rtl">
...
</article>Common errors ва маънои онҳо
Destination website errors бояд English, clear ва understandable бошанд, то troubleshooting зуд анҷом шавад.
Export API errors
Webhook errors
Final API ва Webhook testing checklist
Баъд аз implementation, ин tests connection-ро аз beginning то end verify мекунанд.
Export Token ва JSON test
GET https://oxiranker.com/api/v1/users/domains/1/export/articles?limit=2&offset=0
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonGET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonWebhook test
Агар destination Webhook-ро бо Postman бе signature call кунед, бояд security error receive кунед. Ин маънои онро дорад, ки endpoint active аст ва security-ро checks мекунад.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}Сипас дар дохили OxiRanker ба Test Webhook клик кунед. Агар receiver webhook.test-ро supports кунад, response_status бояд 200 бошад.
Important rules барои correct implementation
Ин rules бояд дар API integration риоя шаванд, то system secure, stable ва maintainable бимонад.
Complete API flow summary
Баъд аз анҷоми ин steps, custom website-и шумо метавонад articles-и generated дар OxiRanker-ро securely receive, store, mirror ва publish кунад.