Skip to main content

Basic send

curl -X POST https://api.torpedo.co.mz/api/v1/emails \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@myapp.com>",
    "to": "user@example.com",
    "subject": "Welcome to Acme",
    "html": "<h1>Welcome!</h1>"
  }'
const res = await fetch('https://api.torpedo.co.mz/api/v1/emails', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TORPEDO_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'Acme <hello@myapp.com>',
    to: 'user@example.com',
    subject: 'Welcome to Acme',
    html: '<h1>Welcome!</h1>',
  }),
})
const { data } = await res.json()
import httpx

res = httpx.post(
    'https://api.torpedo.co.mz/api/v1/emails',
    headers={'X-API-Key': TORPEDO_API_KEY},
    json={
        'from': 'Acme <hello@myapp.com>',
        'to': 'user@example.com',
        'subject': 'Welcome to Acme',
        'html': '<h1>Welcome!</h1>',
    },
)
data = res.json()['data']
package main

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

func main() {
	payload, _ := json.Marshal(map[string]string{
		"from":    "Acme <hello@myapp.com>",
		"to":      "user@example.com",
		"subject": "Welcome to Acme",
		"html":    "<h1>Welcome!</h1>",
	})

	req, _ := http.NewRequest("POST", "https://api.torpedo.co.mz/api/v1/emails", bytes.NewBuffer(payload))
	req.Header.Set("X-API-Key", os.Getenv("TORPEDO_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
	fmt.Println(resp.Status)
}
import java.net.http.*;
import java.net.URI;

var body = """
    {"from":"Acme <hello@myapp.com>","to":"user@example.com",
     "subject":"Welcome to Acme","html":"<h1>Welcome!</h1>"}""";

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.torpedo.co.mz/api/v1/emails"))
    .header("X-API-Key", System.getenv("TORPEDO_API_KEY"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("TORPEDO_API_KEY"));

var payload = new {
    from = "Acme <hello@myapp.com>",
    to = "user@example.com",
    subject = "Welcome to Acme",
    html = "<h1>Welcome!</h1>",
};

var response = await client.PostAsJsonAsync("https://api.torpedo.co.mz/api/v1/emails", payload);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
Response:
{
  "data": {
    "id": "01960000-0000-0000-0000-000000000000",
    "status": "queued"
  }
}
The response is 202 Accepted — delivery is asynchronous.

The from field

Accepts two formats:
hello@myapp.com
Acme <hello@myapp.com>
The domain (myapp.com) must be verified in your workspace. If you use a domain-scoped API key, the from domain must also match the key’s domain. For best deliverability and trust, prefer a monitored sender like hello@ or support@ instead of no-reply@ whenever possible.

html and text

Provide html, text, or both. If you send only html, Torpedo generates a plain-text version automatically. Plain text is useful for:
  • Email clients that don’t render HTML
  • Spam filters that check text vs HTML ratio
  • Accessibility tools
If you want full control over the plain-text part, provide text explicitly:
"text": "Welcome! Visit https://myapp.com to get started."
To send an HTML-only email without a text part, set text to an empty string.

Email status lifecycle

queued → sent → delivered
              ↘ bounced
              ↘ complained
              ↘ failed
Poll GET /api/v1/emails/{id} to check status, or use webhooks for real-time updates.

Idempotency

Pass an Idempotency-Key header to prevent duplicate sends if your request is retried:
curl -X POST https://api.torpedo.co.mz/api/v1/emails \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: order-123-welcome-email" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
If a request with the same key was already accepted, Torpedo returns the original 202 response without sending again. Keys are scoped to your workspace.

Reply-to

Set a different address for replies:
{
  "from": "Acme <hello@myapp.com>",
  "replyTo": "support@myapp.com",
  ...
}

Quota limits

Sending fails with 402 if you exceed your plan’s monthly or daily quota:
{
  "errors": [{ "message": "Monthly email quota exceeded" }]
}
Check current usage via GET /api/v1/workspace/usage.