Mosaqo webhook’ları
Abone olduğunuz her olay için endpoint’inize imzalı JSON gövdesiyle bir POST gönderir, siz kabul edene kadar artan aralıklarla yeniden deneriz.
Abone olma
Ya Bulk & API bölümünden bir endpoint ekleyin ya da webhooks:write kapsamlı bir anahtarla kendi kodunuzdan kaydedin — bir otomasyon platformu bir tetikleyiciyi tam olarak böyle açıp kapatır:
curl -X POST https://api.mosaqo.app/v1/public-api/webhooks \
-H "Authorization: Bearer $MOSAQO_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/mosaqo",
"eventTypes": ["qr.published", "qr.updated"]
}'Yanıt imzalama secret değerini içerir. Yalnızca bir kez gösterilir — saklayın ya da aboneliği sonradan değiştirin.
Olaylar
| Olay | Ne zaman tetiklenir |
|---|---|
qr.created | Uygulamada veya API üzerinden bir kod oluşturulduğunda |
qr.published | Bir kod yayına girip yönlendirmesi yanıt vermeye başladığında |
qr.updated | İçerik, tasarım, ad veya durum değiştiğinde |
qr.archived | Bir kod arşivlenip yönlendirme sunucusundan kaldırıldığında |
qr.deleted | Bir kod kalıcı olarak silindiğinde |
redirect.rule_changed | Bir hedef veya hedefleme kuralı değiştiğinde |
scan.aggregate_ready | Bir tarama toplaması hazır olduğunda |
export.ready | Asenkron bir dışa aktarma bittiğinde |
approval.changed | Bir inceleme onaylandığında veya reddedildiğinde |
bulk.completed | Toplu bir iş tamamlandığında |
Payload
Bir olay QR koduyla ilgiliyse data.qr, GET /qr/{id} çağrısının döndürdüğü özetin aynısını taşır — yani tetikleyicinin, kendisine az önce bildirilen kaydı geri sorması gerekmez.
{
"id": "0b0f…",
"type": "qr.published",
"workspaceId": "2f60…",
"occurredAt": "2026-08-05T10:20:30.456Z",
"data": {
"qrId": "e7b7…",
"source": "public_api",
"qr": {
"id": "e7b7…",
"name": "Autumn campaign",
"slug": "autumn-campaign-1a2b3c4d",
"mode": "dynamic",
"contentType": "url",
"status": "published",
"approvalState": "not_required",
"folderId": null,
"updatedAt": "2026-08-05T10:20:30.401Z",
"publicUrl": "https://mosaqo.link/aB3dEf"
}
}
}Bir teslimatı doğrulama
Her istek bir zaman damgası ve hem damgayı hem gövdeyi kapsayan bir imza taşır:
X-Mosaqo-Timestamp: 1785942718
X-Mosaqo-Signature: t=1785942718,v1=<hex HMAC-SHA256 of "{timestamp}.{raw body}">
X-Mosaqo-Delivery: 0b0f…
X-Mosaqo-Event: qr.publishedZaman damgası kendi saatinizden uzak olan her şeyi reddetmek, yakalanmış bir teslimatı yeniden oynatılamaz kılan şeydir; bu kontrolü atlamayın. İmzaları sabit sürede karşılaştırın ve isteğin ham gövdesini kullanın: ayrıştırılmış JSON’u yeniden serileştirmek baytları değiştirir ve imza artık tutmaz.
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyMosaqoWebhook(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
header.split(',').map((piece) => piece.split('=').map((s) => s.trim())),
);
const timestamp = Number(parts.t);
if (!Number.isFinite(timestamp)) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest();
const received = Buffer.from(parts.v1 ?? '', 'hex');
return expected.length === received.length && timingSafeEqual(expected, received);
}PHP
<?php
function verify_mosaqo_webhook(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
$parts = [];
foreach (explode(',', $header) as $piece) {
[$key, $value] = array_map('trim', explode('=', $piece, 2));
$parts[$key] = $value;
}
if (!isset($parts['t'], $parts['v1'])) return false;
if (abs(time() - (int) $parts['t']) > $tolerance) return false;
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1']);
}Python
import hmac, time
from hashlib import sha256
def verify_mosaqo_webhook(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(piece.strip().split("=", 1) for piece in header.split(","))
if "t" not in parts or "v1" not in parts:
return False
if abs(time.time() - int(parts["t"])) > tolerance:
return False
signed = f"{parts['t']}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])Yeniden denemeler ve hatalar
Olayı kaydeder kaydetmez herhangi bir 2xx ile yanıt verin; işi sonra yapın. Üstel artan aralıklarla altı kez yeniden deneriz, dolayısıyla yavaş bir işleyici yinelenen teslimatlara dönüşür — X-Mosaqo-Delivery değerini kendi tarafınızda idempotentlik anahtarı olarak kullanın.
Endpoint’ler herkese açık bir sunucuda HTTPS olmalıdır. Yerel ve özel adresler abonelik oluşturulurken zaten reddedilir.
Kullanımdan kaldırılan imza
X-Mosaqo-Signature-V0, yalnızca gövde üzerinden hesaplanan daha eski bir imzayı taşır. Yineleme korumasını ifade edemez, mevcut alıcılar geçiş yapsın diye bir sürüm daha gönderilir ve yeni hiçbir şeyde kullanılmamalıdır.
Tarama verileri dışarı gönderilmez
Bilinçli olarak tarama başına webhook yoktur. Mosaqo tarama düzeyindeki analitiği üçüncü taraf endpoint’lerine göndermez ve yukarıdaki tablodaki hiçbir olay bunu taşımaz.
Tarama geçmişi yalnızca kendi anahtarınızla siz çekerseniz erişilebilir — GET /scans — ve o zaman bile çalışma alanınızın analitik gizlilik ayarlarına tabidir: kapattığınız boyutlar hiçbir satırda görünmez, bot filtresi geçerlidir ve “benzersiz taramalar” ın arkasındaki takma adlı ziyaretçi özeti asla dışa verilmez. Yanıt, ne aldığınızı bilmeniz için allowedDimensions listeler.
scan.aggregate_ready bir toplamanın hazır olduğunu söyler; taramaların kendisini içermez. Entegrasyonunuzun gerçekten gerçek zamanlı tek tek taramalara ihtiyacı varsa bu talep üzerine mümkündür — var olduğunu varsaymak yerine bizimle konuşun.
Webhook alamıyorsanız
Bunun yerine siz sorun. GET /qr?updatedSince=… değişen kodları verir, GET /scans?since=… ise tarama geçmişini kararlı bir imleçle ileriye doğru gezer.