SMS API

You use the SMS API to send and receive a high volume of SMS anywhere in the world.

The workflow for sending outbound messages using the SMS API is:

workflow_call_api_outbound
  1. Create a request to send an SMS.
  2. Check the response codes and ensure that you sent the request correctly.
  3. Your message is delivered to the handset. The user's handset returns a delivery receipt.
  4. If you set callback in the request, check that your user received your SMS correctly.

If you rent one or more virtual numbers from Nexmo, Inbound Messages to that number are sent to your webhook endpoint.

To ensure that your traffic is send over the best possible route, use Conversion API to tell us about the reliability of your 2FA communication. Adding your conversion data means Nexmo delivers your messages faster and more reliably.

By default, your Nexmo account is configured for REST. If you already use SMPP and want to use Nexmo, see how to configure SMPP Access

Implementing the SMS API workflow

To use the SMS API:

  1. Create a request and send an SMS:
    #!/bin/bash
    
    base_url='https://rest.nexmo.com'
    version=''
    action='/sms/json'
    key='API_KEY'
    secret='API_SECRET'
    to='441632960961'
    from='441632960960'
    text="A text sent using the SMS API."
    
    curl -X POST "$base_url$version$action" \
    -d api_key=$key \
    -d api_secret=$secret \
    -d to=$to \
    -d from="$from" \
    --data-urlencode "text=$text"
    
    <?php
    $url = 'https://rest.nexmo.com/sms/json?' . http_build_query(
        [
          'api_key' =>  'API_KEY',
          'api_secret' => 'API_SECRET',
          'to' => '441632960960',
          'from' => '441632960061',
          'text' => 'Hello from Nexmo'
        ]
    );
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    
    echo $response;
    
    import urllib
    import urllib2
    
    params = {
        'api_key': 'API_KEY',
        'api_secret': 'API_SECRET',
        'to': '441632960960',
        'from': '441632960961',
        'text': 'Hello from Nexmo'
    }
    
    url = 'https://rest.nexmo.com/sms/json?' + urllib.urlencode(params)
    
    request = urllib2.Request(url)
    request.add_header('Accept', 'application/json')
    response = urllib2.urlopen(request)
    
    require "net/http"
    require "uri"
    
    uri = URI.parse("https://rest.nexmo.com/sms/json")
    params = {
        'api_key' => 'API_KEY',
        'api_secret' => 'API_SECRET',
        'to' => '441632960960',
        'from' => '441632960961',
        'text': 'Hello from Nexmo'
    }
    
    response = Net::HTTP.post_form(uri, params)
    
    puts response.body
    
    var https = require('https');
    
    var data = JSON.stringify({
     api_key: 'API_KEY',
     api_secret: 'API_SECRET',
     to: '441632960960',
     from: '441632960961',
     text: 'Hello from Nexmo'
    });
    
    var options = {
     host: 'rest.nexmo.com',
     path: '/sms/json',
     port: 443,
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
       'Content-Length': Buffer.byteLength(data)
     }
    };
    
    var req = https.request(options);
    
    req.write(data);
    req.end();
    
    var responseData = '';
    req.on('response', function(res){
     res.on('data', function(chunk){
       responseData += chunk;
     });
    
     res.on('end', function(){
       console.log(JSON.parse(responseData));
     });
    });
    
    If you are sending to more than one country read Global messaging and ensure that your messages are correctly formatted for country and character set.
  2. Check the response codes to ensure that you sent the request to Nexmo correctly:
    <?php
      //Decode the json object you retrieved when you ran the request.
      $decoded_response = json_decode($response, true);
    
      error_log('You sent ' . $decoded_response['message-count'] . ' messages.');
    
      foreach ( $decoded_response['messages'] as $message ) {
          if ($message['status'] == 0) {
              error_log("Success " . $message['message-id']);
          } else {
              error_log("Error {$message['status']} {$message['error-text']}");
          }
      }
    
    import json
    
    #Using the response object from the request
    
    if response.code == 200 :
        data = response.read()
        #Decode JSON response from UTF-8
        decoded_response = json.loads(data.decode('utf-8'))
        # Check if your messages are succesful
        messages = decoded_response["messages"]
        for message in messages:
            if message["status"] == "0":
                print "success"
    else :
        #Check the errors
        print "unexpected http {code} response from nexmo api". response.code
    
    require 'json'
    
    #Decode the json object from the response object you retrieved from the request.
    if response.kind_of? Net::HTTPOK
      decoded_response = JSON.parse(response.body )
    
      messagecount = decoded_response["message-count"]
    
      decoded_response["messages"].each do |message|
        if message["status"] == "0"
            p "message " + message["message-id"] + " sent successfully.\n"
          else
            p "message has error " + message["status"]  + " " + message["error-text"]
        end
      end
    else
      puts response.code + " error sending message"
    end
    
    //Decode the json object you retrieved when you ran the request.
    
    var decodedResponse = JSON.parse(responseData);
    
    console.log('You sent ' + decodedResponse['message-count'] + ' messages.\n');
    
    decodedResponse['messages'].forEach(function(message) {
        if (message['status'] === "0") {
          console.log('Success ' + decodedResponse['message-id']);
        }
        else {
          console.log('Error ' + decodedResponse['status']  + ' ' +  decodedResponse['error-text']);
        }
    });
    
  3. Your message is delivered to the handset. The user's handset returns a delivery receipt.
  4. If you set callback in the request, use the Delivery Receipt sent to your webhook endpoint to check that your user received your SMS correctly:
    <?php
    // work with get or post
    $request = array_merge($_GET, $_POST);
    
    // Check that this is a delivery receipt.
    if (!isset($request['messageId']) OR !isset($request['status'])) {
        error_log('This is not a delivery receipt');
        return;
    }
    
    //Check if your message has been delivered correctly.
    if ($request['status'] == 'delivered') {
        error_log("Your message to {$request['msisdn']} (message id {$request['messageId']}) was delivered.");
        error_log("The cost was {$request['price']}.");
    } elseif ($request['status'] == 'accepted') {
        error_log("Your message to {$request['msisdn']} (message id {$request['messageId']}) was accepted by the carrier.");
        error_log("The cost was {$request['price']}.");
    } else {
        error_log("Your message to {$request['msisdn']} has a status of: {$request['status']}.");
        error_log("Check err-code {$request['err-code']} against the documentation.");
    }
    
    #To run this code, replace the MyHandler in
    #https://wiki.python.org/moin/BaseHttpServer With the following code,
    from urlparse import urlparse, parse_qs
    class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_GET(s):
            """Tell Nexmo that you have recieved the GET request."""
            s.send_response(200)
            s.send_header("Content-type", "text/html")
            s.end_headers()
            """Parse parameters in the GET request"""
            parsed_path = urlparse(s.path)
            try:
                    delivery_receipt = dict(
                    [p.split('=') for p in parsed_path[4].split('&')])
            except:
                    delivery_receipt = {}
    
                """Check the is a delivery receipt"""
            if 'text' in delivery_receipt:
                print ("This is not a delivery receipt")
            elif delivery_receipt['status'] != "delivered":
                print "Fail:" + delivery_receipt['status']
                + ": " + delivery_receipt['err-code'] +  ".\n"
            else:
                print "success"
                    """Handle the DLR for a message sent succesfully
                        The following parameters in the delivery receipt should
                        match the ones in your request:
                         * Request - from, dlr - to
                         * Response - message-id, dlr - messageId
                         * Request - to, Responese - to, dlr - msisdn
                         * Request - client-ref, dlr - client-ref
                        Use the documentation to check the values."""
    
    require 'socket'
    require 'uri'
    
    def handle_delivery_receipt(request_line)
    #Parse the parameters and check if the message was delivered
      params = URI::decode_www_form(request_line).to_h
      if params["status"].nil? or params["messageId"].nil?
        p ('This is not a delivery receipt')
      elsif params["status"] != 'delivered'
        p ("Your request " + params["status"] +
        "because of " + params["err-code"] );
      else
        p ("Success for request " + params['messageId'] )
      end
    end
    
    # Initialize a TCPServer
    server = TCPServer.new('', 9999)
    
    # Wait for connections
    loop do
      # Wait until a client connects
      socket = server.accept
    
      method, path = socket.gets.split
      handle_delivery_receipt(path)
    
      # Return the 200 so Nexmo does not send the DLR to you repeatedly
      resp = "Thank you"
      headers = ["HTTP/1.1 200 OK",
                 "Content-Type: text/html; charset=iso-8859-1",
                 "Content-Length: #{resp.length}\r\n\r\n"].join("\r\n")
      socket.puts headers
      socket.puts resp
      # Close the socket, terminating the connection
      socket.close
    end
    
    var app = require('express')();
    app.set('port', (process.env.PORT || 5000));
    app.use(require('body-parser').urlencoded({
        extended: false
    }));
    // Handle GET webhook
    app.get('/delivery-receipt-webhook', function(req, res) {
        handleWebhook(req.query, res);
    });
    // Handle POST webhook
    app.post('/delivery-receipt-webhook', function(req, res) {
        handleWebhook(req.body, res);
    });
    
    function handleWebhook(params, res) {
        if (!params['status'] || !params['messageId']) {
            console.log('This is not a delivery receipt');
        } else {
            //This is a DLR, check that your message has been delivered correctly
            if (params['status'] !== 'delivered') {
                console.log("Fail:", params['status'], ": ", params['err-code']);
            } else {
                console.log("Success");
              /*
                * The following parameters in the delivery receipt should match the ones
                * in your request:
                * Request - from, dlr - to\n
                * Response - message-id, dlr - messageId
                * Request - to, Responese - to, dlr - msisdn
                * Request - client-ref, dlr - client-ref
               */
            }
        }
        res.sendStatus(200);
    }
    app.listen(app.get('port'), function() {
        console.log('Example app listening on port', app.get('port'));
    });
    
  5. If you rent one or more virtual numbers from Nexmo, Inbound Messages to that number are sent to your webhook endpoint. The following code example shows how to handle inbound messages:
    <?php
    // work with get or post
    $request = array_merge($_GET, $_POST);
    
    // check that request is inbound message
    if (!isset($request['to']) OR !isset($request['msisdn']) OR !isset($request['text'])) {
        error_log('not inbound message');
        return;
    }
    
    //Deal with concatenated messages
    $message = false;
    if (isset($request['concat']) AND $request['concat'] == true ) {
        //message can be reassembled using the part and total
        error_log("this message is part {$request['concat-part']} of {$request['concat-total']} for {$request['concat-ref']}");
    
        //generally this would be a database
        session_start();
        session_id($request['concat-ref']);
    
        if (!isset($_SESSION['messages'])) {
            $_SESSION['messages'] = array();
        }
    
        $_SESSION['messages'][] = $request;
    
        if (count($_SESSION['messages']) == $request['concat-total']) {
            error_log('received all parts of concatenated message');
    
            //order messages
            usort(
                $_SESSION['messages'], function ($a , $b) {
                    return $a['concat-part'] > $b['concat-part'];
                }
            );
    
            $message = array_reduce(
                $_SESSION['messages'], function ($carry, $item) {
                    return $carry . $item['text'];
                }
            );
        } else {
            error_log('have ' . count($_SESSION['messages']) . " of {$request['concat-total']} message");
        }
    }
    
    //Handle message types
    switch ($request['type']) {
        case 'binary':
            error_log("got a binary message with a UDH: {$request['udh']}");
            break;
    
        case 'unicode':
            //Do some unicode stuff
            error_log("got a unicode message");
        default:
            error_log('message from: ' . $request['msisdn']);
            error_log("the message body is: {$request['text']}");
            if ($message) {
                error_log("the concatenated message is: {$message}");
            }
            break;
    }
    
    #To run this code, replace the MyHandler in
    #https://wiki.python.org/moin/BaseHttpServer With the following code,
    class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_GET(s):
    
            """Tell Nexmo that you have recieved the GET request."""
            s.send_response(200)
            s.send_header("Content-type", "text/html")
            s.end_headers()
    
            """Parse parameters in the GET request"""
        parsed_path = urlparse(s.path)
        try:
                inbound_message = dict([p.split('=') for p in parsed_path[4].split('&')])
        except:
                inbound_message = {}
    
        message = ''
            """Check the is an inbound message"""
        if  (not inbound_message.has_key('to') ) or (not inbound_message.has_key('msisdn')) or (not inbound_message.has_key('text')):
            p ("This is not an inbound message")
        elif inbound_message.has_key('concat'):
            """Deal with a concatenated message"""
            message_parts = shelve.open( inbound_message['concat-ref'])
            message_parts[ inbound_message['concat-part']] = inbound_message['text']
            no_of_parts = len(message_parts)
            if Integer(inbound_message['concat-total']) == no_of_parts:
                iterator = iter(message_parts)
                for i in iterator:
                    message = message_parts[i] + message
            message_parts.close()
        elif not message:
            message = inbound_message['text']
    
        if ( inbound_message['type'] == 'binary'):
            print "Do some binary stuff"
        elif (inbound_message['type'] == 'unicode'):
            print "Do some unicode stuff"
        elif message:
            print ( inbound_message['msisdn'] + " says " + message )
    
    require 'socket'
    require 'uri'
    require 'pstore'
    
    def handle_inbound_message(request_line)
      #Parse the parameters and check if the message was delivered
      inbound_message = URI::decode_www_form(request_line).to_h
    
      message = ''
      #Check the is an inbound message
      if not inbound_message['to'].nil?  or not inbound_message['msisdn'].nil? or not inbound_message['text'].nil?
          p ( inbound_message['msisdn'] )
      elsif not inbound_message['concat']
        #Create or open peristent storage
        message_parts = PStore.new(inbound_message['concat-ref'])
        #Add this part of the message
        message_parts.transaction do
          message_parts[ inbound_message['concat-part']] = inbound_message['text']
        end
        no_of_parts = message_parts.size
        if inbound_message['concat-total'].to_i == no_of_parts
          message_parts.transaction(true) do  # begin read-only transaction, no changes allowed
            message_parts.roots.each do |message_part|
              message = message + message_part
            end
          end
        end
      elsif not message.nil?
        message = inbound_message['text']
      end
      if ( inbound_message['type'] == 'binary')
        p( "Do some binary stuff")
      elsif (inbound_message['type'] == 'unicode')
        p ("Do some unicode stuff")
      elsif not message.nil?
        p ( inbound_message['msisdn'] + " says " + message )
      end
    end
    
    # Initialize a TCPServer
    server = TCPServer.new('', 9999)
    
    # Wait for connections
    loop do
      # Wait until a client connects
      socket = server.accept
      handle_inbound_message(path)
    
      # Return the 200 so Nexmo does not send the DLR to you repeatedly
      resp = "Thank you"
      headers = ["HTTP/1.1 200 OK",
                 "Content-Type: text/html; charset=iso-8859-1",
                 "Content-Length: #{resp.length}\r\n\r\n"].join("\r\n")
      socket.puts headers
      socket.puts resp
      # Close the socket, terminating the connection
      socket.close
    end
    
    var app = require('express')();
    app.set('port', (process.env.PORT || 5000));
    app.use(require('body-parser').urlencoded({
        extended: false
    }));
    // Handle GET webhook
    app.get('/delivery-receipt-webhook', function(req, res) {
        handleWebhook(req.query, res);
    });
    // Handle POST webhook
    app.post('/delivery-receipt-webhook', function(req, res) {
        handleWebhook(req.body, res);
    });
    
    function handleWebhook(params, res) {
        if (!params['status'] || !params['messageId']) {
            console.log('This is not a delivery receipt');
        } else {
            //This is a DLR, check that your message has been delivered correctly
            if (params['status'] !== 'delivered') {
                console.log("Fail:", params['status'], ": ", params['err-code']);
            } else {
                console.log("Success");
              /*
                * The following parameters in the delivery receipt should match the ones
                * in your request:
                * Request - from, dlr - to\n
                * Response - message-id, dlr - messageId
                * Request - to, Responese - to, dlr - msisdn
                * Request - client-ref, dlr - client-ref
               */
            }
        }
        res.sendStatus(200);
    }
    app.listen(app.get('port'), function() {
        console.log('Example app listening on port', app.get('port'));
    });
    
Previous   Next