How to Set Up an Inbound Email Webhook

Provision an inbound email address and start receiving structured JSON webhooks in minutes. No SMTP server, no MX record changes — just an API call and an HTTPS endpoint.

Table of Contents
  1. Overview
  2. Prerequisites
  3. Step-by-Step Instructions
  4. Code Example
  5. Common Pitfalls

Overview

An inbound email webhook is a combination of two things: an email address that accepts incoming mail, and an HTTP endpoint that receives that mail as a POST request. Setting one up with JsonHook takes under 10 minutes and requires no email server infrastructure.

The setup flow is:

  1. Create a JsonHook inbound address via API (returns an email address and a webhook secret)
  2. Build or identify an HTTPS endpoint that accepts POST requests
  3. Implement HMAC signature verification in your endpoint
  4. Test the full pipeline with a real email send

Once set up, every email sent to your JsonHook address is automatically parsed and delivered to your endpoint — with automatic retries, delivery logs, and HMAC signatures included on all plans.

Prerequisites

What you need before starting:

  • JsonHook account: Register at jsonhook.com. No credit card required for the free tier.
  • API key: Generate one from Settings → API Keys in the dashboard.
  • Webhook endpoint URL: An HTTPS URL that returns a 2xx response to POST requests. Must be publicly reachable — not localhost.
  • A tool for local development: ngrok, Cloudflare Tunnel, or Expose.dev to expose a local server during setup and testing.

Set Up Your Inbound Email Webhook

10 minutes. One API call. Start receiving email as JSON today.

Get Free API Key

Step-by-Step Instructions

Complete setup from scratch:

  1. Create the inbound address:
    curl -X POST https://api.jsonhook.com/v1/addresses   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
        "webhookUrl": "https://yourapp.com/webhooks/email",
        "label": "Production inbound"
      }'
    Response includes: email (the address to receive mail), secret (for HMAC verification), id (address ID for management).
  2. Save the secret securely — store it in your environment as JSONHOOK_SECRET.
  3. Implement your endpoint. At minimum, verify the signature and return 200. Add business logic incrementally.
  4. Send a test email to the address from step 1 — any email client works.
  5. Check delivery in the dashboard under Deliveries. You should see a successful delivery within seconds.
  6. Inspect your server logs to confirm the payload was received and parsed correctly.
  7. Iterate on your handler logic — add field extraction, DB writes, downstream calls, etc.

Code Example

Minimal webhook endpoint implementation in Node.js that handles setup verification and real email processing:

import express from "express";
import crypto from "crypto";

const app = express();

// IMPORTANT: use raw body parser so we can verify HMAC
app.use("/webhooks", express.raw({ type: "application/json" }));

app.post("/webhooks/email", (req, res) => {
  // 1. Verify signature
  const signature = req.headers["x-jsonhook-signature"] as string;
  const expected = crypto
    .createHmac("sha256", process.env.JSONHOOK_SECRET!)
    .update(req.body)
    .digest("hex");

  if (!signature || !crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )) {
    console.warn("Invalid signature — rejecting delivery");
    return res.status(401).json({ error: "Invalid signature" });
  }

  // 2. Parse payload
  const { email, deliveryId, receivedAt } = JSON.parse(req.body.toString());

  // 3. Log and process
  console.log({
    deliveryId,
    receivedAt,
    from: email.from,
    subject: email.subject,
    attachments: email.attachments.length,
  });

  // 4. Acknowledge immediately
  res.sendStatus(200);
});

app.listen(3000, () => console.log("Webhook server running on :3000"));

Common Pitfalls

Issues that commonly arise during initial setup:

  • Endpoint not publicly reachable. JsonHook cannot deliver to localhost. Always use a tunnel tool during development so JsonHook can reach your local server.
  • Using express.json() instead of express.raw(). The parsed body will not match the raw bytes JsonHook signed. Always use raw body parsing for the webhook route.
  • Forgetting to return 200 on the test delivery. If your handler throws an error on the test, it will show as failed in the delivery log and JsonHook will schedule a retry. Make sure your handler handles all edge cases gracefully.
  • Using a self-signed certificate. JsonHook only delivers to endpoints with a valid TLS certificate from a recognized CA. Use Let's Encrypt or a similar CA for your webhook endpoint.
  • Testing with an email sending tool that modifies the message. Some email clients add signatures or modify formatting. Send a simple test from a known-clean source to diagnose unexpected payload contents.

Frequently Asked Questions

How many inbound addresses can I create?

The free tier includes 1 inbound address. The Starter plan ($12/mo) includes 5 addresses. The Pro plan ($39/mo) includes unlimited addresses. You can create and delete addresses via the API or dashboard at any time.

Can I change the webhook URL after creating an address?

Yes. Use the PATCH /v1/addresses/{id} endpoint to update the webhook URL, label, or secret for any address. Changes take effect immediately. In-flight deliveries to the old URL are not affected.

Is the inbound address permanent?

Addresses persist until you explicitly delete them via the API or dashboard. They do not expire. On the free tier, addresses may be deactivated after 90 days of inactivity — a warning email is sent before deactivation.

What does the delivery log show?

The delivery log shows each delivery attempt including: timestamp, delivery ID, address ID, HTTP status code returned by your endpoint, response time, and whether retries are pending. You can view logs in the dashboard or query them via the API for programmatic monitoring.