MailLandDocs

Inbound Email Overview

Receive and process incoming emails with Mail.Land's inbound gateway

MailLand's inbound gateway receives emails on your domains and delivers them to your application via webhooks or stores them for retrieval. Use inbound processing to build support inboxes, parse machine-generated emails, or route replies to your app.

How It Works

Configure your domain

Point your domain's MX records to MailLand's inbound servers. The domain needs inboundEnabled and an active tenant. MailLand accepts mail only for a recipient matching an enabled route, or any address if catchAllEnabled is on; anything else is refused at RCPT.

Create routing rules

Define routes that match the recipient's local part and assign an action (forward, webhook, or store).

Receive parsed emails

MailLand processes the email (parses headers, body, and attachments) and delivers the result to your webhook endpoint or stores it for API retrieval.

Inbound Settings

Settings are per domain, not workspace-wide:

GET https://api.mail.land/v1/transactional/inbound/domains/{domainId}/settings
PUT https://api.mail.land/v1/transactional/inbound/domains/{domainId}/settings

Authenticate with Authorization: Bearer <token> or X-API-Key: <api key>.

Get inbound settings
curl https://api.mail.land/v1/transactional/inbound/domains/k3v9x2m8q1w7z4/settings \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Inbound settings response
{
  "isEnabled": true,
  "catchAllEnabled": true,
  "maxMessageSizeBytes": 26214400,
  "retentionDays": 30,
  "dailyLimit": 10000,
  "storeEmails": true
}

Update settings:

PUT https://api.mail.land/v1/transactional/inbound/domains/k3v9x2m8q1w7z4/settings
Update settings request
{
  "isEnabled": true,
  "catchAllEnabled": true,
  "maxMessageSizeBytes": 26214400,
  "retentionDays": 30,
  "dailyLimit": 10000,
  "storeEmails": true
}
Update inbound settings
curl -X PUT https://api.mail.land/v1/transactional/inbound/domains/k3v9x2m8q1w7z4/settings \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "isEnabled": true,
    "catchAllEnabled": true,
    "maxMessageSizeBytes": 26214400,
    "retentionDays": 30,
    "dailyLimit": 10000,
    "storeEmails": true
  }'
FieldTypeDescription
isEnabledboolEnable/disable inbound processing for this domain
catchAllEnabledboolAccept mail to any recipient on the domain, not just recipients matching an enabled route
maxMessageSizeBytesintMaximum accepted message size in bytes for this domain, enforced with SMTP 552 5.3.4. The settings API accepts up to 50 MiB, but the platform never accepts more than 25 MiB regardless of this setting
dailyLimitintMaximum inbound messages accepted per day for this domain
retentionDaysintDays to retain stored emails. Default 30, allowed 1–90
storeEmailsboolWhether to store the full parsed email for later retrieval
routeCountintNumber of routes configured for this domain

There is no virus scanning, and there is no single workspace-wide webhookUrl/webhookSecret: webhooks are attached per route via webhookId (see Routes).

maxMessageSizeBytes, dailyLimit and retentionDays are enforced. Mail is received at <inboundSubdomain>.<domain>, the host the MX record points to, and the root domain still receives too. A message over the domain's maxMessageSizeBytes is refused with SMTP 552 5.3.4, and never above the platform limit of 25 MiB even if a larger value is set (the settings API accepts up to 50 MiB, but only 25 MiB is ever enforced). Once dailyLimit (default 10,000) is reached for the day, senders get a temporary 452 4.2.2 and are expected to retry later, mail is not lost. Stored messages are deleted once retentionDays (default 30, allowed 1–90) elapses.

Email Statuses

Returned as strings, not numbers:

StatusDescription
ReceivedEmail received, pending processing
ProcessingBeing processed (parsing, spam check, routing)
ProcessedProcessed and its action completed
DeliveredSuccessfully delivered (forwarded or webhook sent)
FailedProcessing failed
ExpiredRetention period elapsed; email was deleted

There is no separate Quarantined status: spam is a boolean isSpam flag on the email, independent of status.

List Inbound Emails

GET https://api.mail.land/v1/transactional/inbound/emails
List inbound emails
curl https://api.mail.land/v1/transactional/inbound/emails \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
ParameterTypeDefaultDescription
cursorstring-Opaque cursor from the previous page's nextCursor. Omit for the first page.
limitint20Items per page (max 100), clamped rather than rejected
domainIdstring-Filter by domain public ID
statusstring-Filter by email status (Received, Processing, Processed, Delivered, Failed, Expired)
routeIdstring-Filter by matched route
isSpambool-Filter by spam flag. Supplying isSpam (true or false) decides the spam dimension on its own and disables the default excludeQuarantined filter, so isSpam=true returns spam rows (still subject to the stored-only rule below). Omit isSpam to keep the inbox default of hiding spam.
fromdatetime-Emails received after this time
todatetime-Emails received before this time
searchstring-Search subject and sender (address or name); does not match the recipient
sortBystring-Sort field: subject, from, or the default receivedAt
sortDescbooltrueSort descending
excludeQuarantinedbooltrueExclude spam-flagged emails from the list. Ignored when isSpam is supplied.
includeWebhookOnlyboolfalseInclude emails that were not stored at receive time (webhook-only or drop routes). By default these are hidden, so a tenant whose routes are all webhook-only sees an empty list here.

There is no subject-only search, no sender/recipient address filter by themselves, and from/to here are dates, not addresses (use search for a sender/subject text match).

Inbound emails list response
{
  "items": [
    {
      "id": "k7m2x9q4p1w8z3",
      "domainId": "k3v9x2m8q1w7z4",
      "domainName": "yourdomain.com",
      "matchedRouteId": "r7k3m9q2p4w8x1",
      "matchedRouteName": "Support inbox",
      "fromAddress": "[email protected]",
      "fromName": "Customer Name",
      "subject": "Need help with my account",
      "textPreview": "Hi, I need help with...",
      "hasHtml": true,
      "hasText": true,
      "attachmentCount": 1,
      "sizeBytes": 45230,
      "status": "Delivered",
      "spfResult": "Pass",
      "dkimResult": "Pass",
      "dmarcResult": "Pass",
      "isSpam": false,
      "spamScore": 1.2,
      "receivedAt": "2026-02-17T14:30:00Z",
      "expiresAt": "2026-03-19T14:30:00Z"
    }
  ],
  "nextCursor": "eyJ2IjoxLCJzIjoiY3JlYXRlZGF0OmRlc2MiLCJrIjoi...",
  "hasMore": true
}

There is no to field on the list item: recipients live on the detail response's envelopeRecipients/headerRecipients. status is a string, not a number.

Get Email Details

GET https://api.mail.land/v1/transactional/inbound/emails/{id}
Get email details
curl https://api.mail.land/v1/transactional/inbound/emails/{id} \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Returns the parsed email's metadata, headers, a text preview, and attachment metadata, not the full HTML/text body. Fetch the raw message for that (see below).

Inbound email detail response
{
  "id": "k7m2x9q4p1w8z3",
  "domainId": "k3v9x2m8q1w7z4",
  "domainName": "yourdomain.com",
  "matchedRouteId": "r7k3m9q2p4w8x1",
  "matchedRouteName": "Support inbox",
  "fromAddress": "[email protected]",
  "fromName": "Customer Name",
  "subject": "Need help with my account",
  "textPreview": "Hi, I need assistance with...",
  "hasHtml": true,
  "hasText": true,
  "attachmentCount": 1,
  "sizeBytes": 45230,
  "status": "Delivered",
  "spfResult": "Pass",
  "dkimResult": "Pass",
  "dmarcResult": "Pass",
  "isSpam": false,
  "spamScore": 1.2,
  "receivedAt": "2026-02-17T14:30:00Z",
  "expiresAt": "2026-03-19T14:30:00Z",
  "envelopeFrom": "[email protected]",
  "replyTo": "[email protected]",
  "messageId": "<[email protected]>",
  "inReplyTo": null,
  "dateHeader": "2026-02-17T14:30:00Z",
  "authResultsHeader": "spf=pass dkim=pass dmarc=pass",
  "spamReason": null,
  "sourceIp": "203.0.113.5",
  "heloHostname": "mail.example.com",
  "processingError": null,
  "processedAt": "2026-02-17T14:30:01Z",
  "envelopeRecipients": [
    { "address": "[email protected]", "localPart": "support", "matchedRouteId": "r7k3m9q2p4w8x1" }
  ],
  "headerRecipients": [
    { "type": "To", "address": "[email protected]", "name": null }
  ],
  "attachments": [
    { "id": "a3k7m2q9p4w1x8", "fileName": "screenshot.png", "contentType": "image/png", "sizeBytes": 34500, "isInline": false, "contentId": null }
  ],
  "webhookAttempts": [
    { "attemptNumber": 1, "status": "Delivered", "webhookUrl": "https://yourapp.com/webhooks/inbound", "httpStatusCode": 200, "errorMessage": null, "durationMs": 145, "attemptedAt": "2026-02-17T14:30:02Z", "nextRetryAt": null }
  ]
}

Authentication Results

Each inbound email includes SPF, DKIM, and DMARC verification results. The possible values differ per check: headerRecipients[].type is "To", "Cc", or "Bcc" (matching case), and dateHeader is an ISO 8601 timestamp, not the raw RFC 2822 header string.

spfResultDescription
NoneNo SPF record found or check not performed
PassSender is authorized
FailSender is not authorized
SoftFailSender may not be authorized
NeutralNo statement about authorization
TempErrorTemporary DNS error
PermErrorPermanent DNS error
dkimResultDescription
NoneNo DKIM signature found or check not performed
PassSignature verified
FailSignature verification failed
TempErrorTemporary DNS error
PermErrorPermanent DNS error
dmarcResultDescription
NoneNo DMARC policy found or check not performed
PassAlignment passed
FailAlignment failed
QuarantinePolicy recommends quarantine
RejectPolicy recommends reject

Download Raw Email

GET https://api.mail.land/v1/transactional/inbound/emails/{id}/raw
Download raw email
curl https://api.mail.land/v1/transactional/inbound/emails/{id}/raw \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -o email.eml

Returns the original raw email (RFC 5322 format) as a file download.

Delete Emails

Delete a single email:

DELETE https://api.mail.land/v1/transactional/inbound/emails/{id}
Delete inbound email
curl -X DELETE https://api.mail.land/v1/transactional/inbound/emails/{id} \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Bulk delete emails:

POST https://api.mail.land/v1/transactional/inbound/emails/bulk-delete
Bulk delete request
{
  "ids": ["k7m2x9q4p1w8z3", "p3q8w2x7m9k4z1", "w9k3p7m2q8x4z6"]
}
Bulk delete inbound emails
curl -X POST https://api.mail.land/v1/transactional/inbound/emails/bulk-delete \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["k7m2x9q4p1w8z3", "p3q8w2x7m9k4z1", "w9k3p7m2q8x4z6"]
  }'

There is also POST /v1/transactional/inbound/emails/bulk-mark-spam with the same {"ids": [...]} body, to flag up to the same batch of emails as spam in one call.

Quarantine

Emails with isSpam: true show up here (there is no virus scanning, and no separate Quarantined status, spam is a boolean flag). View quarantined emails:

GET https://api.mail.land/v1/transactional/inbound/quarantine
List quarantined emails
curl https://api.mail.land/v1/transactional/inbound/quarantine \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Release an email from quarantine:

POST https://api.mail.land/v1/transactional/inbound/quarantine/{id}/release
Release from quarantine
curl -X POST https://api.mail.land/v1/transactional/inbound/quarantine/{id}/release \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Releasing an email only clears its isSpam flag. It does not re-run routing and does not re-send webhooks. To re-deliver the webhook for a released (or any) email, call POST /v1/transactional/inbound/emails/{id}/replay-webhook: this queues one durable delivery to the route's matched webhook and returns 202 Accepted (see Inbound Webhooks). An optional ?addToAllowlist=true query parameter also adds the sender to the allowlist at release time.

Allowlist and Blocklist

Allowlist

Emails from allowlisted senders bypass spam filtering:

GET https://api.mail.land/v1/transactional/inbound/allowlist
Get allowlist
curl https://api.mail.land/v1/transactional/inbound/allowlist \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
POST https://api.mail.land/v1/transactional/inbound/allowlist
Add to allowlist
{
  "pattern": "[email protected]",
  "matchType": "ExactEmail",
  "description": "Trusted partner"
}
Add to allowlist
curl -X POST https://api.mail.land/v1/transactional/inbound/allowlist \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "pattern": "[email protected]",
    "matchType": "ExactEmail",
    "description": "Trusted partner"
  }'

Returned and sent as strings, not numbers:

Match TypeDescription
ExactEmailMatch exact email address
DomainMatch all emails from domain
WildcardPatternWildcard pattern match

Blocklist

Emails from blocklisted senders are automatically handled:

GET https://api.mail.land/v1/transactional/inbound/blocklist
Get blocklist
curl https://api.mail.land/v1/transactional/inbound/blocklist \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
POST https://api.mail.land/v1/transactional/inbound/blocklist
Add to blocklist
{
  "pattern": "[email protected]",
  "matchType": "ExactEmail",
  "action": "RejectAtSmtp",
  "description": "Known spammer"
}
Add to blocklist
curl -X POST https://api.mail.land/v1/transactional/inbound/blocklist \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "pattern": "[email protected]",
    "matchType": "ExactEmail",
    "action": "RejectAtSmtp",
    "description": "Known spammer"
  }'

Returned and sent as strings, not numbers. Default action is MarkAsSpam if omitted:

Blocklist ActionDescription
RejectAtSmtpReject at SMTP level (550 response)
MarkAsSpamAccept but mark as spam (default)
DropSilently drop the email

Delete from allowlist or blocklist:

DELETE https://api.mail.land/v1/transactional/inbound/allowlist/{id}
DELETE https://api.mail.land/v1/transactional/inbound/blocklist/{id}
Delete from allowlist or blocklist
# Delete from allowlist
curl -X DELETE https://api.mail.land/v1/transactional/inbound/allowlist/{id} \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Delete from blocklist
curl -X DELETE https://api.mail.land/v1/transactional/inbound/blocklist/{id} \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Inbound Statistics

GET https://api.mail.land/v1/transactional/inbound/stats
Get inbound stats
curl https://api.mail.land/v1/transactional/inbound/stats \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
ParameterTypeDescription
domainIdstringFilter by domain public ID
fromdatetimeFilter by receivedAt on or after this time
todatetimeFilter by receivedAt before the end of this day
Inbound stats response
{
  "totalEmails": 1500,
  "todayEmails": 42,
  "receivedCount": 1500,
  "processingCount": 5,
  "processedCount": 1345,
  "failedCount": 20,
  "spamCount": 80,
  "webhookFailedCount": 3,
  "storageUsedBytes": 52428800,
  "byDomain": [
    { "domainId": "k3v9x2m8q1w7z4", "domainName": "yourdomain.com", "count": 1500 }
  ],
  "byStatus": { "Received": 5, "Processing": 5, "Processed": 1345, "Delivered": 1300, "Failed": 20, "Expired": 50 },
  "dailyStats": [
    { "date": "2026-02-17", "total": 120, "processed": 115, "spam": 8 }
  ]
}

There is no spamRate/averageSizeBytes field, the shape above is exhaustive.

Error Responses

StatusCodeDescription
400VALIDATION_FAILEDInvalid request parameters
403FORBIDDENInsufficient permissions
404RESOURCE_NOT_FOUNDEmail not found

Next Steps

  • Routes: configure routing rules for incoming emails
  • Webhooks: receive parsed email data via webhook
  • Domains: set up MX records for inbound processing

On this page