Installation and Setup

Install the HTTP client for making API requests:

npm install axios

Environment Variables

Create a .env.local file:

# .env.local
NESTSMS_API_KEY=nsms_live_your_api_key_here
NESTSMS_API_URL=https://api.nestsms.com

Server Actions

Use Server Actions to securely communicate with the NestSMS API from your Next.js application without exposing your API key to the client.

Send SMS Server Action

Create a server action to send SMS:

// app/actions/sms.ts
"use server";

export async function sendSMS(phoneNumber: string, message: string) {
  try {
    const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        to: phoneNumber,
        message: message,
        sender_id: "YourBrand",
        message_type: "transactional",
      }),
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.error || "Failed to send SMS");
    }

    return {
      success: true,
      jobId: data.job_id,
      message: data.message,
    };
  } catch (error) {
    console.error("SMS Error:", error);
    return {
      success: false,
      error: error instanceof Error ? error.message : "Unknown error",
    };
  }
}

Check SMS Status

// app/actions/sms.ts
"use server";

export async function checkSMSStatus(messageId: string) {
  try {
    const response = await fetch(`https://api.nestsms.com/api/v1/sms/status/${messageId}`, {
      method: "GET",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
      },
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.error || "Failed to check status");
    }

    return {
      success: true,
      status: data.status,
      deliveredAt: data.delivered_at,
      sentAt: data.sent_at,
    };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : "Unknown error",
    };
  }
}

Get Account Balance

// app/actions/sms.ts
"use server";

export async function getBalance() {
  try {
    const response = await fetch("https://api.nestsms.com/api/v1/sms/balance", {
      method: "GET",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
      },
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.error || "Failed to fetch balance");
    }

    return {
      success: true,
      balance: data.balance,
      currency: data.currency,
      smsSentToday: data.sms_sent_today,
    };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : "Unknown error",
    };
  }
}

Client Component Usage

// app/components/sms-form.tsx
"use client";

import { useState } from "react";
import { sendSMS } from "../actions/sms";

export function SMSForm() {
  const [phone, setPhone] = useState("");
  const [message, setMessage] = useState("");
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<any>(null);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);
    setResult(null);

    const res = await sendSMS(phone, message);
    setResult(res);
    setLoading(false);

    if (res.success) {
      setPhone("");
      setMessage("");
    }
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <div>
          <label>Phone Number:</label>
          <input
            type="tel"
            value={phone}
            onChange={(e) => setPhone(e.target.value)}
            placeholder="9840123456"
            required
          />
        </div>

        <div>
          <label>Message:</label>
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            placeholder="Your message here"
            required
            maxLength={720}
          />
          <small>{message.length}/720 characters</small>
        </div>

        <button type="submit" disabled={loading}>
          {loading ? "Sending..." : "Send SMS"}
        </button>
      </form>

      {result && (
        <div className={result.success ? "success" : "error"}>
          {result.success ? (
            <>
              <p>✓ SMS sent successfully!</p>
              <p>Job ID: {result.jobId}</p>
            </>
          ) : (
            <p>✗ Error: {result.error}</p>
          )}
        </div>
      )}
    </div>
  );
}

API Routes

Use Next.js API Routes when you want to expose your own backend endpoint that communicates with the NestSMS API.

Create API Route

// app/api/sms/send/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { to, message, senderName } = body;

    // Validate input
    if (!to || !message) {
      return NextResponse.json(
        { success: false, error: "Missing required fields" },
        { status: 400 },
      );
    }

    // Call NestSMS API
    const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        to,
        message,
        sender_id: senderName || "YourBrand",
        message_type: "transactional",
      }),
    });

    const data = await response.json();

    if (!response.ok) {
      return NextResponse.json({ success: false, error: data.error }, { status: response.status });
    }

    return NextResponse.json({
      success: true,
      jobId: data.job_id,
      recipientsCount: data.recipients_count,
      cost: data.cost,
    });
  } catch (error) {
    console.error("API Error:", error);
    return NextResponse.json({ success: false, error: "Internal server error" }, { status: 500 });
  }
}

Bulk SMS API Route

// app/api/sms/bulk/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { recipients, message } = body;

    // Validate - max 1000 recipients per batch
    if (!Array.isArray(recipients) || recipients.length > 1000) {
      return NextResponse.json(
        { success: false, error: "Recipients must be an array (max 1000)" },
        { status: 400 },
      );
    }

    const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        to: recipients,
        message,
        sender_id: "YourBrand",
        message_type: "promotional",
      }),
    });

    const data = await response.json();

    return NextResponse.json(data, { status: response.status });
  } catch (error) {
    return NextResponse.json({ success: false, error: "Internal server error" }, { status: 500 });
  }
}

Status Check API Route

// app/api/sms/status/[messageId]/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest, { params }: { params: { messageId: string } }) {
  try {
    const { messageId } = params;

    const response = await fetch(`https://api.nestsms.com/api/v1/sms/status/${messageId}`, {
      method: "GET",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
      },
    });

    const data = await response.json();

    return NextResponse.json(data, { status: response.status });
  } catch (error) {
    return NextResponse.json({ success: false, error: "Internal server error" }, { status: 500 });
  }
}

OTP Verification

Use the NestSMS API to send one-time passwords for authentication and verification workflows.

Send OTP

// app/actions/otp.ts
"use server";

import { cookies } from "next/headers";

export async function sendOTP(phoneNumber: string) {
  // Generate 6-digit OTP
  const otp = Math.floor(100000 + Math.random() * 900000).toString();

  // Send SMS
  const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.NESTSMS_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: phoneNumber,
      message: `Your verification code is ${otp}. Valid for 5 minutes.`,
      sender_id: "YourApp",
      message_type: "otp",
    }),
  });

  const data = await response.json();

  if (!response.ok) {
    return { success: false, error: data.error };
  }

  // Store OTP in cookie (encrypted in production!)
  cookies().set("otp", otp, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    maxAge: 300, // 5 minutes
  });

  return { success: true, jobId: data.job_id };
}

export async function verifyOTP(phoneNumber: string, enteredOTP: string) {
  const storedOTP = cookies().get("otp")?.value;

  if (!storedOTP) {
    return { success: false, error: "OTP expired" };
  }

  if (storedOTP !== enteredOTP) {
    return { success: false, error: "Invalid OTP" };
  }

  // Clear OTP
  cookies().delete("otp");

  return { success: true };
}

Responses, Errors, and Best Practices

Successful SMS Send Response

{
  "success": true,
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "message": "SMS queued successfully",
  "recipients_count": 1,
  "cost": 1.5
}

Bulk SMS Response

{
  "success": true,
  "job_id": "660f9511-f39c-52e5-b827-557766551111",
  "message": "Bulk SMS queued successfully",
  "recipients_count": 150,
  "cost": 225.0
}

Status Check Response

{
  "success": true,
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "delivered",
  "to": "9840123456",
  "message": "Your OTP is 123456",
  "sender_id": "YourApp",
  "sent_at": "2025-08-21T14:30:00Z",
  "delivered_at": "2025-08-21T14:30:05Z",
  "cost": 1.5
}

Error Response

{
  "success": false,
  "error": "Insufficient balance",
  "code": "INSUFFICIENT_BALANCE"
}

Error Handling

async function sendSMSWithErrorHandling(phone: string, message: string) {
  try {
    const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.NESTSMS_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ to: phone, message, sender_id: "YourBrand" }),
    });

    const data = await response.json();

    if (!response.ok) {
      // Handle specific error codes
      switch (data.code) {
        case "INSUFFICIENT_BALANCE":
          return { error: "Please top up your account balance" };
        case "INVALID_TO_FORMAT":
          return { error: "Invalid phone number format" };
        case "MESSAGE_TOO_LONG":
          return { error: "Message exceeds maximum length" };
        case "RATE_LIMIT_EXCEEDED":
          return {
            error: `Rate limit exceeded. Retry after ${data.retry_after}s`,
          };
        default:
          return { error: data.error || "Failed to send SMS" };
      }
    }

    return { success: true, data };
  } catch (error) {
    return { error: "Network error. Please try again." };
  }
}

Best Practices

  1. Environment Variables: Always use environment variables for API keys
  2. Error Handling: Implement comprehensive error handling
  3. Rate Limiting: Respect API rate limits (60 req/min)
  4. Message Length: Keep messages under 720 characters (text) or 335 (unicode)
  5. Status Tracking: Store job_id to track delivery status
  6. Batch Processing: For bulk SMS, send maximum 1000 recipients per request

Next Steps