HyoNovaDocumentation

Mail API

Send transactional email and manage contacts from your own systems with the Hyo Mail API — API keys, endpoints, examples, idempotency, errors and limits.

AvailableWorks today for workspaces whose plan includes it.ReferenceFor: Developers, IT & security, AdminsLast updated: Introduced:
On this page

The Hyo Mail API lets your own product send transactional email — receipts, confirmations, account notices — one message per call, read each message's status, and keep your contacts in sync. It is part of Hyo Mail, and every plan includes it.

Base URL and authentication#

All endpoints live under one base URL:

text
https://api.hyonova.com

Send an API key as a bearer token on every call:

http
Authorization: Bearer YOUR_API_KEY

Keys start with hnm_. Keep them on your servers. Never put an API key in a web page or a mobile app.

Mail › Automations › API guide shows the same address as Endpoint, with a copy button.

Before you start#

  • A verified sender. Your company verifies its own sending domain (DKIM, SPF through a custom MAIL FROM domain, and DMARC) and adds a sender address on it. Without one, sending answers 409 SENDER_NOT_VERIFIED. See Senders, domains and settings.
  • Mail credits. Each message uses one Mail credit. See Credits below.
  • An API key. Someone with Mail administration — by default an Owner or Admin — creates it.

API keys#

  1. Open the keys. Go to Mail › Automations and open the API keys tab.
  2. Create a key. Choose New API key, give it a name that says where it is used, and tick the Scopes it needs (table below). Give each integration only the scopes it uses.
  3. Copy the secret. Choose Create key. Your new API key is shown once. Store it in your secret manager. If you lose it, create a new key.
ScopeAllows
mail.transactional.sendPOST /mail/v1/send
mail.events.readGET /mail/v1/me, GET /mail/v1/messages, GET /mail/v1/messages/{id}, GET /mail/v1/forms
mail.contacts.readGET /mail/v1/contacts, GET /mail/v1/contacts/{id}
mail.contacts.writePOST /mail/v1/contacts
mail.conversions.writePOST /mail/v1/conversions
mail.integrations.writeThe store endpoints used by the WordPress and WooCommerce plugin

The table of keys shows each key's prefix, scopes and Last used. Revoke stops a key immediately; calls with it then answer 401 API_KEY_INVALID. HyoNova stores only a hash of each key.

Endpoints#

MethodPathScopePurpose
POST/mail/v1/sendmail.transactional.sendQueue one email for delivery
GET/mail/v1/messages/{id}mail.events.readOne message with its delivery events
GET/mail/v1/messagesmail.events.readThe message log, newest first
GET/mail/v1/memail.events.readThe company and key behind the API key, and the limits
GET/mail/v1/contactsmail.contacts.readList contacts
GET/mail/v1/contacts/{id}mail.contacts.readOne contact, by ID or by email address
POST/mail/v1/contactsmail.contacts.writeCreate or update a contact by email
GET/mail/v1/formsmail.events.readActive sign-up forms
POST/mail/v1/conversionsmail.conversions.writeRecord an order or another conversion for campaign attribution
GET/mail/v1/openapi.jsonnoneThe OpenAPI 3.0 document of this API

To be told about delivery events instead of asking, use Webhooks.

Send an email#

POST /mail/v1/send queues one message and answers 202 Accepted. Send either a template with variables, or a subject and HTML:

FieldTypeNotes
tostring or object"name@example.com" or { "email": "name@example.com", "name": "Name" } (name up to 120 characters)
templateIdstringA template from Mail › Campaigns › My templates. Required unless you send subject and html
variablesobjectValues for the template's variables, such as { "order_number": "1001" }. Strings, numbers, booleans or null
subjectstringUp to 500 characters. Required with html when there is no templateId
htmlstringUp to 400,000 characters. Unsafe content is removed
textstringOptional plain-text version, up to 100,000 characters
senderIdstringA verified sender. Without it, the default verified sender is used
replyTostringA reply-to address
tagsobjectYour own labels, such as { "type": "receipt" } (keys up to 40, values up to 120 characters)
idempotencyKeystringUp to 160 characters. You can send it as the Idempotency-Key header instead

Templates use variables such as {{ order_number }}, with an optional fallback: {{ order_number | default:"—" }}. Values are always inserted as text.

cURL
curl -X POST "https://api.hyonova.com/mail/v1/send" \
  -H "Authorization: Bearer $HYO_MAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001-confirmation" \
  -d '{
    "to": { "email": "name@example.com", "name": "Customer" },
    "templateId": "TEMPLATE_ID",
    "variables": { "order_number": "1001" }
  }'

The response:

json
{ "id": "6f1c2d4e-9a7b-4c1d-8e2f-3a4b5c6d7e8f", "status": "queued", "replayed": false }

Keep the id: it identifies the message in the message log, in GET /mail/v1/messages/{id} and in webhook events.

Idempotency#

Give every logical message its own key — for example order-1001-confirmation — and send the same key again when you retry after a timeout or a network error.

  • The Idempotency-Key header works on every POST endpoint. The first answer under a key is kept for 24 hours for that API key. A retry with the same key and the same body gets the same answer again, with the header Idempotent-Replayed: true. The same key with a different body answers 409 IDEMPOTENCY_CONFLICT. Header keys can be up to 200 characters.
  • On POST /mail/v1/send the key also names the message for your whole company: a later call with the same key returns the original message with "replayed": true and sends nothing new. Keep send keys to 160 characters.

Read a message#

GET /mail/v1/messages/{id} returns one message with its events:

bash
curl "https://api.hyonova.com/mail/v1/messages/MESSAGE_ID" \
  -H "Authorization: Bearer $HYO_MAIL_API_KEY"
json
{
  "id": "6f1c2d4e-9a7b-4c1d-8e2f-3a4b5c6d7e8f",
  "recipient": "name@example.com",
  "subject": "Your order 1001",
  "templateId": "TEMPLATE_ID",
  "status": "delivered",
  "providerMessageId": "…",
  "errorCode": null,
  "createdAt": "2026-10-18T09:30:00.000Z",
  "sentAt": "2026-10-18T09:30:02.000Z",
  "lastEventAt": "2026-10-18T09:30:05.000Z",
  "events": [
    { "type": "sent", "at": "2026-10-18T09:30:02.000Z" },
    { "type": "delivery", "at": "2026-10-18T09:30:05.000Z" }
  ]
}

GET /mail/v1/messages lists messages, newest first, as { "items": [...], "nextCursor": "…" }. Query parameters: limit (1–100, default 25), cursor (the previous nextCursor), status, q (part of the recipient or subject), since (an ISO date-time) and tag (a tag key).

Message statuses#

StatusMeaning
queuedAccepted by the API, waiting to be sent
deferredWaiting for HyoNova's sending pace; it is sent automatically later
processingBeing handed to the email service
sentAccepted by the email service
deliveredThe recipient's mail server accepted it
bouncedThe address does not exist or refused the message permanently
complainedThe recipient reported the message as spam
rejectedThe email service refused the message
failedNot sent; errorCode says why — MAIL_CREDITS_EXHAUSTED (no credits left) or SENDING_SUSPENDED (HyoNova paused sending for your company)
suppressedNot sent, because the address bounced or complained before (errorCode SUPPRESSED)

An unsubscribe from marketing email does not stop transactional email. A hard bounce or a complaint does.

Account: GET /mail/v1/me#

Use it to check a key. It returns the organization (id, name), the key (id, name, prefix, scopes, expiresAt), entitlements (api, smtp), limits (requestsPerMinute, idempotencyWindowHours, keyRotationGraceHours) and apiVersion.

Contacts#

GET /mail/v1/contacts lists contacts as { "items": [...], "nextCursor": "…" }, with limit, cursor, status, q, tag and listId. GET /mail/v1/contacts/{id} accepts a contact ID or an email address.

POST /mail/v1/contacts creates or updates a contact by email. Fields: email (required), firstName, lastName, company, phone, locale, attributes, tags, listIds (up to 20), status (subscribed, pending or unsubscribed) and consent.

Consent is honest by design:

  • A contact becomes subscribed only when you also send consent — { "source": "checkout opt-in", "at": "2026-10-18T09:30:00Z", "reference": "order-1001" }. Without it, a new contact is pending and receives no marketing email.
  • The API never overwrites a stronger state: an unsubscribed, suppressed, bounced or complained contact stays that way.

The answer is the contact, plus created (true for a new contact) and marketable (true only for subscribed).

bash
curl -X POST "https://api.hyonova.com/mail/v1/contacts" \
  -H "Authorization: Bearer $HYO_MAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-name@example.com" \
  -d '{
    "email": "name@example.com",
    "firstName": "Sam",
    "status": "subscribed",
    "consent": { "source": "website newsletter form" }
  }'

Forms and conversions#

  • GET /mail/v1/forms lists your active sign-up forms with publicId, name, fields, consentText, doubleOptIn, language and the hosted url. The WordPress plugin uses it.
  • POST /mail/v1/conversions records an order or another conversion, so campaign reports can attribute revenue to the last verified click. Fields: eventId (your stable ID — sending it again never adds revenue twice), eventType (purchase, order_confirmed, registration, lead or booking), one of contactId, email or customerRef, revenue, currency (three letters, default USD), status (completed, refunded or cancelled), refundedAmount, occurredAt and metadata. It answers 202 with the conversion and an outcome. This endpoint accepts up to 300 requests per minute per key.

Errors#

Errors use this shape. Branch on code, not on the message. Every response carries an X-Request-Id header; include it when you contact HyoNova.

json
{ "error": { "code": "SENDER_NOT_VERIFIED", "message": "Add and verify a sender before sending." } }
StatuscodeMeaning
400VALIDATION_ERRORThe body or query is invalid; the message names each field
401AUTHENTICATION_REQUIREDNo bearer token was sent
401API_KEY_INVALIDThe key is unknown or revoked
401API_KEY_EXPIREDThe key was replaced and its grace period has ended
402INSUFFICIENT_CREDITSYour company has no Mail credits left; nothing was queued
403FORBIDDENThe key does not have the scope this endpoint needs
403FEATURE_NOT_ENTITLEDHyo Mail or its API is not enabled for your company
404NOT_FOUNDThe template, message, contact or list does not exist
409SENDER_NOT_VERIFIEDNo verified sender, or the chosen sender is not verified
409IDEMPOTENCY_CONFLICTThe Idempotency-Key was already used with a different body
429—Too many requests. Detect it by the status; wait for retry-after seconds

Credits#

Each message uses one Mail credit, charged once, when the message is handed to the email service:

  • Retries never charge again.
  • A message the email service permanently rejects gets its credit back. A bounce after the message was accepted does not.
  • When the wallet is empty, POST /mail/v1/send answers 402 INSUFFICIENT_CREDITS. If credits run out between queueing and sending, the message is marked failed with MAIL_CREDITS_EXHAUSTED. Add credits and send it again with a new idempotency key.

There is no monthly allowance and no plan daily cap. See Mail and Sign capacity.

Limits#

  • Requests. 600 requests per minute per API key (300 for POST /mail/v1/conversions). Every answer carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset; a 429 also carries retry-after.
  • Delivery pace. HyoNova paces all sending to stay within its email provider's limits, so a message can wait briefly as queued or deferred before it is sent.
  • Size. html up to 400,000 characters; subject up to 500.

The message log#

Mail › Automations › Messages lists every message sent through the API, with its Recipient, Status and Last event. Open one to see its Events and the Provider message ID. Anyone with access to Mail can read the log; the API keys and Webhooks tabs are for Mail administrators.

The transactional messages page with the Messages, API keys, Webhooks and API guide tabs
Mail › Automations › Messages.

SMTP relay#

Status#

FeatureStatusNotes
Transactional email APIAvailableWorks today for workspaces whose plan includes it.Send email from your own systems with API keys, a message log and signed webhooks. Part of Hyo Mail.
SMTP relay for Hyo MailPlannedOn the HyoNova roadmap. Not built yet and has no release date.Send through Hyo Mail over SMTP.
↑↓ to navigate · Enter to open · Esc to close