Webhook'lar

jekcms dış araçlarla iki yönde konuşur ve iki yön birbirinden tamamen ayrı yerlerde yapılandırılır.

Giden Webhook'lar formu
Bir webhook bir ad, bir https adresi ve bir olay kümesinden ibarettir - başka ayar yok.

Gelen yön otomasyon API'sidir: dış bir iş akışı bitmiş bir makaleyi, bir görseli ya da bir durum sorgusunu sitenize POST'lar, jekcms de gereğini yapar. Önceden kaydedilecek bir şey yoktur - API anahtarı oluşturur ve uçları çağırmaya başlarsınız.

Giden yön bildirim tarafıdır: Yönetim → API Anahtarları altında bir adres kaydedersiniz, ilgilendiğiniz olayları seçersiniz ve bunlardan biri gerçekleştiğinde jekcms o adrese imzalı bir JSON gövdesi POST'lar.

İkisi de panelde aynı ekranda yaşar - admin/api-keys.php - çünkü ikisi de bir kimlik bilgisiyle başlar.

Gelen yön: webhook uçları

Her çağrı, JSON gövdeyle https://siteniz.com/api/v1/webhook/{eylem} adresine bir POST'tur.

İçerik eylemleri şunlar: publish (oluştur ve hemen yayınla), schedule (ileri tarihli yayınla oluştur), draft (taslak oluştur), update ve delete. Medya ya media (bir adresten indir) ya da media-base64 (ham veri - bir iş akışının az önce ürettiği görseli böyle devreder) ile gelir. Toplu işler için bulk-publish ve bulk-import var.

Geri kalan dört uç, bir iş akışını yeniden koşturmayı güvenli kılar. check-source bir kaynak adresin daha önce içe aktarılıp aktarılmadığını söyler. status bir yazının güncel durumunu döner. ai-enhance mevcut içeriğin AI meta alanlarını doldurur. test ise veritabanına hiç dokunmadan n8n webhook connection successful yanıtını veren bir bağlantı yoklamasıdır.

Bu dört uç, Toplu İçerik Planla ile oluşturduğunuz bir grubu yürütmek için var. queue-pending zamanı gelmiş content_generation görevlerini dağıtır ve işleniyor diye işaretler. content-generate bitmiş makaleyi geri gönderip görevi kapatır. queue-complete ile queue-fail de iş akışınız bittiğinde ya da pes ettiğinde sonucu bildirir. pinterest-feed sıra dışı olanı: son yayınlanan yazıların pin'e hazır başlık, açıklama ve etiketleriyle birlikte salt okunur bir JSON listesi - sizin adınıza pin atan araçlar için. Kendisi Pinterest'i çağırmaz.

Tanınmayan bir eylem, hata mesajında geçerli eylemlerin tam listesiyle birlikte 400 döner; yani yazım hatası kendi kendini teşhis eder.

Kimlik doğrulama

API anahtarı yeterlidir ve kullanmanız gereken de odur. Yönetim → API Anahtarları altında bir tane oluşturun ve Authorization: Bearer <anahtar> olarak gönderin. X-API-Key ile Api-Key de kabul edilir. Sebebi şu: Apache ve CGI kurulumları Authorization başlığını her zaman PHP'ye geçirmiyor, jekcms de başlığın düşmüş olabileceği her yere bakıyor.

Anahtarlar SHA-256 özeti olarak saklanır; yani sızan bir veritabanı kimseye çalışan kimlik bilgisi vermez. Anahtarın kendine ait bir yetki kapsamı yoktur: hangi kullanıcı için üretildiyse o kullanıcı gibi, tam olarak o rolün yetkileriyle davranır. Rolleri planlamadan önce bilinmesi gereken bir şey var: Yönetim → API Anahtarları ekranı yalnız yöneticiye açıktır ve ürettiği her anahtarı o an giriş yapmış yöneticinin üstüne yazar. Kullanıcı seçici yoktur; yani panelden daha düşük yetkili bir anahtar üretemezsiniz. Ürettiğiniz her anahtara yönetici kimliği gibi davranın. İçerik oluşturan, değiştiren ve silen yazma eylemleri ayrıca publish_posts yetkisi ister; abone düzeyindeki bir anahtar doğrudan reddedilir.

Kodda ikinci bir mekanizma daha var ve bunu net söylemek gerekiyor, çünkü ilk okuyuşta yanıltıyor. handleWebhook() bir HMAC imzasını doğruluyor - X-Webhook-Signature ya da X-N8N-Signature, ham gövdenin N8N_WEBHOOK_SECRET ile anahtarlanmış HMAC-SHA256 değeri - ama yalnız istek kimliği doğrulanmamış bir kullanıcıyla geldiyse. Kimlik doğrulama her istekte önce koşuyor ve kimliksiz isteği webhook işleyicisine hiç girmeden 401 ile reddediyor; yani o dala bugün ulaşılamıyor. İmza, API anahtarının yerine geçmez. Anahtarı gönderin; araçlarınız istiyorsa ayrıca imzalayın, ama yalnız imzaya dayanan bir akış kurmayın.

$body = json_encode(['title' => 'Hello', 'content' => '...']);

$ch = curl_init('https://yoursite.com/api/v1/webhook/draft');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $body,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . getenv('JEKCMS_API_KEY'),
    ],
]);
echo curl_exec($ch);
const body = JSON.stringify({ title: 'Hello', content: '...' });

await fetch('https://yoursite.com/api/v1/webhook/draft', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.JEKCMS_API_KEY}`,
    },
    body,
});
import json, os, requests

body = json.dumps({'title': 'Hello', 'content': '...'})

requests.post(
    'https://yoursite.com/api/v1/webhook/draft',
    data=body,
    headers={
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + os.environ['JEKCMS_API_KEY'],
    },
)

Gövdeyi, seri hâle getirdiğiniz baytların aynısı olarak gönderin. Bir kez serileştirin ve o dizeyi iletin; arada yeniden kodlamak, "terminalimde çalışıyor ama akışımda çalışmıyor" bildirimlerinin neredeyse tamamının sebebidir.

Ne dönüyor

Başarılı bir içerik çağrısı, oluşturulan yazının kimliğini, kısa adını ve adresini döner. İş akışınızı iki hata yanıtına göre tasarlamakta fayda var.

422 ve {"success": false, "error": "quality_gate", "blocks": [...]} içerik kalite kapısının yayına izin vermediği anlamına gelir. Her blok bir kod taşır - short_content, short_title, duplicate_title, similar_content, no_h2, no_featured_image, no_internal_link, broken_local_image ve birkaçı daha - ve yanında iki dilde insan mesajı gelir. Bu bir iletim hatası değildir: aynı gövdeyi tekrar göndermek aynı sonucu verir. İçeriği düzeltin ya da taslak olarak gönderin.

409, o başlıkta ya da kısa adda bir yazının zaten bulunduğunu söyler. Aynı iş akışını ikinci kez koşturduğunuzda her şeyin kopyalanmamasını bu sağlıyor.

İstekler ayrıca IP başına kayan bir saat penceresinde sınırlanır; yani kontrolden çıkmış bir döngü veritabanınıza değil bir duvara çarpar.

İnsanı döngüde tutmak

Hedeflediğiniz uç, editöryel politikanın kendisidir. draft ya da schedule gönderirseniz içerik panele normal bir taslak olarak düşer; bir editör yayınlayana kadar hiçbir şey herkese açık olmaz ve kalite kapısı o anda çalışır. publish ya da content-generate gönderirseniz çağrı döner dönmez yayına girer.

content-generate her zaman yayınlar; taslak kipi yoktur, çünkü bir kuyruk görevini kapatmak için vardır. Toplu bir hatta inceleme adımı istiyorsanız son düğümü draft'a yöneltin ve kuyruk görevini queue-complete ile ayrıca kapatın.

Yeni bir iş akışında aklıselim yöntem şudur: ilk birkaç düzine öğeyi draft üzerinden koşturun, çıktıları okuyun ve gözetimsiz üretimin sağlam durduğunu gördükten sonra son düğümü publish'e çevirin. Otomatik taslaklar yanlış, tekrarlı ya da yüzeysel olabilir; okumadan yayınlamak arama görünürlüğüne ve okur güvenine mal olur.

Giden yön: kendi araçlarınıza haber vermek

Yönetim → API Anahtarları ekranını açın ve Giden Webhook'lar bölümüne inin. Bir ad, https:// ile başlayan bir adres ekleyin ve istediğiniz olayları işaretleyin: yazı yayınlandı, yazı güncellendi, yazı silindi, yorum geldi, üye kaydı. Hiçbirini işaretlemezseniz her olay gönderilir.

Kaydedildiğinde imza anahtarı bir kez gösterilir. Hemen kopyalayın; şifreli saklanır ve bir daha görüntülenmez. Sunucuda şifreleme kullanılamıyorsa webhook, düz metin sırla oluşturulmak yerine reddedilir.

Her teslimat şu biçimde bir POST'tur:

{
  "event": "post_published",
  "site": "https://siteniz.com",
  "timestamp": "2026-09-13T10:15:00+03:00",
  "data": {
    "id": 123,
    "title": "…",
    "slug": "…",
    "status": "published",
    "url": "https://siteniz.com/…"
  }
}

ve şu başlıklarla gelir:

Content-Type: application/json
User-Agent: jekcms-webhook/1.0
X-Jek-Event: post_published
X-Jek-Signature: sha256=<gövdenin imza anahtarıyla HMAC-SHA256'sı>

Kendi tarafınızda doğrulayın: ham gövdeyi imza sırınızla HMAC'leyin, sonucun başına sha256= koyun ve sabit zamanda karşılaştırın. Karşılaştırmayı aldığınız baytlar üzerinde yapın - ayrıştırmadan ve yeniden kodlamadan önce.

data bloğu olaya göre değişir. Yorum olayında id, post_id, author_name ve status gelir; üye kaydında id, name ve email gelir ve olay yalnız e-posta ilk kez doğrulandığında fırlar, her girişte değil.

Teslimat davranışı

Teslimatlar hiçbir sayfayı yavaşlatmaz. İstek sırasında kuyruğa alınır, yanıt ziyaretçiye teslim edildikten sonra boşaltılır. Her isteğe dört saniyelik zaman aşımı verilir; boşaltmanın tamamı istek başına on iki saniye ve yirmi teslimatla sınırlıdır - bunun ötesi sessizce düşmez, deferred olarak kaydedilir ve elle yeniden gönderilebilir.

Her deneme kaydedilir. API Anahtarları ekranında bir webhook satırını açtığınızda son teslimatları HTTP durum koduyla birlikte görürsünüz; herhangi birinde Yeniden gönder düğmesi saklanan gövdenin aynısını tekrar yollar. Webhook başına son yüz teslimat tutulur, eskiler otomatik budanır. Test düğmesi örnek bir gövde göndererek, gerçek bir olay hiç oluşmadan adresi doğrulamanızı sağlar.

Başarısızlıkta otomatik yeniden deneme yoktur. Uç noktanızdan dönen bir 500 kaydedilir ve yeniden göndermek size bırakılır. Bu bilinçli bir karar: bozuk bir adrese yeniden deneme fırtınası açmak, listede duran kırmızı bir satırdan daha kötüdür.

Yeniliklerden ilk sen haberdar ol

Yeni özellikler, sürüm notları ve CMS rehberleri. Ayda birkaç e-posta gönderiyoruz.