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