---
name: nitrosend/onboarding
version: 2026.07.01
updated: 2026-07-01
description: Plan a first branded email send on Nitrosend. Either a fast sandbox test or a live approval-ready campaign. From one bounded objective and a nitro_get_status snapshot. Also the canonical primer on Nitrosend vocabulary, send modes, MCP tools, resources, the response envelope, and the delivery lifecycle for agents that have just connected.
---

# Nitrosend Onboarding

**TL;DR.** Nitrosend is an AI-native multi-channel (email + SMS) marketing
automation platform driven through an MCP server. This skill teaches an
agent the platform. Vocabulary, send modes, tool surface, response
envelope, lifecycle, safety. And turns one bounded first-email objective
into a deterministic plan along the campaign-first path. Two paths:
`quickstart_first_email` (fast sandbox test) or `live_campaign_readiness`
(approval-ready live campaign).

**Who this file is for.** Primarily agents immediately after MCP connect.
Also developers evaluating the platform.

**Related artifacts.** `nitro://guide` (authoritative reference, live),
`nitro://schema` (section/trigger/filter specs), `https://nitrosend.com/llms-full.txt`
(full-platform context), `https://api.nitrosend.com/openapi.yaml` (REST API).
If those disagree with this file, they win. They come from runtime state.

## Before you start

The caller needs the Nitrosend MCP connected and a Nitrosend account signed
in. OAuth handles signup and auth in-flow. No API key, no dashboard visit
for standard clients.

**Choose the right surface.** For Claude Desktop, Claude.ai, Claude Cowork,
ChatGPT, Cursor, Gemini, VS Code, Windsurf, and Zed, use the remote MCP
connector. Do not recommend the Nitrosend CLI, `nitrosend login`, or the local
`@nitrosend/mcp` bridge for standard Claude Desktop setup. Those are terminal
and stdio fallback surfaces, not the chat-client onboarding path.

The Nitrosend CLI is for a user terminal, CI, and headless agent runners with
real shell and network access. If an agent's command sandbox cannot reach
`api.nitrosend.com`, verify Nitrosend through the connected MCP tools instead
of trying to install or authenticate the CLI inside that sandbox.

**Connect once.** Pick your client:

```bash
# Claude Code
claude mcp add --transport http nitrosend https://api.nitrosend.com/mcp

# Codex CLI
codex mcp add nitrosend --url https://api.nitrosend.com/mcp
```

For **Claude Desktop**, **ChatGPT** (Developer Mode), **Cursor**, **Gemini**,
**VS Code**, **Windsurf**, or **Zed**, add a remote HTTP MCP server with URL
`https://api.nitrosend.com/mcp` via the client's MCP settings UI. Per-client
guides: <https://docs.nitrosend.com/integrations/overview>.

Sign in when your client opens the browser prompt. If it does not prompt:

- Claude Code: `/mcp`
- Codex CLI: `codex mcp login nitrosend`

Headless or CI clients without an MCP client can authenticate with a
`nskey_live_*` Bearer token instead. The key can be minted entirely over
HTTP with no dashboard visit — see "Headless / coding-agent path" below.

**Verify connection.** After signing in, call `nitro_get_status`. A
healthy connection returns JSON with `account`, `brand`, `issues`, and
`recommendations` keys. Failure at this step is almost always an auth
problem. Re-run the login command above. Then read `nitro://guide` and
`nitro://account` to ground the session.

Once connected, call `nitro_get_status`. Its JSON response is the
`account_status_json` input to this skill.

## Headless / coding-agent path (no dashboard, no MCP client)

For coding agents and builder stacks that can call HTTP but have no MCP
client (Replit, Lovable, v0, Bolt, CI runners), the whole first-send loop
runs over REST with zero dashboard visits:

1. **Create an account.** `POST /v1/signup` with `{ "user": { "email", "password" } }`. Returns a 15-day JWT in the `Authorization` response header.
2. **Mint an API key.** `POST /v1/my/api_keys` with that JWT. Returns `nskey_live_...`; store it as a secret.
3. **Send immediately from the sandbox.** `POST /v1/my/messages` with `{ "channel": "email", "to", "subject", "html" }` plus an `Idempotency-Key` header. A brand-new free account sends from the shared sandbox (`<brand>@nitr-o.com`) with no DNS, capped at 50 emails/month. Sending is async, so poll `GET /v1/my/messages/{id}` and read `status`.
4. **Go to production (two links you hand the user).** Verifying a real sending domain needs DNS and paying needs a card, so hand both off: create a domain and share its verify link, and share the Stripe `checkout_url` from `POST /v1/my/subscription/checkout`. $0 plans activate with no card.

Node/Python `fetch`/`requests` calls work as-is; only the bare `Python-urllib` user agent is WAF-blocked. Once a client can speak MCP, the `nitro_*` tools above are the richer surface.

## Core principles

Apply these before doing anything else:

1. **Campaign-first.** For "send an email" asks, default to a campaign
   (one design → audience, approval gate). Flows are for automation, not
   one-shots; transactional is for system messages, not marketing. This
   ordering matches how users think in a marketing context and keeps the
   delivery lifecycle explicit.
2. **Brand auto-applies.** Do not invent colors, fonts, or an unsubscribe
   footer. Read `nitro://brand-kit` before composing; pass `header`/`footer`
   sections with empty props.
3. **Never send live without explicit user confirmation.** Draft, review,
   test. But `nitro_control_delivery(action: "approve" | "schedule")`
   only after the user says go. If audience is `all_contacts`, also
   require `confirm_send_to_all: true`.
4. **Trust the snapshot.** Do not assume state. Inspect
   `nitro_get_status` and skip setup steps that are already complete
   (see "Partial-state handling" below).
5. **Read the response envelope.** Every tool response carries sidecar
   enrichers with guidance. Use them (see "Response envelope" below).

## Core vocabulary

Know these terms before planning anything:

- **contact**. A person in the audience. Has `email` and/or `phone`
  channels with separate subscribed state.
- **contact_list**. A named group of contacts (static membership).
- **segment**. A dynamic filter-based subset of contacts.
- **brand**. Visual identity: colors, fonts, logo, tone, physical address.
  Applied automatically to every email. Scrape from a URL or set explicitly.
- **domain**. A verified sending domain. Required for live sends. The
  shared sandbox domain `nitr-o.com` is Free-tier, pre-verified, and fine
  for test sends only.
- **template**. An email design stored as JSON sections (not an HTML
  string). Every email. Campaign, flow step, or standalone. Is a template.
- **campaign**. A marketing broadcast to an audience (list, segment, or
  all contacts). Must be approved before it can send.
- **flow**. An automated sequence triggered by an event (e.g.
  `contact_add`, list join, custom event). Can contain email, SMS, delay,
  and branching steps.
- **message**. A per-send record. Transactional messages fire
  immediately; campaign and flow messages are created at send time.
- **warmup**. Per-domain daily send caps that ramp on new domains.
  Checked but does not block approval; can block at send time.
- **account tier** - `free`, `paid`, `trusted`. Free is the default.
- **idempotency_key**. Per-send dedup key. Same key returns the original
  message instead of double-sending. Use for every transactional send.

## Pick the right send mode

```
Does the user want ONE recipient, right now, tied to app code?
  → Transactional  (POST /v1/my/messages or the Node SDK)

Does the user want ONE design sent to MANY recipients, once or scheduled?
  → Campaign       (nitro_compose_campaign → review → approve)

Does the user want a SEQUENCE triggered by an event (welcome series,
abandoned cart, re-engagement, winback)?
  → Flow           (nitro_compose_flow → review → activate)

Still unsure?
  → Ask the user. Do not guess.
```

### 1. Campaign: bulk broadcast

One design sent to an audience, once or scheduled.

- Draft via MCP: `nitro_compose_campaign(name, subject, sections, audience_type, ...)`. Creates campaign + template + backing flow atomically.
- Review: `nitro_review_delivery(target_type: "campaign", target_id)`. Share `editor_url` with the user.
- Test (optional): `nitro_send_test_message(target_type: "campaign", ...)`.
- Approve/launch: `nitro_control_delivery(target_type: "campaign", action: "approve" | "schedule", ...)`.
- Also available via REST: `POST /v1/my/campaigns`, `POST /v1/my/campaigns/{id}/send`. See <https://api.nitrosend.com/openapi.yaml>.

Default tool when the user says "draft/compose/send an email."

### 2. Flow: automated sequence

Event-triggered series of steps (email + SMS + delay + branching).
Typical triggers: `contact_add`, list join, custom event.

- Build: `nitro_compose_flow(name, mode: "create" | "replace" | "patch", trigger, steps, ...)`.
- Review: `nitro_review_delivery(target_type: "flow", target_id)`.
- Approve/activate: `nitro_control_delivery(target_type: "flow", action: "approve", ...)`.

Flows are **out of scope for this skill**, but recognize flow-shaped asks
(welcome series, abandoned cart, re-engagement) and route to
`nitro_compose_flow` instead of forcing a campaign.

### 3. Transactional: single-recipient immediate

One email or SMS to one recipient, right now. No audience, no approval,
no lifecycle. Use for receipts, password resets, OTPs, order
confirmations, system notifications.

**Via REST API**. For application backends and non-MCP contexts:

```bash
curl -X POST https://api.nitrosend.com/v1/my/messages \
  -H "Authorization: Bearer nskey_live_..." \
  -H "Idempotency-Key: order-42" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "email",
    "to": "user@example.com",
    "subject": "Your receipt",
    "html": "<h1>Thanks!</h1><p>Your order is confirmed.</p>",
    "data": {"order_id": "..."}
  }'
```

Both paths are equivalent. Same message record, same pipeline, same
dedupe semantics. Prototype with the MCP tool, ship with the REST call.
Same-key retries dedupe via `Idempotency-Key` header or `idempotency_key`
field.

Transactional is **out of scope for this skill**. Route directly.

## MCP tool surface

All 25 tools are prefixed `nitro_`:

**Account + status**
- `nitro_get_status`. Current account, brand, sender, dashboard links, readiness, warmup, issues, recommendations
- `nitro_select_account` / `nitro_select_brand`. Switch the active account/brand. OAuth-only: API-key connections are pinned to their key's brand.
- `nitro_configure_account`. Sender defaults (`from_name`, `from_email`, `reply_to`, test recipients)
- `nitro_configure_providers`. BYO email provider credentials + status
- `nitro_manage_billing`. Plan, checkout, checkout_status, list plans

**Brand + assets**
- `nitro_set_brand_kit`. Scrape from URL or set colors/fonts/logo/address directly
- `nitro_ingest`. Upload local/chat image; returns CDN URL for email fields

**Domains**
- `nitro_manage_domains`. Add, verify, check_dns, list, remove

**Audience**
- `nitro_import_contacts`. Inline records (<100) or reference a pre-uploaded CSV import
- `nitro_search_contacts`. Full-text find by email/name/phone
- `nitro_query`. Structured filters across any entity (contacts, campaigns, flows, templates, segments, lists, …). Pagination: pass `page` + `per_page`; read `X-Total-Count` etc.
- `nitro_manage_audience`. Contacts, subscriptions, lists, events, segments, tags
- `nitro_define_segment`. Filter-based contact subset (`preview_only: true` by default)

**Composing**
- `nitro_compose_campaign` - **default for drafting emails**. Campaign + template + flow
- `nitro_compose_flow`. Create/replace/patch an automation flow
- `nitro_manage_template`. Create/update/clone a reusable standalone template (not sendable on its own. Attach to a campaign first)

**Delivery**
- `nitro_review_delivery`. Read-only validation, preflight, spam score, SMS segment info, `editor_url`
- `nitro_send_test_message`. Real test send to the saved test recipient (no audience, no approval)
- `nitro_send_message`. Single transactional send (email/SMS) to one recipient, immediate. Same as `POST /v1/my/messages`.
- `nitro_control_delivery`. Approve, schedule, live, pause, cancel, archive (campaigns + flows)

**Intelligence**
- `nitro_get_insights`. Analytics, trends, benchmarks, recommendations
- `nitro_search_docs`. Search the Nitrosend docs / knowledge base for how-tos and reference.
- `nitro_set_memory`. Read/update/patch/append the AI memory document

**Support**
- `nitro_request_support`. Escalate to Nitrosend. Call only when the user asks explicitly or all other tools are exhausted.

### When to read each resource

- **Always at session start:** `nitro://guide` (authoritative platform
  reference. Vocabulary, workflows, constraints).
- **Before composing an email:** `nitro://brand-kit` (voice, colors,
  company), then `nitro://schema` (section prop specs).
- **When validating state:** `nitro://account` (live tier, counts,
  onboarding, Brand Kit status).
- **When stuck on error codes or provider quirks:** `nitro://providers`,
  `nitro://config`.
- **For reference payloads:** `nitro://examples/email`,
  `nitro://examples/flow`, `nitro://examples/campaign`.
- **For cross-session memory:** `nitro://memory` (see "Memory" below).
- **For marketing best-practice lookups:** `nitro://knowledge/index`,
  then `nitro://knowledge/{slug}` for a specific topic.

## Response envelope

Every tool response carries structured sidecars. Read them. They contain
the platform's best guidance for what to do next.

- **`feedback.knowledge`**. Relevant marketing best practice surfaced
  for the current action (populated on goal-driven + insights responses).
- **`feedback.quality`**. Quality/readability notes about the content
  you just created.
- **`flywheel_context`** - `{applied, data_maturity, contributions}` -
  how much the platform's learning model informed the response.
- **`memory_delta`**. Emitted when the call changed the AI memory
  document. Non-null means "the platform learned something from this."
- **`next_action`**. Plain-string recommendation. If present, surface
  it to the user unless you have a better one.
- **On error:** `how_to_fix` (remediation) and sometimes
  `suggested_tool_call` (exact next call to make).

### Error handling

Nitrosend errors are actionable. **Do not retry the same call on error.**
Read `how_to_fix` and follow it. If `suggested_tool_call` is present, call
that instead. If the error is ambiguous, ask the user for what
`how_to_fix` is missing, not for "what to do next." Common classes:

- **Validation (422)**. Missing required field or bad value. `how_to_fix`
  names the field.
- **Preflight block**. E.g. domain not verified, physical address
  missing, no subject. Fix the named blocker, then re-call.
- **Warmup / rate limit**. Reschedule or wait. `warmup.remaining_today`
  tells you today's budget.
- **Auth**. Re-run the client's login command.

## Composing emails

Emails are **structured sections**, not HTML strings. The brand theme
auto-applies. Do not override colors or fonts unless the user asks.

Minimum viable email = header + text + button + footer:

```json
[
  {"type": "header", "props": {}},
  {"type": "text",   "props": {"content": "<h1>Hello!</h1><p>Your message here.</p>"}},
  {"type": "button", "props": {"text": "Get Started", "href": "https://example.com"}},
  {"type": "footer", "props": {}}
]
```

`header` auto-fills the logo from brand. `footer` auto-fills company
name, physical address, and unsubscribe link. Pass `{"props": {}}` for
both.

Section types: `header`, `text`, `image`, `button`, `hero`, `columns`
(2-4; cannot nest), `product`, `social`, `divider`, `spacer`, `footer`.
Read `nitro://schema` for full prop specs.

Images: public URLs drop straight into `image.src`, `product.image_url`,
`hero.background_url`, `theme.logo_url`. For local or chat-attached
images, call `nitro_ingest` first and use the returned CDN URL.
Never persist `data:` URIs.

Always pass `name` + `subject` + `sections` together when composing a
campaign. A campaign with only a name fails review.

## Delivery lifecycle

Campaigns and flows both use an approval-based state machine:

```
draft → approve → live/scheduled ↔ pause → cancel → archive
```

Approval gates (all must pass for a live send):

- domain verified (or sandbox on Free tier)
- active subscription
- physical address set (CAN-SPAM)
- template has subject + content
- sender email configured (`from_email` matches a verified domain)

`nitro_control_delivery` drives every transition. `approve` makes a
campaign eligible; `schedule` requires `scheduled_at` (ISO 8601);
`reject` reverts to draft. Warmup is checked but does **not** block
approval. It can block at send time, so check `warmup.remaining_today`
before large sends.

Test sends (`nitro_send_test_message`) bypass the lifecycle. No domain,
contacts, or approval needed. The quickstart path exploits this.

### Safety rules

- **Never approve a live send without explicit user confirmation.**
  Present the review + `editor_url` first and wait.
- **Audience `all_contacts` requires `confirm_send_to_all: true`.** Only
  set it after the user re-confirms with that audience named.
- **Schedule beats live.** If the user is unsure, propose a schedule;
  they can cancel before `scheduled_at`.

## Partial-state handling

Never assume a blank account. Inspect the snapshot and skip what is done:

| Snapshot signal | Skip |
|---|---|
| `brand.setup_complete: true` | `nitro_set_brand_kit` (unless user requests a change) |
| `account.domain_verified: true` OR `account.using_sandbox: true` | `nitro_manage_domains` (quickstart path) |
| `account.contact_count > 0` AND objective is quickstart/test | `nitro_import_contacts` |
| `config.from_email` matches a verified domain | `nitro_configure_account` |

Plan the shortest incomplete prefix of the path's tool order. Do not
reset progress. If unsure whether a step is needed, ask the user.

## Memory

`nitro_set_memory` maintains a per-brand markdown notepad that persists
across sessions. Use it for:

- Learned user preferences (brand voice, typical audience, send cadence).
- Goals and active initiatives (Q2 launch, winback campaign in flight).
- Corrections from the user ("don't use pink buttons", "always sign off
  from Jenna").

Operations: `read` (get current), `update` (replace entirely),
`patch` (replace a single `## section` by heading), `append` (add to
end). Soft cap 10KB, hard cap 15KB. Read `nitro://memory` any time to
see the current document.

Write to memory after the user corrects you, confirms a non-obvious
choice, or states a goal. Do not write ephemera (today's subject line).

## Pricing & tiers: quick reference

Read `nitro://account` for the current account's live caps. Defaults:

| Tier | Email/mo | AI/mo | Brands | Domains | Seats | Branding | Notable |
|---|---|---|---|---|---|---|---|
| Free | 8,000 then 500/mo | 20 | 1 | 1 | 1 | shown | sandbox 50/mo until you verify a domain |
| Pro | 20,000 | 1,000 | 1 | 1 | 3 | removable | $0.19/100 email overage |
| Ultra | 125,000 | 5,000 | 5 | 3 | 10 | removable | highest self-serve volume |
| Enterprise | 500k-4M+ | unlimited | unlimited | unlimited | unlimited | removable | SSO, dedicated IP add-on ($50/mo via SES) |

**Upgrade from inside the agent:** call
`nitro_manage_billing(operation: "checkout", params: {plan_slug: "pro"})`
and share the returned checkout URL with the user. Poll
`operation: "checkout_status"` until paid. No need to leave the chat.

## Observability after send

A send is not "done" when it goes live. Propose this follow-up to the
user:

- **T+1 hour:** `nitro_get_insights(scope: "campaign", campaign_id)` -
  first-hour open/click signal, bounce spikes, spam complaints.
- **T+24 hours:** second check for settled metrics, plus a
  recommendation summary if the insights envelope includes one.
- **On anomalies:** suggest concrete fixes (reschedule, suppress, rewrite,
  domain check) based on `feedback.knowledge` in the insights response.
  Use A/B testing for subject lines or content when the campaign needs a
  measured comparison.

## What this skill plans

Plans **one campaign-first first-email path** from one bounded objective
and a `nitro_get_status` snapshot. It does not plan flows, SMS campaigns,
or transactional sends. Route those to the tools documented above.

Output: one `onboarding_plan` object. Do not invent status or skip the
snapshot. If the snapshot is missing information needed to choose a safe
path, return `decision: "needs_input"` instead of guessing.

### Inputs

- `objective`. The bounded user goal
- `account_status_json`. JSON string from a recent `nitro_get_status`
- `operator_context`. Optional extra guardrails from the caller
- `client_surface`. Optional; usually `runx_skill_cli` or `mcp_direct`

### Output schema (`onboarding_plan`)

```yaml
decision: ready | needs_input | reject
chosen_path: quickstart_first_email | live_campaign_readiness | none
rationale: string                     # why this path, why not the other
execution_surface:
  primary:  runx_skill_cli | nitrosend_mcp
  fallback: nitrosend_mcp  | runx_skill_cli
ordered_tool_calls:
  - tool: nitro_*
    purpose: string                   # why this call, in this order
    requires_confirmation: boolean    # true only for nitro_control_delivery
human_actions: [string]               # DNS records, explicit approvals
blockers: [string]                    # preconditions still missing
needs_input: [string]                 # what to ask the user
unsupported_requirements: [string]    # asks outside this lane
rejection_reason: string              # only when decision=reject
success_checkpoint:
  milestone: string                   # e.g. test_email_received
  description: string
```

`ordered_tool_calls` must stay in canonical campaign-first order.
`nitro_control_delivery` must only appear with
`requires_confirmation: true`.

## Supported paths

### `quickstart_first_email`

Fastest inbox win. A branded test email, sandbox-acceptable, no live
audience send.

Tool order:

1. `nitro_set_brand_kit`. Skip if `brand.setup_complete` is true
2. `nitro_compose_campaign`
3. `nitro_review_delivery`. Share `editor_url` with the user
4. `nitro_send_test_message`

Do not require domain verification, contact import, or live approval.

### `live_campaign_readiness`

Real audience send or approval-ready live campaign.

Tool order:

1. `nitro_set_brand_kit`. Skip if `brand.setup_complete`
2. `nitro_manage_domains`. Skip if `account.domain_verified`
3. `nitro_import_contacts`. Skip if `account.contact_count > 0` and the user is not importing new
4. `nitro_configure_account`. Skip if `from_email` already matches a verified domain
5. `nitro_compose_campaign`
6. `nitro_review_delivery`
7. `nitro_send_test_message`
8. `nitro_control_delivery` - `requires_confirmation: true`

`nitro_control_delivery` is only for post-confirmation planning. Never
present it as automatic or immediate.

## Worked example

**Input**

```yaml
objective: Get a first branded test email into my inbox today. Sandbox is fine.
operator_context: Keep this on the fastest campaign-first first-email path.
account_status_json: |
  {
    "status": "needs_attention",
    "account": {"tier": "free", "can_send": true, "domain_verified": false,
                "using_sandbox": true, "contact_count": 0, "campaign_count": 0},
    "brand":   {"setup_complete": false, "company_name": "Acme Co"},
    "issues":  [{"area":"brand_kit","severity":"warning",
                 "message":"Physical address not set",
                 "how_to_fix":"Use nitro_set_brand_kit to set physical_address"}],
    "recommendations": [
      {"action":"Set physical address","tool":"nitro_set_brand_kit"},
      {"action":"Verify a sending domain","tool":"nitro_manage_domains"}
    ]
  }
```

**Output**

```yaml
onboarding_plan:
  decision: ready
  chosen_path: quickstart_first_email
  rationale: >
    The caller wants the fastest inbox win and the snapshot already allows
    sandbox test sending without live-send setup.
  execution_surface:
    primary: runx_skill_cli
    fallback: nitrosend_mcp
  ordered_tool_calls:
    - tool: nitro_set_brand_kit
      purpose: Set Brand Kit identity and physical address before composing.
      requires_confirmation: false
    - tool: nitro_compose_campaign
      purpose: Create the campaign draft for the first branded email.
      requires_confirmation: false
    - tool: nitro_review_delivery
      purpose: Review validation, preflight, and editor URL before any inbox check.
      requires_confirmation: false
    - tool: nitro_send_test_message
      purpose: Send a real test message to the operator inbox.
      requires_confirmation: false
  human_actions:
    - Review the editor preview before the test send.
  blockers: []
  needs_input: []
  unsupported_requirements: []
  success_checkpoint:
    milestone: test_email_received
    description: A branded campaign draft has been reviewed and a test email lands in the inbox.
```

Two more worked examples (live readiness + rejected multi-lane ask) live
in the harness at `/X.yaml` on the same origin.

## After the first send

- **Want an automation, not a one-shot?** `nitro_compose_flow`. Welcome
  series, abandoned cart, re-engagement, re-permission.
- **Transactional integration (receipts, OTPs, order confirmations)?**
  Use `POST /v1/my/messages` or the Node SDK from application code. Spec:
  <https://api.nitrosend.com/openapi.yaml>.
- **Close the observability loop.** Schedule T+1h and T+24h
  `nitro_get_insights` checks (see "Observability after send").
- **Hit the Free cap or want higher volume?** Upgrade inline with
  `nitro_manage_billing(operation: "checkout", ...)` or see
  <https://nitrosend.com/pricing>.
- **Verify a sending domain, invite teammates, or connect BYO provider
  keys?** <https://docs.nitrosend.com>.

## Reject instead of guessing

This skill plans a first email send. One campaign, one Brand Kit-driven design,
one test or live broadcast. Reject asks outside that lane and route to
the appropriate tool:

- flow-first onboarding → `nitro_compose_flow`
- SMS-first onboarding → `nitro_compose_campaign(channel: "sms", ...)`
- transactional-only sends → `POST /v1/my/messages` or the Node SDK
- cross-lane "set everything up" asks → call `nitro_get_status`, then
  ask the user to narrow the goal
- combined flow + campaign + SMS orchestration → split into bounded
  asks, one tool per bounded goal

Use `decision: "needs_input"` when the ask is within this skill's lane
but too vague to choose between quickstart and live readiness.

## Current operating notes

- **Structured email editor**. Every email is JSON sections. Paste HTML
  only inside a `text` section's `content` prop.
- **A/B testing**. Use campaign variants for measured subject or content
  comparisons.
- **SMS tiers**. SMS requires a paid tier and a Twilio integration.
- **Column structure**. Keep column sections one level deep and compose
  more complex layouts as sibling sections.
- **HTML content**. Put arbitrary HTML in a `text` section's `content`
  prop.
