ReceiveHQ

API reference

Webhook payload documentation

ReceiveHQ delivers inbound email to your HTTPS endpoint as an HTTP POST. Choose a payload format per endpoint so your existing Postmark, Mailgun, SendGrid, CloudMailin, Inbound, EmailConnect, or MailSlurp handler can stay unchanged.

Overview

Messages arrive over SMTP, are parsed into a canonical Postmark-shaped object, then encoded for the format configured on the webhook endpoint. Format is snapshotted when a delivery is created — changing an endpoint later does not rewrite in-flight deliveries.

POSTHTTPS webhook URL6 formatsOptional Basic auth
  • postmark — application/json (default)
  • mailgun_sendgrid — multipart/form-data with both providers’ field names
  • cloudmailin — application/json
  • inbound — application/json (inbound.new shape)
  • emailconnect — application/json
  • mailslurp — application/json (+ ReceiveHQ body extension)

Delivery protocol

Request

ItemValue
MethodPOST
URLYour endpoint webhook URL
User-Agentreceivehq-inbound/1.0
Content-Typeapplication/json for Postmark, CloudMailin, Inbound, EmailConnect, and MailSlurp; multipart boundary set automatically for Mailgun / SendGrid
AuthorizationOptional Basic <base64(user:pass)> when configured on the endpoint

Response semantics

StatusBehavior
2xxSuccess — delivery acknowledged
406Permanent drop — no further retries
OtherTransient failure — retried with backoff

ReceiveHQ does not send provider HMAC / signature headers (Postmark X-Postmark-Signature, Mailgun signing, etc.). Authenticate with HTTPS + Basic auth (or your own shared-secret query/path scheme).

Formats

Set payloadFormat when creating or updating a webhook endpoint in the console (Settings / domain setup). Values:

IDLabelEncoding
postmarkPostmark (JSON)application/json
mailgun_sendgridMailgun / SendGrid (multipart)multipart/form-data
cloudmailinCloudMailin (JSON)application/json
inboundInbound / inbound.new (JSON)application/json
emailconnectEmailConnect (JSON)application/json
mailslurpMailSlurp (JSON)application/json

Postmark (JSON)

format = postmarkContent-Type: application/json

Compatible with Postmark Inbound webhook bodies. The full parsed message — including RawEmail — is posted as one JSON object.

Top-level fields

FieldTypeDescription
FromNamestringDisplay name from the From header
MessageStreamstringAlways "inbound"
FromstringSender email address only (not Name <email>)
FromFullAddressStructured From: Email, Name, MailboxHash
TostringFormatted To recipients
ToFullAddress[]Structured To list
Cc / CcFullstring / Address[]Carbon copy
Bcc / BccFullstring / Address[]Blind carbon copy when present in the MIME
OriginalRecipientstringSMTP envelope RCPT TO (or first To if envelope missing)
SubjectstringSubject line
MessageIDstringMessage-ID without surrounding angle brackets, or a generated UUID
ReplyTostringReply-To header value
MailboxHashstringPlus-address local-part after + (e.g. invoices+abc@… → abc)
DatestringParsed Date / Date header
TextBodystringPlain-text body
HtmlBodystringHTML body
StrippedTextReplystringBest-effort quoted-reply strip (not identical to Postmark)
TagstringAlways ""
HeadersHeader[]All headers as { Name, Value }
AttachmentsAttachment[]See Attachments
RawEmailstringFull raw MIME message (UTF-8 string)

Address object

FieldTypeDescription
EmailstringMailbox address
NamestringDisplay name (may be empty)
MailboxHashstringPlus-tag for this address when present

Attachment object

FieldTypeDescription
NamestringFilename
ContentstringBase64-encoded bytes
ContentTypestringMIME type
ContentLengthnumberDecoded byte length
ContentIDstring | nullCID for inline parts; null otherwise

Example

{
  "FromName": "Acme Billing",
  "MessageStream": "inbound",
  "From": "vendor@acme.com",
  "FromFull": {
    "Email": "vendor@acme.com",
    "Name": "Acme Billing",
    "MailboxHash": ""
  },
  "To": "invoices@yourdomain.com",
  "ToFull": [
    {
      "Email": "invoices@yourdomain.com",
      "Name": "",
      "MailboxHash": ""
    }
  ],
  "Cc": "",
  "CcFull": [],
  "Bcc": "",
  "BccFull": [],
  "OriginalRecipient": "invoices@yourdomain.com",
  "Subject": "Invoice 1042",
  "MessageID": "msg-id@mx.example",
  "ReplyTo": "",
  "MailboxHash": "",
  "Date": "Tue, 8 Sep 2026 12:00:00 +0000",
  "TextBody": "Please find invoice 1042 attached.",
  "HtmlBody": "<p>Please find invoice 1042 attached.</p>",
  "StrippedTextReply": "",
  "Tag": "",
  "Headers": [
    { "Name": "Subject", "Value": "Invoice 1042" },
    { "Name": "From", "Value": "Acme Billing <vendor@acme.com>" },
    { "Name": "Date", "Value": "Tue, 8 Sep 2026 12:00:00 +0000" }
  ],
  "Attachments": [
    {
      "Name": "invoice.pdf",
      "Content": "<base64>",
      "ContentType": "application/pdf",
      "ContentLength": 24830,
      "ContentID": null
    }
  ],
  "RawEmail": "From: vendor@acme.com\r\nTo: invoices@yourdomain.com\r\n..."
}

Mailgun / SendGrid (multipart)

format = mailgun_sendgridContent-Type: multipart/form-data

A single multipart body includes both SendGrid Inbound Parse and Mailgun Routes field names. Handlers that expect either provider can read their own keys from the same request.

Each attachment is appended twice: SendGrid-style attachment1, attachment2, … and Mailgun-style attachment-1, attachment-2, … as binary file parts (not base64).

SendGrid-style fields

FieldTypeDescription
fromstringSender (Postmark From)
tostringFormatted To
ccstringFormatted Cc
subjectstringSubject
textstringPlain-text body
htmlstringHTML body
envelopeJSON string{ to: string[], from: string } — SMTP envelope recipients + mail from
headersstringRaw header block: Name: Value lines joined by \n
charsetsJSON stringFixed UTF-8 map for to, from, subject, text, html
emailstringFull raw MIME
attachmentsstringAttachment count as a decimal string
attachmentNfileBinary attachment parts: attachment1, attachment2, …
attachment-infoJSON stringMap of SendGrid keys → { filename, type, content-id? }

Mailgun-style fields

FieldTypeDescription
senderstringEnvelope / From address
recipientstringPrimary recipient (OriginalRecipient)
body-plainstringPlain-text body
body-htmlstringHTML body
message-headersJSON stringArray of [Name, Value] pairs
body-mimestringFull raw MIME
attachment-countstringAttachment count as a decimal string
attachment-NfileBinary parts: attachment-1, attachment-2, …
content-id-mapJSON stringMap of Content-ID → Mailgun attachment key (e.g. attachment-1)

Field sketch

from=vendor@acme.com
to=invoices@yourdomain.com
cc=
subject=Invoice 1042
text=Please find invoice 1042 attached.
html=<p>Please find invoice 1042 attached.</p>
envelope={"to":["invoices@yourdomain.com"],"from":"vendor@acme.com"}
headers=Subject: Invoice 1042\nFrom: Acme Billing <vendor@acme.com>
charsets={"to":"UTF-8","from":"UTF-8","subject":"UTF-8","text":"UTF-8","html":"UTF-8"}
email=<raw MIME>
sender=vendor@acme.com
recipient=invoices@yourdomain.com
body-plain=Please find invoice 1042 attached.
body-html=<p>Please find invoice 1042 attached.</p>
message-headers=[["Subject","Invoice 1042"],["From","Acme Billing <vendor@acme.com>"]]
body-mime=<raw MIME>
attachments=1
attachment-count=1
attachment1=<binary file>
attachment-1=<binary file>
attachment-info={"attachment1":{"filename":"invoice.pdf","type":"application/pdf"}}
content-id-map={}

CloudMailin (JSON)

format = cloudmailinContent-Type: application/json

Normalised hash shape used by CloudMailin JSON webhooks. There is no top-level raw MIME field in this format (use Postmark or Mailgun / SendGrid if you need the full .eml).

Top-level fields

FieldTypeDescription
headersobjectHeader map. Duplicate names become string[]; otherwise string
envelopeobjectSMTP envelope metadata (see below)
plainstringPlain-text body
htmlstringHTML body
reply_plainstringStripped reply text (from Postmark StrippedTextReply)
attachmentsarrayBase64 attachments (see below)

envelope

FieldTypeDescription
tostringPrimary recipient
fromstringEnvelope mail from / From
recipientsstring[]All envelope RCPT TO addresses
helo_domainstringAlways "" (not available after SMTP accept)
remote_ipstringAlways ""
tlsbooleanAlways true

Attachment object

FieldTypeDescription
contentstringBase64-encoded bytes
file_namestringFilename
content_typestringMIME type
sizenumberByte length
dispositionstring"inline" if Content-ID present, else "attachment"
content_idstring?Only included when the part has a Content-ID

Example

{
  "headers": {
    "Subject": "Invoice 1042",
    "From": "Acme Billing <vendor@acme.com>",
    "Date": "Tue, 8 Sep 2026 12:00:00 +0000"
  },
  "envelope": {
    "to": "invoices@yourdomain.com",
    "from": "vendor@acme.com",
    "helo_domain": "",
    "remote_ip": "",
    "recipients": ["invoices@yourdomain.com"],
    "tls": true
  },
  "plain": "Please find invoice 1042 attached.",
  "html": "<p>Please find invoice 1042 attached.</p>",
  "reply_plain": "",
  "attachments": [
    {
      "content": "<base64>",
      "file_name": "invoice.pdf",
      "content_type": "application/pdf",
      "size": 24830,
      "disposition": "attachment"
    }
  ]
}

Inbound / inbound.new (JSON)

format = inboundContent-Type: application/json

Compatible with inbound.new webhook bodies (event email.received). Useful when migrating from Inbound without rewriting your handler.

Attachments include a ReceiveHQ content field with base64 bytes. downloadUrl is left empty — ReceiveHQ does not host vendor-style attachment download URLs.

Top-level fields

FieldTypeDescription
eventstringAlways "email.received"
timestampstringISO 8601 send time
emailobjectParsed email object (see below)
endpointobjectEndpoint metadata: id, name, type "webhook"

email

FieldTypeDescription
idstringReceiveHQ message id (or Message-ID fallback)
messageIdstringSMTP Message-ID header value
from / toobjectAddress group with text + addresses[] of { name, address }
recipientstringPrimary envelope / OriginalRecipient address
subjectstringSubject line
receivedAtstringISO 8601 received time
parsedDataobjectFull parsed payload including bodies, raw MIME, attachments, headers
cleanedContentobjectCleaned html/text mirrors plus flags hasHtml / hasText

email.parsedData

FieldTypeDescription
textBodystringPlain-text body
htmlBodystringHTML body
rawstringFull raw MIME (UTF-8 string)
attachmentsarrayAttachment list (see below)
headersobjectFlat header map (duplicate names joined with comma)

Attachment object

FieldTypeDescription
filenamestringFilename
contentTypestringMIME type
sizenumberByte length
downloadUrlstringAlways "" (use content instead)
contentstringReceiveHQ extension: base64-encoded bytes

Example

{
  "event": "email.received",
  "timestamp": "2026-09-08T12:00:00.000Z",
  "email": {
    "id": "msg-db-id",
    "messageId": "<msg-id@mx.example>",
    "from": {
      "text": "Acme Billing <vendor@acme.com>",
      "addresses": [{ "name": "Acme Billing", "address": "vendor@acme.com" }]
    },
    "to": {
      "text": "invoices@yourdomain.com",
      "addresses": [{ "name": null, "address": "invoices@yourdomain.com" }]
    },
    "recipient": "invoices@yourdomain.com",
    "subject": "Invoice 1042",
    "receivedAt": "2026-09-08T12:00:00.000Z",
    "parsedData": {
      "messageId": "<msg-id@mx.example>",
      "date": "2026-09-08T12:00:00.000Z",
      "subject": "Invoice 1042",
      "from": { "text": "Acme Billing <vendor@acme.com>", "addresses": [{ "name": "Acme Billing", "address": "vendor@acme.com" }] },
      "to": { "text": "invoices@yourdomain.com", "addresses": [{ "name": null, "address": "invoices@yourdomain.com" }] },
      "cc": null,
      "bcc": null,
      "replyTo": null,
      "textBody": "Please find invoice 1042 attached.",
      "htmlBody": "<p>Please find invoice 1042 attached.</p>",
      "raw": "From: vendor@acme.com\r\n...",
      "attachments": [
        {
          "filename": "invoice.pdf",
          "contentType": "application/pdf",
          "size": 24830,
          "contentDisposition": "attachment",
          "downloadUrl": "",
          "content": "<base64>"
        }
      ],
      "headers": { "Subject": "Invoice 1042" }
    },
    "cleanedContent": {
      "html": "<p>Please find invoice 1042 attached.</p>",
      "text": "Please find invoice 1042 attached.",
      "hasHtml": true,
      "hasText": true,
      "attachments": [],
      "headers": {}
    }
  },
  "endpoint": { "id": "ep_id", "name": "Support", "type": "webhook" }
}

EmailConnect (JSON)

format = emailconnectContent-Type: application/json

Compatible with EmailConnect.eu default webhook JSON (message + envelope). Enrichment blocks such as spam scores are omitted unless ReceiveHQ has equivalent data.

Attachments include inline base64 content (sync upload style). Standard Webhooks signing headers are not sent — use HTTPS + optional Basic auth.

Top-level fields

FieldTypeDescription
messageobjectMessage body, addresses, and attachments
envelopeobjectEnvelope / processing metadata

message

FieldTypeDescription
senderobjectsender.email and sender.name
recipientobjectrecipient.email, name (null), tag (plus-address local part when present)
subjectstringSubject line
datestringISO 8601 date
contentobjectcontent.text / html / markdown / links / optional reply
attachmentsarrayAttachment list with content base64, status completed, uploadType sync

message.content

FieldTypeDescription
textstringPlain-text body
htmlstringHTML body
markdownstringPlain text copied as markdown (no converter)
linksarrayAlways [] (link extraction not implemented)
replyobject?Present when stripped reply text is available: { text }

envelope

FieldTypeDescription
messageIdstringSMTP Message-ID
returnPathstringEnvelope mail from
allRecipientsobjectto / cc / bcc address arrays
headersobjectFlat header map
processedobjecttimestamp, domain, alias, originalSize

Example

{
  "message": {
    "sender": { "email": "vendor@acme.com", "name": "Acme Billing" },
    "recipient": { "email": "invoices@yourdomain.com", "name": null, "tag": null },
    "subject": "Invoice 1042",
    "date": "2026-09-08T12:00:00.000Z",
    "content": {
      "text": "Please find invoice 1042 attached.",
      "html": "<p>Please find invoice 1042 attached.</p>",
      "markdown": "Please find invoice 1042 attached.",
      "links": []
    },
    "attachments": [
      {
        "filename": "invoice.pdf",
        "contentType": "application/pdf",
        "size": 24830,
        "content": "<base64>",
        "status": "completed",
        "uploadType": "sync"
      }
    ]
  },
  "envelope": {
    "messageId": "<msg-id@mx.example>",
    "returnPath": "vendor@acme.com",
    "xOriginalTo": "invoices@yourdomain.com",
    "xMailer": null,
    "allRecipients": {
      "to": ["invoices@yourdomain.com"],
      "cc": [],
      "bcc": []
    },
    "headers": { "Subject": "Invoice 1042" },
    "processed": {
      "timestamp": "2026-09-08T12:00:00.000Z",
      "domain": "yourdomain.com",
      "alias": "invoices@yourdomain.com",
      "originalSize": 1234
    }
  }
}

MailSlurp (JSON)

format = mailslurpContent-Type: application/json

Shaped like MailSlurp NEW_EMAIL webhook payloads (metadata fields). Real MailSlurp webhooks omit bodies — ReceiveHQ adds textBody, htmlBody, and attachments so the webhook is usable without a MailSlurp API.

textBody, htmlBody, and attachments are ReceiveHQ extensions. Vendor signature headers (x-signature) are not sent.

Top-level fields

FieldTypeDescription
messageIdstringIdempotent delivery id
webhookIdstringEndpoint id
eventNamestringAlways "NEW_EMAIL"
emailIdstringReceiveHQ message id
createdAtstringISO 8601 event time
to / from / cc / bccstring[] / stringto (string[]), from (string), cc / bcc (string[])
subjectstringSubject line
attachmentMetaDatasarrayVendor-style attachment metadata (id, name, contentType, contentLength)
textBodystringReceiveHQ extension: plain-text body
htmlBodystringReceiveHQ extension: HTML body
attachmentsarrayReceiveHQ extension: metadata + base64 content

Example

{
  "messageId": "delivery-id",
  "webhookId": "endpoint-id",
  "eventName": "NEW_EMAIL",
  "webhookName": "QA",
  "inboxId": "endpoint-id",
  "domainId": null,
  "emailId": "message-id",
  "createdAt": "2026-09-08T12:00:00.000Z",
  "to": ["invoices@yourdomain.com"],
  "from": "vendor@acme.com",
  "cc": [],
  "bcc": [],
  "subject": "Invoice 1042",
  "attachmentMetaDatas": [
    {
      "id": "message-id-att-0",
      "name": "invoice.pdf",
      "contentType": "application/pdf",
      "contentLength": 24830
    }
  ],
  "textBody": "Please find invoice 1042 attached.",
  "htmlBody": "<p>Please find invoice 1042 attached.</p>",
  "attachments": [
    {
      "id": "message-id-att-0",
      "name": "invoice.pdf",
      "contentType": "application/pdf",
      "contentLength": 24830,
      "content": "<base64>"
    }
  ]
}

Attachments

  • Postmark / CloudMailin — base64 in JSON (Content / content).
  • Mailgun / SendGrid — raw binary multipart file parts; metadata in attachment-info / content-id-map.
  • Inbound / EmailConnect — base64 in JSON (content). Inbound downloadUrl is empty.
  • MailSlurp — attachmentMetaDatas for metadata; attachments[] with content for bytes (ReceiveHQ extension).
  • Inline images with a Content-ID are marked accordingly in each format.
  • Prefer reading structured bodies (TextBody / plain / text) for app logic; use raw MIME when you need full fidelity.

Compatibility notes

  • Canonical parse is Postmark-shaped; other formats are transforms of that object.
  • From in Postmark JSON is the bare email address. Use FromFull.Name for the display name.
  • StrippedTextReply / reply_plain are best-effort and may differ from Postmark’s stripper.
  • CloudMailin helo_domain and remote_ip are empty stubs; tls is hardcoded true.
  • Inbound attachment content and MailSlurp textBody/htmlBody/attachments are ReceiveHQ extensions documented above — not present on the vendor wire formats.
  • Unknown format IDs normalize to postmark. Cloudflare Workers bindings and ParseForce per-customer schemas are not offered as formats.

Configure the same format your handler was written for. Switching from Postmark JSON to multipart (or the reverse) without updating your parser will look like “empty” fields even though delivery succeeded with 2xx.