Webhook

Bir çalışan okuttuğunda, bir okutma düzeltildiğinde ya da bir izin talep edilip onaylandığında Burdaa sisteminizin adresine anında imzalı bir bildirim gönderir. Ay sonunu ya da gece aktarımını beklemeden ERP veya İK sisteminiz güncel kalır.

Kurulum

Panelde Şirket Ayarları → Entegrasyonlar → Webhook bölümünden bir https adresi ekleyin. Eklerken bir imza anahtarı (whsec_…) bir kez gösterilir; sisteminiz gelen isteğin Burdaa'dan geldiğini bununla doğrular.

  • Şirket başına en fazla 5 adres. Yerel ve iç ağ adresleri kabul edilmez.
  • Panelden Test Gönder ile ping olayı gönderebilir, son gönderimlerin durumunu görebilirsiniz.
  • Her adres için hangi olayların gideceğini seçersiniz: Okutmalar ve/veya İzinler.
  • Adresi geçici olarak kapatabilir ya da silebilirsiniz.
  • Anahtarı Yenile ile yeni bir imza anahtarı alırsınız. Eski anahtar hemen geçersiz olur; sisteminizdeki anahtarı aynı anda güncelleyin.

Olaylar

OlayNe zaman
punch.createdGiriş ya da çıkış okutuldu (yöneticinin elle girdiği kayıt dahil)
punch.updatedYönetici okutmayı düzeltti; data.previous önceki değerleri taşır
punch.deletedOkutma silindi; silinen kaydın son hali gelir
leave.createdİzin talep edildi ya da yönetici izin girdi
leave.updatedİzin onaylandı, reddedildi ya da tarihi / türü değişti; data.previous önceki değerleri taşır
leave.deletedİzin silindi
pingPanelden gönderilen test

İstek gövdesi

Her olay adresinize JSON gövdeli bir POST olarak gelir. Olay türü X-Burdaa-Event, gönderim kimliği X-Burdaa-Delivery başlığındadır.

POST https://sizin-adresiniz
X-Burdaa-Event: punch.created
X-Burdaa-Delivery: 9b1e…
X-Burdaa-Signature: t=1760593202,v1=5f2b…

{
  "id": "6f1c…",
  "type": "punch.created",
  "created_at": "2026-10-16T05:40:02+00:00",
  "company_id": "…",
  "data": {
    "punch_id": "…",
    "employee_id": "…",
    "employee_code": "1042",
    "full_name": "Ayşe Yılmaz",
    "type": "check_in",
    "method": "qr_dynamic",
    "occurred_at": "2026-10-16T05:40:00+00:00",
    "location_id": "…",
    "location_name": "Kadıköy",
    "store_code": "M012"
  }
}
AlanTürAçıklama
iduuidOlay kimliği; tekrar denemelerde aynı kalır
typestringOlay türü
data.typestringcheck_in | check_out
data.methodstringqr_dynamic, qr_static (QR) ya da manual (yönetici girişi)
data.occurred_atdatetimeOkutma zamanı (UTC)
data.employee_codestring | nullSicil no; kendi sisteminizle eşleştirmek için
data.store_codestring | nullMağaza kodu
data.previousobjectYalnızca punch.updated: önceki type, occurred_at, location_id

İzin olaylarında data, /leaves uç noktasındaki alanları taşır:

leave.updated — data
{
  "leave_id": "…",
  "employee_id": "…",
  "employee_code": "1042",
  "full_name": "Ayşe Yılmaz",
  "type": "annual",
  "status": "approved",
  "start_date": "2026-11-03",
  "end_date": "2026-11-07",
  "days": 5,
  "deduct_from_pay": false,
  "previous": { "type": "annual", "status": "pending", "start_date": "2026-11-03", "end_date": "2026-11-07" }
}

Teslim ve tekrar deneme

  • Herhangi bir 2xx cevap "ulaştı" sayılır. Cevabı 10 saniye içinde verin; ağır işi kuyruğa alıp hemen dönün.
  • Diğer cevaplarda ya da zaman aşımında 1 dk, 5 dk, 15 dk, 1 sa, 3 sa, 6 sa ve 12 sa sonra tekrar denenir (toplam 8 deneme, yaklaşık 1 gün).
  • Aynı olay birden fazla gelebilir: gövdedeki id ile çift kaydı eleyin. Olayların sırası garanti değildir; sıralama için occurred_at kullanın.
  • Tüm denemeler biten gönderim panelde "Ulaşmadı" olarak görünür; Tekrar gönder ile baştan gönderebilirsiniz. Günlerce ulaşılamayan adres otomatik kapatılır.
  • Kaçırılan olayları /punches ve /leaves ile telafi edebilirsiniz.

İmza doğrulama

X-Burdaa-Signature başlığında t gönderim zamanı (unix saniye), v1 ise t + "." + ham gövde metninin imza anahtarıyla HMAC-SHA256 özetidir (hex). Gövdeyi JSON olarak ayrıştırmadan, geldiği haliyle doğrulayın; 5 dakikadan eski t değerlerini reddedin.

Node.js
import crypto from "node:crypto"

function verify(rawBody, header, secret) {
  const [, t, v1] = header.match(/^t=(\d+),v1=([0-9a-f]+)$/) ?? []
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
  return v1.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}
Python
import hmac, hashlib, re, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]+)", header)
    if not m or abs(time.time() - int(m[1])) > 300:
        return False
    expected = hmac.new(secret.encode(), m[1].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(m[2], expected)
C#
using System.Security.Cryptography;
using System.Text;

static bool Verify(string rawBody, string header, string secret)
{
    var parts = header.Split(',');
    var t = parts[0].Substring(2);
    var v1 = parts[1].Substring(3);
    if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(t)) > 300) return false;
    using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var expected = Convert.ToHexString(h.ComputeHash(Encoding.UTF8.GetBytes($"{t}.{rawBody}"))).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(v1), Encoding.UTF8.GetBytes(expected));
}