Bu rehberde: OrqLabs'taki olayları (aday nitelendi, gönderi yayınlandı, bütçe uyarısı…) kendi sisteminize anında iletirsiniz. 28 olayın listesi, imza doğrulama kodu ve yeniden deneme kuralları burada. Bu rehber yazılımcılar içindir.
⏱ 15 dakika
Kısaca
Giden webhook, OrqLabs'ın sizin sisteminize "bir şey oldu" diye haber vermesidir. Örneğin: bir aday nitelendiğinde CRM'inize kayıt açmak veya bir onay istendiğinde Slack'e düğmeli mesaj göndermek. Uç noktayı Ayarlar → Webhook'lar sayfasında tanımlarsınız; OrqLabs her olayı imzalı bir JSON olarak gönderir.

Genel bilgi
Giden webhook'lar OrqLabs'daki olayları (lead nitelendi, gönderi yayınlandı, konuşma devredildi, bütçe uyarısı…) sizin HTTP uç noktanıza anlık iletir. Üç kullanım yolu vardır:
| Yol | Nerede | Ne gönderir |
|---|---|---|
| Olay abonelikleri | Ayarlar (Settings) → Webhooks | Seçtiğiniz PLATFORM_EVENTS olayları, ham zarf |
utility.webhookOut node'u | İş akışı içinde | Node'a gelen item'lar, event adı serbest (workflow.output) |
leads.crmSync (WEBHOOK hedefi) | İş akışı içinde | Lead nesnesi, lead.synced |
Bu rehber olay aboneliklerine odaklanır; imza ve zarf üçünde de aynıdır.
Uç nokta oluşturma
Ayarlar → Webhooks → Yeni uç nokta:
| Alan | Açıklama |
|---|---|
| URL | HTTPS zorunlu (üretimde); localhost yalnızca geliştirme modunda |
| İmza gizli anahtarı (secret) | Boş bırakılırsa üretilir; bir kez gösterilir |
| Olaylar | Aşağıdaki listeden çoklu seçim; * tümü |
| Proje filtresi | Boşsa organizasyon geneli |
| Aktif | Devre dışı uç nokta olayları biriktirmez |
Kaydettikten sonra Test gönder düğmesi ping türünde bir zarf yollar; yanıtınız ve süresi ekranda görünür. Teslim geçmişi (son 7 gün) uç nokta ayrıntısında: durum kodu, deneme sayısı, gövde önizlemesi, Yeniden gönder.
Zarf (PlatformEventEnvelope)
{
"id": "evt_01J8Z…",
"type": "lead.qualified",
"organizationId": "org_01J…",
"projectId": "prj_01J…",
"at": "2026-09-26T09:14:03.120Z",
"payload": { … olaya özgü … }
}
id teslim başına değil olay başına benzersizdir; yeniden denemelerde aynı id gelir (idempotency anahtarı). projectId proje bağlamı olmayan olaylarda (integration.*, budget.* organizasyon düzeyinde) bulunmaz.
Olaylar (PLATFORM_EVENTS)
Lead
| Olay | Ne zaman | payload başlıca alanlar |
|---|---|---|
lead.created | Yeni lead (form, webhook, ajan, içe aktarma) | lead { id, name, email, phone, company, website, status, score, tags, source } |
lead.updated | Herhangi bir alan/durum değişimi | lead, changes { field: { from, to } }, actor |
lead.qualified | Durum QUALIFIED oldu | lead, score, scoreReason, agentRunId? |
lead.replied | Lead outreach/kampanyaya yanıt verdi | lead, conversationId, channel, text |
Mesajlaşma
| Olay | Ne zaman | payload |
|---|---|---|
message.received | Gelen mesaj | conversationId, channel, contactId, contactName, text, mediaUrl?, leadId? |
message.sent | Giden mesaj (bot/insan/kampanya) | conversationId, channel, to, text, sender (bot/human/campaign), messageId |
conversation.opened | Yeni konuşma | conversationId, channel, contactId, leadId? |
conversation.handoff | İnsana devir | conversationId, reason, assignedToId?, by (bot/human) |
conversation.closed | Kapatıldı | conversationId, resolvedBy (bot/human), durationSec |
İçerik
| Olay | Ne zaman | payload |
|---|---|---|
post.created | Taslak oluşturuldu | post { id, platform, body, status, scheduledAt, integrationId } |
post.approved | Onaylandı | post, approvedBy, editedBody? |
post.published | Yayınlandı | post, externalId, url, publishedAt |
post.failed | Yayın hatası | post, error { code, message, retryable } |
Kampanya
| Olay | Ne zaman | payload |
|---|---|---|
campaign.started | Kampanya başladı | campaign { id, name, channel, total } |
campaign.finished | Bitti/iptal | campaign, stats { sent, delivered, read, replied, failed, skipped } |
İş akışı ve ajan
| Olay | Ne zaman | payload |
|---|---|---|
workflow.run.started | Run başladı | runId, workflowId, workflowName, triggerType |
workflow.run.finished | Run SUCCEEDED | runId, workflowId, status, costUsd, durationMs, itemCount |
workflow.run.failed | Run FAILED | runId, workflowId, error, failedNodeId, failedNodeName |
agent.run.finished | Ajan çalıştırması bitti (her durum) | agentRunId, type, status (succeeded/failed/budget_exceeded/max_steps), summary, usage { inputTokens, outputTokens, costUsd, modelId }, reportId? |
agent.report.created | Rapor kaydedildi | report { id, type, title, summary, metrics }, agentType, agentRunId |
Analitik
| Olay | Ne zaman | payload |
|---|---|---|
analytics.synced | Günlük GA4/GSC çekimi bitti | integrationId, source (ga4/gsc), date, metrics { sessions, users, conversions, … } |
analytics.threshold | Eşik kuralı tetiklendi | ruleId, metric, value, threshold, compare, direction |
Entegrasyon
| Olay | Ne zaman | payload |
|---|---|---|
integration.connected | Yeni bağlantı | integrationId, provider, name, projectId? |
integration.error | AUTH/UPSTREAM hatası; yeniden yetkilendirme gerekli | integrationId, provider, code, message |
Onay ve bütçe
| Olay | Ne zaman | payload |
|---|---|---|
approval.requested | Onay görevi oluştu | approvalId, title, description, runId, nodeId, expiresAt |
approval.resolved | Onaylandı/reddedildi | approvalId, decision (approved/rejected), by, comment?, editedPayload? |
budget.warning | Aylık bütçe %80 | scope (organization/workflow), limitUsd, usedUsd, pct |
budget.exceeded | Bütçe aşıldı | scope, limitUsd, usedUsd, runId? |
Toplam 28 olay. trigger.event node'unda bunların bir alt kümesi seçilebilir; webhook'lar hepsini alır.
Teslim ve yeniden deneme
- Teslim
webhooks-outkuyruğu üzerinden asenkron yapılır; olay ile teslim arası genellikle < 2 sn. - İstek:
POST,Content-Type: application/json, gövde zarf; zaman aşımı 10 sn. - Başarı: herhangi bir 2xx. Gövde okunmaz.
- Başarısızlık (bağlantı hatası, zaman aşımı, 3xx/4xx/5xx): üstel geri çekilmeyle 5 deneme (yaklaşık 1 dk, 5 dk, 15 dk, 30 dk sonra; toplam ~1 saat). 410 Gone alınırsa uç nokta otomatik devre dışı bırakılır.
- Ardışık 100 başarısız teslimden sonra uç nokta pasife alınır ve organizasyon sahibine bildirim gider.
- Sıra garantisi yoktur;
atalanına göre sıralayın. - Yük boyutu 256 KB ile sınırlıdır; büyük nesneler (
lead,post) kısaltılabilir,…Idalanlarıyla API'den tam kaydı çekin.
İmza başlıkları
| Başlık | İçerik |
|---|---|
X-AgentFlow-Event | Zarfın type alanı (lead.qualified) |
X-AgentFlow-Timestamp | Gönderim zamanı, Unix saniye |
X-AgentFlow-Signature | sha256=<hex> – "<timestamp>.<ham gövde>" dizesi üzerinden uç noktanın secret'ı ile HMAC-SHA256 |
X-AgentFlow-Delivery | Teslim denemesi kimliği (yeniden denemelerde değişir; id değişmez) |
User-Agent | AgentFlow-Webhooks/1.0 |
Bu başlık adları OrqLabs'un belgelenen sözleşmesidir; ilk teslimde uç noktanızın loglarından doğrulayın. İmza, X-AgentFlow-Timestamp değeri, bir nokta ve ham gövdenin birleştirilmesiyle ("<timestamp>.<gövde>") hesaplanır; zaman damgası imzaya dahil olduğu için tekrar saldırılarına karşı 5 dakikalık tolerans uygulayın ve id ile idempotency sağlayın.
Doğrulama (Node.js / Express)
import express from "express";
import crypto from "node:crypto";
const app = express();
const SECRET = process.env.ORQLABS_WEBHOOK_SECRET;
const seen = new Set(); // üretimde Redis/DB kullanın
app.post("/orqlabs/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.get("X-AgentFlow-Signature") ?? "";
const ts = Number(req.get("X-AgentFlow-Timestamp") ?? 0);
// 1) Zaman toleransı (5 dk)
if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(401).send("stale");
// 2) HMAC-SHA256: "<timestamp>.<ham gövde>" üzerinden
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(`${ts}.`).update(req.body).digest("hex");
const a = Buffer.from(sig), b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.status(401).send("bad signature");
// 3) Idempotency
const event = JSON.parse(req.body.toString("utf8"));
if (seen.has(event.id)) return res.status(200).send("dup");
seen.add(event.id);
// 4) Hızlı yanıt, işlemi arka plana al
res.status(200).send("ok");
setImmediate(() => handle(event));
});
function handle(event) {
switch (event.type) {
case "lead.qualified":
// CRM'e yaz
break;
case "approval.requested":
// Slack'e onay düğmeli mesaj gönder
break;
case "budget.warning":
// Finans ekibine uyarı
break;
}
}
app.listen(3001);
Önemli: express.json() kullanmayın; gövde ayrıştırıldıktan sonra yeniden serileştirilirse imza tutmaz. Ham gövdeyi (express.raw) doğrulayın.
Python (FastAPI) için aynı mantık: hmac.new(secret, f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest() ve hmac.compare_digest.
Örnek senaryolar
| Senaryo | Olaylar | Uç noktanız ne yapar |
|---|---|---|
| Slack'te tek tıkla onay | approval.requested, approval.resolved | Slack Block Kit mesajı; düğme → POST /orgs/:orgId/approvals/:id/approve |
| CRM senkronu | lead.qualified, lead.replied, lead.updated | Salesforce/Pipedrive'a upsert |
| İçerik arşivi | post.published | Yayınlanan gönderiyi veri ambarına yaz |
| Maliyet izleme | agent.run.finished, budget.warning | usage.costUsd toplamı; eşikte PagerDuty |
| Müşteri destek entegrasyonu | conversation.handoff | Zendesk/Freshdesk bileti aç |
| Veri gölü | * | Tüm zarfları S3/BigQuery'e yaz |
Sık sorulan sorular
Aynı olay iki kez gelebilir mi?
Evet; adresiniz geç yanıt verirse yeniden denenir. Zarftaki id alanıyla tekilleştirin.
İmza neden tutmuyor?
İmza ham gövde üzerinden hesaplanır. Gövdeyi JSON olarak ayrıştırıp yeniden serileştirmeyin; "<timestamp>.<ham gövde>" kullanın.
Adresim kapalıyken olaylar kaybolur mu? Yaklaşık 1 saat boyunca 5 kez denenir. Teslim geçmişinden Yeniden gönder ile elle de gönderebilirsiniz.