Webhook kataloğu
Çalışma alanında bir şey olduğunda SoliAgile senin adresine imzalı bir POST gönderir. Bu sayfa hangi olayların gönderildiğini, yükün nasıl göründüğünü ve imzayı nasıl doğrulayacağını anlatıyor.
Abone olunabilir olaylar
Uç nokta oluştururken bu olaylardan istediklerini seçersin; hiç seçmezsen hepsi gönderilir. Ayarlar → Webhook’lar’dan yönetilir.
| Olay | Ne zaman gönderilir |
|---|---|
issue.created | Yeni bir issue açıldığında. |
issue.updated | Issue’nun alanları değiştiğinde (başlık, açıklama, öncelik, tahmin, proje, döngü…). |
issue.deleted | Issue kalıcı olarak silindiğinde. Yük, silinme anındaki son hâli taşır. |
issue.assigned | Atanan kişi değiştiğinde — atama kaldırıldığında da gönderilir. |
issue.status_changed | Issue başka bir iş akışı durumuna geçtiğinde. |
comment.created | Bir issue’ya yorum yazıldığında. |
comment.deleted | Yorum silindiğinde. |
Teslimat geçmişinde webhook.test tipini de görebilirsin: “Test gönder” düğmesinin ürettiği teslimat. Abone olunabilir bir olay değildir; uç noktanın erişilebilirliğini ve imza doğrulamanı kanıtlamak için vardır.
Yük şekli
Her teslimat aynı zarfla gelir; olaya özgü kısım data alanındadır.
{
"id": "01JZ...", // webhook-id ile AYNI — dedup anahtarı
"type": "issue.status_changed",
"occurredAt": "2026-07-31T09:14:22.031Z",
"tenant": { "id": "…", "slug": "acme" },
"actor": { "id": "…", "displayName": "Ada Lovelace" },
"data": { "issue": { "identifier": "ACM-142", "title": "…", "stateId": "…" } }
}- webhook-id başlığı zarfın id alanıyla aynıdır — tekrarları bununla ayıkla.
- Sıra garantisi yoktur: iki olay farklı sırada ulaşabilir. Olayın gerçekleştiği an occurredAt alanındadır.
- data olay anının fotoğrafıdır; kaynak kayıt sonradan değişse bile güncellenmez.
- Başarısız teslimatlar artan aralıklarla yeniden denenir. 2xx dışındaki her yanıt başarısız sayılır, bu yüzden uç noktan işi kuyruğa alıp hemen 2xx dönmeli.
İmza doğrulama
Her istek üç başlık taşır. İmza, Standard Webhooks (svix) konvansiyonunu izler.
webhook-id | Teslimatın benzersiz kimliği — tekrarları ayıklamak için de kullanılır. |
webhook-timestamp | Gönderim anı (Unix saniye). Çok eski istekleri reddet: tekrar saldırılarına karşı ilk savunma. |
webhook-signature | Boşlukla ayrılmış bir veya daha çok `v1,<base64>` adayı. Sır döndürüldüğünde geçiş süresince iki aday birlikte gelir. |
SDK ile (önerilen)
@vennyx/soliagile-sdk doğrulamayı hazır getirir: birden çok imza adayını, sabit zamanlı karşılaştırmayı ve zaman kayması kontrolünü kendisi yapar.
import { verifyWebhookSignature } from '@vennyx/soliagile-sdk';
const result = await verifyWebhookSignature({
secret: process.env.SOLIAGILE_WEBHOOK_SECRET,
headers: request.headers,
body: rawBody, // HAM gövde — JSON.parse ETMEDEN
});
if (!result.valid) {
// 'missing-headers' | 'malformed-secret' | 'stale-timestamp' | 'signature-mismatch'
return new Response(result.reason, { status: result.reason === 'missing-headers' ? 400 : 401 });
}SDK olmadan
Başka bir dilde yazıyorsan hesabın tamamı şu kadar:
import { createHmac, timingSafeEqual } from 'node:crypto';
// Sır: "whsec_<base64>". Önek YALNIZ insan için — HMAC anahtarına GİRMEZ.
const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signed = `${id}.${timestamp}.${rawBody}`; // rawBody: HAM gövde (JSON.parse ETMEDEN)
const expected = 'v1,' + createHmac('sha256', key).update(signed).digest('base64');
// Header boşlukla ayrılmış BİRDEN ÇOK aday taşıyabilir (sır rotasyonu).
const ok = headers['webhook-signature']
.split(' ')
.some((candidate) => candidate.length === expected.length && timingSafeEqual(Buffer.from(candidate), Buffer.from(expected)));İmzayı HAM gövdeyle hesapla. JSON’u parse edip yeniden serileştirirsen anahtar sırası veya boşluklar değişir ve imza tutmaz — bu en sık yapılan hata.
Sonrası
Uç noktaları API’den de yönetebilirsin; SDK imza doğrulamasını hazır getirir.