Samva is in early access — self-serve signup is limited. Have a team invite? Sign up with that email. Contact us for access.

Samva

Send an email

Send a transactional email with the Samva TypeScript SDK or the REST API.

This guide shows the fastest way to send an email with Samva — from a single line of TypeScript to a raw REST call in the language of your choice. Each recipe is self-contained; pick the one that matches your stack.

Email first. Samva is launching with email. SMS, WhatsApp, and voice are staged and will be documented as they ship.

You need a Samva API key. Grab one from your dashboard. Brand-new accounts can send a first test email right away; verify a sending domain before you send from your own address or move real traffic into production. See Verify your domain when you're ready to set that up.

Send with the TypeScript SDK

The email.send() facade is the canonical path: pass the recipient, subject, and HTML body directly and you're done.

  1. Install the SDK.

    bash npm install samva
    bash bun add samva
    bash yarn add samva
  2. Create a client and send the email.

    import { createClient } from "samva";
    
    const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
    
    const result = await samva.email.send({
      to: "ada@example.com",
      subject: "Welcome to Samva",
      html: "<h1>Welcome!</h1><p>Thanks for joining.</p>",
    });
    
    if (result.error) {
      console.error("Failed to send:", result.error);
    } else {
      console.log("Email sent:", result.data?.id);
    }

The to field accepts a single value or an array, and each entry can be a plain email string, { email }, or { contactId }. You can also pass text for a plaintext fallback, plus optional fields like attachments, templateId, templateData, and metadata. See the TypeScript SDK reference for the full surface.

Send to an existing contact or multiple recipients

email.send() is a thin facade over the unified messages.send() API. Reach for messages.send() directly when you need to address existing contacts by contactId, send to multiple recipients, or thread into an existing conversation — here to is always an array and the email content is nested under email.

await samva.messages.send({
  to: [{ contactId: "9b2c0e3a-7d41-4f2e-8a16-1f2c3d4e5f60" }, { email: "grace@example.com" }],
  channel: "email",
  email: {
    subject: "Welcome to Samva",
    html: "<h1>Welcome!</h1><p>Thanks for joining.</p>",
  },
});

For why the facade and the unified API exist side by side, see Email and the unified API.

Send with the REST API

There is no email.send HTTP endpoint — the SDK facade is sugar over POST /v1/messages. Every REST call uses that endpoint with the unified body shape: a channel of email and the content nested under email. Authenticate with the X-API-Key header.

curl -X POST https://api.samva.app/v1/messages \
  -H "X-API-Key: sk_sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": [{ "email": "ada@example.com" }],
    "channel": "email",
    "email": {
      "subject": "Welcome to Samva",
      "html": "<h1>Welcome!</h1><p>Thanks for joining.</p>",
      "text": "Welcome! Thanks for joining."
    }
  }'

To address an existing contact, swap email for contactId in the to array — both forms hit the same endpoint:

curl -X POST https://api.samva.app/v1/messages \
  -H "X-API-Key: sk_sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": [{ "contactId": "9b2c0e3a-7d41-4f2e-8a16-1f2c3d4e5f60" }],
    "channel": "email",
    "email": { "subject": "Welcome to Samva", "html": "<h1>Welcome!</h1>" }
  }'
import requests

headers = {
    "X-API-Key": "sk_sm_your_api_key",
    "Content-Type": "application/json",
}

response = requests.post(
    "https://api.samva.app/v1/messages",
    headers=headers,
    json={
        "to": [{"email": "ada@example.com"}],
        "channel": "email",
        "email": {
            "subject": "Welcome to Samva",
            "text": "Welcome! Thanks for joining.",
        },
    },
)

print(response.json())
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

func main() {
    payload := map[string]any{
        "to":      []map[string]string{{"email": "ada@example.com"}},
        "channel": "email",
        "email": map[string]string{
            "subject": "Welcome to Samva",
            "text":    "Welcome! Thanks for joining.",
        },
    }

    body, _ := json.Marshal(payload)

    req, _ := http.NewRequest("POST",
        "https://api.samva.app/v1/messages",
        bytes.NewBuffer(body))

    req.Header.Set("X-API-Key", "sk_sm_your_api_key")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
}
<?php
$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.samva.app/v1/messages',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: sk_sm_your_api_key',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'to' => [['email' => 'ada@example.com']],
        'channel' => 'email',
        'email' => [
            'subject' => 'Welcome to Samva',
            'text' => 'Welcome! Thanks for joining.',
        ],
    ]),
]);

$response = curl_exec($ch);
curl_close($ch);

print_r(json_decode($response, true));

A successful call returns the new message with its id and status:

{
  "id": "4f8e1c2a-3b6d-4e9f-a012-3456789abcde",
  "status": "pending"
}

For every parameter, status code, and pagination detail, see the REST API reference.

Next steps

On this page