postmark-webhooks

Receive and process Postmark webhooks. Use when setting up Postmark webhook handlers, handling email delivery events, processing bounces, opens, clicks, spam complaints, or subscription changes.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "postmark-webhooks" with this command: npx skills add hookdeck/webhook-skills/hookdeck-webhook-skills-postmark-webhooks

Postmark Webhooks

When to Use This Skill

  • Setting up Postmark webhook handlers for email event tracking
  • Processing email delivery events (bounce, delivered, open, click)
  • Handling spam complaints and subscription changes
  • Implementing email engagement analytics
  • Troubleshooting webhook authentication issues

Essential Code

Authentication

Postmark does NOT use signature verification. Instead, webhooks are authenticated by including credentials in the webhook URL itself.

// Express - Basic Auth in URL
// Configure webhook URL in Postmark as:
// https://username:password@yourdomain.com/webhooks/postmark

app.post('/webhooks/postmark', express.json(), (req, res) => {
  // Basic auth is handled by your web server or proxy
  // Additional validation can check expected payload structure

  const event = req.body;

  // Validate expected fields exist
  if (!event.RecordType || !event.MessageID) {
    return res.status(400).send('Invalid payload structure');
  }

  // Process event
  console.log(`Received ${event.RecordType} event for ${event.Email}`);

  res.sendStatus(200);
});

// Alternative: Token in URL
// Configure webhook URL as:
// https://yourdomain.com/webhooks/postmark?token=your-secret-token

app.post('/webhooks/postmark', express.json(), (req, res) => {
  const token = req.query.token;

  if (token !== process.env.POSTMARK_WEBHOOK_TOKEN) {
    return res.status(401).send('Unauthorized');
  }

  const event = req.body;
  console.log(`Received ${event.RecordType} event`);

  res.sendStatus(200);
});

Handling Multiple Events

// Postmark sends one event per request (not batched)
app.post('/webhooks/postmark', express.json(), (req, res) => {
  const event = req.body;

  switch (event.RecordType) {
    case 'Bounce':
      console.log(`Bounce: ${event.Email} - ${event.Type} - ${event.Description}`);
      // Update contact as undeliverable
      break;

    case 'SpamComplaint':
      console.log(`Spam complaint: ${event.Email}`);
      // Remove from mailing list
      break;

    case 'Open':
      console.log(`Email opened: ${event.Email} at ${event.ReceivedAt}`);
      // Track engagement
      break;

    case 'Click':
      console.log(`Link clicked: ${event.Email} - ${event.OriginalLink}`);
      // Track click-through rate
      break;

    case 'Delivery':
      console.log(`Delivered: ${event.Email} at ${event.DeliveredAt}`);
      // Confirm delivery
      break;

    case 'SubscriptionChange':
      console.log(`Subscription change: ${event.Email} - ${event.ChangedAt}`);
      // Update subscription preferences
      break;

    case 'Inbound':
      console.log(`Inbound email from: ${event.Email} - Subject: ${event.Subject}`);
      // Process incoming email
      break;

    case 'SMTP API Error':
      console.log(`SMTP API error: ${event.Email} - ${event.Error}`);
      // Handle API error, maybe retry
      break;

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

  res.sendStatus(200);
});

Common Event Types

EventRecordTypeDescriptionKey Fields
BounceBounceHard/soft bounce or blocked emailEmail, Type, TypeCode, Description
Spam ComplaintSpamComplaintRecipient marked as spamEmail, BouncedAt
OpenOpenEmail opened (requires open tracking)Email, ReceivedAt, Platform, UserAgent
ClickClickLink clicked (requires click tracking)Email, ClickedAt, OriginalLink
DeliveryDeliverySuccessfully deliveredEmail, DeliveredAt, Details
Subscription ChangeSubscriptionChangeUnsubscribe/resubscribeEmail, ChangedAt, SuppressionReason
InboundInboundIncoming email receivedEmail, FromFull, Subject, TextBody, HtmlBody
SMTP API ErrorSMTP API ErrorSMTP API call failedEmail, Error, ErrorCode, MessageID

Environment Variables

# For token-based authentication
POSTMARK_WEBHOOK_TOKEN="your-secret-token-here"

# For basic auth (if not using URL-embedded credentials)
WEBHOOK_USERNAME="your-username"
WEBHOOK_PASSWORD="your-password"

Security Best Practices

  1. Always use HTTPS - Never configure webhooks with HTTP URLs
  2. Use strong credentials - Generate long, random tokens or passwords
  3. Validate payload structure - Check for expected fields before processing
  4. Implement IP allowlisting - Postmark publishes their IP ranges
  5. Consider using a webhook gateway - Like Hookdeck for additional security layers

Local Development

For local webhook testing, use Hookdeck CLI:

brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/postmark

No account required. Provides local tunnel + web UI for inspecting requests.

Resources

  • overview.md - What Postmark webhooks are, common event types
  • setup.md - Configure webhooks in Postmark dashboard
  • verification.md - Authentication methods and security best practices
  • examples/ - Complete implementations for Express, Next.js, and FastAPI

Recommended: webhook-handler-patterns

For production-ready webhook handling, also install the webhook-handler-patterns skill:

Related Skills

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

github-webhooks

No summary provided by upstream source.

Repository SourceNeeds Review
General

stripe-webhooks

No summary provided by upstream source.

Repository SourceNeeds Review
General

webhook-handler-patterns

No summary provided by upstream source.

Repository SourceNeeds Review