OxiRanker Dokümantasyonu
Web sitenizi OxiRanker servislerine bağlamak için düzenli bir dokümantasyon merkezi. WordPress entegrasyonu veya API entegrasyonu ile başlayın, ardından kılavuzu adım adım takip edin.
API servisi
Custom websites, custom CMS platformları, Next.js, Laravel, Node.js, PHP veya Python backendleri için Export API ve Webhook kullanın.
OxiRanker destination website için permanent hosting değildir
OxiRanker, destination website content, images veya exported files için permanent hosting garantisi vermez. OxiRanker user-content hosting service değildir ve generated content, cover images, Open Graph images veya output files OxiRanker infrastructure üzerinde 90 günden fazla kalmayabilir.
Bu nedenle output alındıktan sonra destination website, final article, images, SEO data, Open Graph data ve Schema data değerlerini kendi database, server, storage veya CDN içinde store etmeli ve public display için OxiRanker URLs değerlerine bağlı kalmamalıdır.
Bağlantı eğitim videosu
WordPress bağlamadan veya API entegrasyonunu implement etmeden önce bu videoyu görsel bir walkthrough olarak kullanın.
Output flow adım adım implement edin
Önce Export Token ve Webhook Secret değerlerini backend içinde store edin, ardından Webhook receiver, database, UPSERT logic, Image Mirroring ve daily sync işlemlerini tamamlayın.
OxiRanker içinde kayıt olun ve domain oluşturun
Önce OxiRanker içinde kayıt olun, account içine sign in yapın ve Domains bölümünden Add Domain üzerine tıklayın.
Doğru domain formatı
Domain name alanına yalnızca root domain girin. Adres http, https veya www içermemelidir.
example.comYanlış formatlar
Domain alanına bu formatları girmeyin.
https://example.com
http://example.com
www.example.comDomain sahipliğini doğrulayın
Bu step içinde domain size ait olduğunu veya onu yönetmek için access sahibi olduğunuzu kanıtlamanız gerekir.
DNS TXT
ÖnerilirDomain DNS içine TXT record ekleyin. Cloudflare için bu genellikle en kolay method olur.
DNS Name:
_yourdomain-verification.example.com
TXT Value:
acb1****29803HTTP File
OxiRanker doğrulayabilsin diye web sitenizin .well-known path içine bir text file yerleştirin.
File URL:
https://example.com/.well-known/yourdomain-verification.txt
File Content:
220daaa37****42730f27Meta Tag
Web sitenizin homepage head section içine bir meta tag yerleştirin.
<meta name="yourdomain-verification" content="190208d****d275b65" />İlk domain doğrulandıktan sonra ücretsiz token alın
İlk domain verify edildikten sonra OxiRanker initial testing için account içine 1000 free tokens ekler.
Dilleri ve Blog URL değerini yapılandırın
Blog URLs bölümünde web sitenizin single-language mi multilingual mi olduğunu ve generated articles için hangi languages kullanılacağını tanımlayın.
Dilleri seçin
OxiRanker 24 languages destekler. Web sitesi yapınıza göre bir veya birden fazla languages seçebilirsiniz.
Blog URL girin
Her language için o language blog URL değerini girin. Web siteniz multilingual ise her language path değerini ayrı ayrı girin.
https://example.com/fa/blog
https://example.com/en/blogHer language için keywords ekleyin
Keywords tab içinde her language için en az 3 ve en fazla 50 keywords ekleyin.
Ana kural
Her language için keywords aynı language içinde girilmelidir. Article language English ise keywords de English olmalıdır.
Keywords nasıl eklenir
Her keyword yazdıktan sonra eklemek için Enter tuşuna basın. Tüm keywords tamamlandıktan sonra Save tıklayın.
Domain Article Profile tamamlayın
Article Profile, OxiRanker'e brand değerinizin ne olduğunu, kim için yazdığını, hangi tone kullanması gerektiğini ve hangi topics değerlerinden kaçınması gerektiğini söyler.
Industry
Market veya business category tanımlar. Örnek: Dubai içinde real estate consulting ve property buying and selling.
Business description
Business ne sattığını, kime hizmet ettiğini, nasıl çalıştığını ve onu farklı yapan şeyleri açıklar.
Audience
Content kime hitap etmelidir bunu tanımlar. Her audience ayrı olarak save edilebilir.
Tone
Generated content için style ve voice kontrol eder; örneğin formal, educational, simple, technical veya sales-oriented.
Content goal
Articles neyi başarmalı tanımlar; örneğin customers çekmek, traffic artırmak veya users eğitmek.
Yasaklı konular
Brand konuşmamalı veya ilişkilendirilmemeli olan her şey buraya girilir.
Optional CTA
CTA, reader article okuduktan sonra yapmasını istediğiniz action anlamına gelir. Hangi text suitable emin değilseniz bu field empty bırakmak daha iyidir.
İç ve dış bağlantılar
OxiRanker articles içinde internal links ve external links değerlerini intelligent şekilde uygulayabilir.
Internal links
Bu option default olarak enabled gelir ve enabled kalması daha iyidir. Internal linking, search engines için web sitenizin content structure yapısını daha iyi anlamaya yardımcı olur.
İlk article içinde internal links genellikle oluşturulmaz çünkü henüz previous article yoktur. İkinci article itibarıyla system internal linking yapabilir.
External links
External links official ve trusted sources üzerine eklenir ve SEO authority unintentionally aktarılmasın diye nofollow olarak marked edilir.
AI ile profile quality kontrol edin
Article Profile tamamlandıktan sonra OxiRanker profile quality review yapar.
Article summaries için Telegram bağlayın
Profile başarılı şekilde save edildikten sonra article summaries değerlerini Telegram channel üzerine göndermeyi enable edebilirsiniz.
Bot ekleyin
@Oxiranker_Bot botunu Telegram channel içine ekleyin ve Admin yapın.
Channel username girin
Channel name değerini @ ile girin. Örnek: @my_channel
Summary delivery enable edin
Valid channel kaydedildikten sonra article summary delivery enable edin.
Automatic article engine yapılandırın
Automatic article generation tab içinde automatic article generation engine yönetebilirsiniz.
Smart topic selection
System suitable, unique ve publishable topics seçmek için keywords ve Article Profile kullanır.
Brand-aware generation
Article generation sırasında tone, audience, content goal ve brand restrictions dikkate alınır.
Scheduled publishing
Monthly capacity ay boyunca dağıtılır, böylece content publishing daha natural ve consistent görünür.
Monthly capacity
7 articles / month
10 articles / month
15 articles / month
30 articles / month30 articles neredeyse her gün bir article anlamına gelir. 15 articles neredeyse iki günde bir anlamına gelir. 10 articles yaklaşık iki-üç günde bir anlamına gelir. 7 articles yaklaşık üç-dört günde bir anlamına gelir.
Article length ve image
Article length Short, Standard veya Long olabilir. Standard çoğu web sitesi için balanced choice olur.
Article images enabled veya disabled yapılabilir ve image theme Light veya Dark seçilebilir.
Generate üzerinden ilk article oluşturun
Domain hazırlandıktan sonra Generate menu açın, domain seçin ve New Content veya New Article tıklayın.
Language
Her content request için yalnızca bir language seçilir.
Topic
Net bir topic yazın. Source URL optional olur.
Image
Image enable veya disable edin ve Light veya Dark theme seçin.
Payment ve generation
Cost wallet içinden deducted edilir ve article generation başlar.
Preview, SEO Report ve JSON Output review edin
Article generated olduktan sonra quality review ve publishing preparation için birkaç important outputs kullanılabilir.
Preview
Article text ve generated image gösterir. Bu section publishing öncesi final quality review için kullanışlıdır.
SEO Report
SEO Score, title, meta description, canonical, keywords, schema, headings, links, images ve OpenGraph dahil understandable SEO report gösterir.
JSON Output
Technical users için full article output gösterir; slug, html_body, schema, image URLs, tags, category, reading time ve created/updated timestamps içerir.
Export API ve Webhook tam olarak ne yapar?
OxiRanker içinde article generated olduktan sonra destination website bunu Export API ile read edebilir veya article ready olduğunda Webhook ile notified olabilir.
Method 1: Export API
Destination website gerektiğinde Export Token ile OxiRanker üzerinden articles read edebilir. Bu method manual sync, daily sync, article list pages ve full article JSON fetching için kullanılır.
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 olduğunda veya article updated olduğunda OxiRanker destination website endpoint değerini notify eder. Webhook full article göndermez. Destination website Export JSON API üzerinden full article fetch edebilsin diye yalnızca article identifier gönderir.
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 ve Image Mirroring
Destination website final article ve images değerlerini kendi infrastructure içinde store etmelidir.
Destination website ne store etmelidir
Doğru image path örneği
OxiRanker image URLs değerlerini directly ve permanently kullanmak doğru değildir. Image download edilmeli ve article URLs destination website internal URLs ile replace edilmelidir.
https://oxiranker.com/uploads/articles/3_5/cover.webphttps://example.com/uploads/site-articles/oxiranker/1/5/cover.webpExport Token ve ready endpoints alın
Export Token ve ready-to-use endpoints article page, Outputs tab ve API Output section üzerinden kullanılabilir.
Export Token nereden alınır
https://oxiranker.com/en/pannel/generate/{domainId}/articles/{articleId}Ready-to-use endpoints
Aynı article page üzerinde, API Output section içinde, Ready-to-use endpoints prepared URLs gösterir.
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=0Webhook Secret ve destination endpoint oluşturun
Webhook Secret de article page, Outputs tab ve API Output section üzerinden oluşturulur.
Webhook Secret nerede oluşturulur?
Fixed destination endpoint
OxiRanker destination Webhook için yalnızca aşağıdaki fixed path kabul eder. Destination website bu endpoint değerini backend içinde create etmelidir.
POST /api/oxiranker/webhook
https://example.com/api/oxiranker/webhookBu endpoint yalnızca POST accept etmeli, raw body preserve etmeli, timestamp ve signature verify etmeli ve webhook.test için successful response return etmelidir.
Destination website için required .env values
OxiRanker destination website files veya .env üzerine access sahibi değildir. Destination website developer bu values değerlerini kendi backend içinde store etmelidir.
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 ve ZIP
Destination website Export API kullanarak article list veya her article için full output receive edebilir.
Domain için list of articles alın
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 değerini tanımlar ve şu anda 1 ile 50 arasında limited olur. offset pagination için kullanılır. lang optional olur ve fa, en veya tr gibi specific language articles fetch etmek için kullanılır.
Full article değerini JSON olarak alın
GET https://oxiranker.com/api/v1/users/domains/:domainId/articles/:articleId/export/json
Authorization: Bearer YOUR_EXPORT_TOKEN
Accept: application/jsonBu destination website için en important output değeridir. Destination website full article JSON değerini kendi database içinde store etmeli ve public display için kendi database üzerinden read etmelidir.
Ready HTML alın
GET https://oxiranker.com/api/v1/users/domains/1/articles/5/export/html?canonical_mode=absolute&include_css=0
Authorization: Bearer YOUR_EXPORT_TOKENHTML ready output isteyen websites için suitable olur, ancak daha professional approach JSON store etmek ve destination website kendi template ile render etmektir.
ZIP output alın
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 ve README.txt içerir.
Webhook Signature ve secure Webhook receiving
Destination website original raw body kullanarak Webhook signature verify etmelidir.
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 önce JSON parse edip sonra tekrar stringify ederse order veya spacing değişebilir ve signature invalid olabilir. Signature verification için original raw body kullanılmalıdır.
Webhook receiving için simple Node.js code sample
Bu Express sample raw body, timestamp, signature, webhook.test ve article events handle eder.
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 değerlerini kendi database içinde store etmeli ve unique constraint ile duplicates prevent etmelidir.
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);UPSERT ile duplicates prevent edin
Article her Webhook için yeniden inserted edilmemelidir. Zaten exists ise updated edilmelidir.
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();Webhook sonrası full article fetch ve sync yapın
Webhook geldiğinde destination website Export API üzerinden full article fetch etmeli, images mirror etmeli ve article UPSERT etmelidir.
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 ve image storage rules
Destination website images download etmeli, kendi infrastructure üzerinde store etmeli ve article URLs replace etmelidir.
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.webpReliability için daily sync
Webhook alone yeterli değildir. Destination website missed articles recover etmek için daily sync de bulundurmalıdır.
Daily sync neden gerekli?
Daily sync algorithm
Destination website üzerinde articles gösterin
Articles site_articles içine storing edildikten sonra destination website her public display için OxiRanker'e connect etmemelidir. Kendi database üzerinden read etmelidir.
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 ve HTML rendering
Destination website SEO data değerlerini kendi database üzerinden read edip article page üzerinde render etmelidir.
SEO fields
HTML rendering
<div
className="article-content"
dangerouslySetInnerHTML={{ __html: article.html_body }}
/>Destination website strict security policy içeriyorsa HTML değerini saving öncesinde veya displaying öncesinde sanitize edebilir.
RTL ve LTR
Her article lang, dir ve is_rtl fields içerir. dir rtl ise article page right-to-left rendered edilmelidir.
<article dir="rtl">
...
</article>Common errors ve anlamları
Destination website errors English, clear ve understandable olmalıdır ki troubleshooting hızlı yapılabilsin.
Export API errors
Webhook errors
Final API ve Webhook testing checklist
Implementation sonrasında bu tests connection değerini beginning ile end arasında verify eder.
Export Token ve 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 değerini Postman ile signature olmadan call ederseniz security error receive etmelisiniz. Bu, endpoint active ve checks security demektir.
{
"error": "missing_webhook_signature",
"message": "Webhook signature header is required."
}Sonra OxiRanker içinde Test Webhook tıklayın. Receiver webhook.test supports ediyorsa response_status 200 olmalıdır.
Correct implementation için important rules
System secure, stable ve maintainable kalması için API integration içinde bu rules takip edilmelidir.
Complete API flow summary
Bu steps tamamlandıktan sonra custom website, OxiRanker içinde generated articles değerlerini securely receive, store, mirror ve publish edebilir.