How to Receive Email in Your Backend Application

Your backend can receive email without running an SMTP server. JsonHook handles all email reception and delivers clean JSON to your existing API endpoints.

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

Overview

Receiving email in a backend application traditionally meant running your own SMTP server, handling deliverability, managing MX records, writing a MIME parser, and dealing with spam filtering — a significant operational burden. Most developers avoid this by using a third-party service to handle the email reception layer.

JsonHook is that service. It exposes a simple API: create an inbound address, specify your webhook URL, and every email sent to that address is delivered to your backend as a structured JSON POST request within seconds. Your backend application only needs to implement a standard HTTP endpoint — no email-specific code, no SMTP, no MIME parsing.

This approach works with any backend language or framework. A Django view, an Express route, a FastAPI endpoint, a Rails controller, a Go HTTP handler — all are identical from JsonHook's perspective. If it speaks HTTP and returns 200, it can receive email.

Prerequisites

Before integrating JsonHook into your backend:

  • Backend HTTP server — any framework in any language that can expose an HTTPS endpoint
  • Public HTTPS URL — your webhook endpoint must be reachable from the internet. Use a tunnel (ngrok, Cloudflare Tunnel) in development.
  • JsonHook account and API key
  • Understand the payload schema — review the payload documentation before writing your handler

Add Email Reception to Your Backend

One API call, one HTTPS endpoint, and your backend receives email. Start free.

Get Free API Key

Step-by-Step Instructions

Integrate email reception into your backend in these steps:

  1. Add a new route to your backend. Create an endpoint at a path like /webhooks/inbound-email. Initially, just log the raw request body to understand the payload structure.
  2. Create a JsonHook address:
    POST https://api.jsonhook.com/v1/addresses
    Authorization: Bearer YOUR_API_KEY
    Content-Type: application/json
    
    {
      "webhookUrl": "https://yourapp.com/webhooks/inbound-email"
    }
  3. Store the returned secret securely in your environment variables (not in code).
  4. Implement signature verification in your route handler before processing any payload.
  5. Parse the JSON body and extract the fields your application needs.
  6. Return 200 immediately and defer heavy processing to a background job.
  7. Test end-to-end by sending an email to your JsonHook address and confirming your backend processes it correctly.

Code Example

A Ruby on Rails controller for receiving inbound email:

# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:inbound_email]
  before_action :verify_jsonhook_signature, only: [:inbound_email]

  def inbound_email
    payload = JSON.parse(request.raw_post)
    email = payload["email"]

    InboundEmailJob.perform_later(
      from:        email["from"],
      subject:     email["subject"],
      text_body:   email["textBody"],
      delivery_id: payload["deliveryId"]
    )

    head :ok
  end

  private

  def verify_jsonhook_signature
    sig = request.headers["X-JsonHook-Signature"]
    expected = OpenSSL::HMAC.hexdigest(
      "SHA256",
      ENV["JSONHOOK_SECRET"],
      request.raw_post
    )
    head :unauthorized unless ActiveSupport::SecurityUtils
      .secure_compare(sig, expected)
  end
end

Note: perform_later enqueues a background job (Active Job / Sidekiq), ensuring the controller responds immediately and heavy work happens asynchronously.

Common Pitfalls

Common backend integration mistakes:

  • CSRF protection blocking the webhook. Web frameworks often include CSRF protection that rejects POST requests without a CSRF token. Disable it specifically for your webhook route (while keeping it enabled for all user-facing routes).
  • Body parser consuming raw bytes. HMAC verification requires the raw request body. Configure your framework to provide raw bytes to the webhook route, not a pre-parsed object. In Express, use express.raw(); in Django, read request.body.
  • Secrets in code. Store your JsonHook webhook secret in environment variables or a secrets manager — never committed to your repository.
  • No idempotency. A retry from JsonHook can cause duplicate processing. Log processed deliveryId values and skip duplicates.
  • Assuming the endpoint will never be attacked directly. Even with HMAC verification, enforce rate limiting on your webhook endpoint to prevent resource exhaustion attacks.

Frequently Asked Questions

Does my backend need to be always online to receive email?

No — but brief outages are fine. JsonHook retries failed deliveries (timeouts, 5xx responses) with exponential backoff for up to 2 hours. As long as your backend recovers within that window, all emails will be delivered. For planned maintenance, you can pause delivery on a JsonHook address from the dashboard.

Can I receive email in a serverless function (Lambda, Vercel, Cloudflare Workers)?

Yes. JsonHook works with any HTTPS endpoint including serverless functions. The cold start latency for serverless functions is transparent to JsonHook — it simply waits for the response (up to 10 seconds). For latency-sensitive pipelines, keep your function warm or use a long-running server.

How do I handle different email types in the same backend?

Create separate JsonHook addresses for each email type, each pointing to a different endpoint or route in your backend. This is cleaner than a single endpoint that dispatches based on content — it uses per-address routing instead of subject-line pattern matching.

Can I receive emails from any sender or only specific senders?

By default, a JsonHook inbound address accepts email from any sender. If you need to restrict accepted senders, implement sender validation in your webhook handler by checking email.from against an allowlist before processing the payload.