← Back to docs

Inbound Webhooks

Inbound Webhooks

Postscale can parse incoming emails and deliver them to your application via webhooks. This enables powerful use cases like support ticket systems, reply detection, and email-based workflows.

Setup

  1. Configure your domain's MX records to point to Postscale.
  2. From the dashboard, open Webhooks, click Add Webhook, point it at your endpoint URL, and check email.received (any other events you want are also fine — one webhook can subscribe to many).
  3. Copy the signing secret (whsec_…) shown in the dialog. It is only displayed once; save it in your secrets manager.

You can also create the webhook via API:

curl -X POST https://api.postscale.io/v1/webhooks \
  -H "Authorization: Bearer ps_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/postscale-inbound",
    "events": ["email.received"]
  }'

The response body contains the webhook record and the plaintext secret. Save it then — GET /v1/webhooks will not return it again.

Webhook Payload

When an email arrives, Postscale POSTs a JSON body like this to your endpoint:

{
  "event": "email.received",
  "timestamp": "2026-04-25T10:30:00Z",
  "email_id": "0c1f2a3b-…",
  "message_id": "<unique-id@example.com>",
  "from": "customer@example.com",
  "to": "support@yourapp.com",
  "subject": "Help with my order",
  "text": "Hi, I need help with order #12345...",
  "html": "<p>Hi, I need help with order #12345...</p>",
  "headers": [
    { "name": "Message-ID", "value": "<unique-id@example.com>" },
    { "name": "In-Reply-To", "value": "<previous-id@yourapp.com>" }
  ],
  "attachments": [
    {
      "filename": "screenshot.png",
      "content_type": "image/png",
      "size": 45678,
      "url": "https://files.postscale.io/..."
    }
  ],
  "spf": "pass",
  "dkim": "pass"
}

Each request also carries metadata headers:

X-Postscale-Event: email.received
X-Postscale-Delivery-ID: <uuid>      (unique per attempt — use for dedup)
X-Postscale-Attempt: 1               (1-indexed; bumps on retry)
X-Postscale-Signature: t=…,v1=…      (verification — see below)

Verifying Webhooks

Every request signed by Postscale carries an X-Postscale-Signature header of the form t=<unix>,v1=<hex_hmac_sha256>. Verify it on every request to ensure the delivery came from Postscale and was not tampered with or replayed.

The full algorithm, t= / v1= format details, dual-signature handling during a rotation grace window, and Node + Python reference verifiers live in the main Webhooks doc — that page is shared across inbound and outbound webhooks because the verification protocol is identical.

Threading

Use the In-Reply-To and References headers to match replies to original messages and maintain conversation threads.

Attachments

Each attachment exposes a presigned url valid for 24 hours. Download it within that window if you need to keep a copy — Postscale doesn't re-issue URLs on demand.