Webhook’ları alma
Webhook, sisteminize Sursa’daki bir değişikliği gerçekleştiği anda bildirir: bir kart yayımlanır, doğrulanır ya da
arşivlenir, bir soru yanıtsız kalır. Sursa, olayı içeren bir HTTPS POST isteğini kaydettiğiniz adrese gönderir. Bu
kılavuz, yeniden denemelerde, sıranın değiştiği ve teslimlerin yinelendiği durumlarda doğru çalışan bir alıcı
oluşturur.
Her olayın ne anlama geldiği ve neler içerdiği için Webhook’lara genel bakış sayfasına ve API başvurusundaki olay kataloğuna bakın.
1. Webhook kaydedin
Bir yönetici webhook’ları Sursa’da Administration → Integrations → Webhooks and API (yönetim → entegrasyonlar →
webhook’lar ve API) bölümünde ya da admin rolüne ve webhooks:manage kapsamına sahip bir anahtarla
POST /v1/webhooks çağrısıyla kaydeder:
curl -X POST https://api.sursa.com/v1/webhooks \
-H "Authorization: Bearer $SURSA_ADMIN_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/sursa",
"description": "Sync verified cards to the help center",
"events": ["card.published", "card.updated", "card.verified", "card.archived"]
}'Yanıt, imzalama gizli değerini whsec_ ile başlayan secret alanında içerir. Bu değer bir kez gösterilir. Onu
alıcınızla birlikte, örneğin SURSA_WEBHOOK_SECRET ortam değişkeninde saklayın.
Adres https kullanmalı ve herkese açık IP adreslerine çözümlenmelidir: özel, geri döngü (loopback) ve diğer
ayrılmış ağlardaki adresler hem kayıt sırasında hem de her teslimde reddedilir. Kendi bilgisayarınızda geliştirme
yapmak için yerel alıcınızı, ona herkese açık bir HTTPS adresi veren bir tünel hizmetiyle dışarıya açın.
İstediğiniz zaman POST /v1/webhooks/{webhook_id}/test ile bir test olayı
gönderin. Yanıt, alıcınızın ne yanıt verdiğini gösterir.
2. Bir teslimin görünümü
POST /sursa HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
webhook-id: evt_0123456789abcdef0123456789abcdef
webhook-timestamp: 1791194400
webhook-signature: v1,l1LLl/aEl4dQq7Geb383KbyNeZgs7VZpiw6iuoJgunI=
X-KB-Signature: t=1791194400,v1=82aa6b27378d89d7d880d17c3fe96268598443ae0e64d059c0c41e94e5d3ffbe
X-KB-Event-Id: evt_0123456789abcdef0123456789abcdef
X-KB-Event: card.verified
X-KB-Delivery: 99812
X-KB-Attempt: 1
X-KB-Sequence: 1042
{"id":"evt_0123456789abcdef0123456789abcdef","type":"card.verified","created_at":"2026-10-05T10:00:00Z","org_id":1,"api_version":"1.60.0","sequence":1042,"object":{"type":"card","id":"c1"},"payload":"full","data":{"card":{"id":"c1"}},"test":false}
| Başlık | Anlamı |
|---|---|
webhook-id | Olayın kimliği. Her yeniden denemede aynıdır. Yinelenenleri ayıklamak için kullanın. |
webhook-timestamp | Bu denemenin imzalandığı zaman, Unix saniyesi olarak. |
webhook-signature | Standard Webhooks biçimindeki imza. |
X-KB-Signature | Aynı teslimin eski Sursa biçimindeki imzası. |
X-KB-Event | Olay türü. |
X-KB-Attempt | 1’den başlayan deneme numarası. |
X-KB-Sequence | Bu teslimin, webhook’un teslim sırasındaki konumu. |
Başlık adlarında büyük-küçük harf ayrımı yoktur; kullandığınız çatı (framework) bunları X-Kb-Signature ya da
x-kb-signature olarak gösterebilir.
Gövde olayın kendisidir. data alanı türe bağlıdır. thin veri yüküne ayarlanmış bir webhook olayı data olmadan
alır; bunun yerine nesneyi kendi anahtarınızla API üzerinden okuyun.
3. İmzayı doğrulayın
Güvenmeden önce her teslimin imzasını kontrol edin. İki biçim de gövdenin baytlarını olduğu gibi imzalar; bu yüzden herhangi bir JSON ayrıştırmasından önce ham gövdeyi okuyun: JSON’u yeniden serileştirmek baytları değiştirir ve denetimi bozar.
| Biçim | İmzalanan dize | Anahtar | İmza |
|---|---|---|---|
Standard Webhooks (webhook-signature) | <webhook-id>.<webhook-timestamp>.<body> | Gizli değerin whsec_ sonrasındaki kısmı, base64 ile çözülmüş hâli | v1, + HMAC-SHA256 değerinin base64 kodlaması |
Eski biçim (X-KB-Signature) | <t>.<body> | Gizli değerin tamamı, metin olarak | v1= + HMAC-SHA256 değerinin onaltılık gösterimi |
Herhangi birini doğrulamak yeterlidir. Standard Webhooks başlığını tercih edin: aynı denetim birçok hazır
kütüphanede bulunur. Gizli değer yenilenirken her başlık, geçerli her gizli değer için bir imza taşır
(webhook-signature içinde boşlukla ayrılmış, X-KB-Signature içinde yinelenen v1=). İmzalardan herhangi biri
eşleşirse teslim geçerlidir.
Ayrıca saatinizden 5 dakikadan fazla sapan bir zaman damgasını reddedin. Bu, birinin kaydedilmiş bir teslimi daha sonra yeniden göndermesini engeller.
# Check a saved delivery by hand: body.json holds the raw body, the values come from its headers.
SECRET="whsec_…"; WEBHOOK_ID="evt_…"; TIMESTAMP="1791194400"
# Standard Webhooks: compare with the value after "v1," in webhook-signature
KEY=$(printf '%s' "${SECRET#whsec_}" | tr '_-' '/+')
while [ $(( ${#KEY} % 4 )) -ne 0 ]; do KEY="$KEY="; done
KEY_HEX=$(printf '%s' "$KEY" | openssl base64 -d -A | xxd -p | tr -d '\n')
{ printf '%s.%s.' "$WEBHOOK_ID" "$TIMESTAMP"; cat body.json; } \
| openssl dgst -sha256 -mac HMAC -macopt "hexkey:$KEY_HEX" -binary | openssl base64 -A
# Earlier format: compare with the value after "v1=" in X-KB-Signature
{ printf '%s.' "$TIMESTAMP"; cat body.json; } | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1İmza doğrulama kodunuzu test edin
Kodunuzu gerçek teslimlere bağlamadan önce bu değerlerle çalıştırın. Saat zaman damgasına ayarlandığında iki denetim de başarılı olmalı, gövdenin bir baytı değiştiğinde ise başarısız olmalıdır.
| Girdi | Değer |
|---|---|
| Gizli değer | whsec_test_secret_0123456789abcdef |
webhook-id | evt_0123456789abcdef0123456789abcdef |
webhook-timestamp ve t | 1791194400 |
| Gövde | {"id":"evt_0123456789abcdef0123456789abcdef","type":"card.verified","created_at":"2026-10-05T10:00:00Z","org_id":1,"data":{"card":{"id":"c1"}}} |
webhook-signature | v1,+xxWSXH6weQJ/MVg4+N78a1Dj4y/AQ4p94+PRNzpECg= |
X-KB-Signature | t=1791194400,v1=ce26a19cb63cd3ae620217bf7688e1df1044fa7d59d9ae148d215f0292b768dc |
4. Hızlı yanıt verin, işi sonra yapın
Sursa yanıtınız için en fazla 10 saniye bekler. Herhangi bir 2xx durumu teslimin başarılı olduğu anlamına gelir;
yönlendirme, zaman aşımı ya da kapanan bağlantı dahil diğer her şey teslimin başarısız olduğu ve yeniden deneneceği
anlamına gelir.
Bu yüzden alıcı yalnızca doğrulamalı, olayı kaydetmeli ve yanıt vermelidir. Asıl işi daha sonra kendi kuyruğunuzdan yapın.
// Node.js 20+, no framework. verifyStandard and verifyLegacy come from step 3.
import http from "node:http";
const SECRET = process.env.SURSA_WEBHOOK_SECRET;
http
.createServer(async (req, res) => {
const parts = [];
for await (const part of req) parts.push(part);
const body = Buffer.concat(parts); // the raw bytes, before any JSON parsing
const valid = req.headers["webhook-signature"]
? verifyStandard(SECRET, req.headers, body)
: verifyLegacy(SECRET, req.headers["x-kb-signature"], body);
if (!valid) {
res.writeHead(400).end();
return;
}
const event = JSON.parse(body);
await saveEventOnce(event); // insert into your table or queue, keyed by event.id; ignore a duplicate
res.writeHead(204).end();
})
.listen(3000);2xx yanıtını yalnızca olay güvenle saklandıktan sonra verin. Önce yanıt verir ve ardından çökerseniz olay sizin için
kaybolur, çünkü Sursa onu zaten teslim edilmiş saymıştır.
5. Yeniden denemeler
Başarısız bir teslim giderek uzayan aralarla yeniden denenir: yaklaşık 1 dakika, 5 dakika, 30 dakika, 2 saat, 5 saat, 10 saat, 24 saat ve 24 saat sonra; yaklaşık üç güne yayılan dokuz deneme. Birçok webhook’tan gelen yeniden denemeler aynı anda gelmesin diye her ara %20’ye kadar değişir.
Retry-Afteriçeren bir429ya da503yanıtı, sonraki denemeyi en az o süre kadar, en fazla 24 saat sonrasına erteler. Sisteminiz aşırı yüklendiğinde ya da bakımdayken bunu kullanın.410 Goneyanıtı webhook’u devre dışı bırakır: bunu yalnızca o adrese artık teslim istemediğinizde gönderin.- Art arda 10 başarısız denemeden sonra webhook duraklatılır ve kuruluşun yöneticilerine bildirilir. Olaylar
birikmeye devam eder ve webhook yönetim arayüzünde ya da
POST /v1/webhooks/{webhook_id}/resumeile yeniden etkinleştirildiğinde sırayla teslim edilir. - Son denemeden sonra teslim
exhaustedolarak işaretlenir. Onu elle yeniden gönderebilirsiniz.
Bir webhook’un teslimleri; her deneme, yanıt kodunuz ve süresiyle birlikte
GET /v1/webhooks/{webhook_id}/deliveries tarafından ve yönetim
arayüzünde listelenir.
POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver bir
teslimi yeniden gönderir. Yeniden teslimin webhook-id değeri aynıdır.
6. Sıra ve yinelenenler
- Sıra. Bir webhook’a yapılan teslimler
sequencesırasına göre kesin olarak birbiri ardına gider. Bir teslim yeniden denenirken sonrakiler bekler. Farklı webhook’lar birbirini beklemez. - Yinelenenler. Aynı olay birden fazla kez gelebilir; örneğin yanıtınız geri dönerken kaybolduğunda. İşlediğiniz
her olayın
webhook-iddeğerini (gövdedekiidde aynıdır) saklayın ve daha önce gördüğünüz bir olayı atlayın. - Durumlarla düşünme. Aynı nesnedeki değişiklikler için
created_atya dasequencedeğerini sakladığınız değerle karşılaştırın ve elinizdeki durumdan eski bir olayı yok sayın. Emin olmadığınızda nesnenin güncel hâlini API üzerinden okuyun: olay size bir şeyin değiştiğini söyler, API ise şu anki hâlini.
7. Olay beslemesiyle mutabakat yapın
Her olay, ona abone olan bir webhook olsun ya da olmasın, olay beslemesinde de 30 gün saklanır. Alıcınız yeniden
denemelerin kapsadığından daha uzun süre çalışmadıysa ya da hiçbir şeyin kaçırılmadığını kontrol etmek istiyorsanız
beslemeyi admin rolüne ve webhooks:manage kapsamına sahip bir anahtarla okuyun:
curl -G https://api.sursa.com/v1/webhook-events \
-H "Authorization: Bearer $SURSA_API_KEY" \
--data-urlencode "type=card.*" \
--data-urlencode "created_since=2026-10-20T00:00:00Z" \
--data-urlencode "limit=200"Beslemedeki bir olay, teslimdekiyle aynı id değerine ve biçime sahiptir; yalnızca sequence, payload ve test
teslim alanları yoktur. Alıcınız daha önce gördüğü olayları atladığı için mutabakatı istediğiniz sıklıkta, örneğin
saatte bir çalıştırabilirsiniz.
GET /v1/webhook-events/{event_id} tek bir olayı kimliğiyle okur.
8. Gizli değeri yenileyin
POST /v1/webhooks/{webhook_id}/rotate-secret yeni bir gizli değer
oluşturur ve öncekini, varsayılan olarak 24 saat, en fazla 7 gün süren bir örtüşme süresi boyunca geçerli tutar.
Örtüşme süresince her teslim iki gizli değerle de imzalanır.
- Gizli değeri yenileyin ve yeni gizli değeri alıcınızda eskisinin yanına kaydedin.
- Bir teslimi, iki gizli değerden biriyle doğrulanıyorsa kabul edin.
- Örtüşme süresi bittikten sonra eski gizli değeri kaldırın.
Eski gizli değer sızdıysa beklemek yerine örtüşme süresini hemen sonlandırın:
POST /v1/webhooks/{webhook_id}/revoke-previous-secret ile ya da
gizli değeri "overlap_hours": 0 ile yenileyerek.
Kontrol listesi
- İmzalardan birini ham gövde üzerinde doğrulayın ve zaman damgasını kontrol edin.
- Olayı
webhook-idanahtarıyla saklayın, ardından 10 saniye içinde2xxyanıtı verin. - Yinelenen ve eskimiş olayları atlayın; önemli olduğunda nesnenin güncel hâlini API üzerinden okuyun.
- Sursa’nın teslimleri yavaşlatmasını istediğinizde
Retry-Afterile429ya da503yanıtı verin; hiçbir zaman yanlışlıkla410yanıtı vermeyin. - Bilmediğiniz olay türlerini ve alanları atlayın.
- Olay beslemesiyle düzenli aralıklarla mutabakat yapın.