Entegrasyon API'si
seopanel sitenizdeki SEO sorunlarını bulur ve bitmiş düzeltme talimatları üretir. Bu API o talimatları kendi backend'inize çekmeniz, kendi içerik şemanıza eşlemeniz ve kalıcı yazmanız içindir. Uygulayan taraf sizin kodunuzdur.
Temel adres: https://otopilot.net/api/seo-repair/v1 v1.1.0
OpenAPI belgesini indir (JSON)Tüm uçları, izinleri ve hata kodlarını içeren OpenAPI 3.1 belgesi; Postman, Insomnia veya kod üreticilerine doğrudan aktarılabilir.
Başlangıç
- Panelden bir API anahtarı oluşturun (SEO Hata Düzeltme → Kanal & Kurulum → Geliştirici API'si). Anahtar, SEO Hata Düzeltme kapalıyken de oluşturulabilir.
- POST /handshake ile anahtarı sınayın.
- GET /rules/{projectId} ile düzeltmeleri çekin. İlk çağrıda since vermeyin; sonrakilerde dönen cursor değerini since olarak gönderin.
- Her düzeltmeyi id ile eşleyerek kendi tarafınıza kalıcı yazın.
- POST /rules/{projectId}/applied ile hangilerini uyguladığınızı bildirin.
- Bitirince aşağıdaki kurulum kontrol listesini madde madde geçin.
Senkron döngüsü (zorunlu)
seopanel düzeltmeleri hazırlar ama sitenize kendisi yazmaz. Sisteminiz düzeltmeleri düzenli olarak çekmedikçe hiçbir düzeltme sitenize ulaşmaz ve panel bunu "Siten düzeltmeleri almıyor" uyarısıyla gösterir.
- Zamanlanmış görev: döngüyü en az 15 dakikada bir çalıştırın. Hız sınırı anahtar başına dakikada 300 istektir.
- cursor değerini kalıcı saklayın. Kaybederseniz sonraki çağrı tüm seti yeniden getirir; zararsızdır, id ile tekilleştirin.
- removedIds gelirse o düzeltmeleri kendi tarafınızda geri alın.
- Olay adresleri (webhook) tanımladıysanız "Yeni düzeltme hazır" (fix.ready) bildirimi gelir; içerik bildirimde değil, uçtan çekilir. Bildirimi kaçırmanıza karşı zamanlanmış çekimi yine de sürdürün.
Kimlik doğrulama
Her istekte proje anahtarınızı gönderin. İki biçim de kabul edilir:
X-SP-Auth: sp_…
Authorization: Bearer sp_…Kodunuzun yazıldığı API sürümünü X-SP-Api-Version başlığıyla bildirin (ör. 1.0.0). Panel, bu değere göre eksik güncellemeleri gösterir; başlık yoksa "siteniz sürüm bildirmiyor" der. Yanıtlar da güncel sürümü aynı başlıkla döner.
X-SP-Api-Version: 1.1.0Anahtar yönetimi
- Göster: mevcut anahtarı tekrar gösterir, değiştirmez; çalışan entegrasyon etkilenmez.
- Yeniden üret: eski anahtar anında geçersiz olur; bakım penceresinde yapın.
- Bağlantıyı kes: anahtar iptal edilir, sonraki istekler 401 alır.
IP izin listesi
İsteğe bağlı. Açarsanız listede olmayan adresten gelen istek 403 SP_API_IP_DENIED alır; hata metni isteğin geldiği adresi söyler ve panelde "Listeye ekle" ile eklenebilir.
Hız sınırı
Anahtar başına dakikada yaklaşık 300 istek. Aşılırsa 429 SP_API_RATE_LIMITED ve Retry-After başlığı döner.
Hata biçimi
Hatalar her zaman şu gövdeyle döner: success false, error (insan-okur metin), code (makine-okur kod).
{
"success": false,
"error": "API anahtarı geçersiz.",
"code": "SP_API_INVALID_KEY"
}| Kod | Durum | Anlamı |
|---|---|---|
SP_API_NO_KEY | 401 | Anahtar gönderilmedi. |
SP_API_INVALID_KEY | 401 | Anahtar geçersiz ya da iptal edilmiş. |
SP_API_KEY_PROJECT_MISMATCH | 401 | Yoldaki projectId anahtarın projesi değil. |
SP_API_IP_DENIED | 403 | İstek IP izin listesinde olmayan bir adresten geldi. |
SEO_REPAIR_DISABLED | 403 | Proje için SEO Hata Düzeltme kapalı. |
PROJECT_INACTIVE | 403 | Proje duraklatılmış ya da etkin değil. |
SP_API_SINCE_INVALID | 400 | since ISO 8601 tarih değil. |
SP_API_LIMIT_INVALID | 400 | limit 1-500 arasında tam sayı değil. |
SP_API_APPLIED_IDS_REQUIRED | 400 | Gövdede ids dizisi yok ya da boş. |
SP_API_RATE_LIMITED | 429 | İstek sınırı aşıldı. |
INTEGRATION_SCOPE_MISSING | 403 | Anahtarda bu uç için izin açık değil. |
INTEGRATION_MODULE_INACTIVE | 403 | Uç bir modül gerektiriyor ve modül sitede etkin değil. |
INTEGRATION_IDEMPOTENCY_CONFLICT | 409 | Aynı Idempotency-Key farklı bir istekle kullanıldı. |
INTEGRATION_IDEMPOTENCY_KEY_INVALID | 400 | Idempotency-Key boş ya da 128 karakterden uzun. |
INTEGRATION_NOT_IMPLEMENTED | 501 | Modül açık ama bu uç için işleyici henüz yayında değil. |
MODULE_DISABLED | 403 | Paketinizde bot takibi kapalı; ziyaretler kaydedilmez. |
Uç noktalar
Tüm uçlar JSON konuşur. SEO uçları /api/seo-repair/v1, entegrasyon uçları /api/integrations/v1 altındadır.
Form, sohbet, randevu ve yorum modülleri bu sitede etkin olmadığı sürece "Modül gerekir" rozetli uçlar 403 INTEGRATION_MODULE_INACTIVE döner. Gerçek bir yanıt uydurulmaz.
Bağlantı
- POST
/api/seo-repair/v1/handshakeAnahtarı doğrula - GET
/api/seo-repair/v1/statusSenkron durumu
Düzeltmeler
- GET
/api/seo-repair/v1/rules/{projectId}Düzeltmeleri çekSEO Hata Düzeltme açık olmalı - POST
/api/seo-repair/v1/rules/{projectId}/appliedUyguladığını bildirSEO Hata Düzeltme açık olmalıIdempotency-Key - GET
/api/seo-repair/v1/content/{projectId}Yayına hazır içerikleri çek - POST
/api/seo-repair/v1/content/{projectId}/appliedİçeriğin sitede uygulandığını bildirIdempotency-Key
Bot ziyaretleri
- POST
/api/seo-repair/v1/bot-visits/{projectId}Bot ziyaretlerini gönderIdempotency-Key
MCP
- POST
/api/seo-repair/v1/mcpMCP sunucusu (JSON-RPC)
Entegrasyon
- GET
/api/integrations/v1/meAnahtar, izinler, açık modüller
Randevular · modül gerekir
- GET
/api/integrations/v1/bookings/offeringsHizmetleri listeleModül gerekirİzin: bookings:read - GET
/api/integrations/v1/bookings/availabilityBoş saatlerModül gerekirİzin: bookings:read - GET
/api/integrations/v1/bookings/appointmentsRandevuları listeleModül gerekirİzin: bookings:read - POST
/api/integrations/v1/bookings/appointmentsRandevu açModül gerekirİzin: bookings:writeIdempotency-Key - GET
/api/integrations/v1/bookings/appointments/{appointmentId}Randevuyu okuModül gerekirİzin: bookings:read - POST
/api/integrations/v1/bookings/appointments/{appointmentId}/approveOnaylaModül gerekirİzin: bookings:writeIdempotency-Key - POST
/api/integrations/v1/bookings/appointments/{appointmentId}/cancelİptal etModül gerekirİzin: bookings:writeIdempotency-Key
Bot ziyaretleri
Sitenizin sunucusunun gördüğü istekleri toplu gönderin (adres, user-agent, zaman, istemci IP'si). Bot olmayan istekler bizde elenir; yalnız yapay zekâ ve arama motoru botları kaydedilir ve panelde Bot Ziyaretleri sayfasında görünür. Sunucu tarafında sayıldığı için JavaScript ya da görsel çalıştırmayan botlar da görünür; piksel bu ziyaretleri göremez.
- Tek çağrıda en fazla 500 kayıt işlenir, fazlası yok sayılır. İstekleri bellekte biriktirip birkaç saniyede bir ya da 100 istekte bir tek çağrıyla gönderin.
- ip önerilir: doğrulama için gereklidir. ip göndermeyen kayıtlar doğrulanmamış ("–") olarak saklanır ve panelde görünür; geçersiz biçimli ip ise reddedilir. IP'yi kendi proxy/CDN başlığınızdan alın (örn. CF-Connecting-IP); istemcinin gönderdiği X-Forwarded-For taklit edilebilir. IP bot ağından gelmiyorsa ziyaret "Doğrulandı" değil "–" olarak görünür.
- ts epoch milisaniye ya da ISO 8601 olabilir; yoksa, geçersizse ya da gelecekteyse alındığı an kullanılır. 7 günden eski kayıtlar saklanmaz. accepted işlenen kayıt sayısı, stored bot olarak tanınıp kaydedilen ziyaret sayısıdır.
- Anahtar yalnız kendi projesinin adresine yazabilir (başka projectId için 401 SP_API_KEY_PROJECT_MISMATCH). SEO Hata Düzeltme kapalı olsa da çalışır; aynı gövdeyi yeniden göndermemek için Idempotency-Key ekleyin.
curl -X POST https://otopilot.net/api/seo-repair/v1/bot-visits/PROJE_KIMLIGI \
-H "X-SP-Auth: $SP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: toplu-2026-10-27-1" \
-d '{"hits":[{"url":"/hakkimizda","ua":"Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot)","ts":1790000000000,"ip":"20.171.207.1"}]}'{
"success": true,
"accepted": 120,
"stored": 7
}Hatalar: 401 anahtar yok/geçersiz/başka proje, 403 IP reddi ya da paketinizde bot takibi kapalı (MODULE_DISABLED), 400 geçersiz gövde, 429 hız sınırı.
Sunucu günlüğünden gönderme
Aşağıdaki bağımlılıksız Node betiği nginx access_log dosyasını kaldığı yerden okur, bot benzeri satırları süzer ve 500'lük gruplar halinde gönderir. Cron ile 5 dakikada bir çalıştırın.
const fs = require('fs')
const crypto = require('crypto')
const LOG = process.env.ACCESS_LOG || '/var/log/nginx/access.log'
const STATE = process.env.STATE_FILE || '/var/tmp/sp-bot-visits.offset'
const PROJECT_ID = process.env.SP_PROJECT_ID
const API_KEY = process.env.SP_API_KEY
const ENDPOINT = 'https://otopilot.net/api/seo-repair/v1/bot-visits/' + PROJECT_ID
const LINE = /^(\S+) \S+ \S+ \[([^\]]+)\] "(?:GET|HEAD|POST) (\S+) [^"]*" \d+ \S+ "[^"]*" "([^"]*)"/
const BOTISH = /bot|crawl|spider|slurp|gpt|claude|perplexity|anthropic|bytespider|amazonbot|applebot|meta-external|duckassist|mistral|cohere|ccbot/i
function parseTime(s) {
const m = /^(\d+)\/(\w+)\/(\d+):(\d+):(\d+):(\d+) ([+-]\d{4})$/.exec(s)
if (!m) return Date.now()
const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
const utc = Date.UTC(+m[3], months.indexOf(m[2]), +m[1], +m[4], +m[5], +m[6])
const off = (m[7][0] === "-" ? -1 : 1) * (parseInt(m[7].slice(1, 3), 10) * 60 + parseInt(m[7].slice(3), 10))
return utc - off * 60000
}
async function send(hits) {
const key = crypto.createHash('sha256').update(JSON.stringify(hits)).digest('hex').slice(0, 64)
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-SP-Auth': API_KEY, 'Idempotency-Key': key },
body: JSON.stringify({ hits }),
})
if (!res.ok) throw new Error('bot-visits ' + res.status + ' ' + (await res.text()))
}
(async () => {
let offset = 0
try { offset = parseInt(fs.readFileSync(STATE, 'utf8'), 10) || 0 } catch {}
const size = fs.statSync(LOG).size
if (size < offset) offset = 0
const fd = fs.openSync(LOG, "r")
const buf = Buffer.alloc(size - offset)
fs.readSync(fd, buf, 0, buf.length, offset)
fs.closeSync(fd)
const text = buf.toString("utf8")
const end = text.lastIndexOf("\n") + 1
const hits = []
for (const line of text.slice(0, end).split("\n")) {
const m = LINE.exec(line)
if (!m || !BOTISH.test(m[4])) continue
hits.push({ ip: m[1], ts: parseTime(m[2]), url: m[3], ua: m[4] })
}
for (let i = 0; i < hits.length; i += 500) await send(hits.slice(i, i + 500))
fs.writeFileSync(STATE, String(offset + Buffer.byteLength(text.slice(0, end))))
})().catch((e) => { console.error(e.message); process.exit(1) })
*/5 * * * * SP_PROJECT_ID=PROJE_KIMLIGI SP_API_KEY=sp_... /usr/bin/node /opt/sp-bot-visits.js >> /var/log/sp-bot-visits.log 2>&1İnsan ziyaretçilerin kaydı tutulmaz: yalnız user-agent'ı tanınan botların adresi, user-agent'ı ve IP'si saklanır.
Uç ayrıntıları
POST /handshake
Anahtarın geçerli olduğunu ve hangi projeye bağlı olduğunu gösterir. Durum değiştirmez; kurulumda sağlık kontrolü olarak kullanın.
{
"success": true,
"ok": true,
"projectId": "665f1c2a9b1e4a0012a3b4c5",
"domain": "ornek.com"
}GET /status
Senkron sağlığı: mod, aktif kanal, son çekim, düzeltmelerin alınıp alınmadığı, durum sayıları ve bildirdiğiniz API sürümü.
GET /rules/{projectId}
Uygulanmaya hazır düzeltmeleri döner. İlk senkronda since vermeyin; artımlı senkronda sakladığınız cursor'ı since olarak gönderin. hasMore true ise dönen cursor ile tekrar çağırın. Boş mutations arıza demek değildir; sebebini emptyReason söyler. Çekilen düzeltmeler çekildi olarak işaretlenir.
Sorgu parametreleri: since (ISO 8601), limit (1-500, varsayılan 100), url (tek sayfa).
{
"success": true,
"mutations": [
{
"id": "665f1c2a9b1e4a0012a3b4c5",
"url": "https://ornek.com/hakkimizda",
"updatedAt": "2026-10-26T10:12:00.000Z",
"rules": [
{ "selector": "meta[name=description]", "action": "set_attribute", "attribute": "content", "value": "Şirketimizin kuruluşu, ekibi ve hizmet alanları." }
]
}
],
"removedIds": [],
"textFixes": [],
"removedTextFixIds": [],
"cursor": "2026-10-26T10:12:00.000Z",
"hasMore": false,
"mode": "full",
"emptyReason": null
}emptyReason değerleri
up_to_date— since cursor'ından beri yeni düzeltme yok. Normal; aynı cursor ile tekrar çağırın.awaiting_manual_generation— Proje yarı otonom modda, henüz düzeltme üretilmedi. Panelden üretin ya da tam otonom moda alın.awaiting_approval— Düzeltmeler var ama onaylanmadı. Panelden onaylayın.in_progress— Bu tur hazırlanıyor. Bekleyip cursor ile tekrar çağırın.none_yet— Bu proje için henüz düzeltme hazırlanmadı. Sonraki taramadan sonra tekrar çağırın.inactive_channel— Projenin aktif kanalı Geliştirici API'si değil; düzeltmeler başka bir kanaldan gidiyor.
POST /rules/{projectId}/applied
Kendi sisteminize yazdığınız düzeltmelerin id'lerini bildirir; düzeltme Uygulandı kademesine geçer ve doğrulama taraması neyi arayacağını bilir. İdempotenttir. Blog yazısı ve yeni sayfa için urls ile sitedeki adresi de gönderin; projenin alan adı dışındaki adres yok sayılır.
{ "ids": ["665f1c2a9b1e4a0012a3b4c5"], "urls": { "665f1c2a9b1e4a0012a3b4c5": "https://ornek.com/blog/seo-nedir" } }Yanıt: acknowledged (ilk kez işaretlenen), requested (gönderdiğiniz), invalid (bu projede karşılığı olmayan), ignored (zaten uygulanmış ya da şu an uygulanamaz durumda olan).
{ "success": true, "acknowledged": 1, "requested": 1, "invalid": 0, "ignored": 0 }POST /mcp
JSON-RPC 2.0 konuşan MCP ucu (Streamable HTTP, tek yanıt). initialize, ping, tools/list ve tools/call desteklenir; araçlar test_connection, get_seo_fixes, report_applied ve get_status. GET 405 döner.
Talimat türleri
Her kuralın action alanı ne yapacağınızı söyler. Tanımadığınız talimatı atlayabilirsiniz; doğrulama taraması eksik kalanı raporlar.
| action | Gelen alanlar | Sizde ne yapılır |
|---|---|---|
set_attribute | selector, attribute, value | İlgili elemanın niteliğini yazın. meta[name=description] + content meta açıklama, link[rel=canonical] + href canonical, meta[name=robots] + content robots. |
set_html | selector, value | Seçilen elemanın iç HTML'ini value ile değiştirin (h1 sayfa başlık alanı). |
replace_html | selector, value | set_html ile aynı. |
replace_text | selector, matchText, value | Elemanın metninde matchText'i bulup value ile değiştirin. |
remove_element | selector | Elemanı kaldırın ya da gizleyin. |
add_class | selector, value | Sınıf ekleyin (görsel; çoğu backend'de atlanabilir). |
remove_class | selector, value | Sınıfı çıkarın (görsel; çoğu backend'de atlanabilir). |
remove_attribute | selector, attribute | Niteliği silin. |
change_tag | selector, value | Elemanın etiketini değiştirin (div → h2). Şablon katmanında ele alın. |
create_if_missing | createObject, position | Eksikse yeni eleman ekleyin (ör. eksik meta etiketi). |
add_redirect | from, to, status | Kalıcı yönlendirme kuralı yazın. |
remove_redirect | from | Daha önce yazdığınız yönlendirmeyi kaldırın. |
inject_related_links | links[] | İçeriğe "ilgili sayfalar" iç linkleri ekleyin. |
create_page | title, content, slug, excerpt, postType, tags, featuredImageUrl, status | Yeni sayfa ya da blog yazısı oluşturun. Yayın adresini applied bildiriminde urls ile gönderin. |
set_http_header | headers | HTTP yanıt başlığı ekleyin (ör. X-Robots-Tag). Sunucu ya da CDN katmanında uygulanır. |
inject_css | css | Site geneli CSS ekleyin. |
put_file | path, value | path'teki dosyayı value içeriğiyle oluşturun ya da güncelleyin (robots.txt, sitemap.xml). |
optimize_media | src, maxWidth, maxHeight | src'teki görseli en-boy oranını koruyarak kutuya sığdırıp daha hafif biçimde yayınlayın. Orijinali silmeyin. |
insert_content_block | blockId, position, html | İçeriğin başına (start) ya da sonuna (end) html bloğunu ekleyin. Aynı blockId ikinci kez gelirse eskisini değiştirin. |
replace_phrase | selector, matchText, value | selector içindeki görünen metinde matchText ifadesini value ile değiştirin; boşluk farkları yok sayılır. |
selector bir CSS/DOM seçicisidir. DOM'u olmayan bir sistemde onu anlamına göre kendi alanınıza eşleyin: meta[name=description] meta açıklama, title sayfa başlığı, link[rel=canonical] canonical. Şablona bağlı serbest seçicileri çözemiyorsanız atlayın.
Örnekler
curl -X POST https://otopilot.net/api/seo-repair/v1/handshake \
-H "X-SP-Auth: $SP_API_KEY" \
-H "X-SP-Api-Version: 1.1.0"
curl "https://otopilot.net/api/seo-repair/v1/rules/$SP_PROJECT_ID?limit=100" \
-H "Authorization: Bearer $SP_API_KEY" \
-H "X-SP-Api-Version: 1.1.0"
curl -X POST https://otopilot.net/api/seo-repair/v1/rules/$SP_PROJECT_ID/applied \
-H "X-SP-Auth: $SP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids":["<id>"],"urls":{"<id>":"https://ornek.com/blog/seo-nedir"}}'const BASE = 'https://otopilot.net/api/seo-repair/v1';
const PROJECT_ID = process.env.SP_PROJECT_ID;
const HEADERS = {
'X-SP-Auth': process.env.SP_API_KEY,
'X-SP-Api-Version': '1.1.0',
'Content-Type': 'application/json',
};
async function syncFixes(store) {
let since = await store.getCursor();
let hasMore = true;
while (hasMore) {
const qs = new URLSearchParams({ limit: '100', ...(since ? { since } : {}) });
const res = await fetch(`${BASE}/rules/${PROJECT_ID}?${qs}`, { headers: HEADERS });
if (res.status === 429) return;
if (!res.ok) throw new Error(`pull ${res.status}`);
const body = await res.json();
const applied = [];
for (const m of body.mutations) {
await store.applyRules(m.url, m.rules);
applied.push(m.id);
}
for (const id of body.removedIds || []) await store.revert(id);
if (applied.length) {
await fetch(`${BASE}/rules/${PROJECT_ID}/applied`, {
method: 'POST', headers: HEADERS, body: JSON.stringify({ ids: applied }),
});
}
since = body.cursor;
await store.saveCursor(since);
hasMore = body.hasMore;
}
}<?php
$base = 'https://otopilot.net/api/seo-repair/v1';
$projectId = getenv('SP_PROJECT_ID');
$headers = [
'X-SP-Auth: ' . getenv('SP_API_KEY'),
'X-SP-Api-Version: 1.1.0',
'Content-Type: application/json',
];
function sp_request(string $method, string $url, array $headers, ?array $body = null): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
return [$status, json_decode((string) $raw, true) ?? []];
}
$since = file_exists('cursor.txt') ? trim(file_get_contents('cursor.txt')) : null;
do {
$query = http_build_query(array_filter(['limit' => 100, 'since' => $since]));
[$status, $data] = sp_request('GET', "$base/rules/$projectId?$query", $headers);
if ($status !== 200) break;
$applied = [];
foreach ($data['mutations'] as $m) {
apply_rules($m['url'], $m['rules']);
$applied[] = $m['id'];
}
foreach ($data['removedIds'] as $id) revert_fix($id);
if ($applied) sp_request('POST', "$base/rules/$projectId/applied", $headers, ['ids' => $applied]);
$since = $data['cursor'];
if ($since) file_put_contents('cursor.txt', $since);
} while (!empty($data['hasMore']));Döngüyü 15 dakikada bir çalıştırın. Blog yazıları ve sayfa düzeltmeleri aynı döngüden gelir; ayrı zamanlama gerekmez.
*/15 * * * * cd /var/www/site && node scripts/seopanel-sync.js >> /var/log/seopanel-sync.log 2>&1MCP sunucusu
Yapay zekâ asistanınızı (Claude Desktop, Cursor) aynı anahtarla bağlayın. Araçlar: test_connection, get_seo_fixes, report_applied, get_status.
Sunucu JSON-RPC 2.0 konuşur: POST ile tek yanıt, bildirimler için 202; GET 405 döner.
{
"mcpServers": {
"seopanel": {
"url": "https://otopilot.net/api/seo-repair/v1/mcp",
"headers": {
"X-SP-Auth": "sp_…"
}
}
}
}{
"mcpServers": {
"seopanel": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://otopilot.net/api/seo-repair/v1/mcp",
"--header",
"X-SP-Auth:sp_…"
]
}
}
}{
"mcpServers": {
"seopanel": {
"command": "npx",
"args": [
"tsx",
"mcp/server.ts"
],
"env": {
"SEOPANEL_API_URL": "https://otopilot.net",
"SEOPANEL_API_KEY": "sp_…"
}
}
}
}Depo yerelde çalışıyorsa asistanın bağlanacağı komut: npx tsx mcp/server.ts. SEOPANEL_API_URL ve SEOPANEL_API_KEY ortam değişkenlerini verin.
Claude web arayüzündeki "özel bağlayıcı ekle" yalnızca OAuth kabul eder; bu yüzden MCP sunucusu oradan eklenemez.
Entegrasyon
GET /api/integrations/v1/me anahtarın projesini, açık izinleri, sitede etkin modülleri ve olay adlarını döner. Kurulumda sağlık kontrolü olarak kullanın.
curl https://otopilot.net/api/integrations/v1/me \
-H "X-SP-Auth: $SP_API_KEY"{
"success": true,
"data": {
"project": {
"id": "665f1c2a9b1e4a0012a3b4c5",
"domain": "ornek.com"
},
"scopes": [
"forms:read"
],
"modules": [],
"events": []
}
}modules alanı bu sitede gerçekten açık modüllerin listesidir (conversations, forms, bookings, comments, reviews). Modül yoksa boş liste döner; bu bir hata değildir.
Modül uçları · modül gerekir
Randevu uçları yalnız Online Randevu modülü sitenizde açıksa ve anahtarda ilgili izin açıksa çalışır. Modülü kullanmıyorsanız bu uçları kurmanız gerekmez; SEO düzeltmeleri ve blog teslimi bunlara bağlı değildir.
| Grup | İzinler | Gereken modül |
|---|---|---|
| Randevular | bookings:read, bookings:write | Online Randevu |
{
"success": false,
"error": "Bu uç \"forms\" modülünü gerektirir; modül bu sitede etkin değil.",
"code": "INTEGRATION_MODULE_INACTIVE"
}Modül açık değilken bu uçlar boş liste ile 200 dönmez; 403 INTEGRATION_MODULE_INACTIVE döner. Anahtarda izin yoksa 403 INTEGRATION_SCOPE_MISSING döner.
İçerik API'si (hesap anahtarı)
Hesap anahtarı tüm projelerinize erişen kişisel anahtardır; proje anahtarından ayrıdır. Hesap → API Erişimi sayfasından seçtiğiniz izinlerle oluşturun, Authorization başlığında Bearer olarak gönderin. Anahtar yalnız oluşturulurken gösterilir.
curl https://otopilot.net/api/public/v1/me \
-H "Authorization: Bearer spt_…"Anahtar başına dakikada 120 istek, bunun 30 tanesi yazma olabilir. Yanıtlarda önbellek kapalıdır.
| Uç nokta | İzin | Açıklama |
|---|---|---|
GET /api/public/v1/me | account:read | Hesap, plan ve kredi bakiyesi |
GET /api/public/v1/projects | projects:read | Erişebildiğiniz projeler |
GET /api/public/v1/projects/{projectId}/articles | content:read | Projenin yazılarını sayfalı listeler; status ile süzülür |
POST /api/public/v1/projects/{projectId}/articles | content:write | Yazı üretimini başlatır; bakiyeden düşer, 202 ile kimlik döner |
GET /api/public/v1/articles/{articleId} | content:read | Yazıyı metni, SEO alanları ve denetim sonuçlarıyla getirir |
PATCH /api/public/v1/articles/{articleId} | content:write | Yazıyı düzenler; yayındaki yazıda siteye gitmez, sync ile gönderilir |
POST /api/public/v1/articles/{articleId}/publish | content:publish | Yazıyı bağlı kanala taslak veya yayın olarak gönderir |
POST /api/public/v1/articles/{articleId}/sync | content:publish | Yayındaki yazının düzenlemesini siteye gönderir |
GET /api/public/v1/projects/{projectId}/report | reports:read | Son 30 günün içerik sayıları, harcanan kredi ve bu ayın blog planı |
Yayın uçları paneldeki denetimlerin aynısından geçer: bloklayan kontrolü olan yazı yayınlanmaz, yazma ve yayın için ayrı izin gerekir, ekip üyesinin rolü burada da geçerlidir. POST isteklerinde Idempotency-Key başlığıyla tekrar güvenli hale gelir. Hata gövdesi success, error ve code alanlarını taşır; 401 anahtar geçersiz, 403 izin yok, 402 kredi yetersiz, 422 denetim engeli, 429 sınır aşıldı demektir.
Webhooks
Seçtiğiniz olaylar olduğu anda verdiğiniz https adresine imzalı JSON olarak POST edilir. Adresleri panelde Entegrasyonlar → Olay adresleri bölümünden eklersiniz; en çok 5 adres. Modülü sitede açık olmayan olaylar seçilemez.
| Olay | Ne zaman | Modül gerekir |
|---|---|---|
fix.ready | Yeni düzeltme hazır | Modül gerekmez |
form.submission.created | Yeni form kaydı | Formlar |
chat.conversation.started | Yeni konuşma başladı | Konuşmalar |
chat.lead.captured | Ziyaretçi bilgilerini bıraktı | Konuşmalar |
chat.handoff.requested | Ziyaretçi bir yetkiliyle görüşmek istedi | Konuşmalar |
chat.message.created | Yeni mesaj (ziyaretçi, bot ya da ekip) | Konuşmalar |
chat.conversation.closed | Konuşma kapatıldı | Konuşmalar |
booking.appointment.created | Yeni randevu | Randevular |
booking.appointment.confirmed | Randevu onaylandı | Randevular |
booking.appointment.rescheduled | Randevu yeni saate alındı | Randevular |
booking.appointment.cancelled | Randevu iptal edildi | Randevular |
social.comment.created | Yeni Instagram yorumu | Yorumlar |
review.created | Yeni Google yorumu | Google Yorumları |
content.published | İçerik yayınlandı | Modül gerekmez |
content.updated | Yayındaki içerik güncellendi | Modül gerekmez |
{
"id": "evt_9f2c1a7b3d5e8a4c01",
"type": "fix.ready",
"occurredAt": "2026-10-26T09:15:00.000Z",
"projectId": "clx0abc123",
"data": {
"fixId": "clx8k2m0a0001",
"pageUrl": "https://ornek.com/hakkimizda",
"ruleCount": 3
}
}content.published ve content.updated olaylarının data alanında yazının tamamı gelir: contentId, op, title, slug, url, html, excerpt, metaTitle, metaDescription, keywords, faqJsonLd, featuredImage ve publishedAt. Görseller gövdeye gömülmez; imzalı adres olarak gelir ve 7 gün geçerlidir, kalıcı kullanım için kendi sunucunuza kopyalayın. content.published aynı yazı için bir kez gönderilir; Blogger'da zamanlanan yazıda publishedAt ileri tarihlidir.
Her teslimde X-SP-Signature (t=zaman damgası, v1=HMAC-SHA256 imzası), X-SP-Event (olay adı), X-SP-Event-Id (tekrar eden teslimi ayıklamak için) ve X-SP-Delivery (teslim kimliği) başlıkları gönderilir. İmza, t + "." + ham gövde üzerinden olay adresinin imza anahtarıyla (whsec_…) hesaplanır; her denemede t yenilenir. Gövdeyi ayrıştırmadan, gelen ham metin üzerinden doğrulayın ve t değeri 5 dakikadan eskiyse reddedin.
X-SP-Signature: t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-SP-Event: fix.ready
X-SP-Event-Id: evt_9f2c1a7b3d5e8a4c01
X-SP-Delivery: clx8k2m0a0001const crypto = require('crypto')
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(parts.v1 || "")
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
İmza anahtarı adres eklenirken ya da yenilenirken yalnız bir kez gösterilir; sonradan yalnız son 4 hanesi görünür. Anahtarı yenilerseniz eskisi anında geçersiz olur.
Panelde "Deneme gönder" ile ping olayı yollayabilirsiniz; gerçek olaylarla aynı imzayı taşır, yeniden denenmez ve hata sayacına yazılmaz.
Teslim kuralları
- Yalnız 2xx yanıt başarılı sayılır.
- Yönlendirmeler izlenmez; 3xx yanıt başarısız sayılır.
- Yanıt için 10 saniye beklenir.
- Başarısız teslim artan aralıkla toplam 8 kez denenir (ilk deneme hemen; sonra 1 dk, 5 dk, 15 dk, 1 sa, 3 sa, 6 sa ve 12 sa sonra). 8. denemeden sonra vazgeçilir; panelde "Yeniden gönder" ile yeniden sıraya alınabilir.
- Art arda 50 başarısız denemede olay adresi kapatılır ve bildirim gönderilir. API'den yapılan yazımlar olay üretmez (döngü oluşmaz).
- Aynı olay aynı X-SP-Event-Id ile yeniden gelebilir; tekilleştirmek için bu kimliği kullanın.
- Adres yalnız herkese açık bir https adresi olabilir; iç ağ adreslerine gönderim yapılmaz.
- Teslim kayıtları 30 gün saklanır.
İzinler ve tekrar koruması
Modül uçları anahtarda yalnızca panelde açtığınız izinlerle çalışır: conversations:read|write, forms:read|write, bookings:read|write, comments:read|write, reviews:read. İzni olmayan uç 403 INTEGRATION_SCOPE_MISSING döner. SEO uçları izin sormaz.
POST ve PATCH isteklerine Idempotency-Key başlığı ekleyin (en çok 128 karakter). Aynı anahtarla gelen ikinci istek işi tekrar yapmaz, ilk yanıtı Idempotent-Replayed: true başlığıyla döner; aynı anahtar farklı bir istekle kullanılırsa 409 INTEGRATION_IDEMPOTENCY_CONFLICT. Anahtar 24 saat saklanır.
GET /me anahtarın projesini, açık izinleri, sitede gerçekten açık modülleri ve olay adlarını döner. Modül listesi mağaza aboneliklerinden okunur; hiç modül açık değilse boştur.
Liste uçları limit (en çok 100) ve cursor alır; nextCursor bir sonraki sayfanın imlecidir.
curl -X POST https://otopilot.net/api/integrations/v1/conversations/chat_123/messages \
-H "X-SP-Auth: $SP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: yanit-8841" \
-d '{"text":"Merhaba"}'Tüm liste uçlarında limit en çok 100'dür. cursor/nextCursor ile sayfalayın; since verirseniz yalnız o andan sonra değişenler gelir.
https://otopilot.net/api/integrations/v1
Sürüm notları
Entegrasyonunuz yazıldıktan sonra eklenen her yenilik burada sürümüyle durur. "Yapmanız gereken" boş değilse o yenilikten yararlanmak için kodunuza ekleme gerekir; eklemezseniz mevcut akış bozulmaz.
| Sürüm | Tarih | Yenilik | Yapmanız gereken |
|---|---|---|---|
1.0.0 | 2026-10-26 | İlk sürüm: handshake, status, rules (cursor/since, removedIds), applied (urls ile yazı adresi), MCP sunucusu ve modül uçları için izin/modül kapısı. | Her isteğe kodunuzun yazıldığı sürümü X-SP-Api-Version başlığıyla ekleyin; blog yazısı ve yeni sayfa (create_page) için applied bildirimine urls ekleyin. |
1.1.0 | 2026-10-27 | Bot ziyaretleri ucu (POST /bot-visits/{projectId}) yayında; /me sitede gerçekten açık modülleri döner; modül uçlarında modül kapısı (modül yokken 403 INTEGRATION_MODULE_INACTIVE); yazma isteklerinde Idempotency-Key. | — |
Kurulum kontrol listesi
Entegrasyonu bitirdiğinizi düşündüğünüzde her maddeyi kontrol edin. Biri eksikse düzeltmeler sitenize geç ulaşır ya da hiç ulaşmaz.
- API anahtarı: POST /handshake başarılı dönüyor.
- Sürüm başlığı: her istek X-SP-Api-Version taşıyor.
- İlk çekim: GET /rules/{projectId} ile gelen düzeltmeler veritabanınıza yazıldı ve sitede görünüyor.
- Zamanlanmış senkron: döngü en az 15 dakikada bir çalışıyor ve cursor kalıcı saklanıyor.
- Uygulandı bildirimi: uyguladığınız düzeltmeler için POST /rules/{projectId}/applied çağrılıyor; create_page için urls ile adres de gidiyor.
- removedIds: geri çekilen düzeltmeleri kendi tarafınızda geri alıyorsunuz.
- Panel: "Siten düzeltmeleri almıyor" uyarısı görünmüyor ve uygulanan düzeltmeler yeniden taramada Doğrulandı durumuna geçiyor.
CRM kurulum kontrol listesi
- GET /me başarılı dönüyor ve gerekli izinler scopes içinde görünüyor.
- Kullandığınız modül modules listesinde var; yoksa modül uçları 403 INTEGRATION_MODULE_INACTIVE dönecektir.
- POST ve PATCH isteklerinde Idempotency-Key gönderiyorsunuz.
- Liste uçlarını limit ve cursor ile sayfalıyorsunuz.