Inbox (receiving)
Receive email on your verified domains, manage mailboxes and aliases, and read and reply to received mail.
Helipod Email can also receive mail for a verified domain (or one of its subdomains). Received messages are stored in your project, can be forwarded, trigger an email.received webhook, and can be read in webmail or through this API.
Receiving needs a paid plan that includes mailboxes (402 PLAN_REQUIRED otherwise). All endpoints on this page accept an email API key (or a Helipod API key with the Email API enabled) and act on the key's project.
Set up receiving
Check the receiving host
/domains/{id}/inbound/check| Query | Type | Notes |
|---|---|---|
host | string | The domain itself or a subdomain of it, for example mail.example.com. Defaults to the domain. Must not be the domain's MAIL FROM (bounce) host. |
curl "https://mail.helipod.io/api/email/domains/3f2a9c4e-1b7d-4e0a-9c55-2d8f6b1a7e10/inbound/check?host=mail.example.com" \
-H "Authorization: Bearer your_email_api_key"{ "success": true, "data": { "host": "mail.example.com", "existingMx": ["mx1.current-provider.example"] } }existingMxlists other mail servers that currently receive mail for the host. If it is not empty, enabling receiving on this host moves that mail to Helipod. Use a dedicated subdomain if you want to keep your current mailbox provider for the main domain.- After you publish the receiving MX record, the domain's
inboundMxValid(see step 3) tells you when it is live.
Enable receiving
/domains/{id}/inboundProp
Type
curl -X POST https://mail.helipod.io/api/email/domains/3f2a9c4e-1b7d-4e0a-9c55-2d8f6b1a7e10/inbound \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"enabled":true,"host":"mail.example.com"}'Returns the domain object. Its dnsRecords now include the receiving MX record. Errors: 409 DOMAIN_NOT_VERIFIED, 400 INVALID_HOST, 409 INBOUND_HOST_TAKEN (the host already receives mail in another project), 402 PLAN_REQUIRED.
Publish the MX record and verify
Publish the receiving MX record from dnsRecords at your DNS provider, then call POST /domains/{id}/verify. inboundMxValid on the domain turns true when the record is live.
Create a mailbox
Create at least one address. Mail to an address that does not exist, or is inactive, is dropped.
Usage and limits
/inbound/statscurl https://mail.helipod.io/api/email/inbound/stats \
-H "Authorization: Bearer your_email_api_key"{
"success": true,
"data": {
"available": true,
"unread": 4,
"monthCount": 128,
"mailboxes": 2,
"maxMailboxes": 5,
"includedMailboxes": 5,
"extraMailboxes": 0,
"aliases": 3,
"maxAliasesPerMailbox": 10,
"storageUsedBytes": 73400320,
"storageLimitBytes": 5368709120,
"receivedBytes": 52428800,
"receivedLimitBytes": 10737418240,
"receivedCycleStart": "2026-10-01",
"includedStorageGb": 5,
"extraStorageGb": 0,
"allowForwarding": true,
"receivingDomains": 1,
"spamTrashDays": 30
}
}The response also carries mailboxUnitPrice and storageGbUnitPrice: your plan's monthly add-on prices for one extra mailbox and one extra GB of storage, as decimal strings in Rupiah. available is false when the plan does not include receiving. Spam and Trash empty themselves after spamTrashDays days. Other received mail is kept until you delete it. If the project no longer has a plan that includes receiving, its received mail is deleted 30 days after the last subscription ended.
Addresses
There are two kinds of address:
- mailbox: a real inbox on a receiving domain. Can forward, notify, and sign in to webmail once it has a password. Limited by your plan's mailboxes (
402 MAILBOX_LIMIT). - alias: another address on the same domain that delivers into a mailbox. Limited per mailbox (
402 ALIAS_LIMIT). There are no catch-all addresses.
Plus-addressing works: mail to [email protected] is delivered to [email protected].
Address object
Prop
Type
List addresses
/inbound/addressescurl https://mail.helipod.io/api/email/inbound/addresses \
-H "Authorization: Bearer your_email_api_key"Create an address
/inbound/addressesProp
Type
curl -X POST https://mail.helipod.io/api/email/inbound/addresses \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{
"kind": "mailbox",
"domainId": "3f2a9c4e-1b7d-4e0a-9c55-2d8f6b1a7e10",
"localPart": "support",
"displayName": "Acme Support",
"forwardTo": ["[email protected]"]
}'Response 201: the address object. A new mailbox receives a short welcome message once its domain's MX record is live.
Errors: 404 DOMAIN_NOT_FOUND, 404 MAILBOX_NOT_FOUND, 409 DOMAIN_NOT_RECEIVING, 400 INVALID_ADDRESS, 409 ADDRESS_EXISTS, 402 MAILBOX_LIMIT, 402 ALIAS_LIMIT, 402 FORWARDING_NOT_ALLOWED.
Update an address
/inbound/addresses/{id}Prop
Type
curl -X PATCH https://mail.helipod.io/api/email/inbound/addresses/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"forwardTo":[],"notify":false}'Returns the address object. Setting mailbox-only fields on an alias returns 400 ALIAS_SETTINGS.
Set a webmail password
/inbound/addresses/{id}/passwordThis route is for the dashboard: an API key gets 403 KEY_NOT_ALLOWED, because a key holder who could set the password could read the mailbox.
Prop
Type
curl -X POST https://mail.helipod.io/api/email/inbound/addresses/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d/password \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"password":"correct-horse-battery-staple"}'Returns the address object with webmail: true. Any existing webmail sessions of the mailbox are signed out. Errors: 400 WEAK_PASSWORD, 400 ALIAS_SETTINGS (aliases cannot sign in).
Delete an address
/inbound/addresses/{id}curl -X DELETE https://mail.helipod.io/api/email/inbound/addresses/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d \
-H "Authorization: Bearer your_email_api_key"{ "success": true, "data": { "deleted": true } }Messages already received are kept.
Messages
Message object
The list returns these fields:
Prop
Type
GET /inbound/messages/{id} adds cc, bcc, replyTo, textBody, htmlBody, attachments (index, filename, contentType, size, and cid/inline for inline images), messageIdHeader, inReplyTo, verdicts (spf, dkim, dmarc, spam, virus) and sizeBytes.
List messages
/inbound/messages| Query | Type | Notes |
|---|---|---|
folder | inbox | spam | archive | trash | Default inbox. |
addressId | string (uuid) | Only messages to this address. |
unread | 1 | true | Only unread messages. |
starred | 1 | true | Only starred messages. |
search | string | Subject, sender, snippet or recipients contain this text. Up to 200 characters. |
limit | number | 1 to 200, default 50. |
cursor | string | nextCursor from the previous page. |
curl "https://mail.helipod.io/api/email/inbound/messages?folder=inbox&unread=1" \
-H "Authorization: Bearer your_email_api_key"{
"success": true,
"data": {
"data": [
{
"id": "4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b",
"addressId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"recipient": "[email protected]",
"deliveredTo": ["[email protected]"],
"to": ["[email protected]"],
"from": "[email protected]",
"fromName": "Ana Putri",
"subject": "Question about my order",
"snippet": "Hi, I ordered on Monday and ...",
"receivedAt": "2026-10-16T10:02:11.000Z",
"isRead": false,
"isStarred": false,
"folder": "inbox",
"hasAttachments": true,
"spamVerdict": "PASS",
"virusVerdict": "PASS"
}
],
"nextCursor": null,
"total": 1
}
}total is the number of messages matching the filters (without the cursor). Errors: 400 INVALID_CURSOR.
Get a message
/inbound/messages/{id}curl https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b \
-H "Authorization: Bearer your_email_api_key"Returns 404 MESSAGE_NOT_FOUND for an unknown id.
Update a message
/inbound/messages/{id}Prop
Type
curl -X PATCH https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"isRead":true,"folder":"archive"}'Returns the message in list form. Errors: 400 NOTHING_TO_UPDATE, 400 INVALID_FOLDER.
Bulk update
/inbound/messages/bulkProp
Type
curl -X POST https://mail.helipod.io/api/email/inbound/messages/bulk \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"ids":["4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b"],"action":"archive"}'{ "success": true, "data": { "updated": 1 } }Ids that do not belong to the project are ignored.
Delete a message
/inbound/messages/{id}curl -X DELETE https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b \
-H "Authorization: Bearer your_email_api_key"{ "success": true, "data": { "deleted": true } }Download an attachment
/inbound/messages/{id}/attachments/{index}index is the attachment's index from the message detail.
curl https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b/attachments/0 \
-H "Authorization: Bearer your_email_api_key"{ "success": true, "data": { "url": "https://...", "expiresIn": 60 } }expiresIn is in seconds. Download the file right away and request a new URL if it expires. Errors: 404 ATTACHMENT_NOT_FOUND.
Download the original message
/inbound/messages/{id}/raw.eml file.curl https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b/raw \
-H "Authorization: Bearer your_email_api_key"Same response shape as an attachment. Returns 404 RAW_NOT_AVAILABLE when the original was not kept (for example a message flagged as containing a virus).
Reply to a message
/inbound/messages/{id}/replycharges your walletProp
Type
curl -X POST https://mail.helipod.io/api/email/inbound/messages/4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b/reply \
-H "Authorization: Bearer your_email_api_key" \
-H "Content-Type: application/json" \
-d '{"text":"Hi Ana, your order ships tomorrow.\n\nAcme Support"}'{ "success": true, "data": { "emailId": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f", "jobId": "eml_c3d4e5f6a7b84c9d8e0f" } }Response status is 202. The reply goes to the original Reply-To (or sender), with the subject prefixed Re: and threading headers set. It goes through the normal send path: it counts toward your quota, appears in the email log under emailId, and can fail with the same errors. The original is not quoted; include it in your body if you want it. Errors: 400 BODY_REQUIRED.