GenieOSdocs

Transactional sending

Send a single message, batch up to 1,000 at a time, schedule sends in the future, and attach files.

A transactional send is a single message addressed to a single human in direct response to something they did — a receipt, password reset, login code, shipping notification. It is not bulk; it is not lifecycle drip; it is the API surface most teams reach for first.

Single send

import { GenieOS } from 'genieos';

const mg = new GenieOS();

const res = await mg.templates.send(
  {
    to: 'ada@example.com',
    template: 'order_confirm',
    variables: {
      first_name: 'Ada',
      order_total: '\u00a342.99',
      order_lines: ['Hardback notebook', 'Felt-tip pen'],
    },
    // Optional. Defaults to the workspace\u2019s "from" identity.
    from: { email: 'orders@yourbrand.com', name: 'Your Brand' },
    // Optional. Quoted in the audit log; never sent to the recipient.
    metadata: { order_id: 'ord_8a72c0' },
  },
  { idempotencyKey: 'ord_8a72c0:order_confirm' },
);

console.log(res.id); // msg_01JABC...
from genieos import GenieOS

mg = GenieOS()

res = mg.templates.send(
    to="ada@example.com",
    template="order_confirm",
    variables={
        "first_name": "Ada",
        "order_total": "\u00a342.99",
        "order_lines": ["Hardback notebook", "Felt-tip pen"],
    },
    from_={"email": "orders@yourbrand.com", "name": "Your Brand"},
    metadata={"order_id": "ord_8a72c0"},
    idempotency_key="ord_8a72c0:order_confirm",
)

print(res.id)
curl https://api.genieos.pro/v1/transactional/send \
  -H "Authorization: Bearer $GENIEOS_API_KEY" \
  -H "Idempotency-Key: ord_8a72c0:order_confirm" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "ada@example.com",
    "template": "order_confirm",
    "variables": { ... },
    "from": { "email": "orders@yourbrand.com", "name": "Your Brand" },
    "metadata": { "order_id": "ord_8a72c0" }
  }'

The response:

{
  "id": "msg_01JABC...",
  "status": "queued",
  "template": { "name": "order_confirm", "version": 4 },
  "to": "ada@example.com",
  "created_at": "2026-04-20T11:33:00.123Z"
}

status flows queued \u2192 sent \u2192 delivered. Real-time updates arrive over webhooks (transactional.delivered, transactional.opened, transactional.bounced, transactional.complained).

Batch

For one-off blasts (a digest, a change-log notification, a maintenance heads-up), use the batch endpoint. Up to 1,000 recipients per call, and the call counts as a single rate-limit hit.

await mg.templates.sendBatch({
  template: 'weekly_digest',
  recipients: subscribers.map((s) => ({
    to: s.email,
    variables: {
      first_name: s.firstName,
      headlines: s.weeklyHeadlines,
    },
    metadata: { user_id: s.id },
  })),
});

Server-side, GenieOS:

  1. Validates every entry against the schema contract; rejects the entire batch if any entry fails (so you never get a partial 500).
  2. Splits the batch into per-recipient queue items.
  3. Returns { batch_id, accepted: 1000 }.

Per-recipient state lives on transactional.* events keyed by the returned message_ids, identical to single sends.

Scheduling

Add send_at for deferred delivery. Useful for time-zone-aware sends and cron-shaped marketing.

await mg.templates.send({
  to: 'ada@example.com',
  template: 'event_reminder',
  variables: { event_name: 'Quarterly review' },
  // ISO-8601 UTC, max 30 days in the future.
  send_at: '2026-05-01T09:00:00Z',
});

You can cancel a scheduled message before it leaves the queue:

curl -X DELETE https://api.genieos.pro/v1/transactional/msg_01JABC... \
  -H "Authorization: Bearer $GENIEOS_API_KEY"

Attachments

Transactional supports inline attachments up to 10 MB total per message.

await mg.templates.send({
  to: 'ada@example.com',
  template: 'invoice',
  variables: { amount: '\u00a342.99' },
  attachments: [
    {
      filename: 'invoice-INV-1024.pdf',
      content_type: 'application/pdf',
      data_base64: pdfBuffer.toString('base64'),
    },
  ],
});

Inline images for the body are referenced by cid:filename:

attachments: [
  {
    filename: 'header.png',
    content_type: 'image/png',
    data_base64: pngBuffer.toString('base64'),
    inline: true, // sets Content-Disposition: inline; cid set to "header.png"
  },
],
// In the template:  <img src="cid:header.png" alt="" />

Reply-to and headers

Override per send when you need to thread a customer reply into a specific mailbox or add an Auto-Submitted header.

await mg.templates.send({
  to: 'ada@example.com',
  template: 'support_response',
  variables: { ticket_id: 'tkt_123' },
  reply_to: 'support+tkt_123@yourbrand.com',
  headers: {
    'Auto-Submitted': 'auto-replied',
    'In-Reply-To': '<msg-orig@yourbrand.com>',
  },
});

We strip blacklisted headers (Bcc, Resent-*, Return-Path, anything that would compromise deliverability or impersonate). The full deny-list is in the audit of the transactional.send call.

Suppression lists

Recipients who bounce hard, mark as spam, or hit the unsubscribe link land in the workspace\u2019s suppression list automatically. Sending to a suppressed recipient succeeds the API call but emits transactional.suppressed and skips delivery. You can list and clear suppressions via GET /v1/suppressions and DELETE /v1/suppressions/{email}.

Don\u2019t bypass suppression in production

The flag exists for support flows and re-onboarding tools. Sending to a suppressed recipient without that user\u2019s explicit re-consent puts your domain reputation at risk and is a violation of CAN-SPAM / CASL / GDPR. Just don\u2019t.

Rendering preview

Want to see the rendered HTML / text without actually sending?

curl https://api.genieos.pro/v1/transactional/preview \
  -H "Authorization: Bearer $GENIEOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template": "welcome", "variables": { "first_name": "Ada" } }'

Transactional SMS

Transactional SMS uses the Messaging reserved-key kit. Browse the platform keys, preview the exact text, then send through the same Messaging controller that powers the Inbox.

curl https://api.genieos.pro/v1/messaging/transactional/catalog \
  -H "Authorization: Bearer $GENIEOS_API_KEY"
curl -X POST https://api.genieos.pro/v1/messaging/transactional \
  -H "Authorization: Bearer $GENIEOS_API_KEY" \
  -H "Idempotency-Key: auth_login_482915" \
  -H "Content-Type: application/json" \
  -d '{
    "templateKey": "signin-code",
    "to": "+447700900123",
    "variables": { "code": "482915" }
  }'

For raw E.164 sends outside authentication-class keys, include consentProofId. Magic-link SMS must use a branded short-link domain; generic shorteners are rejected before Twilio is called.

Returns { html, text, subject }. Same code path as send; same schema enforcement; no inbox movement; no charge.

On this page