Docs

Webhook'lar

Son güncelleme: 2026-10-035 dk okuma

Webhook, bir şey olduğunda GREM'in URL'nize gönderdiği bir POST isteğidir: bir iş biter, bir ilan veya lead değişir. Abonelik yönetimi webhooks kapsamını gerektirir ve ücretsizdir. Bir ajansın webhook'ları ajans genelindeki anahtarla yönetilir; temsilciye bağlı bir anahtar 403 alır.

Abone olma

curl -X POST https://developers.grem.capital/api/v1/webhooks \
  -H "Authorization: Bearer gsk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/grem-hook","events":["content.completed","content.failed","lead.created"]}'
{ "id": "...", "url": "https://example.com/grem-hook", "events": [...], "status": "active",
  "failureCount": 0, "lastDeliveryAt": null, "secret": "whsec_..." }
  • url: varsayılan bağlantı noktasında (443) herkese açık bir https:// adresi, içinde user:password olmadan. Başka bağlantı noktaları, adreste kimlik bilgileri, özel, yerel ve iç adresler reddedilir (400). Bağlantı noktası kuralı bir adres belirlediğinizde uygulanır: daha önce başka bir bağlantı noktasıyla kaydedilmiş adresler teslim almaya devam eder.
  • events: aşağıdaki listeden en az bir olay; bilinmeyen adlar atılır.
  • secret: imzalama gizli anahtarı. Yalnızca bir kez döndürülür: saklayın.
  • Hesap başına en fazla 10 adres ve ayrıca test anahtarlarıyla oluşturulan en fazla 10 sandbox adresi; 11. adres 422 business_rule ile reddedilir.
  • Test anahtarıyla oluşturulan adresler sandbox adresleridir: yalnızca sandbox işlerinin olaylarını alırlar (Sandbox).

Abonelikler panelden de yönetilebilir: API → Webhook'lar. İmzalama gizli anahtarını da orada değiştirebilirsiniz.

Yönetim

İstekNe yapar
GET /webhooksabonelikleriniz (gizli anahtarlar olmadan)
PATCH /webhooks/{id}url, events veya status (active / disabled) değiştirme
DELETE /webhooks/{id}aboneliği silme
POST /webhooks/{id}/testdeneme olayı gönderme; {"eventType": "content.completed"} gövdesi biçimi belirler
GET /webhooks/{id}/deliveriesson 50 teslim: durum, yanıt kodu, gecikme, yanıtın başı
POST /webhooks/{id}/deliveries/{deliveryId}/resendteslimi yeniden gönderme; deliveryId, kayıttaki bir satırın _id değeridir

events içinde geçerli hiçbir olay olmayan veya status değeri active / disabled dışında olan PATCH 400 ile reddedilir. Bilinmeyen bir aboneliğin DELETE isteği 404 döndürür. Teslim kaydında payload, olayın data alanıdır.

Olaylar

Biten işler

OlayNe zaman
valuation.completed, valuation.failedtam değerleme raporu
review.completed, review.failedrapor incelemesi
content.completed, content.partial, content.failediçerik paketi (partial: bazı dosyalar başarısız)
text.completed, text.failedmetinler
video.completed, video.failedvideo
avatar.completed, avatar.failedavatar videosu
photo.completed, photo.failedfotoğraf iyileştirme
floorplan.completed, floorplan.failedkat planı
pdf.completed, pdf.failedPDF sunum

data: { "jobId", "status", "state" }. status, aracın büyük harfli ham durumudur (örneğin COMPLETED); karar için state kullanın. Bir iş 30 dakika içinde bitmezse "reason": "timeout" ile .failed. Bir iş yalnızca oluşturulduğunda olayına ait bir abonelik varsa izlenir (İşler ve sonuçlar).

İlanlar

OlayNe zaman
object.createdbir ilan oluşturdunuz
object.updatedbir ilan değişti, moderatör tarafından da
object.publishedbir ilan herkese açık oldu
object.deletedbir taslak silindi veya yayınlanmış bir ilan arşivlendi

data: { "objectId", "kind", "status", "occurredAt" }; object.deleted içinde status yerine mode (deleted veya archived). Tam veriler: GET /objects/{kind}/{id}.

Lead'ler

OlayNe zaman
lead.createdsize yeni bir lead
lead.updateddurum veya sorumlu değişti

data: { "leadId", "status", "type", "source", "objectId", "occurredAt" }. Olayda müşterinin kişisel verileri yoktur: lead GET /leads/{id} ile okunur.

Swipe Match

OlayNe zaman
match.lead.offeredsize (veya ajansınızın bir temsilcisine) bir lead teklif edildi
match.lead.offer_closedteklif siz olmadan kapandı (süre doldu)
match.lead.matchedlead size atandı
match.lead.updatedlead yeni bir aşamaya geçti
match.lead.unassignedlead sizden geri alındı

data: { "leadId", "eventId", "eventCode", "occurredAt", "source", "stage", "active", "leadKind", "clientType", "country", "brokerId", "agencyId" }. Müşterinin adı, telefonu ve e-postası olaylarda asla yer almaz (Swipe Match).

Teslim biçimi

POST https://example.com/grem-hook
Content-Type: application/json
x-grem-event: content.completed
x-grem-signature: t=1759400000,v1=3b78a03b78058d229023db2a0b4ee072...

{ "id": "...", "type": "content.completed", "createdAt": "2026-10-02T10:00:00.000Z", "data": { ... } }
  • id, bir teslimin tüm yeniden denemelerinde aynıdır: bir olayı iki kez işlememek için kullanın.
  • t ve createdAt ilgili denemenin zamanıdır.
  • Listeden bir eventType ile yapılan test teslimi (POST /webhooks/{id}/test) data içinde test: true taşır; eventType olmadan olay türü webhook.test olur. Listede olmayan bir eventType: 400. webhook.test değerinin kendisi kabul edilir.

İmzayı doğrulama

İmza, abonelik gizli anahtarıyla "<t>.<ham gövde>" dizesinin HMAC-SHA256 değeridir, v1 içinde onaltılıktır. Herhangi bir işlemden önce doğrulayın ve eski t değerlerini reddedin (yeniden oynatmaya karşı koruma).

İmzayı JSON'u ayrıştırmadan önce, geldiği gibi ham gövde üzerinden hesaplayın. Yeniden serileştirme baytları değiştirir ve imza eşleşmez.

const crypto = require('crypto');

function verifyGremSignature(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(String(header || '').split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post('/grem-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyGremSignature(process.env.GREM_WEBHOOK_SECRET, req.get('x-grem-signature'), raw)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(raw);
  // event.id, event.type, event.data
  res.sendStatus(200);
});

SDK içinde bunu verifyWebhookSignature() yapar.

Yeniden denemeler ve devre dışı bırakma

  • Sunucunuz 10 saniye içinde 2xx ile yanıt verirse teslim başarılıdır. Hızlı yanıt verin, ağır işi arka planda yapın.
  • Başarısız bir teslim 5 dakika, 30 dakika, 2 saat ve 6 saat sonra yeniden denenir: toplam en fazla 5 deneme.
  • Art arda tamamen başarısız 5 teslimden sonra abonelik devre dışı bırakılır (status: disabled). PATCH /webhooks/{id} ve {"status":"active"} ile veya panelden yeniden etkinleştirin.
  • Teslim günlüğü (GET /webhooks/{id}/deliveries) sunucunuzun yanıt kodunu ve yanıt gövdesinin başını gösterir: bir teslimin neden başarısız olduğunu buradan görürsünüz.

İlgili makaleler

Bu makale yardımcı oldu mu?