Skip to main content

Overview

Webhooks allow you to receive real-time HTTP notifications when events occur in your Migma account. Instead of constantly checking for updates, Migma will automatically send instant POST requests to your specified endpoint whenever something important happens.
Important: Webhooks are triggered only for events that occur through the API v1 endpoints (when using API keys). Events from the Migma web dashboard do not trigger webhooks. This design ensures webhooks are used for API integrations and automations.

Getting Started

1. Create Your Webhook Endpoint

Set up an HTTPS endpoint on your server to receive webhook events:
Node.js
const express = require('express');
const crypto = require('crypto');

app.post('/webhooks/migma', express.json(), (req, res) => {
  // Verify signature (see security section below)
  const signature = req.headers['x-migma-signature'];
  const isValid = verifySignature(req.body, signature, process.env.MIGMA_WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const event = req.body;
  console.log(`Received ${event.type}:`, event.data);

  // Return 200 to acknowledge receipt
  res.sendStatus(200);
});
Python
from flask import Flask, request
import hmac
import hashlib
import json

@app.route('/webhooks/migma', methods=['POST'])
def webhook():
    # Verify signature
    signature = request.headers.get('X-Migma-Signature')
    payload = request.get_json()

    if not verify_signature(payload, signature, os.getenv('MIGMA_WEBHOOK_SECRET')):
        return 'Invalid signature', 401

    # Process the event
    event_type = payload['type']
    print(f"Received {event_type}: {payload['data']}")

    # Return 200 to acknowledge receipt
    return '', 200

2. Register Your Webhook in Migma Dashboard

  1. Log in to your Migma dashboard
  2. Navigate to Settings → API Integration → Webhooks tab
  3. Click Create Webhook
  4. Enter your endpoint URL (must be HTTPS)
  5. Select the events you want to receive
  6. Click Save
  7. Copy your webhook secret (shown only once!)

3. Verify It Works

Use the test button in your dashboard to send a test event to your endpoint. Check that your server receives the request and responds with 200 OK.

Available Events

Migma sends webhooks for the following events:

Email Generation

EventWhen It Fires
email.generation.startedEmail generation begins via API
email.generation.completedEmail successfully generated
email.generation.failedEmail generation encountered an error

Project Import

EventWhen It Fires
project.import.startedBrand import begins via API
project.import.processingBrand is being processed
project.import.completedBrand import finished successfully
project.import.failedBrand import encountered an error

Exports

EventWhen It Fires
export.completedEmail export to Mailchimp/HubSpot/Klaviyo completed
export.failedEmail export encountered an error

Subscribers

EventWhen It Fires
subscriber.addedNew subscriber added via API
subscriber.updatedSubscriber information updated via API
subscriber.unsubscribedSubscriber unsubscribed
subscriber.bulk_importedBulk import of subscribers completed

API Keys

EventWhen It Fires
api_key.createdNew API key created via dashboard
api_key.revokedAPI key revoked via dashboard

Webhook Payload Structure

All webhook events follow this consistent structure:
{
  "id": "evt_1234567890abcdef",
  "type": "email.generation.completed",
  "timestamp": "2025-01-30T12:34:56.789Z",
  "data": {
    // Event-specific data
  },
  "metadata": {
    "projectId": "proj_abc123",
    "conversationId": "conv_xyz789"
  }
}

Field Descriptions

  • id: Unique event identifier (use for idempotency)
  • type: Event type from the list above
  • timestamp: ISO 8601 timestamp when the event occurred
  • data: Event-specific payload (see examples below)
  • metadata: Additional context (project ID, conversation ID, etc.)

Example Payloads

Email Generation Completed

{
  "id": "evt_a1b2c3d4e5f6",
  "type": "email.generation.completed",
  "timestamp": "2025-01-30T12:34:56.789Z",
  "data": {
    "conversationId": "conv_xyz789",
    "projectId": "proj_abc123",
    "subject": "Welcome to Our Newsletter",
    "generationTime": 3245,
    "wordCount": 428
  },
  "metadata": {
    "projectId": "proj_abc123",
    "conversationId": "conv_xyz789"
  }
}

Project Import Completed

{
  "id": "evt_b2c3d4e5f6g7",
  "type": "project.import.completed",
  "timestamp": "2025-01-30T12:35:00.123Z",
  "data": {
    "projectId": "proj_abc123",
    "name": "My Brand",
    "url": "https://mybrand.com",
    "importTime": 45230
  },
  "metadata": {
    "projectId": "proj_abc123"
  }
}

Export Completed

{
  "id": "evt_c3d4e5f6g7h8",
  "type": "export.completed",
  "timestamp": "2025-01-30T12:36:15.456Z",
  "data": {
    "conversationId": "conv_xyz789",
    "provider": "mailchimp",
    "campaignId": "mc_campaign_123",
    "exportTime": 2340
  },
  "metadata": {
    "conversationId": "conv_xyz789"
  }
}

Subscriber Added

{
  "id": "evt_d4e5f6g7h8i9",
  "type": "subscriber.added",
  "timestamp": "2025-01-30T12:37:00.789Z",
  "data": {
    "email": "[email protected]",
    "projectId": "proj_abc123",
    "tags": ["newsletter", "customer"]
  },
  "metadata": {
    "projectId": "proj_abc123"
  }
}

Security

Verifying Webhook Signatures

Always verify the X-Migma-Signature header to ensure requests came from Migma and haven’t been tampered with.

Node.js Implementation

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(computedSignature)
  );
}

app.post('/webhooks/migma', express.json(), (req, res) => {
  const signature = req.headers['x-migma-signature'];
  const isValid = verifyWebhookSignature(
    req.body,
    signature,
    process.env.MIGMA_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  res.sendStatus(200);
});

Python Implementation

import hmac
import hashlib
import json

def verify_webhook_signature(payload, signature, secret):
    computed_signature = hmac.new(
        secret.encode(),
        json.dumps(payload, separators=(',', ':')).encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, computed_signature)

@app.route('/webhooks/migma', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Migma-Signature')
    payload = request.get_json()

    if not verify_webhook_signature(payload, signature, os.getenv('MIGMA_WEBHOOK_SECRET')):
        return 'Invalid signature', 401

    # Process the webhook
    return '', 200

Security Best Practices

  1. Always verify signatures - Never process webhooks without verification
  2. Use HTTPS only - Migma only sends webhooks to HTTPS endpoints
  3. Store secrets securely - Use environment variables, never hardcode
  4. Implement timeouts - Don’t process webhooks older than 5 minutes
  5. Rate limit - Protect against potential abuse

Delivery & Reliability

Non-Blocking Delivery

Webhook triggers are completely non-blocking:
  • ✅ API responses return instantly, regardless of webhook configuration
  • ✅ Webhook delivery happens in the background after your API call succeeds
  • ✅ Webhook failures never affect your API request success
  • ✅ Multiple webhooks are delivered in parallel
What this means: Your API calls are never delayed by webhook delivery, even if your webhook endpoint is slow or down.

Automatic Retries

Migma automatically retries failed webhook deliveries with exponential backoff:
  • 1st retry: After 1 minute
  • 2nd retry: After 5 minutes
  • 3rd retry: After 15 minutes
Return 200-299 status codes to indicate success. Any other status code (or timeout) triggers a retry.

Delivery Guarantees

  • At-least-once delivery: Events are delivered at least once (possibly more during retries)
  • Idempotency: Use the event id to prevent duplicate processing
  • Persistent queue: Delivery attempts are stored for reliability
  • 10-second timeout: Each delivery attempt times out after 10 seconds

Best Practices

Respond Quickly

Acknowledge receipt immediately, then process asynchronously:
app.post('/webhooks/migma', (req, res) => {
  // Acknowledge receipt immediately
  res.sendStatus(200);

  // Process asynchronously
  processWebhookAsync(req.body).catch(err => {
    console.error('Webhook processing error:', err);
  });
});

Implement Idempotency

Use the event id to prevent duplicate processing:
const processedEvents = new Set();

app.post('/webhooks/migma', async (req, res) => {
  const eventId = req.body.id;

  if (processedEvents.has(eventId)) {
    return res.sendStatus(200); // Already processed
  }

  // Process event
  await handleEvent(req.body);
  processedEvents.add(eventId);

  res.sendStatus(200);
});

Handle Retries Gracefully

app.post('/webhooks/migma', async (req, res) => {
  try {
    // Verify signature
    if (!verifySignature(req)) {
      return res.status(401).send('Invalid signature');
    }

    // Process event (idempotent)
    await handleEvent(req.body);

    res.sendStatus(200);
  } catch (error) {
    console.error('Webhook error:', error);

    // Return 500 to trigger retry
    res.status(500).send('Processing failed');
  }
});

Route by Event Type

app.post('/webhooks/migma', async (req, res) => {
  const { type, data } = req.body;

  switch (type) {
    case 'email.generation.completed':
      await handleEmailCompleted(data);
      break;

    case 'email.generation.failed':
      await handleEmailFailed(data);
      break;

    case 'export.completed':
      await handleExportCompleted(data);
      break;

    default:
      console.log(`Unknown event type: ${type}`);
  }

  res.sendStatus(200);
});

Testing

Local Development

Use ngrok to expose your local server:
ngrok http 3000
Then register the ngrok URL (e.g., https://abc123.ngrok.io/webhooks/migma) in your Migma dashboard.

Test Events

Click the test button (🔄) next to your webhook in the dashboard to send a test payload:
{
  "id": "evt_test_123",
  "type": "email.generation.completed",
  "timestamp": "2025-01-30T12:00:00.000Z",
  "data": {
    "conversationId": "test_conv",
    "projectId": "test_proj",
    "subject": "Test Email"
  },
  "metadata": {
    "test": true
  }
}

Debug Mode

Enable verbose logging to troubleshoot:
app.post('/webhooks/migma', (req, res) => {
  console.log('Headers:', req.headers);
  console.log('Body:', JSON.stringify(req.body, null, 2));
  console.log('Signature:', req.headers['x-migma-signature']);

  // ... rest of your code
});

Monitoring

Dashboard Monitoring

Monitor webhook health in your Migma dashboard:
  1. Go to Settings → API Integration → Webhooks
  2. View success/failure counts for each webhook
  3. Click the clock icon to see delivery history
  4. Check response codes and timing for each delivery

Metrics to Track

  • Success rate: Percentage of successful deliveries
  • Response time: How long your endpoint takes to respond
  • Failure patterns: Common error codes or failure reasons
  • Retry frequency: How often retries are needed

Troubleshooting

Common Issues

IssueSolution
Signature verification failsEnsure you’re using the correct secret and computing HMAC over the raw JSON body
TimeoutsRespond with 200 immediately, process asynchronously
Duplicate eventsImplement idempotency using event IDs
Missing eventsCheck your endpoint returns 200-299 status codes
Webhook not firingVerify the event is triggered via API (not dashboard)

HTTP Response Codes

Return these codes to control webhook behavior:
  • 200-299: Success - no retry
  • 400-499: Client error - no retry (except 408, 429)
  • 500-599: Server error - will retry
  • Timeout (10s): Will retry

Rate Limits

Your webhook endpoint should handle:
  • Up to 100 requests per second during high activity
  • Burst traffic from batch operations (e.g., bulk imports)
Ensure your endpoint can scale appropriately.

Example Use Cases

Automated Email Workflows

// Trigger follow-up actions when email generation completes
case 'email.generation.completed':
  const { conversationId, projectId, subject } = data;

  // Log to your analytics
  await analytics.track('email_generated', {
    projectId,
    conversationId,
    subject
  });

  // Notify your team
  await slack.notify({
    channel: '#marketing',
    message: `New email generated: "${subject}"`
  });
  break;

Error Monitoring

// Alert your team when generation fails
case 'email.generation.failed':
  await slack.notify({
    channel: '#alerts',
    message: `Email generation failed for project ${data.projectId}`,
    error: data.error
  });
  break;

Integration Sync

// Sync subscribers to your CRM
case 'subscriber.added':
  await crm.createContact({
    email: data.email,
    source: 'migma',
    tags: data.tags
  });
  break;

Need Help?


Performance: Webhooks are completely non-blocking and never impact API response times. Reliability: Automatic retries with exponential backoff ensure delivery even during temporary outages. Security: HMAC SHA-256 signature verification protects against unauthorized requests.