SingleMail
ENGiriş yap

Webhook'lar

Webhook'lar e-posta olaylarını gerçekleştikleri anda sizin sunucunuza POST eder. Örneğin geri dönen bir adresi CRM'inizde işaretleyebilir ya da teslim edilen faturaları kayda geçirebilirsiniz.

Kurulum

  1. Bir uç nokta yazın

    Sunucunuzda POST kabul eden ve 2xx dönen, herkese açık bir https:// adresi hazırlayın (yerel ağ ve localhost adresleri güvenlik nedeniyle kabul edilmez; yönlendirmeler izlenmez). İşlemi kuyruğa alıp hemen 200 dönmek en iyisidir; 15 saniyede yanıt gelmezse istek başarısız sayılır.

  2. Panelden ekleyin

    Webhooks sayfasında adresi girip olayları seçin. İmza anahtarı (whsec_…) bir kez gösterilir; webhook sayfasından her zaman tekrar görebilirsiniz.

    Webhook ayrıntısı
  3. İmzayı doğrulayın

    Herkes adresinize istek atabilir. Aşağıdaki doğrulama, isteğin gerçekten SingleMail'den geldiğini ve yolda değiştirilmediğini garanti eder.

İstek gövdesi

POST /hooks/singlemail
webhook-id: msg_8c0b6f3e-…
webhook-timestamp: 1791651120
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "type": "email.bounced",
  "created_at": "2026-10-10T14:12:00.000Z",
  "data": {
    "email_id": "abe55c23-533d-41ad-8971-2dad84ad985b",
    "from": "Optik Dünyası <fatura@firma.com>",
    "to": ["olmayan.adres@gmail.com"],
    "subject": "e-Faturanız hazır",
    "tags": [{ "name": "type", "value": "invoice" }],
    "bounce": { "type": "Permanent", "subType": "General" }
  }
}

Doğrulama

İmza, webhook-id.webhook-timestamp.gövde metninin, imza anahtarıyla (whsec_ öneki çıkarılıp base64 çözülmüş hali) alınmış HMAC-SHA256 değeridir (Standard Webhooks). Ham gövdeyi kullanın; JSON'u parse edip tekrar yazarsanız imza tutmaz.

Express (Node.js)
import express from "express";
import { SingleMail } from "@singlemail/node";
const sm = new SingleMail();

app.post("/hooks/singlemail", express.text({ type: "*/*" }), (req, res) => {
  let event;
  try {
    event = sm.webhooks.verify({
      payload: req.body,
      headers: {
        id: req.header("webhook-id"),
        timestamp: req.header("webhook-timestamp"),
        signature: req.header("webhook-signature"),
      },
      secret: process.env.SINGLEMAIL_WEBHOOK_SECRET,
    });
  } catch {
    return res.sendStatus(400);
  }
  if (event.type === "email.bounced") markInvalid(event.data.to[0]);
  res.sendStatus(200);
});
PHP
$secret = base64_decode(substr(getenv("SINGLEMAIL_WEBHOOK_SECRET"), 6));
$payload = file_get_contents("php://input");
$signed = $_SERVER["HTTP_WEBHOOK_ID"] . "." . $_SERVER["HTTP_WEBHOOK_TIMESTAMP"] . "." . $payload;
$expected = "v1," . base64_encode(hash_hmac("sha256", $signed, $secret, true));
if (!hash_equals($expected, $_SERVER["HTTP_WEBHOOK_SIGNATURE"])) { http_response_code(400); exit; }
$event = json_decode($payload, true);

Olay türleri

OlayNe zaman
email.sentRelay e-postayı kabul etti.
email.deliveredAlıcının sunucusu e-postayı aldı.
email.delivery_delayedTeslimat geçici olarak ertelendi.
email.bouncedKalıcı olarak geri döndü; adres engellendi.
email.complainedAlıcı spam olarak işaretledi; adres engellendi.
email.openedE-posta açıldı.
email.clickedBir linke tıklandı; payload'da link var.
email.failedGönderilemedi; payload'da neden var.
email.scheduledE-posta ileri bir tarihe zamanlandı.
email.canceledZamanlanmış e-posta iptal edildi.

Tekrar denemeler

2xx dönmeyen ya da zaman aşımına uğrayan istekler artan aralıklarla (5 sn, 10 sn, 20 sn … ~10 dk) toplam 8 kez denenir. Aynı olay birden fazla gelebilir; webhook-id başlığına göre tekilleştirin.

Bir uç nokta sürekli hata veriyorsa webhook sayfasından geçici olarak durdurabilirsiniz; durdurulan webhook'a olay gönderilmez.