Direct send recipe: one message to one contact, no campaign
This recipe walks one complete pass through Outbound → Direct send: deciding that the send should not be a campaign, picking the contact and channel, choosing a stored template or an ad-hoc body, understanding the approval chain, and reading the audit record afterward. The fuller coordinates — CSV semantics, batch idempotency, template lifecycle — live in the Direct send reference and the goals/approvals/direct-send guide; this page is the operator’s decision loop.1. When to bypass campaign machinery
Direct send earns its name when none of the campaign apparatus applies to the message. Pick direct send when:- A template fix is worth shipping tonight, not next sprint. A corrected reminder or a follow-up phrased fresh reaches one contact now; a campaign draft → launch → audience-resolve round trip adds bookkeeping a single send never benefits from.
- Your backend sends transactional system events. Order-confirmation receipts, appointment reminders, status page updates — one-off events with a natural recipient, not an evaluated segment. A campaign would spend its audience-resolution machinery on a single-ID list anyway.
- A support or ops agent answers one customer. The agent’s desktop needs “send this person a note,” not a draft campaign detached from the conversation. Direct send keeps the message in the same message-history ledger as the conversation.
+15005550000-range recipients) when you are testing integration shape, not messaging a person.
2. Choosing the channel and recipient
Open Outbound → Direct send and click Send to one contact. The picker searches contacts by name, phone, or email and lists every reachable identifier on the row (both the phone and the email, when both exist).- Search, then pick a contact.
- The picker shows every channel the contact is reachable on — SMS for a phone number, Email for an email address, WhatsApp when a WABA sender is connected — with the exact identifier each would address.
- Confirm the channel; the picker routes to that channel’s composer with the recipient prefilled (
/messages/sms?to=<phone>&contactId=<id>— thetoprefills the To field,contactIdrecords which contact row you came from).
3. Template vs ad-hoc body
The composer you land in accepts two body strategies, and which one you pick decides whether next week’s identical message drifts:- Stored template — saved once under Outbound → Templates, then picked from the composer. Pick this when the body will recur (order-note updates, appointment reminders, support follow-ups). Reuse stops copy drift: every send renders the same approved version, and edits re-enter any approval the channel needs (WhatsApp templates re-enter Meta review; most other channels stay
active). - Ad-hoc body — typed in the composer’s freeform field. Pick this when the body is genuinely one-off (a support answer, a one-time correction, a sandbox check). On SMS, the UI flags any unresolved
{{variable}}placeholders before send — replace them; shipping literal{{...}}to a customer is an avoidable failure.
4. Approvals and roles
Direct send is a send, gated like every send:- Send permission. Firing from the console picker, from a channel composer, or over
POST /messages/sendrequires the owner, admin, or developer role. A viewer/light-seat member can browse the chooser page but the send action rejects. - API scope. An API key minted with
messages:writeis required; read-only keys return 403. - The launch gate does not move to direct send. Campaign-scene approval gates (
require_approval_default,min_recipients_for_approval) are a campaign property; the dual-control step for a support agent’s one-off note is conversation ownership, not a campaign gate. When the send looks like it needs an approval chain — anything customer-facing at audience scale — it has outgrown direct send; run it as a gated campaign (see Outbound goals, approvals, and direct send).
5. Recording the audit and reference
Every direct send lands in three consumer-readable ledgers — verify after the send, not assume:- Message history — the single contact’s conversation gains a message row with its delivery status;
contactIdlinks it back to the contact picker selection. - Outbound activity — the Outbound → Activity ledger lists the send with its origin (direct send vs campaign) and a per-send reference the reviewer quotes back.
- Audit log — tenant audit events record the operator who fired the send, the channel, and the approval-chain link when a gated campaign path was involved (for direct send the link is empty by design). Pull the reference from Settings → Audit log when a reviewer asks “who sent this and when.”
6. Failure handling: reading CHANNEL_NOT_CONFIGURED
The most actionable failure a direct send can hit is the platform telling you the chosen channel has no provider credentials yet. CHANNEL_NOT_CONFIGURED surfaces differently depending on the entry point:
- In the console composer, the send fails with a 503 and the composer surfaces “Provider is not configured — add credentials in Admin → System Config → Credentials.” You only see it if you clicked Send; the message row flips to
failedwith the reason attached and no wallet credit burns. - Over the API (
POST /messages/sendor/messages/batch), the same failure returns HTTP 503 witherror.code: "CHANNEL_NOT_CONFIGURED"anddetails.channelnaming the rejected channel. Do not retry programmatically — it is deterministic against the current credential set: either provision the channel credentials, or fall back to a channel the tenant does have (e.g. email when SMS is unowned).
CHANNEL_NOT_CONFIGURED as “fix setup, then re-send” and treat 4xx validation errors (a 422 on an invalid recipient, a 403 on a read-only key) as “fix the request.” Neither charges the wallet.
Worked example: an order-note update from a support agent
A customer calls support asking whether order 1042 shipped; the agent confirms from the order system that it has, and wants to send a one-off SMS closing the loop.- Open Outbound → Direct send, pick Send to one contact.
- Search the customer by phone, pick the SMS channel (the picker labels the exact number it addresses).
- The SMS composer opens with the recipient prefilled. Type the ad-hoc body — “Hi Ava, your order 1042 shipped today — tracking in your email.” — or pick the stored
order-note-updatetemplate to keep copy consistent. On SMS the composer flags unresolved{{variable}}tokens; the agent’s one-off body is clean by construction. - Send. The message lands in the conversation’s message history and the Outbound → Activity ledger with its send reference; the audit log records the agent identity, channel, and timestamp.
- If the send returns 503
CHANNEL_NOT_CONFIGURED, the tenant’s SMS sender is not provisioned — the agent hands the setup to an admin, and in the meantime re-sends the note over Email from the same picker (the contact’s email address was a second reachable identifier).
See also
- Outbound → Direct send reference — CSV semantics,
POST /messages/batchidempotency, rate limits, and per-channel setup - Outbound goals, approvals, and direct send — the campaign approval gate this page deliberately bypasses
- Outbound templates — stored-template lifecycle for the recurring-body path
- Message history and conversation ledgers — where a direct send reports its per-recipient status
- First transactional email — the closest per-channel setup recipe when
CHANNEL_NOT_CONFIGUREDappears (provision the sender, then re-send) - Troubleshooting: APAC channel inbound no-route — the same deterministic-503 pattern on the inbound side