Webhooklar
Sosyal Köprü, belirli olaylar gerçekleştiğinde kaydettiğiniz URL’e HTTP POST isteği gönderir. Her workspace’te en fazla 10 webhook endpoint’i tanımlanabilir.
Desteklenen Olaylar
| Olay | Açıklama |
|---|---|
post.publishing | Gönderi yayınlanmaya başladı (BullMQ worker aldı) |
post.published | Gönderi başarıyla yayınlandı |
post.failed | Gönderi yayınlama başarısız oldu |
post.scheduled | Gönderi planlandı |
post.cancelled | Gönderi iptal edildi |
post.updated | Gönderi güncellendi |
webhook.test | Test ping gönderildi |
Webhook Oluştur
Yeni bir webhook endpoint’i kaydeder. Oluşturma yanıtında dönen secret yalnızca bir kez gösterilir.
İstek Gövdesi
| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
url | string | Evet | HTTPS webhook URL’i |
events | string[] | Evet | Dinlenecek olaylar (en az 1) |
description | string | Hayır | Webhook açıklaması (maks. 255 karakter) |
SSRF Koruması: Yerel IP adresleri (127.0.0.1, 192.168.x.x, 10.x.x.x vb.) reddedilir. URL mutlaka kamuya açık, HTTPS ile erişilebilir bir endpoint olmalıdır.
Kod Örnekleri
Node.js
const response = await fetch('https://api.sosyalkopru.com/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://sizin-siteniz.com/webhooks/sosyalkopru',
events: ['post.published', 'post.failed'],
description: 'Üretim ortamı bildirimleri',
}),
})
const { data } = await response.json()
console.log('Webhook ID:', data.id)
// ⚠️ Secret yalnızca bir kez gösterilir!
console.log('Secret:', data.secret)
// Bunu güvenli bir yerde saklayın (örn. ortam değişkeni)
process.env.SOSYALKOPRU_WEBHOOK_SECRET = data.secretBaşarılı Yanıt (201 Created)
{
"success": true,
"data": {
"id": "wh_01HZGK9P3QABC",
"url": "https://sizin-siteniz.com/webhooks/sosyalkopru",
"events": ["post.published", "post.failed"],
"isActive": true,
"secret": "whsec_aBcDeFgH1234...",
"createdAt": "2025-06-10T12:00:00.000Z"
},
"meta": { "requestId": "req_01XYZ", "timestamp": "...", "version": "1" }
}secret alanı yalnızca oluşturma yanıtında döner. Mutlaka güvenli bir şekilde saklayın. Kaybetmeniz durumunda webhook’u silip yeniden oluşturmanız gerekir.
Webhookları Listele
Tüm webhook endpoint’lerini son teslimat durumuyla birlikte döner.
Node.js
const response = await fetch('https://api.sosyalkopru.com/v1/webhooks', {
headers: { 'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}` },
})
const { data } = await response.json()
data.forEach(wh => {
const durum = wh.isActive ? 'aktif' : 'pasif'
console.log(`${wh.id} [${durum}]: ${wh.url}`)
console.log(` Olaylar: ${wh.events.join(', ')}`)
console.log(` Teslimat: ${wh.deliveryCount} toplam`)
if (wh.lastDelivery) {
console.log(` Son teslimat: ${wh.lastDelivery.status} @ ${wh.lastDelivery.timestamp}`)
}
})Başarılı Yanıt
{
"success": true,
"data": [
{
"id": "wh_01HZGK9P3QABC",
"url": "https://sizin-siteniz.com/webhooks/sosyalkopru",
"events": ["post.published", "post.failed"],
"isActive": true,
"deliveryCount": 47,
"lastDelivery": {
"status": "success",
"timestamp": "2025-06-10T11:55:00.000Z"
},
"createdAt": "2025-06-01T10:00:00.000Z",
"updatedAt": "2025-06-10T11:55:00.000Z"
}
],
"meta": { "requestId": "req_01XYZ", "timestamp": "...", "version": "1" }
}Webhook Detayı
Tek bir webhook’u teslimat istatistikleriyle döner.
Node.js
const response = await fetch(
`https://api.sosyalkopru.com/v1/webhooks/${webhookId}`,
{ headers: { 'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}` } }
)
const { data } = await response.json()
const successRate = data.stats.total > 0
? ((data.stats.success / data.stats.total) * 100).toFixed(1)
: 0
console.log(`Başarı oranı: %${successRate} (${data.stats.success}/${data.stats.total})`)Başarılı Yanıt
{
"success": true,
"data": {
"id": "wh_01HZGK9P3QABC",
"url": "https://sizin-siteniz.com/webhooks/sosyalkopru",
"events": ["post.published", "post.failed"],
"isActive": true,
"stats": {
"total": 47,
"success": 45,
"failed": 2
},
"createdAt": "2025-06-01T10:00:00.000Z",
"updatedAt": "2025-06-10T11:55:00.000Z"
},
"meta": { "requestId": "req_01XYZ", "timestamp": "...", "version": "1" }
}Webhook Güncelle
Webhook URL’ini, event listesini veya aktif durumunu günceller.
İstek Gövdesi
| Alan | Tip | Açıklama |
|---|---|---|
url | string | Yeni webhook URL’i |
events | string[] | Yeni event listesi (en az 1) |
isActive | boolean | false ile webhook’u geçici olarak devre dışı bırakın |
Node.js
// Webhook'u devre dışı bırak
const response = await fetch(
`https://api.sosyalkopru.com/v1/webhooks/${webhookId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ isActive: false }),
}
)
const { data } = await response.json()
console.log('Aktif mi:', data.isActive) // falseWebhook Sil
Node.js
await fetch(`https://api.sosyalkopru.com/v1/webhooks/${webhookId}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}` },
})Test Ping Gönder
webhook.test olayını URL’inize gönderir. Yanıt süresi (durationMs) dahil teslimat detaylarını döner.
Kod Örnekleri
Node.js
const response = await fetch(
`https://api.sosyalkopru.com/v1/webhooks/${webhookId}/test`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.SOSYALKOPRU_API_KEY}` },
}
)
const { data } = await response.json()
console.log('Başarılı :', data.success)
console.log('HTTP Kodu :', data.statusCode)
console.log('Yanıt süresi:', data.durationMs, 'ms')
if (!data.success) {
console.error('Hata:', data.responseBody)
}Başarılı Yanıt
{
"success": true,
"data": {
"deliveryId": "del_01HZGK9P3QABC",
"success": true,
"statusCode": 200,
"responseBody": "{\"received\": true}",
"durationMs": 142
},
"meta": { "requestId": "req_01XYZ", "timestamp": "...", "version": "1" }
}İmza Doğrulama
Gelen webhook isteklerinin gerçekten Sosyal Köprü’den geldiğini doğrulamak için HMAC-SHA256 imzasını kontrol edin.
İstek Başlıkları
| Başlık | Açıklama |
|---|---|
X-SosyalKopru-Signature | sha256= önekli HMAC-SHA256 imzası |
X-SosyalKopru-Event | Olay adı (post.published vb.) |
X-SosyalKopru-Delivery | Teslimat ID’si |
Doğrulama Örnekleri
Node.js
import crypto from 'crypto'
export function webhookDogrula(rawBody, signature) {
const secret = process.env.SOSYALKOPRU_WEBHOOK_SECRET
const beklenen = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody) // ham string ya da Buffer — asla parse edilmiş obje değil!
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(beklenen)
)
}
// Express ile kullanım (ham body gereklidir)
app.post('/webhooks/sosyalkopru', express.raw({ type: '*/*' }), (req, res) => {
const signature = req.headers['x-sosyalkopru-signature'] ?? ''
if (!webhookDogrula(req.body, signature)) {
return res.status(401).json({ error: 'İmza geçersiz' })
}
const olay = JSON.parse(req.body.toString())
console.log('Olay:', olay.event, '| Post:', olay.data?.postId)
res.json({ received: true })
})Webhook Payload Formatı
{
"event": "post.published",
"timestamp": "2025-06-10T12:00:00.000Z",
"data": {
"postId": "post_01HZGK9P3QABC",
"status": "PUBLISHED",
"platforms": [
{
"platform": "instagram",
"status": "PUBLISHED",
"platformPostUrl": "https://instagram.com/p/ABC123"
}
]
}
}Webhook endpoint’iniz 15 saniye içinde 2xx yanıt dönmelidir. Aksi hâlde teslimat başarısız sayılır. İstek alındığında hemen 200 OK dönün, işlemi arka planda gerçekleştirin.
HMAC imzasını hesaplarken ham HTTP body’sini (bytes) kullanın. JSON parse edilmiş nesneyi JSON.stringify ile tekrar stringe çevirmek anahtar sırasını değiştirebilir ve imza doğrulamasını bozabilir.