Direct email and sender pools
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.
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.
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:
{ "tags": ["sales", "latam"] }
Then create or update a campaign:
{ "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.