Webhook bildirimleri

Proje olaylarını alın, ham gövde imzasını doğrulayın ve tekrar teslimleri yönetin.

Son güncelleme:

Bu sayfadaAbonelik 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.

  1. İstek gövdesini değiştirmeden alabilen bir HTTPS alıcısı hazırlayın.
  2. POST /api/webhooklar çağrısında ad, url, olaylar ve isteğe bağlı proje_ids gönderin.
  3. Oluşturma yanıtındaki sir değerini güvenli alana alın; sonraki listelemelerde sır gösterilmez.
  4. POST /api/webhooklar/{wid}/dene ile test teslimi oluşturun.
  5. Teslim listesinden sonucu inceleyin; yalnız teslim_id dö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-olay ve x-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.