Email and the messages API
Understand why Samva exposes both an email send facade and a messages API, and how each fits your integration.
Samva gives you two ways to send email: a small, email-shaped email.send() facade and a
broader messages.send() API that maps directly onto POST /v1/messages. They are not competing
products or different transports; they are two views of the same send pipeline. This page explains
why both exist, how they relate, and what that design buys you over time.
Two shapes, one pipeline
Underneath, every send Samva performs is a message: a piece of content, a recipient, and a channel
that carries it. The messages API names those parts explicitly. When you call messages.send(), you
declare the channel, list your recipients in to, and nest the channel-specific content under a
matching key:
await samva.messages.send({
to: [{ contactId: "contact_k9mvt4znw8rh7q2x" }],
cc: [{ email: "grace@example.com" }],
channel: "email",
email: {
subject: "Welcome to Samva",
html: "<h1>Welcome!</h1><p>Thanks for joining.</p>",
replyTo: "support@example.com",
},
});The email.send() facade is the same call wearing email-shaped clothes. It assumes the channel is
email, flattens the email content up to the top level of the input, and accepts a single recipient
or an array. The result is a call that reads like sending an email rather than configuring a generic
message:
const result = await samva.email.send({
to: "ada@example.com",
cc: "grace@example.com",
replyTo: "support@example.com",
subject: "Welcome to Samva",
html: "<h1>Welcome!</h1>",
});There is no separate email.send HTTP endpoint. The facade is an SDK-only ergonomic wrapper: it
builds the message body shape for you and posts it to POST /v1/messages. From the server's point of
view, both snippets above are the same request to the same endpoint. The difference lives entirely in
the SDK, in service of the developer reading the code.
Why a facade at all
Most email you send is straightforward. You have an address, a subject, and some HTML, and you want
the call site to say exactly that. Forcing every send to spell out channel: "email" and nest content
under an email: key adds ceremony to the common case without adding meaning; the channel is obvious
when you reached for email.send().
The facade optimizes for that common case. Flattening the email fields directly into the SDK input keeps the simple path short and removes a layer of nesting you would otherwise repeat on every send. It is the canonical way to send a one-off transactional email, and it is what the send an email guide walks through.
That convenience is deliberately narrow. The facade does not hide the messages API or replace it; it sits on top of it. When your needs outgrow "one address, one subject, one body," you drop down to the API the facade was built on, and nothing about the underlying send changes.
When to reach for the messages API
Reach for messages.send() (or POST /v1/messages directly) when the send is no longer a single
self-contained email:
- Addressing existing contacts. The
toarray accepts{ contactId }, so you can send to a contact you already store in Samva without re-supplying their email address. - Multiple recipients. When you are addressing a list rather than one person, the array form of
tois the natural fit, withccandbccavailable when you need mail-style copy semantics. - Threading a reply. When a send belongs to an existing conversation, the message body is where you express that relationship so the message lands in the right thread.
The dividing line is conceptual, not performance- or feature-based: the facade is for "send this email
to this address," and the messages API is for everything that needs to talk about messages, recipients,
contacts, and conversations as first-class things. Because the facade compiles down to the same
request, you can start with email.send() and move to messages.send() later without rethinking how
your email is delivered.
REST is always the message shape
The split between a facade and the messages API is an SDK convenience. It does not exist over the wire.
Every REST integration, whether or not you use the TypeScript SDK, sends to POST /v1/messages
using the message body: a channel, a to array, and channel content nested under its key. If you
are calling the API directly, there is one shape to learn, and the SDK's email.send() is simply
producing that shape on your behalf.
This is why the REST reference and the TypeScript SDK reference can describe the same capability from two angles without contradiction: one documents the endpoint, the other documents the ergonomics layered over it.
Why the shape names the channel
POST /v1/messages models a send as a channel value plus that channel's content block. For email,
channel: "email" pairs with an email: { ... } block carrying the subject, HTML, and text. Naming the
channel explicitly is what the email.send() facade hides and what the nested messages.send() shape
exposes.
That explicitness does not change the email path. The email.send() facade is pinned to email by
definition, so its fields, its behavior, and what it sends stay the same. The messages API carries the
general shape; the email facade stays exactly as small as it is today.
This is the tradeoff the two-call design resolves. A single flat API would make every email verbose; forcing a channel into every call is ceremony the facade removes. By offering the facade for the common case and the messages API as its foundation, Samva keeps the simple thing simple.
Related
How Samva works
Understand how a send flows from your code to the inbox, the core objects that model email, and the single-API philosophy behind Samva.
Organizations and tenancy
How an API key binds to a Samva organization, why every record is tenant-scoped, and how teams and roles share that boundary.