Skip to main content
Live. This area is documented as current, user-reliable behavior.

Goal

Build reliable downstream automation from StackShift Mail events without trusting unsigned payloads.

Prerequisites

  • A public HTTPS webhook endpoint
  • Storage for webhook endpoint secret values
  • An idempotent event handler

Workflow

1
List events or inspect a message timeline when debugging.
2
Create a webhook endpoint with a URL and explicit eventTypes.
3
Store the returned secret immediately. It is returned on create and rotate.
4
Verify StackShift webhook signatures with timestamp tolerance before processing.
5
Use delivery detail and retry APIs for failed webhook deliveries.

Event types

  • Message events: mail.message.queued, mail.message.sending, mail.message.sent, mail.message.deferred, mail.message.failed, mail.message.bounced, mail.message.suppressed.
  • Suppression events: mail.suppression.created and mail.suppression.deleted.
  • OTP events: mail.otp.created, mail.otp.sent, mail.otp.verified, mail.otp.failed_attempt, mail.otp.failed, mail.otp.expired, and mail.otp.canceled.
  • Template events: mail.template.created, mail.template.updated, mail.template.deleted, mail.template.version.created, and mail.template.test_sent.
  • Domain events: mail.domain.created, mail.domain.verified, mail.domain.failed, and mail.domain.deleted.
  • Webhook events: mail.webhook.created, mail.webhook.updated, mail.webhook.disabled, mail.webhook.delivery.succeeded, and mail.webhook.delivery.failed.

Create a webhook

const webhook = await stackshift.mail.webhooks.create({
  url: 'https://example.com/webhooks/stackshift-mail',
  description: 'Production mail lifecycle events',
  eventTypes: [
    'mail.message.sent',
    'mail.message.bounced',
    'mail.otp.verified',
  ],
})

// Store webhook.secret now. It is only returned on create or rotate.
console.log(webhook.id, webhook.secret)

Verify a signature

import { verifyWebhookSignature } from '@stackshift-cloud/sdk'

const valid = verifyWebhookSignature({
  rawBody,
  signatureHeader: request.headers.get('StackShift-Signature'),
  timestampHeader: request.headers.get('StackShift-Timestamp'),
  secret: process.env.STACKSHIFT_MAIL_WEBHOOK_SECRET!,
  toleranceSeconds: 300,
})

if (!valid) throw new Error('Invalid StackShift Mail webhook signature')

Operational APIs

  • GET /mail/events filters by type, resourceType, resourceId, messageId, from, to, limit, and cursor.
  • GET /mail/messages/{id}/timeline returns timeline items with type, label, createdAt, and optional metadata.
  • GET /mail/webhooks/{webhookId}/deliveries filters webhook deliveries by status, limit, and cursor.
  • GET /mail/webhook-deliveries/{deliveryId} includes delivery attempts.
  • POST /mail/webhook-deliveries/{deliveryId}/retry requests a retry for a delivery.

Expected result

Your application processes mail lifecycle events once, verifies signatures, and can recover failed webhook deliveries.

Common failures

  • Webhook handler does not preserve the raw request body before JSON parsing.
  • Signature verification uses the wrong secret after rotation.
  • Handler is not idempotent and double-processes retried events.
  • Endpoint repeatedly returns non-2xx responses and deliveries move from failed toward abandoned.

Send email

Send a single outbound email with the official SDK or REST API, then inspect the message, attempts, logs, and timeline.

Bounces, suppressions, and reputation

Handle hard and soft bounces, workspace-scoped suppressions, sending limits, warmup stage, domain reputation, and reputation events.

Templates and OTP

Create versioned templates, preview and test them, send from a template, and use the built-in one-time-code challenge flow.