Skip to content

Code Examples

Every example uses rp_live_... as a placeholder API key. Replace it with your actual key from the dashboard.

POST /api/v1/emails/send

const response = await fetch("https://api.relaypost.dev/v1/emails/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
from: { email: "hello@yourdomain.com", name: "Your App" },
to: [{ email: "user@example.com" }],
subject: "Your receipt",
html: "<h1>Thanks for your purchase!</h1>",
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error.message);
}
const { data } = await response.json();
console.log(data.message_id);

POST /api/v1/emails/send

Reference a template by ID and pass variable data. Do not include html or text when using template_id.

const response = await fetch("https://api.relaypost.dev/v1/emails/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
from: { email: "orders@yourapp.com" },
to: [{ email: "customer@example.com" }],
template_id: "tmpl_abc123",
template_data: {
customerName: "Jane",
orderNumber: "1234",
total: "$49.99",
},
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error.message);
}
const { data } = await response.json();
console.log(data.message_id);

GET /api/v1/emails/:id

const emailId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
const response = await fetch(
`https://api.relaypost.dev/v1/emails/${emailId}`,
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data } = await response.json();
console.log(data.status, data.events);

GET /api/v1/emails

const params = new URLSearchParams({
status: "delivered",
page: "1",
limit: "20",
});
const response = await fetch(
`https://api.relaypost.dev/v1/emails?${params}`,
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data, pagination } = await response.json();
console.log(`${pagination.total_count} emails found`);
data.forEach((email) => console.log(email.subject, email.status));

POST /api/v1/emails/batch

Send the same template to multiple recipients with per-recipient variable data. Maximum 1000 recipients per batch.

const response = await fetch("https://api.relaypost.dev/v1/emails/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
template_id: "tmpl_abc123",
from: { email: "orders@yourapp.com", name: "Your Store" },
subject: "Your order is confirmed",
recipients: [
{
to: { email: "alice@example.com", name: "Alice" },
data: { customerName: "Alice", orderNumber: "ORD-001", total: "$29.99" },
},
{
to: { email: "bob@example.com", name: "Bob" },
data: { customerName: "Bob", orderNumber: "ORD-002", total: "$59.99" },
},
],
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error.message);
}
const { data } = await response.json();
console.log(`Batch ${data.batch_id}: ${data.accepted}/${data.total} accepted`);
if (data.rejected.length > 0) {
data.rejected.forEach((r) => console.log(`Rejected #${r.index}: ${r.error}`));
}

POST /api/v1/emails/validate

const response = await fetch(
"https://api.relaypost.dev/v1/emails/validate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({ email: "user@example.com" }),
}
);
const { data } = await response.json();
console.log(data.verdict); // "deliverable", "risky", or "undeliverable"

POST /api/v1/emails/validate/batch

const response = await fetch(
"https://api.relaypost.dev/v1/emails/validate/batch",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
emails: ["user@example.com", "invalid@", "test@tempmail.com"],
}),
}
);
const { data } = await response.json();
data.forEach((r) => console.log(r.email, r.verdict));

POST /api/v1/domains

const response = await fetch("https://api.relaypost.dev/v1/domains", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({ domain: "yourapp.com" }),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error.message);
}
const { data } = await response.json();
console.log("DNS records to configure:");
data.dns_records.forEach((r) => console.log(`${r.type} ${r.name}${r.value}`));

GET /api/v1/domains

const response = await fetch("https://api.relaypost.dev/v1/domains", {
headers: { Authorization: "Bearer rp_live_your_api_key" },
});
const { data } = await response.json();
data.forEach((d) => console.log(`${d.domain} — verified: ${d.is_verified}`));

GET /api/v1/domains/:id/verify

const domainId = "dom_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/domains/${domainId}/verify`,
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data } = await response.json();
console.log(`SPF: ${data.spf_verified}, DKIM: ${data.dkim_verified}`);

GET /api/v1/suppressions

const response = await fetch(
"https://api.relaypost.dev/v1/suppressions?page=1&limit=50",
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data, pagination } = await response.json();
console.log(`${pagination.total_count} suppressions`);
data.forEach((s) => console.log(s.email, s.reason));

POST /api/v1/suppressions

const response = await fetch("https://api.relaypost.dev/v1/suppressions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
email: "unsubscribed@example.com",
reason: "manual",
}),
});
const { data } = await response.json();
console.log(`Suppression ${data.id} created`);

DELETE /api/v1/suppressions/:id

const suppressionId = "sup_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/suppressions/${suppressionId}`,
{
method: "DELETE",
headers: { Authorization: "Bearer rp_live_your_api_key" },
}
);
const { data } = await response.json();
console.log(`Deleted: ${data.deleted}`);

GET /api/v1/settings/auto-validation

const response = await fetch(
"https://api.relaypost.dev/v1/settings/auto-validation",
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data } = await response.json();
console.log(data.auto_validation_threshold);

PUT /api/v1/settings/auto-validation

const response = await fetch(
"https://api.relaypost.dev/v1/settings/auto-validation",
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({ threshold: "high" }),
}
);
const { data } = await response.json();
console.log(data.auto_validation_threshold);

POST /api/v1/templates

const response = await fetch("https://api.relaypost.dev/v1/templates", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
name: "Order Confirmation",
subject: "Your order #{{orderNumber}}",
html: "<h1>Thanks, {{customerName}}!</h1><p>Order #{{orderNumber}} — {{total}}</p>",
text: "Thanks, {{customerName}}! Order #{{orderNumber}} — {{total}}",
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error.message);
}
const { data } = await response.json();
console.log(`Template created: ${data.id}`);
console.log(`Detected variables: ${data.variables}`);

POST /api/v1/templates

Define typed variables with defaults for validation at send time.

const response = await fetch("https://api.relaypost.dev/v1/templates", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
name: "Shipping Notification",
subject: "Your order {{order_id}} has shipped",
html: `<h1>Hi {{first_name}},</h1>
<p>Your order <strong>{{order_id}}</strong> is on its way!</p>
{{#if tracking_url}}<p><a href="{{tracking_url}}">Track your package</a></p>{{/if}}
{{#each items}}<p>• {{this}}</p>{{/each}}`,
text: "Hi {{first_name}}, your order {{order_id}} has shipped.",
variable_schema: [
{ name: "first_name", type: "string", required: true },
{ name: "order_id", type: "string", required: true },
{ name: "tracking_url", type: "string", required: false },
{ name: "items", type: "array", required: false, default_value: [] },
],
}),
});
const { data } = await response.json();
console.log(data.id, data.variable_schema);

PUT /api/v1/templates/:id

Only include the fields you want to change.

const templateId = "tmpl_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/templates/${templateId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
subject: "Updated: Your order #{{orderNumber}}",
variable_schema: [
{ name: "orderNumber", type: "string", required: true },
{ name: "customerName", type: "string", required: true },
{ name: "total", type: "string", required: true },
],
}),
}
);
const { data } = await response.json();
console.log(`Updated at: ${data.updated_at}`);

GET /api/v1/templates

const params = new URLSearchParams({
is_active: "true",
search: "order",
page: "1",
limit: "20",
});
const response = await fetch(
`https://api.relaypost.dev/v1/templates?${params}`,
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data, pagination } = await response.json();
console.log(`${pagination.total_count} templates found`);
data.forEach((t) => console.log(t.name, t.is_active));

GET /api/v1/templates/:id

Returns the template with its variable schema, auto-detected variables, and usage statistics.

const templateId = "tmpl_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/templates/${templateId}`,
{ headers: { Authorization: "Bearer rp_live_your_api_key" } }
);
const { data } = await response.json();
console.log(data.name, data.variables);
console.log(`Sends last 30 days: ${data.usage?.sends_last_30_days}`);

DELETE /api/v1/templates/:id

Soft-deletes the template by setting is_active to false.

const templateId = "tmpl_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/templates/${templateId}`,
{
method: "DELETE",
headers: { Authorization: "Bearer rp_live_your_api_key" },
}
);
const { data } = await response.json();
console.log(`Deleted: ${data.deleted}`);

POST /api/v1/templates/:id/preview

Render a template with sample data without sending. Missing variables render as empty strings and appear in the warnings array.

const templateId = "tmpl_abc123";
const response = await fetch(
`https://api.relaypost.dev/v1/templates/${templateId}/preview`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
data: {
customerName: "Jane",
orderNumber: "1234",
total: "$49.99",
},
}),
}
);
const { data } = await response.json();
console.log("Subject:", data.subject);
console.log("HTML:", data.html);
console.log("Variables:", data.variables);
if (data.warnings.length > 0) {
console.log("Warnings:", data.warnings);
}

Templates support Handlebars syntax for conditionals ({{#if}}), negated conditionals ({{#unless}}), and iteration ({{#each}}).

{{! Conditional: show tracking link only if provided }}
{{#if tracking_url}}
<p><a href="{{tracking_url}}">Track your package</a></p>
{{/if}}
{{! Negated conditional }}
{{#unless is_free_shipping}}
<p>Shipping: {{shipping_cost}}</p>
{{/unless}}
{{! Iterate over an array }}
<ul>
{{#each items}}
<li>{{this.name}}{{this.price}}</li>
{{/each}}
</ul>
{{! Nested property access }}
<p>Hi {{user.first_name}}, your account {{user.email}} is active.</p>

Here is a complete send example using conditionals and loops:

const response = await fetch("https://api.relaypost.dev/v1/emails/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer rp_live_your_api_key",
},
body: JSON.stringify({
from: { email: "orders@yourapp.com" },
to: [{ email: "customer@example.com" }],
template_id: "tmpl_order_receipt",
template_data: {
user: { first_name: "Jane", email: "customer@example.com" },
order_id: "ORD-2024-001",
items: [
{ name: "Widget Pro", price: "$29.99" },
{ name: "Gadget Plus", price: "$19.99" },
],
tracking_url: "https://track.example.com/abc123",
is_free_shipping: false,
shipping_cost: "$5.99",
},
}),
});
const { data } = await response.json();
console.log(data.message_id);