Developer documentation
Versioned REST API with idempotency keys, structured errors, scoped API keys and signed webhooks. The machine-readable specification is at /v1/openapi.json.
Idempotency
Send an Idempotency-Key with every send. Retries with the same key return the original message and never charge twice.
Final payload
The platform inserts any mandatory prefix exactly once. Billing uses the final text; use /v1/sms/preview to see it first.
Lifecycle
QUEUED → SUBMITTING → PROVIDER_ACCEPTED → DELIVERED. Uncertain provider outcomes are reconciled and never blindly resent.
cURL
curl -X POST https://sms.example.com/v1/sms \
-H "Authorization: Bearer am_live_xxxx_yyyy" \
-H "Idempotency-Key: order-1029-shipped" \
-H "Content-Type: application/json" \
-d '{"to":"+60123456789","body":"Your order has shipped","category":"TRANSACTIONAL"}'JavaScript (fetch)
const res = await fetch("https://sms.example.com/v1/otp", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AMPLIFY_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({ to: "+60123456789", body: "Your code is 482913" }),
});
const msg = await res.json(); // { message_id, status: "QUEUED", details: { segments, charge_micros, final_text } }Python (requests)
import os, uuid, requests
r = requests.post(
"https://sms.example.com/v1/sms",
headers={"Authorization": f"Bearer {os.environ['AMPLIFY_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4())},
json={"to": "+60123456789", "body": "Appointment tomorrow 10:30am", "category": "TRANSACTIONAL"},
timeout=10,
)
r.raise_for_status()
print(r.json()["message_id"])PHP (cURL)
<?php
$ch = curl_init("https://sms.example.com/v1/sms");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AMPLIFY_API_KEY"),
"Idempotency-Key: " . bin2hex(random_bytes(16)),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["to" => "+60123456789", "body" => "Hello", "category" => "TRANSACTIONAL"]),
]);
$out = json_decode(curl_exec($ch), true);Webhook signature verification
// Verify a webhook (Node.js)
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Error codes you should handle
INSUFFICIENT_BALANCECONTENT_BLOCKEDCONTENT_REVIEW_REQUIREDPREFIX_CONFLICTSENDER_NOT_APPROVEDOUTSIDE_SENDING_WINDOWTOO_MANY_SEGMENTSRATE_LIMITEDIDEMPOTENCY_KEY_REQUIREDRECIPIENT_OPTED_OUT