Code Examples
Every example uses rp_live_... as a placeholder API key. Replace it with your actual key from the dashboard.
Emails
Section titled “Emails”Send an email
Section titled “Send an email”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);import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/send", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "from": {"email": "hello@yourdomain.com", "name": "Your App"}, "to": [{"email": "user@example.com"}], "subject": "Your receipt", "html": "<h1>Thanks for your purchase!</h1>", },)response.raise_for_status()
data = response.json()["data"]print(data["message_id"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/send")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {from: { email: "hello@yourdomain.com", name: "Your App" },to: [{ email: "user@example.com" }],subject: "Your receipt",html: "<h1>Thanks for your purchase!</h1>"}.to_json
response = http.request(request)raise "Request failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)["data"]puts data["message_id"]use GuzzleHttp\Client;use GuzzleHttp\Exception\ClientException;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
try { $response = $client->post("/v1/emails/send", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "from" => ["email" => "hello@yourdomain.com", "name" => "Your App"], "to" => [["email" => "user@example.com"]], "subject" => "Your receipt", "html" => "<h1>Thanks for your purchase!</h1>", ], ]);
$data = json_decode($response->getBody(), true)["data"]; echo $data["message_id"];} catch (ClientException $e) { $error = json_decode($e->getResponse()->getBody(), true); echo "Error: " . $error["error"]["message"];}Send with a template
Section titled “Send with a template”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);import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/send", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "from": {"email": "orders@yourapp.com"}, "to": [{"email": "customer@example.com"}], "template_id": "tmpl_abc123", "template_data": { "customerName": "Jane", "orderNumber": "1234", "total": "$49.99", }, },)response.raise_for_status()
data = response.json()["data"]print(data["message_id"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/send")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {from: { email: "orders@yourapp.com" },to: [{ email: "customer@example.com" }],template_id: "tmpl_abc123",template_data: {customerName: "Jane",orderNumber: "1234",total: "$49.99"}}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts data["message_id"]use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/emails/send", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "from" => ["email" => "orders@yourapp.com"], "to" => [["email" => "customer@example.com"]], "template_id" => "tmpl_abc123", "template_data" => [ "customerName" => "Jane", "orderNumber" => "1234", "total" => "$49.99", ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["message_id"];Get email by ID
Section titled “Get email by 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);import requests
email_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"response = requests.get( f"https://api.relaypost.dev/v1/emails/{email_id}", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
data = response.json()["data"]print(data["status"], data["events"])require "net/http"require "json"require "uri"
email_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"uri = URI("https://api.relaypost.dev/v1/emails/#{email_id}")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts "#{data["status"]} — #{data["events"].length} events"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$emailId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
$response = $client->get("/v1/emails/{$emailId}", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["status"];List emails
Section titled “List emails”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));import requests
response = requests.get( "https://api.relaypost.dev/v1/emails", headers={"Authorization": "Bearer rp_live_your_api_key"}, params={"status": "delivered", "page": 1, "limit": 20},)response.raise_for_status()
result = response.json()print(f"{result['pagination']['total_count']} emails found")for email in result["data"]: print(email["subject"], email["status"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails")uri.query = URI.encode_www_form(status: "delivered", page: 1, limit: 20)http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)result = JSON.parse(response.body)puts "#{result["pagination"]["total_count"]} emails found"result["data"].each { |email| puts "#{email["subject"]} — #{email["status"]}" }use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->get("/v1/emails", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "query" => ["status" => "delivered", "page" => 1, "limit" => 20],]);
$result = json_decode($response->getBody(), true);echo $result["pagination"]["total_count"] . " emails found\n";foreach ($result["data"] as $email) { echo $email["subject"] . " — " . $email["status"] . "\n";}Batch send with templates
Section titled “Batch send with templates”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}`));}import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/batch", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "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"}, }, ], },)response.raise_for_status()
data = response.json()["data"]print(f"Batch {data['batch_id']}: {data['accepted']}/{data['total']} accepted")for r in data["rejected"]: print(f"Rejected #{r['index']}: {r['error']}")require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/batch")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {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" }}]}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Batch #{data["batch_id"]}: #{data["accepted"]}/#{data["total"]} accepted"data["rejected"].each { |r| puts "Rejected ##{r["index"]}: #{r["error"]}" }use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/emails/batch", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "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"], ], ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo "Batch {$data['batch_id']}: {$data['accepted']}/{$data['total']} accepted\n";foreach ($data["rejected"] as $r) { echo "Rejected #{$r['index']}: {$r['error']}\n";}Email Validation
Section titled “Email Validation”Validate a single email
Section titled “Validate a single email”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"import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/validate", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={"email": "user@example.com"},)response.raise_for_status()
data = response.json()["data"]print(data["verdict"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/validate")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = { email: "user@example.com" }.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts data["verdict"]use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/emails/validate", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => ["email" => "user@example.com"],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["verdict"];Validate a batch of emails
Section titled “Validate a batch of emails”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));import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/validate/batch", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={"emails": ["user@example.com", "invalid@", "test@tempmail.com"]},)response.raise_for_status()
for r in response.json()["data"]: print(r["email"], r["verdict"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/validate/batch")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {emails: ["user@example.com", "invalid@", "test@tempmail.com"]}.to_json
response = http.request(request)JSON.parse(response.body)["data"].each do |r|puts "#{r["email"]} — #{r["verdict"]}"enduse GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/emails/validate/batch", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => ["emails" => ["user@example.com", "invalid@", "test@tempmail.com"]],]);
$results = json_decode($response->getBody(), true)["data"];foreach ($results as $r) { echo $r["email"] . " — " . $r["verdict"] . "\n";}Domains
Section titled “Domains”Add a domain
Section titled “Add a domain”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}`));import requests
response = requests.post( "https://api.relaypost.dev/v1/domains", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={"domain": "yourapp.com"},)response.raise_for_status()
data = response.json()["data"]print("DNS records to configure:")for r in data["dns_records"]: print(f" {r['type']} {r['name']} → {r['value']}")require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/domains")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = { domain: "yourapp.com" }.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "DNS records to configure:"data["dns_records"].each { |r| puts " #{r["type"]} #{r["name"]} → #{r["value"]}" }use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/domains", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => ["domain" => "yourapp.com"],]);
$data = json_decode($response->getBody(), true)["data"];echo "DNS records to configure:\n";foreach ($data["dns_records"] as $r) { echo " {$r['type']} {$r['name']} → {$r['value']}\n";}List domains
Section titled “List domains”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}`));import requests
response = requests.get( "https://api.relaypost.dev/v1/domains", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
for d in response.json()["data"]: print(f"{d['domain']} — verified: {d['is_verified']}")require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/domains")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)JSON.parse(response.body)["data"].each do |d|puts "#{d["domain"]} — verified: #{d["is_verified"]}"enduse GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->get("/v1/domains", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$domains = json_decode($response->getBody(), true)["data"];foreach ($domains as $d) { echo $d["domain"] . " — verified: " . ($d["is_verified"] ? "true" : "false") . "\n";}Verify domain DNS
Section titled “Verify domain DNS”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}`);import requests
domain_id = "dom_abc123"response = requests.get( f"https://api.relaypost.dev/v1/domains/{domain_id}/verify", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
data = response.json()["data"]print(f"SPF: {data['spf_verified']}, DKIM: {data['dkim_verified']}")require "net/http"require "json"require "uri"
domain_id = "dom_abc123"uri = URI("https://api.relaypost.dev/v1/domains/#{domain_id}/verify")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts "SPF: #{data["spf_verified"]}, DKIM: #{data["dkim_verified"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$domainId = "dom_abc123";
$response = $client->get("/v1/domains/{$domainId}/verify", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo "SPF: " . ($data["spf_verified"] ? "true" : "false");echo ", DKIM: " . ($data["dkim_verified"] ? "true" : "false") . "\n";Suppressions
Section titled “Suppressions”List suppressions
Section titled “List suppressions”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));import requests
response = requests.get( "https://api.relaypost.dev/v1/suppressions", headers={"Authorization": "Bearer rp_live_your_api_key"}, params={"page": 1, "limit": 50},)response.raise_for_status()
result = response.json()print(f"{result['pagination']['total_count']} suppressions")for s in result["data"]: print(s["email"], s["reason"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/suppressions")uri.query = URI.encode_www_form(page: 1, limit: 50)http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)result = JSON.parse(response.body)puts "#{result["pagination"]["total_count"]} suppressions"result["data"].each { |s| puts "#{s["email"]} — #{s["reason"]}" }use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->get("/v1/suppressions", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "query" => ["page" => 1, "limit" => 50],]);
$result = json_decode($response->getBody(), true);echo $result["pagination"]["total_count"] . " suppressions\n";foreach ($result["data"] as $s) { echo $s["email"] . " — " . $s["reason"] . "\n";}Add a suppression
Section titled “Add a suppression”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`);import requests
response = requests.post( "https://api.relaypost.dev/v1/suppressions", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={"email": "unsubscribed@example.com", "reason": "manual"},)response.raise_for_status()
data = response.json()["data"]print(f"Suppression {data['id']} created")require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/suppressions")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = { email: "unsubscribed@example.com", reason: "manual" }.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Suppression #{data["id"]} created"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/suppressions", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => ["email" => "unsubscribed@example.com", "reason" => "manual"],]);
$data = json_decode($response->getBody(), true)["data"];echo "Suppression {$data['id']} created\n";Remove a suppression
Section titled “Remove a suppression”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}`);import requests
suppression_id = "sup_abc123"response = requests.delete( f"https://api.relaypost.dev/v1/suppressions/{suppression_id}", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
data = response.json()["data"]print(f"Deleted: {data['deleted']}")require "net/http"require "json"require "uri"
suppression_id = "sup_abc123"uri = URI("https://api.relaypost.dev/v1/suppressions/#{suppression_id}")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Delete.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Deleted: #{data["deleted"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$suppressionId = "sup_abc123";
$response = $client->delete("/v1/suppressions/{$suppressionId}", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo "Deleted: " . ($data["deleted"] ? "true" : "false") . "\n";Settings
Section titled “Settings”Get auto-validation threshold
Section titled “Get auto-validation threshold”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);import requests
response = requests.get( "https://api.relaypost.dev/v1/settings/auto-validation", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()print(response.json()["data"]["auto_validation_threshold"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/settings/auto-validation")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts data["auto_validation_threshold"]use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->get("/v1/settings/auto-validation", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["auto_validation_threshold"];Update auto-validation threshold
Section titled “Update 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);import requests
response = requests.put( "https://api.relaypost.dev/v1/settings/auto-validation", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={"threshold": "high"},)response.raise_for_status()print(response.json()["data"]["auto_validation_threshold"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/settings/auto-validation")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Put.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = { threshold: "high" }.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts data["auto_validation_threshold"]use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->put("/v1/settings/auto-validation", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => ["threshold" => "high"],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["auto_validation_threshold"];Templates
Section titled “Templates”Create a template
Section titled “Create a template”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}`);import requests
response = requests.post( "https://api.relaypost.dev/v1/templates", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "name": "Order Confirmation", "subject": "Your order #{{orderNumber}}", "html": "<h1>Thanks, {{customerName}}!</h1><p>Order #{{orderNumber}} — {{total}}</p>", "text": "Thanks, {{customerName}}! Order #{{orderNumber}} — {{total}}", },)response.raise_for_status()
data = response.json()["data"]print(f"Template created: {data['id']}")print(f"Detected variables: {data['variables']}")require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/templates")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {name: "Order Confirmation",subject: 'Your order #{{orderNumber}}',html: '<h1>Thanks, {{customerName}}!</h1><p>Order #{{orderNumber}} — {{total}}</p>',text: 'Thanks, {{customerName}}! Order #{{orderNumber}} — {{total}}'}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Template created: #{data["id"]}"puts "Detected variables: #{data["variables"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/templates", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "name" => "Order Confirmation", "subject" => "Your order #{{orderNumber}}", "html" => "<h1>Thanks, {{customerName}}!</h1><p>Order #{{orderNumber}} — {{total}}</p>", "text" => "Thanks, {{customerName}}! Order #{{orderNumber}} — {{total}}", ],]);
$data = json_decode($response->getBody(), true)["data"];echo "Template created: {$data['id']}\n";echo "Detected variables: " . implode(", ", $data["variables"] ?? []) . "\n";Create a template with variable schema
Section titled “Create a template with variable schema”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);import requests
response = requests.post( "https://api.relaypost.dev/v1/templates", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "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": []}, ], },)response.raise_for_status()
data = response.json()["data"]print(data["id"], data["variable_schema"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/templates")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {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: [] }]}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Template: #{data["id"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/templates", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "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" => []], ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo "Template: {$data['id']}\n";Update a template
Section titled “Update a template”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}`);import requests
template_id = "tmpl_abc123"response = requests.put( f"https://api.relaypost.dev/v1/templates/{template_id}", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "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}, ], },)response.raise_for_status()
data = response.json()["data"]print(f"Updated at: {data['updated_at']}")require "net/http"require "json"require "uri"
template_id = "tmpl_abc123"uri = URI("https://api.relaypost.dev/v1/templates/#{template_id}")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Put.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {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 }]}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Updated at: #{data["updated_at"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$templateId = "tmpl_abc123";
$response = $client->put("/v1/templates/{$templateId}", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "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], ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo "Updated at: {$data['updated_at']}\n";List templates
Section titled “List templates”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));import requests
response = requests.get( "https://api.relaypost.dev/v1/templates", headers={"Authorization": "Bearer rp_live_your_api_key"}, params={"is_active": True, "search": "order", "page": 1, "limit": 20},)response.raise_for_status()
result = response.json()print(f"{result['pagination']['total_count']} templates found")for t in result["data"]: print(t["name"], t["is_active"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/templates")uri.query = URI.encode_www_form(is_active: true, search: "order", page: 1, limit: 20)http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)result = JSON.parse(response.body)puts "#{result["pagination"]["total_count"]} templates found"result["data"].each { |t| puts "#{t["name"]} — active: #{t["is_active"]}" }use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->get("/v1/templates", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "query" => ["is_active" => "true", "search" => "order", "page" => 1, "limit" => 20],]);
$result = json_decode($response->getBody(), true);echo $result["pagination"]["total_count"] . " templates found\n";foreach ($result["data"] as $t) { echo $t["name"] . " — active: " . ($t["is_active"] ? "true" : "false") . "\n";}Get a template by ID
Section titled “Get a template by ID”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}`);import requests
template_id = "tmpl_abc123"response = requests.get( f"https://api.relaypost.dev/v1/templates/{template_id}", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
data = response.json()["data"]print(data["name"], data["variables"])print(f"Sends last 30 days: {data.get('usage', {}).get('sends_last_30_days')}")require "net/http"require "json"require "uri"
template_id = "tmpl_abc123"uri = URI("https://api.relaypost.dev/v1/templates/#{template_id}")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Get.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts "#{data["name"]} — variables: #{data["variables"]}"puts "Sends last 30 days: #{data.dig("usage", "sends_last_30_days")}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$templateId = "tmpl_abc123";
$response = $client->get("/v1/templates/{$templateId}", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["name"] . " — variables: " . implode(", ", $data["variables"] ?? []) . "\n";echo "Sends last 30 days: " . ($data["usage"]["sends_last_30_days"] ?? "N/A") . "\n";Delete a template
Section titled “Delete a template”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}`);import requests
template_id = "tmpl_abc123"response = requests.delete( f"https://api.relaypost.dev/v1/templates/{template_id}", headers={"Authorization": "Bearer rp_live_your_api_key"},)response.raise_for_status()
data = response.json()["data"]print(f"Deleted: {data['deleted']}")require "net/http"require "json"require "uri"
template_id = "tmpl_abc123"uri = URI("https://api.relaypost.dev/v1/templates/#{template_id}")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Delete.new(uri)request["Authorization"] = "Bearer rp_live_your_api_key"
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Deleted: #{data["deleted"]}"use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$templateId = "tmpl_abc123";
$response = $client->delete("/v1/templates/{$templateId}", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"],]);
$data = json_decode($response->getBody(), true)["data"];echo "Deleted: " . ($data["deleted"] ? "true" : "false") . "\n";Preview a template
Section titled “Preview a template”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);}import requests
template_id = "tmpl_abc123"response = requests.post( f"https://api.relaypost.dev/v1/templates/{template_id}/preview", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "data": { "customerName": "Jane", "orderNumber": "1234", "total": "$49.99", } },)response.raise_for_status()
data = response.json()["data"]print("Subject:", data["subject"])print("HTML:", data["html"])print("Variables:", data["variables"])if data["warnings"]: print("Warnings:", data["warnings"])require "net/http"require "json"require "uri"
template_id = "tmpl_abc123"uri = URI("https://api.relaypost.dev/v1/templates/#{template_id}/preview")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {data: {customerName: "Jane",orderNumber: "1234",total: "$49.99"}}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts "Subject: #{data["subject"]}"puts "HTML: #{data["html"]}"puts "Variables: #{data["variables"]}"puts "Warnings: #{data["warnings"]}" unless data["warnings"].empty?use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);$templateId = "tmpl_abc123";
$response = $client->post("/v1/templates/{$templateId}/preview", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "data" => [ "customerName" => "Jane", "orderNumber" => "1234", "total" => "$49.99", ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo "Subject: {$data['subject']}\n";echo "HTML: {$data['html']}\n";echo "Variables: " . implode(", ", $data["variables"]) . "\n";if (!empty($data["warnings"])) { echo "Warnings: " . implode(", ", $data["warnings"]) . "\n";}Variable Substitution Examples
Section titled “Variable Substitution Examples”Conditionals and loops
Section titled “Conditionals and loops”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);import requests
response = requests.post( "https://api.relaypost.dev/v1/emails/send", headers={"Authorization": "Bearer rp_live_your_api_key"}, json={ "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", }, },)response.raise_for_status()print(response.json()["data"]["message_id"])require "net/http"require "json"require "uri"
uri = URI("https://api.relaypost.dev/v1/emails/send")http = Net::HTTP.new(uri.host, uri.port)http.use_ssl = true
request = Net::HTTP::Post.new(uri)request["Content-Type"] = "application/json"request["Authorization"] = "Bearer rp_live_your_api_key"request.body = {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"}}.to_json
response = http.request(request)data = JSON.parse(response.body)["data"]puts data["message_id"]use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.relaypost.dev"]);
$response = $client->post("/v1/emails/send", [ "headers" => ["Authorization" => "Bearer rp_live_your_api_key"], "json" => [ "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", ], ],]);
$data = json_decode($response->getBody(), true)["data"];echo $data["message_id"];