Skip to content
Misar.io
Webhooks

Misar Webhooks

Receive real-time event notifications via HTTP callbacks when things happen across Misar products. No polling required.

Quick Start

Quick Start

Register a webhook endpoint in your product dashboard and start receiving events in minutes.

1

Register your endpoint

Go to your product dashboard → Settings → Webhooks and add your HTTPS endpoint.

# Or register via API
curl -X POST https://api.misar.io/mail/v1/webhooks \
  -H "Authorization: Bearer msk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/misar",
    "events": ["email.delivered", "email.bounced"]
  }'
2

Handle the incoming request

// app/api/webhooks/misar/route.ts (Next.js)
import { NextRequest, NextResponse } from "next/server";
import crypto from "node:crypto";

export async function POST(req: NextRequest) {
  const body = await req.text();
  const sig = req.headers.get("x-misar-signature") ?? "";

  const mac = crypto
    .createHmac("sha256", process.env.MISAR_WEBHOOK_SECRET!)
    .update(body)
    .digest("hex");

  if (`sha256=${mac}` !== sig) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  const event = JSON.parse(body);

  switch (event.type) {
    case "email.delivered":
      await markEmailDelivered(event.data.id);
      break;
    case "email.bounced":
      await handleBounce(event.data);
      break;
  }

  return NextResponse.json({ received: true });
}
Payload Schema

Payload Schema

All Misar webhooks share a consistent envelope. The data field is event-specific.

Envelope

{
  "id": "evt_01j9xyz...",
  "type": "email.delivered",
  "product": "mail",
  "timestamp": "2025-01-01T00:00:00Z",
  "api_version": "v1",
  "data": {
    "id": "msg_01j9abc...",
    "to": "[email protected]",
    "subject": "Welcome!",
    "delivered_at": "2025-01-01T00:00:01Z"
  }
}

HTTP Headers

POST /webhooks/misar HTTP/1.1
Host: your-app.com
Content-Type: application/json
X-Misar-Signature: sha256=<hmac>
X-Misar-Event: email.delivered
X-Misar-Delivery: evt_01j9xyz...
User-Agent: Misar-Webhook/1.0

TypeScript Types

interface MisarWebhookEvent<T = unknown> {
  id: string;          // evt_...
  type: string;        // "email.delivered" etc
  product: string;     // "mail" | "blog" | "reach" | "social" | "dev"
  timestamp: string;   // ISO 8601
  api_version: string; // "v1"
  data: T;
}

// Type-safe handler
function handleEvent(event: MisarWebhookEvent) {
  if (event.type === "email.delivered") {
    const data = event.data as EmailDeliveredData;
    // ...
  }
}
Signature Verification

Signature Verification

Every request is signed with HMAC-SHA256 using your webhook secret. Always verify before processing.

TypeScript / Node.js

import crypto from "node:crypto";

function verify(
  body: string,
  sig: string,
  secret: string
): boolean {
  const mac = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return `sha256=${mac}` === sig;
}

Python

import hmac, hashlib

def verify(body: str, sig: str,
           secret: str) -> bool:
    mac = hmac.new(
        secret.encode(),
        body.encode(),
        hashlib.sha256
    ).hexdigest()
    return f"sha256={mac}" == sig

Go

import (
  "crypto/hmac"
  "crypto/sha256"
  "fmt"
)

func verify(body, sig,
  secret string) bool {
  mac := hmac.New(
    sha256.New,
    []byte(secret),
  )
  mac.Write([]byte(body))
  exp := fmt.Sprintf(
    "sha256=%x",
    mac.Sum(nil),
  )
  return hmac.Equal(
    []byte(exp),
    []byte(sig),
  )
}

Events by Product

Subscribe to specific event types per product.

MisarMail

Live
  • email.delivered
  • email.bounced
  • email.opened
  • email.clicked
  • email.unsubscribed
  • campaign.completed

Misar.Blog

Live
  • article.published
  • article.updated
  • article.deleted
  • comment.created

MisarReach

Live
  • lead.found
  • campaign.sent
  • deal.updated
  • contact.replied

Misar.Dev

Live
  • plugin.installed
  • plugin.updated
  • plugin.uninstalled
  • agent.run.completed
  • agent.run.failed

MisarPost

Launching
  • post.published
  • post.scheduled
  • post.failed
  • campaign.completed

Misar.Ink

Coming Soon

    Misar.ai

    Coming Soon
    • project.created
    • deploy.succeeded
    • deploy.failed
    Best Practices

    Best Practices

    Respond quickly

    Return a 2xx status within 10 seconds. If processing takes longer, acknowledge the event immediately and handle it asynchronously via a queue.

    Handle retries idempotently

    Misar retries failed deliveries with exponential backoff (up to 72 hours). Use the event `id` field as an idempotency key to avoid double-processing.

    Always verify signatures

    Never process a webhook payload without verifying the HMAC-SHA256 signature. Reject requests with invalid or missing signatures with 401.

    Monitor failures

    Check your webhook delivery logs in the dashboard. Endpoints that return 5xx for 3 consecutive days will be automatically disabled.

    Ready to integrate webhooks?

    Register your endpoint from the product dashboard or contact us for help setting up your integration.