- The REST API, described on this page.
- The Node.js SDK,
@hiveku-apps/email, which sends single emails through the REST API. See Node.js SDK. - SMTP, for frameworks and services that send over SMTP. See SMTP.
- The Resend-compatible API, for apps built on Resend. See Migrate from Resend.
Authentication
Every request carries an API key as a Bearer token:https://app.hiveku.com/api/v1/email. Call it from your server, never from browser code, where anyone can copy the key.
Send an email
POST /api/v1/email/send sends one email. The key needs the Send Emails permission.
- curl
- Node.js
- Node.js SDK
200 with the email’s id:
sent means Hiveku handed the email off, not that it arrived. Delivery, bounces and opens arrive later as webhook events, and in Settings > Email Service > Email Logs.
Node.js SDK
The@hiveku-apps/email package sends email through the REST API. Use its send() for single emails, through POST /api/v1/email/send. It needs Node.js 18 or later.
send()takesfrom,to,subject,html,text,replyTo,cc,bcc,headers,tagsandattachments.ccandbccare lists, andcontentis the file as base64 text.- It answers what the REST API answers:
id,statusand the other fields above. - A refusal throws a
HivekuEmailError. Itsstatusis the HTTP status,codethe error code, andmessagethe sentence. On a billing block (402), version 1.0.1 puts a code such asaccount_past_dueinmessage. See Errors. - The SDK sets no
Idempotency-Key. For safe retries, batches, templates and lookups, call the REST API. The SDK’ssendBatch()takes at most 50 emails, and version 1.0.1 reads the batch answer under the wrong name, so send batches through the REST API. scheduledAtschedules the email, asscheduled_atdoes (see Fields).
Fields
string | object
required
The sender:
"Acme <noreply@mail.acme.com>", "noreply@mail.acme.com", or { "email": "noreply@mail.acme.com", "name": "Acme" }. The address must be on one of your verified domains, and on the key’s allowed domains if it has any.string | string[]
required
One address or a list. A list may also hold
{ "email": "..." } objects. A name such as "Jane <jane@example.com>" is accepted, but only the address is kept.string | string[]
Copy recipients.
string | string[]
Hidden recipients.
string
required
The subject line.
string
The HTML body. Send
html, text, or both. At least one is required.string
The plain-text body. Sending it along with
html helps delivery.string | string[]
Where replies go.
replyTo works too. An entry that is not an email address is left out and named in warnings. At most 50.array
Files to attach. See Attachments.
string
The same as the
Idempotency-Key header, for clients that cannot set headers. See Idempotency.string | number
When to send it, up to 29 days ahead: an ISO 8601 date and time such as
2026-11-03T09:00:00Z, milliseconds since 1970, or "in 30 min" (minutes, hours or days). A time now or in the past, give or take a minute, sends at once.- The email is stored as
scheduled, and the answer gives itsid,status: "scheduled"andscheduled_at. - At its time Hiveku checks the key, the account, the sender’s domain, the suppression list, your plan and the key’s own daily limit again, then sends it. An email not sent within 15 minutes of its time fails as
schedule_missedand is never sent late. - Scheduling counts against the key’s rate like a send now. An account can have as many scheduled emails waiting as its plan sends in a month; past that,
429 scheduled_limit_reached. - An email whose key is paused or deleted by its time fails and is not sent. So does one that would pass the key’s own daily limit: it fails as
key_daily_limit_reached. - A batch cannot schedule.
to, cc and bcc together.
Headers
Hiveku sets From, To, Cc, Reply-To and Subject from the fields above. Of your own headers, it delivers these:List-UnsubscribeandList-Unsubscribe-Post, for one-click unsubscribeList-IdIn-Reply-ToandReferences, for threadingAuto-SubmittedandPrecedence- any
X-header, except reserved ones such asX-Hiveku-
X- headers may hold text that is not plain ASCII. At most 15 headers are delivered. Anything else is dropped and named in warnings, and the email still goes.
Tags
Tags travel with the email. Webhook events carry them indata.tags, and a webhook can filter on them.
Pass an object of name to value, such as { "category": "orders" }, or Resend’s list, such as [{ "name": "category", "value": "orders" }]. Numbers and true/false become text. A tag in any other shape, such as a plain list of words, is dropped and named in warnings.
Hiveku uses some names itself, so it drops them with a warning: channel, campaign_id, and any name that starts with hiveku_.
Attachments
contentis the file as base64 text. A Node.js Buffer sent as JSON works too.content_typeis optional. Without it, the type comes from the file name.content_idmakes an inline image: refer to it ascid:plus the id in your HTML.contentTypeandcontentIdwork as names too.path, a URL for Hiveku to fetch, is refused with422 attachment_path_not_supported. Send the file itself.- Up to 100 files and 25 MB in all. The whole email may be at most 40 MB as sent. Larger emails are refused with
413. - A batch takes no attachments. Send those emails one at a time.
- An attached web page that asks for a password, or sends the reader to another address as it opens, is refused with
403 attachment_blocked.
Idempotency
Send anIdempotency-Key header to make retries safe. Use a value that names the email, such as order-1024-shipped, from 1 to 256 characters.
- The same key with the same request within 24 hours answers with the first email (
id,status,message_id) and sends nothing again.statusis that email’s status now. - The same key with a different request is refused with
409 invalid_idempotent_request. - While the first request is still being sent, a retry gets
409 concurrent_idempotent_requests. Try again shortly. - If the first request never recorded its result, a retry gets
409 idempotent_request_outcome_unknownwith the email’sid. It may have been sent: look it up before you send again, or use a new key to send regardless. - A send that failed frees its key, so a retry sends.
- Keys belong to one API key. Two API keys can use the same value without clashing.
Batch sending
POST /api/v1/email/batch sends up to 100 emails in one request. Each email takes the fields of a single send, except attachments and scheduled_at.
- Each email is checked on its own. One that fails does not stop the others.
queuedmeans stored and on its way. The answer comes before the emails go out, so follow each one in the logs or by webhook.suppressedlists any recipients left out of an email.- With a key that is not a production key,
"mode": "test"and anotesay nothing was delivered. - The rate limit and the monthly emails count every recipient of every email. When either refuses, the whole batch is refused with
429, and nothing is sent. - One
Idempotency-Keycovers the whole batch. A retry answers with the first ids.
Send with a template
POST /api/v1/email/send-template sends a saved template, filled with your values.
template_idortemplate_namepicks the template.- Placeholders such as
{{customer_name}}and{{order.number}}in the template’s subject, HTML and text are replaced with your values. A placeholder with no value is left as it is. from,subjectandreply_tofall back to the template’s defaults when you leave them out.cc,bcc,headers,tagsandattachmentswork as on a single send.- A missing required variable is refused with
400 missing_variables, which lists them inmissing. An unknown template answers404 not_found. - The answer has the
id, thetemplate(idandname),from,to,subject,statusandcreated_at.
Look up an email
GET /api/v1/email/emails/{id} returns one email: its addresses, subject, HTML and text, status, tags, timestamps (sent_at, delivered_at, opened_at and the rest), open and click counts, any error, and its event timeline.
- The key needs the Read Emails permission. A send-only key gets
403 missing_permission. - A key reads only the emails it sent itself. Any other id answers
404. - Each read is written to the account’s audit log.
- The HTML and text are removed 30 days after sending, and then come back as
null.
POST /api/v1/email/emails/{id}/cancel cancels an email still waiting for its time (status scheduled). One already on its way or sent answers 400 invalid_status. Only the key that scheduled it can cancel it; any other answers 404.
Test keys
Only a production key delivers email. With any other key, Hiveku checks the email as usual (the key, the verified domain, the limits), records it with the statussimulated, answers, and delivers nothing. Simulated emails do not count against your monthly emails. They show in the logs with the status Simulated, so you can open them and check how they rendered. See API keys.
Errors
/send answers an error as { "error": "<sentence>", "code": "<code>" }, plus any details. Batch, send-template and email lookups wrap it as { "error": { "message": "<sentence>", "code": "<code>" } }, and answer a bad key with 401 unauthorized, and a key used from an address it does not allow with 403 ip_not_allowed. The codes below are the ones /send uses.
A
429 carries a Retry-After header and a retry_after field in seconds when waiting helps.