---
title: Mailgun webhooks
description: Verify Mailgun signature fields while retaining unverified event-body provenance.
---

Import the signature verifier and event parser from `@corven/email-sdk/mailgun-webhook`:

```ts
import { Effect } from "effect";
import type { Redacted } from "effect";
import {
  MailgunWebhookEventSchema,
  MailgunWebhookVerifier,
  mailgunWebhookLayer,
  parseMailgunWebhookEvent,
} from "@corven/email-sdk/mailgun-webhook";
import type { MailgunWebhookSignature } from "@corven/email-sdk/mailgun-webhook";

export const verifyIncomingMailgunSignature = Effect.fnUntraced(
  function* verifyIncomingMailgunSignature(signature: MailgunWebhookSignature) {
    const verifier = yield* MailgunWebhookVerifier;

    return yield* verifier.verifySignature(signature);
  },
);

export const parseIncomingMailgunReport = Effect.fnUntraced(function* parseIncomingMailgunReport(
  rawBody: string,
) {
  const event = yield* parseMailgunWebhookEvent(rawBody);

  const category = MailgunWebhookEventSchema.match(event, {
    Complained: () => "ComplaintReport" as const,
    Delivered: () => "RecipientServerAcceptanceReport" as const,
    Failed: ({ severity }) =>
      severity === "permanent"
        ? ("PermanentFailureReport" as const)
        : ("TemporaryFailureReport" as const),
    Other: () => "UnclassifiedReport" as const,
  });

  return { category, provenance: event.provenance };
});

export const incomingMailgunWebhookLayer = (signingKey: Redacted.Redacted) =>
  mailgunWebhookLayer({ signatureField: "signature", signingSecrets: [signingKey] });
```

## Verify signature evidence

`MailgunWebhookVerifier.verifySignature` checks timestamp/token evidence with the configured Redacted signing keys and freshness tolerance. Select `signatureField` explicitly as `signature` or `parent-signature`; the appropriate choice depends on your ingress contract.

The proof covers the signature fields only. Event-body content remains unverified. Store the event with that provenance, even if signature verification succeeds.

## Project event reports

`parseMailgunWebhookEvent(rawBody)` returns supported Delivered, Failed, Complained or Other projections. Failures retain severity where supplied. Projected event provenance remains `UnverifiedPayload`.

Your host must acquire and authenticate the appropriate ingress, bind the account/tenant and retain durable replay state before applying a report. A valid signature, a body account field or a fresh timestamp alone is insufficient.

A recipient-server acceptance report is not permission to send again. Keep event interpretation separate from [outbound recovery policy](/reliability).
