HiheMAX Mail product manual

Set up business email,
then keep it working.
One complete manual.

Follow complete workflows for administrators, mailbox users, campaign operators and developers. Every chapter names the route to open, prerequisites, expected result and common failure.

Workspace administrator

Create a working email workspace

The workspace account administers domains, mailboxes, templates, campaigns, API keys and billing. It is different from a mailbox login.

01

Create the workspace

Open Sign up, enter the workspace owner details, verify the account if prompted, then sign in. Use this account for administration—not for direct IMAP authentication.
02

Choose the next workflow

For a business inbox, connect a domain first. For application email, enable Developer tools, create an API key and either send direct content or activate a template.
03

Know the two logins

Workspace sign-in opens the dashboard. Direct webmail sign-in uses a complete mailbox address such as samson@example.com and that mailbox’s password.
Successful setup means the dashboard loads, the current plan is visible and you can open Domains, Mailboxes, Templates, Campaigns and API from the main navigation.
Domains and DNS

Connect a domain without interrupting mail blindly

Add the domain in HiheMAX Mail, publish the exact records shown for that domain, then verify before creating mailboxes.

01

Add the domain

Open Dashboard → Domains, choose Add domain, enter only the root domain such as example.com, and save. Do not include https://, a path or an email address.
02

Copy generated DNS

Open the domain record. Copy the verification TXT value, MX destination, SPF value and DKIM record exactly as displayed. Your DNS provider may append the root domain automatically; check its preview before saving.
03

Remove conflicts carefully

A second SPF TXT record is invalid; merge permitted senders into one SPF policy. Old MX records can continue routing mail elsewhere. Take a backup of existing records before changing a live domain.
04

Verify

After DNS propagates, return to the domain and choose Verify. Verification checks ownership and the configured mail-server state. DNS propagation may take minutes or several hours depending on TTL and provider caching.
05

Confirm the result

The domain must show verified before mailbox creation. If it remains pending, compare host, type and value character-for-character and query the public DNS record outside your provider dashboard.
Do not delete working MX records until you intend to move inbound mail. Domain verification and live mail cutover are related but not identical operations.
Mailbox administration

Create and manage professional addresses

A mailbox is an actual account under a verified domain, with its own address, password, storage quota and optional forwarding settings.

01

Check prerequisites

The domain must be verified and the active plan must have available mailbox capacity. Open Dashboard → Mailboxes.
02

Create the account

Choose the verified domain, enter the local part before the @ sign, set a display name and strong mailbox password, then save. The resulting address must be unique.
03

Share access safely

Give the user the full address, mailbox password and the Webmail URL through an appropriate private channel. Never place mailbox passwords in tickets or screenshots.
04

Update or remove

Use the mailbox action menu to change editable account details, logo, forwarding or active state. Deleting a mailbox is destructive; export or preserve required mail first.
Workspace credentials administer every mailbox owned by the workspace. Mailbox credentials sign into one mailbox directly.
Mailbox user

Use the inbox, not a decorative preview

Webmail operates against the currently selected mailbox. The interface combines folders, message results, the reader and adjacent productivity tools.

01

Enter webmail

Workspace owners can open Dashboard → Webmail and select an account. Mailbox users can open /webmail-login and enter the full address and mailbox password.
02

Read and organise

Choose Inbox, Sent, Drafts, Trash or Spam; select a message; then reply, forward, star, flag, move or mark it unread. Search operates in the selected mailbox context.
03

Compose

Choose Compose, add recipients, subject and message content, then attach files if required. Confirm the selected From identity before sending.
04

Use workspace tools

Contacts stores people and audience groups. Notes and Calendar remain associated with the selected mailbox. Switching mailboxes changes the active data context.
If the message list is empty, first confirm the selected mailbox and folder. A successful workspace login does not prove that a mailbox exists or has received mail.
Audience management

Turn contacts into reusable groups

Contacts support everyday address-book use and campaign audiences. Groups are labels stored on each contact.

01

Create a contact

Open Webmail → Contacts, choose Add contact, and enter a valid email address plus the person or company details you need.
02

Assign groups

Enter one or more group names separated by commas—for example customers, lagos, product-updates. Reuse the same spelling to keep one coherent group.
03

Maintain consent

A group is an organisational label, not proof of marketing consent. Only campaign people who should receive the message and maintain your own consent or suppression process.
04

Check duplicates

Campaign preflight normalises email addresses and sends once per unique address even when a contact belongs to multiple selected groups.
Template studio

Build reusable messages with safe variables

Templates hold a subject, HTML design, optional text alternative, category and lifecycle status. API and campaign sending require an active template.

01

Create

Open Dashboard → Templates, choose a blank design or starting layout, name it clearly and write the saved subject, HTML and text alternative.
02

Add variables

Use double braces such as {{first_name}}, {{company}} or {{order_id}}. Campaigns automatically provide name, first_name, email and company from contacts.
03

Preview and test

Preview representative short, long and missing variable values. Keep critical information understandable in the text alternative and do not rely on images alone.
04

Activate

Save drafts while editing. Change status to active only when the design is ready for API or campaign use. Archive obsolete designs instead of silently reusing them.
HTML variable values are escaped during rendering. Variables are content fields, not a way to inject arbitrary HTML into the template.
Campaign operator

Prepare, test, review and send a campaign

Campaigns combine one active template, a subject and one or more contact groups. Delivery consumes the same visible email credits as API sending.

01

Prepare the audience

Create contacts, assign consistent group names and confirm the addresses should receive this communication.
02

Create the campaign

Open Dashboard → Campaigns, choose New campaign, enter an internal name, select an active template, set the subject and choose recipient groups.
03

Inspect preview

The audience panel lists matching contacts. Save the campaign, then use Send test to me; the test goes to the signed-in workspace owner and consumes one metered credit.
04

Run preflight

Choose Review & send. Server-side preflight recalculates unique recipients, confirms template ownership and status, and compares required credits with the current balance.
05

Confirm delivery

Confirm the displayed recipient count. The final result records recipient, sent and failed totals. A completed campaign cannot be sent again; create a new campaign for another delivery.
An accepted send is not a guaranteed inbox placement. Provider acceptance, recipient-server delivery, spam filtering, bounces and human engagement are separate states.
Developer tools

Send from a backend with an attributable key

One versioned API and a TypeScript SDK cover transactional and template sending, batch delivery, hosted email verification, your own mailboxes, account inspection and usage history. Every action is metered in credits and charged to the calling key.

01

Enable developer tools

Open Dashboard → API, enable Developer mode if required, then create a key with a label that identifies the application or environment, such as production or staging.
02

Store the key server-side

Copy the hm_… value into a server secret manager or protected environment variable. Never ship it in browser JavaScript, a mobile binary or a public repository.
03

Use the base URL

Every endpoint lives under https://mail.hihemax.com/api/v1 — for example https://mail.hihemax.com/api/v1/send. Use the canonical host name without a trailing slash.
04

Install the SDK

Node and TypeScript projects can install @hihemax/mail instead of hand-rolling HTTP calls.
npm install @hihemax/mail
05

Understand metering

Every delivered send consumes one credit; new workspaces start with a starter balance. Read the live balance anytime with GET /api/v1/me (see Account, usage and errors).
The five developer chapters below are the full manual: API keys and authentication, sending and batch, email accounts, hosted verification, and the account/usage/error reference. The compact single-page reference stays available at /docs/api.
Authentication

Create, copy, rotate and revoke API keys

Keys authenticate every request and are attributed to every metered action. Keys are stored encrypted server-side, can be re-copied when you need the raw value again, and are revoked independently.

01

Send a key two ways

Authenticate each request with Authorization: Bearer hm_… or X-API-Key: hm_… — they are equivalent.
# Either header authenticates the same key. Bearer:
curl https://mail.hihemax.com/api/v1/send \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"to": "a@example.com", "subject": "Hi", "text": "Hello!"}'

# Or X-API-Key:
curl https://mail.hihemax.com/api/v1/send \
  -H "X-API-Key: hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"to": "a@example.com", "subject": "Hi", "text": "Hello!"}'
02

Copy a key again

Open Dashboard → API and choose the reveal action on the key row. The raw hm_… value appears in the reveal banner with a one-click copy button. Keys created after encrypted key storage can be revealed at any time; a key from before that change cannot be recovered — generate a new key instead.
03

Rotate when a key is exposed

Create a second key labelled for the new deployment, switch the backend to it, then revoke the old key once traffic has moved.
04

Revoke a key

Use the remove action on the key row. Revoked keys fail immediately with a 401 and invalid_api_key until a new key is created.
05

Handle authentication errors

A missing header returns missing_api_key, an incorrectly formatted value returns malformed_api_key, and an unknown or revoked key returns invalid_api_key. All return HTTP 401 with the standard error envelope.
The raw key is displayed exactly once per reveal for a reason. Never paste it into logs, support tickets, client-side code or screenshots. Store it in a secret manager.
Sending

Send transactional messages, templates and batches

POST /send accepts direct content or an active template and can send as one of your own mailboxes. POST /send/batch delivers up to 100 messages in one request. Each delivered message counts as one credit.

01

Send direct content

Supply to (one address or an array, max 50), subject, and at least one of text or html. Optional fields include from/fromName (display name only), replyTo, cc, bcc and tags.
curl -X POST https://mail.hihemax.com/api/v1/send \
  -H "X-API-Key: hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "subject": "Your receipt",
    "text": "Payment received."
  }'
02

Send an active template

Use the same endpoint with a templateId owned by the workspace and in active status, plus a variables object for the double-brace placeholders.
curl -X POST https://mail.hihemax.com/api/v1/send \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "templateId": "YOUR_ACTIVE_TEMPLATE_ID",
    "variables": { "first_name": "Ada", "order_id": "HM-1042" }
  }'
03

Send as one of your mailboxes

Pass fromAccount — the mailbox id or full emailAddress of a mailbox you own. The message is delivered from that verified domain with its own sending identity. See the next chapter, Email accounts.
curl -X POST https://mail.hihemax.com/api/v1/send \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "ada@example.com",
    "subject": "Your order receipt",
    "text": "Thanks for your order!",
    "fromName": "Acme Receipts",
    "fromAccount": "hello@acme.com"
  }'
04

Read the response

A 200 means the sending path accepted the message. The response carries the message id, the recipients, the mailbox from when you sent as an account, your remaining credit balance and a requestId for support.
{
  "success": true,
  "id": "<message-id>",
  "to": ["customer@example.com"],
  "from": "hello@acme.com",
  "creditsRemaining": 97,
  "requestId": "req_abc123"
}
05

Send a batch

POST /send/batch takes a messages array of up to 100 send payloads. Each payload is validated individually; accepted messages consume one credit each and the response reports sent and failed items.
curl -X POST https://mail.hihemax.com/api/v1/send/batch \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "to": "a@example.com", "subject": "Receipt", "text": "Thanks" },
      { "to": "b@example.com", "subject": "Receipt", "text": "Thanks" }
    ]
  }'
In direct sends, from is a display name; the technical envelope still uses the system sender. Pass fromAccount to send from your own address on a verified domain so SPF and DKIM align.
Email accounts

List your mailboxes and send as them

The email-accounts API lists every mailbox your workspace owns. Any usable mailbox can become the From address of an API send, so replies land in your own inbox on a verified domain.

01

List your mailboxes

GET /api/v1/email-accounts is free and needs no credits. It returns the mailboxes created under your verified domains.
curl https://mail.hihemax.com/api/v1/email-accounts \
  -H "Authorization: Bearer hm_your_key"
02

Understand the account object

Each account exposes id, emailAddress, displayName, domainId and domainName, the domainVerified and active flags, a computed usable flag, storage quota and usage in bytes, optional forwardingAddress and logoId, and createdAt.
{
  "accounts": [
    {
      "id": "mailbox_abc123",
      "emailAddress": "hello@acme.com",
      "displayName": "Acme Support",
      "domainId": "dom_xyz",
      "domainName": "acme.com",
      "domainVerified": true,
      "active": true,
      "usable": true,
      "storageQuota": 10737418240,
      "storageUsed": 24117248,
      "forwardingAddress": null,
      "logoId": null,
      "createdAt": "2026-06-10T08:00:00.000Z"
    }
  ],
  "requestId": "req_01"
}
03

Use the usable flag

usable is true only when the mailbox is active and its domain is domainVerified. Filter to usable accounts before choosing a From mailbox.
04

Get one mailbox

GET /api/v1/email-accounts/:emailAccountId returns the single account. A missing or foreign id returns HTTP 404 with the code email_account_not_found. Listing and reading accounts never consume credits.
05

Send as the mailbox

Pass the account id or emailAddress as fromAccount on POST /send, optionally with fromName for the display name. The send path resolves the account, authenticates with its mail credentials and delivers from its verified domain; the response reports the used mailbox in from. The send itself costs one credit.
06

Handle sender rejections

When an account cannot be used the request is rejected before any message is sent and no credit is charged:
CodeMeaning
unknown_senderThe account id or address does not exist or is not yours
sender_domain_not_verifiedThe owning domain is not verified
sender_inactiveThe mailbox is disabled
sender_no_credentialsThe mailbox has no usable credentials
sender_auth_unavailableEncrypted credentials could not be decrypted
07

Create a mailbox through the API

POST /api/v1/email-accounts takes domain (a domain id or a full domain name), localPart, a password of at least 8 characters, and optional displayName and forwardingAddress. The domain must be verified and yours. Creation is free; the workspace plan's mailbox cap still applies.
08

Host subdomains for your users

Own a verified parent such as brnd.ng, then provision each tenant subdomain explicitly with POST /api/v1/domains before creating mailboxes under it. Subdomains of an owned, verified parent return verified: true immediately with autoVerifiedByParent naming the parent; anything else needs the DNS proof in verificationToken. Wildcards like *.brnd.ng are rejected — claim subdomains one by one so every tenant stays attributable.
# 1. Provision the tenant subdomain (auto-verified: you own brnd.ng)
curl -X POST https://mail.hihemax.com/api/v1/domains \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "alice.brnd.ng" }'

# 2. Create a mailbox under it (free; plan mailbox cap applies)
curl -X POST https://mail.hihemax.com/api/v1/email-accounts \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "alice.brnd.ng",
    "localPart": "info",
    "password": "use-a-strong-password",
    "displayName": "Alice Shop"
  }'
09

Point DNS once, then per subdomain

Publish wildcard MX and SPF at the parent registrar so every present and future subdomain routes to HiheMAX, keep DMARC on the parent, and publish one DKIM TXT per claimed subdomain from the dkimRecord returned by POST /domains.
# One-time DNS at the brnd.ng registrar (covers every subdomain)
*.brnd.ng.            MX   10 mail.hdock.app.
*.brnd.ng.            TXT  "v=spf1 include:hdock.app ~all"
_brnd DMARC stays on the parent: _dmarc.brnd.ng. TXT "v=DMARC1; p=none"

# Per claimed subdomain, publish the DKIM record from POST /domains
<selector>._domainkey.alice.brnd.ng.  TXT  "v=DKIM1; k=rsa; p=<public-key>"
In the SDK these helpers map one-to-one: mail.emailAccounts.list(), mail.emailAccounts.get(id) and sending with fromAccount on mail.emails.send(). Domain provisioning and mailbox creation are HTTP-only for now — use the curl shapes above from your platform backend.
Verification

Add hosted OTP and magic-link email verification

Start a verification with one request; HiheMAX Mail renders and delivers the email, then your server checks the code or the user follows a signed link back.

01

Start a verification

POST /api/v1/verify/start takes email, type ('otp' or 'magic_link'), and optional codeLength (4–8), expiresIn (seconds, up to 3600), brandName, redirectUrl for magic links and a metadata object. Starting a verification costs one credit.
curl -X POST https://mail.hihemax.com/api/v1/verify/start \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "type": "otp",
    "codeLength": 6,
    "expiresIn": 900,
    "brandName": "Acme"
  }'
02

Check an OTP

POST /api/v1/verify/check submits { verificationId, code } and returns the verified state or a reason such as invalid_code, expired or too_many_attempts, plus attemptsRemaining. Checking is free.
curl -X POST https://mail.hihemax.com/api/v1/verify/check \
  -H "Authorization: Bearer hm_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "verificationId": "ver_abc", "code": "123456" }'
03

Use the magic-link flow

For magic_link, the delivered email contains a signed confirm link to GET /api/v1/verify/confirm?token=…. The user opens it, and the flow redirects to your configured redirectUrl or returns a success page.
04

Build a secure flow

Set an expiresIn that matches your UX, keep the code short so it is typeable, interpret too_many_attempts as a block rather than endless retries, and never trust client-side state as proof of verification.
Verification emails use the same transactional delivery path and credit metering as direct sends.
Reference

Account, usage, errors and the SDK client

Inspect the calling key and credit balance, read the usage ledger, decode every error response, and drive the entire API from the TypeScript SDK.

01

Inspect the key and balance

GET /api/v1/me returns the key context and the current credit balance in one call.
curl https://mail.hihemax.com/api/v1/me \
  -H "Authorization: Bearer hm_your_key"
{
  "success": true,
  "key": { "id": "key_abc", "label": "production" },
  "credits": { "balance": 97, "currency": "USD" },
  "usage": { "recentCount": 3, "recentCost": 3 },
  "requestId": "req_abc123"
}
02

Read the usage ledger

GET /api/v1/usage?limit=50 returns recent metered events for the key: the action, label, count, cost and timestamp. The same data powers the dashboard chart.
curl "https://mail.hihemax.com/api/v1/usage?limit=50" \
  -H "Authorization: Bearer hm_your_key"
03

Decode the error envelope

Every failure returns one envelope with success: false, an error object and a requestId you can quote to support.
{
  "success": false,
  "error": {
    "type": "insufficient_credits",
    "code": "insufficient_credits",
    "message": "Insufficient credits"
  },
  "requestId": "req_xyz"
}
HTTPError typeMeaning
400invalid_requestValidation failed; read the message field
401authenticationMissing, malformed or revoked API key
402insufficient_creditsBalance too low for the action
404not_foundResource missing or not yours
429rate_limitToo many requests; honor Retry-After
5xxserver / providerServer or downstream provider failure
04

Respect the rate limit

Each key is limited to 60 requests per 60 seconds. Exceeding it returns HTTP 429 with a Retry-After header — back off with exponential delay rather than looping.
05

Use the SDK start to finish

npm install @hihemax/mail, instantiate HihemaxMail with your key, then list mailboxes, send as one of them and read the resulting from.
import { HihemaxMail } from '@hihemax/mail'

const mail = new HihemaxMail(process.env.HIHEMAX_API_KEY!)

// List the mailboxes your workspace owns
const { accounts } = await mail.emailAccounts.list()
const usable = accounts.filter((a) => a.usable)

// Send as one of your mailboxes
const res = await mail.emails.send({
  to: 'ada@example.com',
  subject: 'Your order receipt',
  text: 'Thanks for your order!',
  fromAccount: usable[0].id, // or usable[0].emailAddress
})
console.log('sent from', res.from)

// Inspect the key and balance
const me = await mail.account.me()
console.log(me.credits.balance)
06

Where to look next

The compact single-page reference at /docs/api lists every endpoint, payload and response plus the interactive examples. The OpenAPI specification is maintained beside the SDK.
Metering

Understand what consumed credits

The API dashboard reads the real usage ledger. It does not estimate usage from UI activity.

01

Open the dashboard

Go to Dashboard → API. The summary shows active keys, balance, metered requests and credits consumed.
02

Read the chart

The fourteen-day chart groups recorded usage by day. Recent activity identifies the action, key label, count, cost and timestamp.
03

Match actions

Direct and template sends record the calling API key. Campaign delivery records the Campaigns label; owner tests record Campaign test.
04

Query programmatically

Use GET /api/v1/me for key and balance context or GET /api/v1/usage?limit=50 for recent account events.
Plans and Paystack

Know when paid access renews or expires

Plan limits govern domains, mailboxes and storage. The subscription view combines the stored entitlement with verified Paystack state.

01

Choose a plan

Open Pricing or Settings → Subscription, select monthly or yearly billing and continue to Paystack.
02

Return and verify

After payment, return through the configured callback. The server verifies the transaction before updating entitlement; a browser redirect alone is not proof of payment.
03

Review renewal

Settings shows plan, billing cycle, status and renewal or expiry time when available. A provider subscription code enables the hosted management link.
04

Handle expiry

Expired paid access is evaluated by the authenticated subscription path. Renew or choose an available plan rather than relying on a stale client-side badge.
Never send card details, Paystack authorization data or transaction secrets to support. Provide only the safe transaction reference when investigation requires it.
Operational security

Keep four identities separate

Workspace sessions, mailbox credentials, API keys and infrastructure secrets have different purposes and must not be substituted for one another.

01

Workspace account

Use it for domains, accounts, templates, campaigns, API keys, billing and settings. Protect it with a unique password.
02

Mailbox account

Use the complete email address and mailbox password for direct webmail or mail protocols. Rotate it if exposure is suspected.
03

API key

Use only from trusted backend code. Label by environment, review last use and revoke independently when compromised.
04

Provider secrets

Appwrite service keys, mail-server credentials and Paystack secrets remain server-side and must never appear in screenshots, public pages or client bundles.
Diagnosis

Find the failing layer before retrying

Use the symptom to identify whether the problem is DNS, mailbox authentication, application validation, credits, provider delivery or subscription state.

SymptomCheck firstNext action
Domain stays pendingPublic TXT/MX lookup and exact host/valueCorrect DNS, wait for TTL, then verify again
Cannot create mailboxDomain verification and plan mailbox capacityVerify domain or free/upgrade capacity
Webmail login failsFull address, mailbox password and active accountReset mailbox credentials from the workspace
Template send rejectedTemplate owner, ID and active statusActivate the correct workspace template
Campaign shows zero recipientsSelected group spelling on contactsEdit contacts or choose a populated group
Campaign cannot sendPreflight recipient count and credit balanceTop up credits or reduce audience
API returns 401Header, raw key, revocation statusUse a valid server-side key
API returns 402Credit balancePurchase credits before retrying
API returns 429Retry-After valueBack off; do not loop immediately
Rejected when sending as a mailboxMailbox active state and verified owning domainVerify the domain or choose another usable mailbox
Key cannot be revealed againKey created before encrypted key storageGenerate a new key and swap it in the backend
Subdomain rejected by the APIParent ownership, spelling, wildcard useVerify the parent first; claim subdomains one by one, never *
Mailbox creation hits account_limit_reachedWorkspace plan mailbox capUpgrade the plan; each tenant mailbox counts
Accepted but not in inboxSpam folder, recipient address and provider responseSeparate acceptance from final delivery investigation
Plan looks expiredSettings subscription status and provider renewalVerify transaction or renew through Paystack

What to include in a support request

  • • The affected route or product area.
  • • The approximate timestamp and timezone.
  • • The expected result and the actual result.
  • • A safe error code or request ID, if present.
  • • Never include passwords, raw API keys or payment credentials.
Open support resources