GitHub Webhook'ları ile Yapay Zeka Kodlama Ajanları Nasıl Tetiklenir?
GitHub webhook'larını kullanarak yapay zekâ kodlama ajanlarını tetiklemek için bir depo (repository) webhook'unu kontrolünüzdeki bir giriş uç noktasına (intake endpoint) yönlendirin, X-Hub-Signature-256 üstbilgisini ham gövde (raw body) üzerinden doğrulayın, GitHub'ın 10 saniyelik zaman aşımı süresi içinde 202 yanıtı verin ve olayı bir kuyruğa ekleyin. Ardından bir arka plan işçisi (worker) olayı filtreler, bunu ilgili ileti dizisiyle (depo ve PR numarası) eşleştirir ve ajanı başlatır ya da besler.
Bu rehberin geri kalanında her adımı kod örnekleriyle inceleyecek ve kodlama ajanınızın uç noktayı Apidog CLI ile nasıl test edebileceğini göstereceğiz.
Yapay zekâ kodlama ajanları yoklama (polling) yerine neden GitHub webhook'ları ile tetiklenmeli?
Webhook tetikleyicisi, depoda bir şey gerçekleştiğinde (bir yorum, bir inceleme veya tamamlanan bir iş akışı) GitHub'ın sunucunuza yaptığı bir HTTP çağrısıdır. Ajanınız yeni bir teslimat (delivery) gelene kadar uyku modunda kalır; böylece hiçbir hareketlilik yokken sıfır token harcar, sıfır API çağrısı yapar ve bir sonraki yoklamayı (poll) beklemek yerine saniyeler içinde tepki verir.
Her dakika yoklama yapmak (polling), boş yanıtlar yüzünden hız sınırlarını (rate limit) tüketir ve buna rağmen bir dakikaya varan gecikmelere yol açar. Konunun ayrıntılı açıklamasına webhooks vs polling yazımızdan ulaşabilirsiniz.
DeepSeek Harness ekibi de 1 Kasım 2026'da yayımladıkları "Exploring Super-agent Design" başlıklı makalelerinde aynı argümanı öne sürdü. Bu girdileri Tetikleyici (Trigger) olarak adlandırıyorlar: ajan olayları dinler ve bunun dışında sessiz kalır. Makale, kullanıma sunulmuş bir DeepSeek özelliğini değil, bir mimariyi ve prototip bir eklentiyi anlatıyor. Tasarımın tamamını how to build a super-agent yazımızda ayrıntılarıyla ele almıştık; bu yazıda ise herhangi bir kodlama ajanı için yalnızca tetikleyici katmanını oluşturuyoruz.
Bir ajanı hangi GitHub webhook olayları başlatmalı?
Yalnızca bir ekip arkadaşınızın harekete geçmesini bekleyeceğiniz olaylara abone olun. Her fazladan olay; doğrulamanız, saklamanız ve filtrelemeniz gereken ekstra bir teslimat demektir.
| Olay | İzlenecek eylem | Ajanın yaptığı işlem |
|---|---|---|
issue_comment | created | Bir issue veya PR tartışmasındaki yoruma yanıt verir (örneğin: “@bot fix the failing test”) |
pull_request_review_comment | created | Diff üzerindeki belirli bir satıra yapılan yoruma yanıt verir |
pull_request | opened, synchronize | Yeni bir PR'ı inceler veya yeni bir push sonrasında yeniden inceleme yapar |
workflow_run | conclusion: failure ile completed | Başarısız olan logları okur ve bir düzeltme önerir |
check_run | Başarısız bir conclusion ile completed | Yukarıdakiyle aynı, uygulamalar (GitHub Apps) tarafından bildirilen kontroller için geçerlidir |
GitHub webhook olayları referans kılavuzundaki iki ayrıntı sizi hata ayıklama (debugging) zahmetinden kurtarır. İlki, issue_comment olayı hem issue hem de pull request yorumlarında tetiklenir; PR yorumları bir issue.pull_request nesnesi içerir ve ikisini birbirinden bu şekilde ayırt edebilirsiniz. İkincisi, eylemi completed olan workflow_run, çalıştırma başarılı da olsa başarısız da olsa tetiklenir; bu nedenle workflow_run.conclusion değerini kendiniz kontrol etmelisiniz.
İstek yükleri (payload) en fazla 25 MB ile sınırlandırılmıştır; GitHub bundan daha büyük hiçbir şeyi teslim etmez.
Ham gövde üzerindeki X-Hub-Signature-256 imzasını doğrulama
Webhook için bir gizli anahtar (secret) belirlediğinizde GitHub, ham istek gövdesinin HMAC SHA-256 özetini alarak her teslimatı imzalar ve onaltılık (hex) özeti X-Hub-Signature-256: sha256=<digest> olarak gönderir. Uç noktanız bu özeti yeniden hesaplar ve ikisini sabit zamanda (constant-time) karşılaştırır.
GitHub'ın webhook teslimatlarını doğrulama rehberinden üç temel kural:
- Yeniden serileştirilmiş bir JSON nesnesini değil, ham baytları hash'leyin. JSON'ı ayrıştırıp (parse) tekrar dizeye dönüştürmek boşluk karakterlerini değiştirir ve eşleşmeyi bozar.
- Asla
==kullanmayın; Node'uncrypto.timingSafeEqualfonksiyonu gibi sabit zamanlı (constant-time) bir fonksiyonla karşılaştırın. -
X-Hub-Signature-256üstbilgisini kullanın. Daha eski olanX-Hub-Signatureüstbilgisi SHA-1 kullanır ve yalnızca eski entegrasyonlar için mevcuttur.
İşte Express ile Node'da hazırlanmış giriş servisi:
import express from "express"; import crypto from "node:crypto"; const app = express(); const [REDACTED CREDENTIAL]; const seen = new Set(); // use Redis or a DB table in production function validSignature(raw, header) { if (!header) return false; const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(raw).digest("hex"); const a = Buffer.from(expected); const b = Buffer.from(header); return a.length === b.length && crypto.timingSafeEqual(a, b); } app.post("/triggers/github", express.raw({ type: "application/json" }), (req, res) => { if (!validSignature(req.body, req.get("X-Hub-Signature-256"))) { return res.status(401).json({ error: "bad signature" }); } const deliveryId = req.get("X-GitHub-Delivery"); if (seen.has(deliveryId)) { return res.status(200).json({ duplicate: true, trigger_id: deliveryId }); } seen.add(deliveryId); const event = req.get("X-GitHub-Event"); const payload = JSON.parse(req.body); const prNumber = payload.pull_request?.number ?? payload.issue?.number; const threadId = `${payload.repository.full_name}#${prNumber}`; queue.push({ deliveryId, event, threadId, payload }); // your queue client res.status(202).json({ trigger_id: deliveryId, thread_id: threadId }); }); app.listen(3000); İşleyici (handler) yalnızca dört hafif işlem yürütür ve yavaş hiçbir şey yapmaz. Farklı sağlayıcıların imza şemaları için webhook signature verification başlıklı yazımıza göz atabilirsiniz.
10 saniye içinde 2xx yanıtı dönün ve ajan işini kuyruğa alın
GitHub'ın webhook en iyi uygulamaları rehberi, sunucunuzun 10 saniye içinde bir 2xx yanıtı vermesi gerektiğini belirtir. Bu sürenin aşılması durumunda GitHub bağlantıyı sonlandırır ve teslimatı başarısız olarak kaydeder.
Bir ajanın çalışması dakikalar sürer, bu nedenle bu işlem asla gelen isteğin içinde gerçekleştirilemez. Olayı bir kuyruğa (Redis, SQS, bir Postgres tablosu veya kalıcı herhangi bir yapı) ekleyin ve hemen 202 Accepted yanıtı dönün. Ayrı bir worker işlemi kuyruktan veriyi çeker, filtreleri çalıştırır ve ajanı başlatır.
Başarısızlıklar önemlidir: GitHub başarısız olan teslimatları otomatik olarak yeniden göndermez. Bu teslimatları 3 güne kadar manuel olarak veya API aracılığıyla yeniden gönderebilirsiniz; dolayısıyla yavaş çalışan bir işleyici, birisi fark etmediği sürece o yoruma ait tetikleyiciyi sessizce kaybeder.
İki aşamalı filtreleme: Kodla hızlı yol, modelle yavaş yol
Gelen teslimatların çoğunun bir modele asla ulaşmaması gerekir. DeepSeek makalesi filtrelemeyi ikiye ayırır: kodla yazılmış bir hızlı yol (fast path) ve bir LLM'in prompt olarak yazılmış kuralları uyguladığı yavaş yol (slow path). Bu ayrım, maliyetlerin depo gürültüsü yerine gerçek iş yüküne bağlı kalmasını sağlar.
Hızlı yol (kod, mikrosaniyeler içinde çalışır):
- Depo izin listesi: Yalnızca açıkça dahil ettiğiniz depolar.
- Gönderen kuralları: Ajanın kendi bot hesabını yoksayın; aksi halde kendi kendine yanıt verdiği sonsuz bir döngü oluşturursunuz.
- Bahsetme modu: Yalnızca yorumda
@your-botgeçtiğinde çalıştırmayı başlatın. Makale, benimsenmeye değer üç mod listeliyor: yalnızca açık taleplere yanıt ver, yavaş yol için aday olarak değerlendir veya yoksay.
Yavaş yol (LLM, ilk aşamayı geçenler üzerinde çalışır):
Modele yorum metnini ve kısa bir kural istemi (prompt) verin; örneğin: “Bu, bu PR'daki kodu değiştirme talebi midir? Evet veya hayır ve tek satırlık bir gerekçe ile yanıtlayın.” Yalnızca “evet” yanıtı ajanı başlatır. Bu yaklaşım, bahsetme kuralını geçen ancak herhangi bir işlem gerektirmeyen “teşekkürler @bot, harika görünüyor” gibi yorumları yakalar.
function fastPath({ event, payload }) { if (payload.sender?.type === "Bot") return "ignore"; if (!ALLOWED_REPOS.has(payload.repository.full_name)) return "ignore"; if (event === "workflow_run") return payload.workflow_run.conclusion === "failure" ? "run" : "ignore"; const body = payload.comment?.body ?? ""; return body.includes("@your-bot") ? "candidate" : "ignore"; } Aynı ajanın yanıt vermesi için devam mesajlarını ileti dizisi kimliğine (thread_id) göre yönlendirin
İleti dizisi (thread), tek bir PR üzerindeki ardışık olaylar zinciridir. Yukarıdaki işleyicinin acme/api#42 örneğinde yaptığı gibi, thread_id değerini depo adı ile PR veya issue numarasından oluşturun. Makaledeki tasarımda, ilk olay için oluşturulan işçi (worker) bu ileti dizisine abone olur ve devamındaki olaylar başka bir yönlendirme çağrısına gerek kalmadan doğrudan bu işçiye iletilir.
Pratikte bu, thread_id değerinden ajan oturum kimliğine (session id) eşleme yapan bir arama tablosudur (lookup table). Aktif oturumu olan bir ileti dizisi için teslimat geldiğinde, yorumu o oturuma ekleyin. Böylece ajan bağlamını (okuduğu diff, çalıştırdığı test) korur. Bu yapı olmadan, tek bir PR'daki üç ayrı yorum birbiriyle çelişen üç farklı ajanın başlatılmasına neden olur.
Makale, örnek alınmaya değer bir ayrıntı daha ekliyor: Yönlendirici henüz işlem yaparken gelen tetikleyiciler bir sonraki çağrıda toplu olarak (batch) işlenir; böylece art arda gelen beş yorum beş ayrı karar yerine tek bir karar maliyeti yaratır.
X-GitHub-Delivery ile tekilleştirme
Her teslimat bir X-GitHub-Delivery GUID değeri taşır. GitHub belgeleri, yeniden iletilen teslimatların orijinal teslimatla aynı değeri kullandığını belirtir; bu da onu mükemmel bir benzersizlik anahtarı (idempotency key) yapar. Her kimliği saklayın (veritabanınızdaki benzersiz bir dizin işe yarar) ve tekrarlanan istekleri yine de 2xx döndüren etkisiz bir işlem (no-op) olarak ele alın, böylece GitHub bunu başarısız olarak işaretlemez.
Bu yöntem ayrıca yeniden oynatma saldırılarını (replay attacks) da engeller: Ele geçirilen bir teslimatın imzası geçerli görünse bile kimliği zaten saklanmış durumdadır.
Tetikleyici uç noktasını kodlama ajanınızın Apidog CLI ile test etmesini sağlayın
İmza hesaplama mantığını manuel olarak test etmemelisiniz. Giriş servisini yazan kodlama ajanının, testlerini de yazmasını ve Apidog CLI ile çalıştırmasını sağlayın. Kurulumu bir kez yapmanız yeterlidir:
npm install -g apidog-cli@latest apidog auth login --with-token <YOUR_TOKEN> npx -y skills add https://apidog.com # pick your agent when prompted Ardından Claude Code, Codex veya Cursor'a tek bir komut verin:
In Apidog project <projectId>, create the endpoint POST /triggers/github from the sample issue_comment payload in fixtures/issue_comment.json. Then create test cases against the staging environment: 1. valid X-Hub-Signature-256 -> 202 with trigger_id and a thread_id of "<repo>#<number>" 2. a second comment on the same PR -> same thread_id 3. bad signature -> 401 4. same X-GitHub-Delivery sent twice -> second call returns duplicate: true, no new job Sign the bodies with the staging webhook secret in a pre-request script. Run them and summarize any failures. Ajan her şemayı okur, veri yükünü doğrular, kaynakları oluşturur ve bunları çalıştırır:
apidog cli-schema get endpoint-create apidog cli-schema validate endpoint-create --file endpoint.json apidog endpoint create --project <projectId> --file endpoint.json apidog cli-schema get test-case-create apidog cli-schema validate test-case-create --file case-valid-signature.json apidog test-case create --project <projectId> --file case-valid-signature.json apidog run --test-case <caseId> --project <projectId> -e <stagingEnvId> -r cli,json cli raporlayıcısı her doğrulama (assertion) için başarılı veya başarısız durumunu yazdırır ve json raporu ./apidog-reports dizinine kaydedilir; böylece ajan hangi denetimin başarısız olduğunu görür (401 beklediğiniz yerde 200 dönmesi genellikle raw-body ara yazılımının eksik olduğu anlamına gelir).
Aynı apidog run komut satırı -r junit ile CI ortamına eklenebilir; bkz. Apidog CLI in GitHub Actions. Ajan kurulumu hakkında daha fazla bilgi için Apidog CLI in Claude Code yazısını, daha kapsamlı webhook test stratejileri için ise how to test webhooks rehberini okuyabilirsiniz. Apidog'un Ücretsiz planı 4 kullanıcıya kadar olan ekipleri kapsar.
Webhook'u etkinleştirmeden önce
- Gizli anahtar (secret) belirlendi; eksik veya hatalı imza 401 alır.
- 10 saniyenin oldukça altında 202 yanıtı dönülür, ajan işi kuyruğa alınır.
- Teslimat kimlikleri benzersizlik kısıtlamasıyla (unique constraint) saklanır.
- Son 3 güne ait başarısız teslimatları yeniden yürüten bir iş (job) yapılandırıldı.
GitHub'ın kendi çalıştırıcısını (runner) kullanmayı tercih ediyorsanız Claude Code GitHub Actions barındırılan (hosted) bir alternatiftir; webhook giriş katmanı ise filtreleme ve ileti dizisi yönlendirmesi kazandırır. Kendi tarafınızdaki yeniden denemeler için how to design reliable webhooks kılavuzuna göz atabilirsiniz.
SSS
Depo webhook'u mu yoksa GitHub App mi kullanmalıyım? Tek bir ekip için bir depo veya organizasyon webhook'u yeterlidir. Bir GitHub App ise ajana kendi bot kimliğini, daha ayrıntılı izinleri ve birçok organizasyona yüklenebilme imkânını sağlar.
Uç noktam çökerse ne olur? GitHub teslimatı başarısız olarak kaydeder ve yeniden denemez. Son 3 güne ait teslimatları webhook ayarlarından veya REST API aracılığıyla yeniden gönderebilirsiniz.
Ajanın kendi kendine yanıt vermesini nasıl engellerim? Başka hiçbir şey çalışmadan önce, hızlı yol filtresinde sender değeri bot hesabınız olan tüm teslimatları eleyin.
Neden 200 yerine 202 dönülmeli? Her ikisi de GitHub için başarılı kabul edilir. 202 Accepted, günlüklerinizi (logs) okuyan herkese işin tamamlanmadığını, kuyruğa alındığını belirtir.
DeepSeek süper ajan tasarımı hemen etkinleştirebileceğim bir şey mi? Hayır. Makale bir mimariyi anlatmaktadır ve prototip eklentisi resmi bir DeepSeek ürünü değildir. Tetikleyici katmanını yukarıda gösterildiği gibi kendiniz inşa edersiniz.


