---
title: Direct email and sender pools
description: Send text or your own HTML without a campaign, add copies, and select accounts by labels.
---

## Send standalone HTML

`POST /v1/messages` accepts standalone content. It does not require a campaign, step, variant or saved contact. `from` must match a connected mailbox in your workspace.

`preheader` optionally sets the inbox preview snippet, using the same `variables.*` placeholders as the body. Its maximum is 300 characters. It is rendered and frozen at acceptance, escaped into hidden HTML, and kept separate from the full `text` alternative.

```bash
curl -X POST https://api.norbelys.com/v1/messages \
  -H "Authorization: Bearer $NORBELYS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: account-notification-1042' \
  -d '{
    "from":"santi@try-arbol.com",
    "to":["ada@example.com","grace@example.com"],
    "cc":["team@example.com"],
    "bcc":["archive@example.com"],
    "reply_to":"santi@try-arbol.com",
    "subject":"Hello {{ variables.company }}",
    "html":"<p>Hello {{ variables.company }}</p>",
    "text":"Hello {{ variables.company }}",
    "variables":{"company":"Acme"}
  }'
```

The `202` response means the message is durably queued. Poll the returned `Location` to see `Queued`, `Sending`, `Sent`, `Failed`, `Suppressed`, `Cancelled` or `Uncertain`. `Sent` means transport acceptance, not guaranteed inbox placement. An uncertain result must be reconciled before sending again. Reuse the original idempotency key for retries; a different body with that key returns `409`.

`to`, `cc` and `bcc` contain email strings. There must be 1–50 distinct total recipients, with at least one To address. Duplicates across all three lists are rejected ignoring ASCII case. They receive one shared MIME message. To and CC are visible to recipients; BCC is omitted from SMTP headers. Gmail and Microsoft API submission includes BCC privately so the provider can construct the recipient envelope. Workspace-authenticated API responses include BCC for the sender's own records.

Each envelope recipient consumes one unit of the mailbox's daily quota. Pacing, provider backoff and suppression checks apply to all sends. If any recipient is suppressed, the entire shared message is suppressed before transport submission; use separate messages when you need independent delivery decisions. Delivery events include `recipient_email` where the provider supplies it. Multi-recipient detail uses `delivery_status: per_recipient`; read the events instead of assuming one recipient's result applies to everyone.

Optional `send_at` schedules delivery at an RFC 3339 timestamp, at most one year ahead. Content and variables are frozen at acceptance. HTML variables are escaped; subject and text variables are plain. Missing values fail validation unless a `default("value")` fallback is present. The same bounded renderer supports `recipient.*` and `variables.*` paths, without executable blocks or arbitrary expressions. Direct `recipient.email` is the first To address; all recipients share that rendered content. Prefer `variables.*` for shared content. Custom variables allow at most 50 keys under the contact custom-field limits. Supply `html` or `text`. Text-only input produces escaped HTML with line breaks and keeps the original text as its MIME alternative. Customer HTML keeps its own layout, styles and colors; when both are supplied, `text` is the alternative. See [text, native HTML and Zapier](/email-content).

Standalone messages return null campaign, sequence, step and variant IDs. They do not create hidden campaigns or add campaign tracking/unsubscribe footers. They appear in the workspace message log and operational totals; campaign engagement reports remain campaign specific. For personalized outreach with unsubscribe handling, use a campaign variant.

## Use campaign content through the same resource

Supply `step_id` with either `person_id`, a `recipient` object, or `person_ids` for a batch of 1–100 saved contacts. Each batch contact gets a separate personalized message and its own outcome. Batches require `Idempotency-Key`. Do not mix standalone `html`/`from` fields with a campaign send shape; unknown and incompatible fields are rejected.

Add `cc` and `bcc` arrays directly to a campaign variant. Each contact's message copies those addresses. Copy settings are versioned with the content: updating the variant does not change accepted messages. The same 50-recipient total and suppression checks apply.

## Select sending accounts dynamically

Label accounts through the existing mailbox resource:

```json
{ "tags": ["sales", "latam"] }
```

Then create or update a campaign:

```json
{ "mailbox_tags": ["sales"], "mailbox_ids": [], "timezone": "America/Bogota" }
```

The pool is the union of explicit `mailbox_ids` and accounts with **any** selected tag in the same workspace. Labels are exact and case-sensitive, unique, and limited to 20 labels of 1–64 characters. An empty `mailbox_tags` array disables tag selection. An empty `mailbox_ids` array removes explicit accounts. `mailbox_count` reports the current union; detail `mailbox_ids` reports only the explicitly configured IDs.

Adding or removing a mailbox label updates the pool immediately, without copying memberships into every campaign. New contacts rotate through eligible accounts. Follow-ups retain their previous account while it remains eligible. Removing it from the pool, disabling it or marking it failed selects another eligible account. Daily limits, minimum intervals and temporary provider backoff wait on the same account, preserving affinity. Accepted or ambiguous submissions are never reassigned or silently resent. With no eligible account, work waits instead of selecting an unrelated workspace account.

An explicitly configured From domain still restricts which accounts can take over; a tag does not authorize arbitrary sender identities. A paused campaign still stays paused.
