SMS Callbacks

You can create analytics on your SMS and MMS traffic by using event-based webhooks — user-defined HTTP callbacks — to track the delivery status of outgoing and incoming messages.

For every SMS and MMS message you send and receive, Plivo sends a status update to a URL you configure as a callback. You can store the information on your server for delivery status analysis.SMS callbacksThe webhooks you integrate with Plivo are triggered by incoming messages or calls. Upon one of these events, Plivo makes an HTTP request (POST or GET) to an endpoint URL you’ve configured for the webhook. In the callback, you can send all the information related to the message either in the POST request’s body or the GET request’s query parameters. To handle a webhook, you must create a listener (web app) that can accept these HTTP requests from Plivo.

Webhooks for outbound SMS

Here’s how to use Plivo APIs to send SMS messages from your web application and include a callback URL. Before you get started, you must buy an SMS-enabled Plivo phone number to send messages to the US and Canada. You can purchase numbers from Phone Numbers > Buy Number section of the Plivo console, or by using the Numbers API.

Buy a New Plivo Number

For Plivo to invoke your webhook and send HTTP requests, you must configure the URL parameter with your webhook URL and set the method as either POST or GET, then specify this URL for message status-related callbacks.

See the send SMS usage guide for more information about how to send SMS messages on a two-way SMS-enabled Plivo phone number.

Code

1
2
3
4
5
6
7
8
9
10
11
12
import plivo

client = plivo.RestClient('<auth_id>','<auth_token>')
response = client.messages.create(
    src='<sender_id>',
    dst='<destination_number>',
    text='Hello, this is a sample text',
    url='https://<yourdomain>.com/sms_status/',
)
print(response)
# prints only the message_uuid
print(response.message_uuid)
1
2
3
4
5
6
7
8
9
10
11
12
13
require "plivo"
include Plivo

api = RestClient.new("<auth_id>","<auth_token>")
response = api.messages.create(
	src:'<sender_id>', 
	dst:"<destination_number>", 
	text:"Hello, this is a sample text",
	url: "https://<yourdomain>.com/sms_status/",
)
puts response
#Prints only the message_uuid
puts response.message_uuid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var plivo = require('plivo');

(function main() {
    'use strict';
    var client = new plivo.Client("<auth_id>", "<auth_token>");
    client.messages.create({
        src: "+12025550000",
        dst: "+12025551111", 
        text: "Hello, this is a sample message",
        method: "GET",
        url: "https://<yourdomain>.com/sms_status/"
    }, ).then(function(response) {
        console.log(response);
        //Prints only the message_uuid
        console.log(response.messageUuid);
    }, );
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
require 'vendor/autoload.php';
use Plivo\RestClient;

$client = new RestClient("<auth_id>","<auth_token>");
$response = $client->messages->create(
  [  
    "src" => "<sender_id>",
    "dst" => "<destination_number>",
    "text"  =>"Hello, this is a sample text",
    "url"=>"https://<yourdomain>.com/sms_status/",
 ]
);
print_r($response);
// Prints only the message_uuid
print_r($response->getmessageUuid(0));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.IOException;
import java.net.URL;
import java.util.Collections;

import com.plivo.api.Plivo;
import com.plivo.api.exceptions.PlivoRestException;
import com.plivo.api.models.message.Message;
import com.plivo.api.models.message.MessageCreateResponse;

class MessageCreate {
    public static void main(String[] args) {
        Plivo.init("<auth_id>", "<auth_token>");
        try {
            MessageCreateResponse response = Message.creator("+12025550000", Collections.singletonList("+12025551111"),
                    "Hello, this is a test message").url(new URL("https://<yourdomain>.com/sms_status/"))
                    .create();
            System.out.println(response);
            // Prints only the message_uuid
            System.out.println(response.getMessageUuid());
        }

        catch (PlivoRestException | IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"fmt"

	"github.com/plivo/plivo-go/v7"
)

func main() {
	client, err := plivo.NewClient("<auth_id>", "<auth_token>", &plivo.ClientOptions{})
	if err != nil {
		fmt.Print("Error", err.Error())
		return
	}
	response, err := client.Messages.Create(
		plivo.MessageCreateParams{
			Src:  "<sender_id>",
			Dst:  "<destination_number>",
			Text: "Hello, this is a sample text",
			URL:  "https://<yourdomain>.com/sms_status/",
		},
	)
	if err != nil {
		fmt.Print("Error", err.Error())
		return
	}
	fmt.Printf("Response: %#v\n", response)
	// Prints only the message_uuid
	fmt.Printf("Response: %#v\n", response.MessageUUID)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using Plivo;

namespace PlivoExamples
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var api = new PlivoApi("<auth_id>","<auth_token>");
            var response = api.Message.Create(
                src: "+12025550000",
                dst: "+12025551111",
                text: "Hello, this is sample text",
                url: "https://<yourdomain>.com/sms_status/"
                );
            Console.WriteLine(response);
            // Prints the message_uuid
            Console.WriteLine(response.MessageUuid[0]);
        }
    }
}
1
2
3
4
curl -i --user auth_id:auth_token \
-H "Content-Type: application/json" \
-d '{"src": "+12025550000","dst": "+12025551111", "text": "Hello, this is a sample text.", "url":"https://<yourdomain>.com/sms_status/"}' \
https://api.plivo.com/v1/Account/{auth_id}/Message/
Notes:
  • Replace the auth_id and auth_token placeholders with your authentication credentials, which you can find on the Overview screen of the Plivo console.
  • Replace the src placeholder with the phone number you purchased and dst with the phone number you’ll be sending SMS messages to. Both should be in E.164 format.
  • Replace https://<yourdomain>.com/sms_status/ with the webhook URL you’ve set up.
  • If you’re using a Plivo trial account, you can send SMS messages only to phone numbers that have been verified with Plivo. You can verify phone numbers through the Phone Numbers > Sandbox Numbers page.

Webhooks for inbound SMS

See the receive SMS usage guide for more information about how to receive SMS messages on a two-way SMS-enabled Plivo phone number.

Handle callbacks in your web app

To handle callbacks in your app, your endpoint should:

  • Capture HTTP requests
  • Respond to the requests

When Plivo sends the HTTP request callbacks to the webhook during an event, you should capture the request (POST or GET based on the type you’ve defined for the URL) and respond with a 200 OK response. You can store the callback data to your database.

Note: Plivo automatically retries webhooks three times if an HTTP 200 status code is not returned:
  • First at 60 seconds after the original attempt.
  • Second at 120 seconds after the first retry attempt.
  • Third at 240 seconds after the second retry attempt.

Test and validate

Let’s take a look at an example. Typically, you would include a URL that points to your web app, but we’ll use a URL from RequestBin, a service that lets you collect, analyze, and debug HTTP requests, so we can check the callbacks.

  • Create a new bin in RequestBin.
  • Replace the “url” placeholder with the URL of the new bin.
  • Run the code that appear above. You should receive the SMS message on the phone number defined in destination field, and see callback requests in RequestBin similar to the screenshots below for callback events such as queued, sent, and delivered.

Queued status callback

Queued status event

Sent status callback

Sent status event

Delivered status callback

Delivered status event

Note:
  • Callbacks for incoming messages work in the same manner as outbound messages.
  • You should also receive statuses such as undelivered or failed, and some messages may remain in the sent state. See more information about our different SMS statuses.

Validating callbacks

To avoid spoof attacks, you can validate the callbacks that your server URL receives. All requests made by Plivo to your server URLs include X-Plivo-Signature-V2, X-Plivo-Signature-Ma-V2, and X-Plivo-Signature-V2-Nonce HTTP headers. You can use them to validate that a request is from Plivo, as we discuss in our signature validation guide.

Conversion feedback to Plivo

You can store the data from the message status callbacks in your database for analytics. You can also choose to provide the data to Plivo as message delivery feedback. Plivo’s Conversion Feedback API lets you update Plivo on conversions for your critical two-factor authentication (2FA) and one-time password (OTP) messages. Your feedback helps us ensure consistently high delivery rates in countries where carrier networks may be unstable. Plivo’s machine-learning-based dynamic routing engine uses all of our customers’ feedback to ensure that your messages are delivered over the best-performing carrier route to the destination mobile network.