Public Api
Public API — Third-Party Integration Guide

Send WhatsApp from any app

SmsPort exposes a Twilio-style REST API so you can send WhatsApp messages, check delivery status, and receive real-time webhooks — all using a single API key. No username or password sharing required.

1

Generate your API Key

From your SmsPort dashboard, go to Setup → API Keys & Developer Settings. Click New API Key, give it a label (e.g. "My CRM"), choose live environment, and optionally enter a webhook URL for delivery receipts. Click Generate Key.

⚠️ The full key is shown only once. Copy it immediately and store it securely (environment variable, secrets manager). Never commit it to source code.

Your key will look like:

text
sk_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6
2

Base URL & Authentication

All public API requests go to the base URL below. Authenticate by passing your key in theAuthorization header.

text
Base URL: https://api.smsport.in
http
Authorization: ApiKey sk_live_your_key_here

# Also accepted:
X-Api-Key: sk_live_your_key_here
3

Find your Sender Phone Number ID

Every message needs a from field — this is your WhatsApp Phone Number ID (not the phone number itself). List available senders:

bash
curl https://api.smsport.in/api/v1/senders \
  -H "Authorization: ApiKey sk_live_your_key_here"
json
{
  "senders": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "phoneNumber": "+917600000000",
      "verifiedName": "MyBusiness",
      "status": "connected"
    }
  ]
}
💡 Copy the id field — this is your Phone Number ID used as from in all message requests.
4

Send a Message

4a — Send a text message

bash
curl -X POST https://api.smsport.in/api/v1/messages/send/text \
  -H "Authorization: ApiKey sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "550e8400-e29b-41d4-a716-446655440000",
    "to": "+919876543210",
    "text": "Hello! Your order #1234 has been shipped. 🚀"
  }'
json
{
  "message": {
    "id": "msg-uuid-here",
    "sid": "msg-uuid-here",
    "status": "queued",
    "to": "+919876543210",
    "from": "550e8400-e29b-41d4-a716-446655440000",
    "kind": "text",
    "metaMessageId": null,
    "createdAt": "2026-05-23T18:30:00Z"
  }
}

4b — Send a template message

Templates must be pre-approved by Meta. Use GET /api/v1/templates to list templates, GET /api/v1/templates/:id for ID-based definition lookup, or GET /api/v1/templates/resolve?name=...&language=... for exact name+language lookup.

bash
curl -X POST https://api.smsport.in/api/v1/messages/send/template \
  -H "Authorization: ApiKey sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "550e8400-e29b-41d4-a716-446655440000",
    "to": "+919876543210",
    "templateName": "order_confirmation",
    "languageCode": "en",
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Rahul" },
          { "type": "text", "text": "#ORD-5678" }
        ]
      }
    ]
  }'

4c — Send an image / media message

bash
curl -X POST https://api.smsport.in/api/v1/messages/send/media \
  -H "Authorization: ApiKey sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "550e8400-e29b-41d4-a716-446655440000",
    "to": "+919876543210",
    "mediaType": "image",
    "mediaUrl": "https://yourcdn.com/invoice.jpg",
    "caption": "Your invoice for March 2026"
  }'

Template definitions for safe runtime mapping

Pull the template definition before send so your runtime values align with Meta placeholder order.

bash
# List templates (APPROVED by default)
curl "https://api.smsport.in/api/v1/templates?language=en_US&limit=50&offset=0" \
  -H "Authorization: ApiKey sk_live_your_key_here"

# Get one template with components and placeholders
curl "https://api.smsport.in/api/v1/templates/30c095f8-48db-4125-95ef-628dd63b17c5" \
  -H "Authorization: ApiKey sk_live_your_key_here"

# Resolve by exact name + language
curl "https://api.smsport.in/api/v1/templates/resolve?name=appointment_notification&language=en_US" \
  -H "Authorization: ApiKey sk_live_your_key_here"
json
{
  "template": {
    "id": "30c095f8-48db-4125-95ef-628dd63b17c5",
    "name": "appointment_notification",
    "language": "en_US",
    "category": "UTILITY",
    "status": "APPROVED",
    "components": [
      {
        "type": "BODY",
        "text": "Hello {{1}}, your appointment with {{2}} is confirmed.",
        "requiredVariables": 2,
        "variableOrder": ["{{1}}", "{{2}}"]
      }
    ],
    "updatedAt": "2026-05-24T10:30:00.000Z"
  }
}
5

Check Message Status

Use the id returned at send time to poll delivery status:

bash
curl https://api.smsport.in/api/v1/messages/msg-uuid-here \
  -H "Authorization: ApiKey sk_live_your_key_here"

Message status progresses through:

queued→ sending→ sent→ delivered→ read→ failed
6

Receive Delivery Webhooks

Set WhatsApp Webhook (Panel)

You can set a webhook URL to receive real-time WhatsApp delivery reports and inbound messages as a JSON POST object.

Set the webhook here:
Visit your API Integration Dashboard → Scroll to Webhook Configuration → Toggle Enable Webhooks, paste your URL, and click "Save Configuration".

Webhook payloads

SmsPort pushes two types of events to your webhook: Delivery Receipts and Inbound Replies.

json
// Outbound Delivery Receipt
{
  "event": "message.delivered",
  "apiVersion": "v1",
  "messageId": "msg-uuid-here",
  "tenantId": "tenant-uuid-here",
  "status": "delivered",
  "to": "+919876543210",
  "from": "550e8400-e29b-41d4-a716-446655440000",
  "metaMessageId": "wamid.HBgM...",
  "errorMessage": null,
  "timestamp": "2026-05-23T18:35:00Z"
}

// Inbound Customer Reply
{
  "event": "message.received",
  "apiVersion": "v1",
  "messageId": "msg-inbound-uuid",
  "tenantId": "tenant-uuid-here",
  "status": "received",
  "to": "550e8400-e29b-41d4-a716-446655440000",
  "from": "+919876543210",
  "text": "Thank you for the receipt!",
  "timestamp": "2026-05-23T18:36:00Z"
}

Webhook Response Fields

FieldTypeDescription
eventStringThe type of event: message.delivered or message.received
messageIdStringUnique SmsPort identifier for the message.
statusStringThe delivery status (e.g., sent, delivered, read, failed, received)
toStringFor outbound, the recipient's phone number. For inbound, your Sender ID.
fromStringFor outbound, your Sender ID. For inbound, the customer's phone number.
metaMessageIdStringThe underlying WhatsApp (wamid) identifier.
textStringThe content of the customer's message (only present for message.received).
errorMessageString | nullContains error details if the message failed to deliver.
timestampString (ISO 8601)The UTC date and time when the event occurred.

Verify the webhook signature

Every request includes X-SmsPort-Signature: sha256=<hmac>. Verify it using your webhook secret (shown when you create the key):

javascript
const crypto = require('crypto');

function verifyWebhook(req, webhookSecret) {
  const signature = req.headers['x-smsport-signature'];
  const expected = 'sha256=' + crypto
    .createHmac('sha256', webhookSecret)
    .update(JSON.stringify(req.body))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express example
app.post('/webhook/smsport', express.json(), (req, res) => {
  if (!verifyWebhook(req, process.env.SMSPORT_WEBHOOK_SECRET)) {
    return res.status(401).send('Unauthorized');
  }

  const { event, messageId, status, to } = req.body;
  console.log(`Message ${messageId} to ${to} is now: ${status}`);

  res.sendStatus(200); // Always respond 200 quickly
});
7

Complete Code Examples

Copy and paste these full working examples into your application.

const SMSPORT_API_KEY = process.env.SMSPORT_API_KEY; // sk_live_...
const PHONE_NUMBER_ID = process.env.SMSPORT_PHONE_NUMBER_ID;
const BASE_URL = 'https://api.smsport.in';

async function sendWhatsApp(to, text) {
  const res = await fetch(`${BASE_URL}/api/v1/messages/send/text`, {
    method: 'POST',
    headers: {
      'Authorization': `ApiKey ${SMSPORT_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ from: PHONE_NUMBER_ID, to, text }),
  });

  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.message || 'Send failed');
  }

  return res.json(); // { message: { id, status, ... } }
}

// Usage
const { message } = await sendWhatsApp('+919876543210', 'Hello from my app!');
console.log('Queued:', message.id);

All Public Endpoints

MethodEndpointDescriptionAuth
GET/api/v1/sendersList phone numbersApiKey
GET/api/v1/templatesList templates (filters: status,name,language,limit,offset)ApiKey
GET/api/v1/templates/:idGet full template definition (components + placeholder order)ApiKey
GET/api/v1/templates/resolve?name=...&language=...Resolve full template by exact name+languageApiKey
POST/api/v1/messages/send/textSend text messageApiKey
POST/api/v1/messages/send/mediaSend image/doc/audio/videoApiKey
POST/api/v1/messages/send/templateSend template messageApiKey
GET/api/v1/messages/:idGet message statusApiKey
GET/api/v1/messagesList recent messagesApiKey
POST/api/v1/keysCreate API keyJWT (dashboard)
GET/api/v1/keysList API keysJWT (dashboard)
PATCH/api/v1/keys/:idUpdate label/webhook URLJWT (dashboard)
DELETE/api/v1/keys/:idRevoke API keyJWT (dashboard)

Error Handling

HTTP StatusMeaningFix
401Invalid or missing API keyCheck Authorization header format
400Validation errorCheck request body — see message field
404Message not foundUse correct message ID for your tenant
429Rate limit exceededBack off and retry after a few seconds
500Server errorRetry with exponential backoff
json
// All errors return this shape:
{
  "statusCode": 401,
  "message": "Invalid or revoked API key.",
  "error": "Unauthorized"
}

Idempotency — Prevent Duplicate Sends

Pass a unique idempotencyKey in your request body. If the same key is sent twice, SmsPort returns the original message without sending a duplicate — safe for retries.

json
{
  "from": "phone-number-id",
  "to": "+919876543210",
  "text": "Your OTP is 4821",
  "idempotencyKey": "otp-user-42-attempt-1"
}

Next Steps