Skip to content

Email to webhook, parsed to JSON

Send mail to an endpoint's mailhooks.cc address and webhooks.cc posts every email to your server as JSON. No mail server to run and no MIME to parse. Each request is signed with Standard Webhooks headers and retried until your server answers 2xx.

Start free:

On every plan. Read the forwarding docs for the full payload and examples in Node and Python.

Set it up

  1. 1

    Create an endpoint

    Sign up free and create an endpoint. It receives email at its slug at mailhooks.cc.

  2. 2

    Add your URL

    In the endpoint's Settings, under Forwarding, enter your server's https URL and save. A signing secret starting with whsec_ is created; copy it into your handler.

  3. 3

    Send a test delivery

    Send test delivery posts the newest email, or a sample, to your URL once and shows the status, the time and the start of your server's answer.

  4. 4

    Turn forwarding on

    From then on every email the endpoint receives is posted to your URL as JSON, usually within a second or two.

What your server receives

One POST per email. The body is the parsed message; the headers let you check that it came from webhooks.cc. Every copy of one email carries the same webhook-id, so a handler can skip a retry it already processed.

POST /hooks/email HTTP/1.1
Content-Type: application/json
webhook-id: msg_0b9e5f3a6c1d4f7e9a2b3c4d5e6f7a8b
webhook-timestamp: 1791450602
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "type": "email.received",
  "timestamp": "2026-10-08T09:30:01.882Z",
  "data": {
    "id": "0b9e5f3a-6c1d-4f7e-9a2b-3c4d5e6f7a8b",
    "address": "[email protected]",
    "tag": "billing",
    "subject": "Question about invoice 2291",
    "from": { "name": "Ines Duarte", "address": "[email protected]" },
    "to": [{ "name": null, "address": "[email protected]" }],
    "text": "Hi, the invoice lists two seats but we have three...",
    "html": "<div>Hi, the invoice lists two seats...</div>",
    "codes": [],
    "links": [],
    "attachments": [
      { "filename": "invoice-2291.pdf", "contentType": "application/pdf", "size": 48211 }
    ],
    "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass", "tls": "TLSv1_3" },
    "headers": { "subject": "Question about invoice 2291", "...": "..." },
    "test": false
  }
}

Shortened. The full JSON also carries the endpoint, cc and reply-to addresses, the date, the message id, the size and which parts were cut. codes and links are left out when the endpoint's owner turned off "Show codes and links found in emails".

Verify the signature

The signature is an HMAC-SHA256 of the id, the timestamp and the raw body, keyed with your endpoint's secret. Check it, and that the timestamp is recent, before you parse the body:

import { verifyStandardWebhookSignature } from "@webhooks-cc/sdk";

export async function POST(request: Request) {
  const rawBody = await request.text();
  const headers = Object.fromEntries(request.headers);
  const age = Math.abs(Date.now() / 1000 - Number(headers["webhook-timestamp"]));
  const ok =
    age <= 300 &&
    (await verifyStandardWebhookSignature(rawBody, headers, process.env.FORWARD_SECRET!));
  if (!ok) return new Response("Invalid signature", { status: 401 });

  const { data } = JSON.parse(rawBody);
  await handleInboundEmail(data); // your code
  return new Response(null, { status: 204 });
}

The official Standard Webhooks libraries verify the same headers in Go, Python, Ruby, Java, PHP, Rust, C# and more.

What people build with it

Handle replies and inbound requests

Give customers or a partner an address and turn what they send into tickets, comments or records in your app.

React to services that only send email

Some vendors notify by email and nothing else. Forward those messages and handle them like any other webhook.

Drive end-to-end tests

Let a test server receive the signup or login email as JSON, with the one-time code and the main link already picked out.

Feed email into a workflow or an agent

Post structured email to an automation, a queue or an AI agent without writing a MIME parser first.

Delivery and limits

  • Any 2xx accepts a delivery; redirects are not followed
  • 15 seconds for your server to answer
  • 8 retries over about a day, then marked failed
  • Every try logged, with a Redeliver button
  • https URLs on public host names only
  • Text and HTML up to 256 KB each; attachment contents not included
  • Every plan, Free included
  • No extra quota: each email counted once when it arrived

Testing the emails your own app sends rather than handling inbound mail? See email testing

Email to webhook questions

Your first forwarded email

Sign up free, create an endpoint, add your URL and send it an email.

No credit card