# Receiving emails

> Receive inbound email on a SendPing domain: every message is parsed and delivered to your webhook as an email.received event.

SendPing supports **receiving** email (commonly called *inbound*) alongside sending. Inbound is useful for building support inboxes, processing forwarded receipts and attachments, and replying to customers in-thread — all driven by your own code.

SendPing accepts every incoming message for your receiving domain, parses the body and attachments, stores the message, and then sends a `POST` request to a webhook endpoint you choose. Your application reacts to that callback.

## How it works

SendPing processes all incoming mail for your receiving domain, parses the contents and attachments, and delivers an `email.received` webhook to your endpoint. Receiving always runs on [your own domain](https://www.sendping.co/docs/receiving/custom-domains) — the same domain you verified for sending.

> **Note:** **Any** email sent to your receiving domain is accepted and forwarded to your webhook. Route intelligently on the `to` field of the event: if your domain is `yourdomain.tld`, you receive mail for `anything@yourdomain.tld`. Adding the `MX` record to a subdomain works the same way — `anything@subdomain.yourdomain.tld`.

## 1. Turn on receiving for your domain

Receiving is enabled per domain, and the domain must already be **verified** for sending. See [custom receiving domains](https://www.sendping.co/docs/receiving/custom-domains) for the full walkthrough:

1. **Open Domains** — Go to the Domains page in the SendPing dashboard and open a verified domain.
2. **Turn on Receiving** — Enable the "Receiving" toggle. SendPing then shows the one `MX` record to add.
3. **Add the MX record** — Paste the `MX` record (host and priority) into your DNS provider and save. Mail for `anything@yourdomain.tld` then arrives in SendPing.

## 2. Configure a webhook

Create a webhook endpoint that subscribes to the `email.received` event:

1. **Open Webhooks** — Go to the Webhooks page in the dashboard.
2. **Add a webhook** — Click "Add Webhook" and enter the public URL of your endpoint.
3. **Subscribe to email.received** — Select the `email.received` event type and save.

> **Note:** For local development, expose your localhost server with a tunnel such as [ngrok](https://ngrok.com/download) or VS Code Port Forwarding. These give your dev machine a public URL (e.g. `https://example123.ngrok.io/api/webhook`) that SendPing can call.

## 3. Receive email events

In your application, create a route that accepts `POST` requests and reacts when the event `type` is `email.received`:

**Node.js**

```js
// app/api/events/route.ts (Next.js route handler)
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

export const POST = async (request: NextRequest) => {
  const event = await request.json();

  if (event.type === 'email.received') {
    // event.data.email_id, event.data.from, event.data.to, ...
    return NextResponse.json(event);
  }

  return NextResponse.json({});
};
```

**Express**

```js
import express from 'express';

const app = express();
app.use(express.json());

app.post('/api/events', (req, res) => {
  const event = req.body;

  if (event.type === 'email.received') {
    // process the inbound email by id
    console.log(event.data.email_id, event.data.from, event.data.subject);
    return res.json(event);
  }

  res.json({});
});
```

**Python**

```python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/api/events")
def events():
    event = request.get_json()

    if event.get("type") == "email.received":
        data = event["data"]
        print(data["email_id"], data["from"], data["subject"])
        return jsonify(event)

    return jsonify({})
```

Once you receive the event you can fetch the full message and process its body and attachments. We strongly recommend [verifying the webhook signature](https://www.sendping.co/docs/webhooks/verify) so only genuine SendPing requests are accepted.

## The email.received payload

The webhook body carries the event envelope plus a `data` object with the message **metadata** — id, addressing, `message_id`, subject, and attachment metadata:

```json
{
  "type": "email.received",
  "created_at": "2026-02-22T23:41:12.126Z",
  "data": {
    "email_id": "56761188-7520-42d8-8898-ff6fc54ce618",
    "created_at": "2026-02-22T23:41:11.894719+00:00",
    "from": "onboarding@test.sendping.co",
    "to": ["delivered@test.sendping.co"],
    "bcc": [],
    "cc": [],
    "received_for": ["forwarded@example.com"],
    "message_id": "<111-222-333@email.example.com>",
    "subject": "Sending this example",
    "attachments": [
      {
        "id": "2a0c9ce0-3112-4728-976e-47ddcd16a318",
        "filename": "avatar.png",
        "content_type": "image/png",
        "content_disposition": "inline",
        "content_id": "img001"
      }
    ]
  }
}
```

> **Warning:** The webhook does **not** include the email body, headers, or attachment content — only metadata. Call the [Retrieve received email](https://www.sendping.co/docs/receiving/get-email-content) API for the body and headers, or the [List attachments](https://www.sendping.co/docs/receiving/attachments) API for attachment content. This keeps payloads small enough for serverless platforms with limited request-body sizes.

### Envelope fields

The top-level envelope wraps every webhook and identifies the event:

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | No | The event type. For inbound mail this is always `email.received`. |
| `created_at` | string | No | ISO 8601 timestamp for when the event was generated. |
| `data` | object | No | The message metadata (fields below). |

### data fields

The `data` object carries the parsed addressing and threading metadata for the received message:

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `email_id` | string | No | The received-email id. Pass it to `GET /emails/receiving/:id` to fetch the body/headers, or to `GET /emails/receiving/:id/attachments` for files. |
| `created_at` | string | No | ISO 8601 timestamp (with timezone offset) for when the message was received and stored. |
| `from` | string | No | The envelope/header `From` address of the inbound message. |
| `from_name` | string | null | No | The display name from the `From` header, decoded; `null` when the header carried only an address. |
| `to` | string[] | No | The `To` recipients the message was addressed to. Route or filter on this to decide how to handle the mail. |
| `cc` | string[] | No | The `Cc` recipients, if any (empty array when none). |
| `bcc` | string[] | No | The `Bcc` recipients exposed to SendPing, if any (empty array when none). |
| `received_for` | string[] | No | The address(es) the message was actually delivered for at the SMTP envelope level — useful when the mail was forwarded or aliased and the visible `to` differs from the delivery target. |
| `message_id` | string | No | The original `Message-ID` header (angle-bracketed, e.g. `<111-222-333@email.example.com>`). Use it as `In-Reply-To` to [reply in thread](https://www.sendping.co/docs/receiving/reply-to-emails). |
| `subject` | string | No | The decoded `Subject` header. |
| `attachments` | object[] | No | Attachment **metadata** only (id, filename, content_type, content_disposition, content_id) — not the bytes. See [attachment fields](https://www.sendping.co/docs/receiving/attachments). |

## What you can do with a received email

- [Get the email content](https://www.sendping.co/docs/receiving/get-email-content) — HTML, plain text, and headers.
- [Process attachments](https://www.sendping.co/docs/receiving/attachments) — download and inspect attached files.
- [Forward the email](https://www.sendping.co/docs/receiving/forward-emails) to another address.
- [Reply in the same thread](https://www.sendping.co/docs/receiving/reply-to-emails) using the original `message_id`.

## FAQ

### Will I receive mail for any address at my domain?

Yes. Once the MX record is in place for your [custom domain](https://www.sendping.co/docs/receiving/custom-domains), SendPing receives mail for *any* address at that domain — `<anything>@yourdomain.tld`. Filter or route on the `to` field of the event.

### Can I receive on a subdomain?

Yes. Add the MX record to any subdomain (e.g. `subdomain.yourdomain.tld`) and receive mail there.

### Will I lose mail if my webhook endpoint is down?

No. SendPing stores every inbound message as soon as it arrives, so you can always retrieve it from the dashboard or via the [Receiving API](https://www.sendping.co/docs/receiving/get-email-content) even if your endpoint was unavailable. Failed webhook deliveries are retried, and you can replay individual events from the [Webhooks](https://www.sendping.co/docs/webhooks/overview) page.

### How do I confirm a webhook really came from SendPing?

Every webhook is signed. Verify the signature headers before trusting the payload — see [Verify webhook requests](https://www.sendping.co/docs/webhooks/verify).
