Webhook bildirimleri
Proje olaylarını alın, ham gövde imzasını doğrulayın ve tekrar teslimleri yönetin.
Son güncelleme:
Bu sayfada
Abonelik oluşturmaYönetim uçlarıOlay gövdesiOlay kataloğuİmza doğrulamaTeslim ve yeniden denemeSabit doğrulama vektörüYeniden deneme aralıklarıAbonelik oluşturma
Webhook yönetimi kurum yöneticisinin oturum çerezini kullanır. Erişim anahtarı yönetim oturumunun yerine geçmez. GIS_WEBHOOK=kapali olduğunda yönetim ve teslim kapalıdır.
- İstek gövdesini değiştirmeden alabilen bir HTTPS alıcısı hazırlayın.
POST /api/webhooklarçağrısındaad,url,olaylarve isteğe bağlıproje_idsgönderin.- Oluşturma yanıtındaki
sirdeğerini güvenli alana alın; sonraki listelemelerde sır gösterilmez. POST /api/webhooklar/{wid}/deneile test teslimi oluşturun.- Teslim listesinden sonucu inceleyin; yalnız
teslim_iddönmesi alıcının başarıyla teslim aldığını göstermez.
{
"ad": "Proje bildirimleri",
"url": "https://entegrasyon.example/webhook",
"olaylar": ["degisiklik", "tasarim", "aktarim"],
"proje_ids": [123]
}
Adres ve proje kimliği örnektir. proje_ids: null kurumun ana projelerini kapsar. Geçerli olaylar degisiklik, tasarim, aktarim; test tesliminin olay değeri dene olur. Tasarım dalındaki ham düzenleme olayları ayrı proje yayını gibi gönderilmez.
Yönetim uçları
| Yöntem ve uç | İşlev |
|---|---|
GET /api/webhooklar |
Abonelikleri listeler |
POST /api/webhooklar |
Abonelik oluşturur; 201 ve bir kez gösterilen sir |
PATCH /api/webhooklar/{wid} |
Ad, URL, olay, proje kapsamı veya aktif durumunu değiştirir |
DELETE /api/webhooklar/{wid} |
Aboneliği iptal eder; 204 |
POST /api/webhooklar/{wid}/sir |
Sırrı yeniler; yeni sir döner |
POST /api/webhooklar/{wid}/dene |
Test teslimini kuyruğa ekler |
GET /api/webhooklar/{wid}/teslimler |
items ve next ile teslim geçmişini sayfalar |
POST /api/webhooklar/{wid}/teslimler/{tid}/yeniden |
Teslimi yeniden beklemeye alır |
Teslim listesi after ve limit alır. Gövdeyi okumak için govde=1&limit=1 kullanılır. Sır yenileme önceki sırrı değiştirir; alıcı yapılandırmasını aynı geçişte güncelleyin.
Olay gövdesi
Bildirim olay, olay_id, zaman ve kurum_id taşır; proje olaylarında proje_id bulunur. Değişiklik bildirimi işlem grubu, değişiklik kimliği aralığı, sayaçlar ve ayrıntıları almak için api.changes yolu verir. Tasarım bildirimi tasarım kimliği ile durum değişikliğini; aktarım bildirimi aktarım kimliği, kaynak, hedef ve sayaçları taşır.
Gövde tam proje veya geometri kopyası değildir. Ayrıntıları alırken v1 API yetkilerini ayrıca uygulayın. Olay gövdesinin zaman alanı ile teslimin imza zamanı farklı kavramlardır.
Olay kataloğu
x-gis-olay |
Gövdeye eklenen alanlar | Anlamı |
|---|---|---|
degisiklik |
batch, kind, degisiklik, sayilar, kullanici_id, anahtar_id, api.changes |
Ana projedeki işlem grubunun değişiklik aralığı ve sayaçları. |
tasarim |
tasarim.id, tur, eylem, onceki, sonraki |
Tasarımın kayıtlı durum geçişi. Alanlar tasarim nesnesi içindedir. |
aktarim |
aktarim.import_id, kaynak_id, mod, hedef, sayilar |
Kaynak aktarımının hedefi ve sonucu. Alanlar aktarim nesnesi içindedir. |
dene |
Ortak olay alanları | Yönetim ekranından istenen test teslimi; proje verisi değiştiği anlamına gelmez. |
Proje bildirimlerinde kurum_id, proje_id, olay, olay_id ve zaman birlikte değerlendirilir. Bazı sayaçlar kısaltıldığında kesildi: true görülebilir. Ham nesne veya geometri yerine olayın kapsamı bildirilir.
İmza doğrulama
Her teslim aşağıdaki başlıkları taşır:
webhook-id: teslim boyunca korunan olay kimliği.webhook-timestamp: Unix saniyesi; her teslim denemesinde yenilenir.webhook-signature:v1,önekiyle Base64 HMAC-SHA256 imzası.x-gis-olayvex-gis-surum: olay türü ve uygulama sürümü.
Başlık ve imzalanan ileti biçimi Standard Webhooks düzenini izler. Aşağıdaki örnekler ek doğrulama paketi gerektirmeden aynı HMAC-SHA256 denetimini yapar. İmza anahtarı, whsec_ önekinden sonraki Base64 bölümünün çözülmesiyle alınır. İmzalanan baytlar webhook-id + "." + webhook-timestamp + "." + ham HTTP gövdesi birleşimidir. JSON’u ayrıştırıp yeniden yazmak imzayı bozabilir.
Hazır doğrulayıcı kullanmak için Standard Webhooks projesinin Python, JavaScript/TypeScript ve Go başvuru kütüphanelerini inceleyin. Ham gövdeyi, özgün başlıkları ve abonelik sırrını kütüphanenin doğrulama işlevine verin. Seçtiğiniz kütüphane sürümünü aşağıdaki test vektörüyle ve alıcınızın zaman politikasıyla doğrulayın.
Aşağıdaki örnekler imzayı sabit zamanlı karşılaştırır ve beş dakikalık zaman penceresi uygular. Zaman penceresi alıcı politikasına ait örnektir; sunucuda aynı pencerenin zorunlu olduğu anlamına gelmez.
import base64
import binascii
import hashlib
import hmac
import math
import re
import time
def webhook_dogrula(sir, basliklar, ham_govde, simdi=None):
h = {k.lower(): v for k, v in basliklar.items()}
olay_id = h.get("webhook-id", "")
damga = h.get("webhook-timestamp", "")
imzalar = h.get("webhook-signature", "").split()
if not isinstance(ham_govde, bytes) or not sir.startswith("whsec_"):
raise ValueError("Webhook girdisi geçersiz")
if not re.fullmatch(r"[A-Za-z0-9_-]{1,100}", olay_id):
raise ValueError("Olay kimliği geçersiz")
if not re.fullmatch(r"[0-9]{1,12}", damga):
raise ValueError("Zaman damgası geçersiz")
simdi = time.time() if simdi is None else simdi
if not isinstance(simdi, (int, float)) or not math.isfinite(simdi):
raise ValueError("Geçerli saat gerekli")
if abs(simdi - int(damga)) > 300:
raise ValueError("İmza zamanı kabul penceresi dışında")
try:
anahtar = base64.b64decode(sir[6:], validate=True)
except (ValueError, binascii.Error) as hata:
raise ValueError("Webhook sırrı geçersiz") from hata
if len(anahtar) != 32:
raise ValueError("Webhook sırrı geçersiz")
ileti = f"{olay_id}.{damga}.".encode("ascii") + ham_govde
beklenen = base64.b64encode(
hmac.new(anahtar, ileti, hashlib.sha256).digest()
).decode("ascii")
if not any(
parca.startswith("v1,") and hmac.compare_digest(parca[3:], beklenen)
for parca in imzalar
):
raise ValueError("Webhook imzası geçersiz")
return olay_id
import { createHmac, timingSafeEqual } from 'node:crypto';
export function webhookDogrula(sir, basliklar, hamGovde, simdi = Date.now() / 1000) {
const h = Object.fromEntries(Object.entries(basliklar).map(([k, v]) => [k.toLowerCase(), v]));
const id = h['webhook-id'] || '';
const damga = h['webhook-timestamp'] || '';
if (!Buffer.isBuffer(hamGovde) || !/^whsec_[A-Za-z0-9+/]{43}=$/.test(sir)) {
throw new Error('Webhook girdisi geçersiz');
}
if (!/^[A-Za-z0-9_-]{1,100}$/.test(id) || !/^[0-9]{1,12}$/.test(damga)) {
throw new Error('Webhook başlığı geçersiz');
}
if (!Number.isFinite(simdi) || Math.abs(simdi - Number(damga)) > 300) {
throw new Error('İmza zamanı kabul penceresi dışında');
}
const anahtar = Buffer.from(sir.slice(6), 'base64');
if (anahtar.length !== 32 || anahtar.toString('base64') !== sir.slice(6)) {
throw new Error('Webhook sırrı geçersiz');
}
const beklenen = createHmac('sha256', anahtar)
.update(`${id}.${damga}.`, 'ascii').update(hamGovde).digest();
const dogru = (h['webhook-signature'] || '').split(/\s+/).some(parca => {
if (!/^v1,[A-Za-z0-9+/]{43}=$/.test(parca)) return false;
const aday = Buffer.from(parca.slice(3), 'base64');
return aday.length === beklenen.length &&
aday.toString('base64') === parca.slice(3) && timingSafeEqual(aday, beklenen);
});
if (!dogru) throw new Error('Webhook imzası geçersiz');
return id;
}
package webhook
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"net/http"
"regexp"
"strconv"
"strings"
"time"
)
func Dogrula(sir string, baslik http.Header, hamGovde []byte, simdi time.Time) (string, error) {
id, damga := baslik.Get("webhook-id"), baslik.Get("webhook-timestamp")
if !regexp.MustCompile(`^[A-Za-z0-9_-]{1,100}$`).MatchString(id) ||
!regexp.MustCompile(`^[0-9]{1,12}$`).MatchString(damga) {
return "", errors.New("Webhook başlığı geçersiz")
}
if !strings.HasPrefix(sir, "whsec_") {
return "", errors.New("Webhook sırrı geçersiz")
}
anahtar, err := base64.StdEncoding.Strict().DecodeString(sir[6:])
if err != nil || len(anahtar) != 32 {
return "", errors.New("Webhook sırrı geçersiz")
}
saniye, err := strconv.ParseInt(damga, 10, 64)
if err != nil { return "", errors.New("Zaman damgası geçersiz") }
fark := simdi.Unix() - saniye
if fark < -300 || fark > 300 {
return "", errors.New("İmza zamanı kabul penceresi dışında")
}
mac := hmac.New(sha256.New, anahtar)
mac.Write([]byte(id + "." + damga + "."))
mac.Write(hamGovde)
beklenen := mac.Sum(nil)
for _, parca := range strings.Fields(baslik.Get("webhook-signature")) {
if !strings.HasPrefix(parca, "v1,") { continue }
aday, err := base64.StdEncoding.Strict().DecodeString(parca[3:])
if err == nil && hmac.Equal(aday, beklenen) { return id, nil }
}
return "", errors.New("Webhook imzası geçersiz")
}
Ham gövdeyi HTTP sunucunuzdan bayt olarak alın. İmzayı doğruladıktan sonra JSON’u çözün ve olay_id için kalıcı tekillik kontrolü yapın. İşleme alınan kimlik ile iş sonucunu aynı işlem içinde saklayın; aynı kimlik tekrar geldiğinde işi yeniden uygulamadan başarılı yanıt verin. Zaman kontrolü tek başına tekrar işlemeyi önlemez.
Teslim ve yeniden deneme
Başarı yalnız HTTP 2xx yanıtıdır. Başarısız teslimler artan aralıklarla yeniden denenir; aynı olay kimliği korunur, imza zamanı yenilenir. Kalıcı teslim durumları bekliyor, tamam, vazgecildi değerleridir. Sürekli başarısız abonelik askıya alınabilir; teslim geçmişini ve aboneliğin etkinliğini birlikte kontrol edin.
Alıcıda işlem tamamlanıp yanıt kaybolursa aynı olay tekrar gelebilir. Kesin olarak tek teslim varsaymayın. Yönlendirmeler izlenmez; hedef URL doğrudan alıcı olmalıdır. Hedef adresler kurulumun ağ kurallarına göre doğrulanır. Kurum içi kurulumda HTTP ancak açıkça etkinleştirildiğinde kullanılabilir; varsayılan HTTPS’tir.
Sabit doğrulama vektörü
Aşağıdaki sır yalnız açık test verisidir; gerçek abonelikte kullanılmaz. ham_govde metnini UTF-8 baytlarına çevirirken sonuna satır sonu eklemeyin. Testte saate bağlı sonuç çıkmaması için doğrulayıcıya simdi değerini verin; gerçek alıcıda güncel saati kullanın.
{
"sir": "whsec_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
"simdi": 1794301200,
"basliklar": {
"webhook-id": "msg_ornek_202611",
"webhook-timestamp": "1794301200",
"webhook-signature": "v1,y7FOL9NFVYbYF7S9s3xUMs1jV8lwLCPYltF293j/mrs="
},
"ham_govde": "{\"olay\":\"dene\",\"olay_id\":\"msg_ornek_202611\",\"kurum_id\":7,\"zaman\":\"2026-11-10T09:00:00+00:00\"}",
"beklenen": "msg_ornek_202611"
}
Geçerli girdide sonuç msg_ornek_202611 olmalıdır. Gövde, olay kimliği veya imza değiştirilirse doğrulama reddedilir. Kabul penceresinin dışındaki zaman da reddedilmelidir. Birden çok imza varsa desteklenen ve geçerli v1, girdisinin bulunması yeterlidir. Zaman penceresi ve tekillik kaydı ayrı kontrollerdir.
Yeniden deneme aralıkları
| Başarısız teslimden sonra | Sonraki denemeye temel bekleme |
|---|---|
| İlk hata | 5 saniye |
| İkinci hata | 5 dakika |
| Üçüncü hata | 30 dakika |
| Dördüncü hata | 2 saat |
| Beşinci hata | 5 saat |
| Altıncı hata | 10 saat |
| Yedinci hata | 24 saat |
| Sekizinci hata | vazgecildi durumu |
Temel beklemeye küçük rastgele sapma uygulanır; tablo kesin teslim zamanı veya hizmet süresi taahhüdü değildir. İşçi ve ağ koşulları gerçek deneme zamanını etkiler. Alıcı, doğrulanan olayı kalıcı kuyruğa aldıktan sonra 2xx döndürebilir; uzun süren işin tamamlanmasını HTTP bağlantısında bekletmeyin. Kuyruğa yazma başarısızsa başarı yanıtı vermeyin.
Abonelik sırrı değiştirildiğinde alıcı yapılandırmasını da yenileyin. Manuel yeniden denemeden önce teslim geçmişini, anahtar gerektiren veri okuması için v1 API sayfasını kullanın.