groundmail

Features

Receive email on your own domain

Replies to receipts, support addresses and reply-by-email flows land in the same account that sends your transactional mail. Point MX at us and handle each message with a webhook or the API.

What you get

Send and receive in one place

One account, one set of domains, and one place to see both sides of the conversation.

Signed webhooks

An email.received event fires when mail arrives, signed and delivered like your outbound delivery events.

Full messages over the API

List received mail with GET /received-emails, filtered by domain. Fetch one message for text and HTML bodies.

Attachments

Attachment names, types and sizes come with every message. Retrieve the base64 content when you need it.

Threading headers

Message-ID, In-Reply-To and References are included, so you can match a reply to the email that caused it.

Spam status

Each message carries a spam verdict, so your handler can drop junk before it reaches a person.

Dashboard view

Scan incoming mail in the Inbound dashboard while you build and debug your handler.

Setup

Turn it on per domain

Inbound is a setting on a domain you already manage in Groundmail.

  • Create a domain with inbound_enabled, or enable it on an existing one
  • Verify the domain and publish the MX record we return
  • Subscribe a webhook to email.received, or poll GET /received-emails
  • Use a subdomain so your main mailbox provider is untouched

Create a receiving domain

curl -X POST https://api.groundmail.dev/domains \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "inbound.example.com",
    "inbound_enabled": true
  }'

# Publish the MX record from the response:
# inbound.example.com  MX 10  mx.groundmail.dev

Payload

A webhook you can wire up in minutes

The event tells you who wrote, to which address, and what they attached. Fetch the full message by id when your handler needs the body.

email.received

{
  "type": "email.received",
  "created_at": "2026-07-26T12:00:01.000Z",
  "data": {
    "email_id": "inb_abc123",
    "from": "Sender <sender@example.org>",
    "to": ["support@inbound.example.com"],
    "subject": "Re: Your receipt",
    "in_reply_to": "<msg_123@yourdomain.com>",
    "spam_status": "NotSpam",
    "attachment_count": 1,
    "attachments": [
      { "filename": "invoice.pdf", "content_type": "application/pdf", "size": 48291 }
    ]
  }
}

FAQ

Common questions

Will this break mail for my main domain?

Inbound needs the hostname's MX record to point at Groundmail, which replaces any mailbox provider on that same hostname. Use a subdomain such as inbound.example.com and your existing mailboxes keep working.

Does the webhook include the message body?

The webhook carries metadata: sender, recipients, subject, threading headers, spam status and attachment details. Fetch GET /received-emails/{id} for the text and HTML bodies and base64 attachment data.

How do I verify the webhook came from Groundmail?

Inbound events are signed like every other webhook event, with the secret shown when you create the endpoint. The Node SDK includes a helper that checks the signature for you.

Put your transactional email on solid ground.

Start free on our transactional pool. Move to dedicated IPs or a dedicated server when you need the isolation. Same API either way.