# How to Optimize Message Deliverability with Message Feedback

In this guide you will learn when, why and how to send [Message Feedback](/docs/messaging/api/message-feedback-resource) to report the outcome of whether the recipient of a message performed a specific tracked user action.

Message Feedback

* Allows Twilio to optimize message deliverability.
* Supports gathering of [Messaging Insights](/docs/messaging/features/messaging-insights) on the conversion of One-Time Passwords (OTP) and similar tracked user actions performed by the message recipient.

## Before you begin

### When should you use Message Feedback?

Message Feedback is intended for use cases where sending a message leads to a trackable user action performed by the message recipient. The primary use case is the sending of messages with one-time passwords (OTP) and similar authentication or account verification codes (PINs) in two-factor or multi-factor authentication (2FA or MFA) scenarios.

In these cases, there are consistently occurring trackable user actions which are uniquely identifiable, so they can be traced back to a specific sent Message and its Message Feedback subresource.

Examples of such trackable user actions include:

* A user receives a message with a one-time verification code and enters it into a website or app for 2FA/MFA.
* A user receives a message with a temporary password and uses it to reset the account password.
* A user receives a message and replies to it with a call or message.
* A user clicks on a unique link contained in the message.

### Why should you send Message Feedback?

Sending messages nationally or internationally is not a one-size-fits-all operation. Message deliverability varies by geography, involved carriers, use case, and even for individual customers. Twilio uses a mix of automated algorithms and manual adjustments to ensure the best possible message deliverability for customers.

By providing Message Feedback you serve two related purposes:

* You complement the delivery status data automatically captured by Twilio and involved carriers throughout the message lifecycle with valuable information about the actual performance of user actions by the message recipient. This additional information allows Twilio to further optimize message deliverability both in the aggregate and for your account specifically.
* You provide the very data that allow the real-time [Messaging Insights OTP Conversion Report in the Console](https://www.twilio.com/console/sms/insights/delivery?q=\(activeInsightsView:passcode-conversion\)) to be compiled. This report is a conversion funnel analysis tool covering messages tracked with feedback from sending, through successful delivery to confirmed user action performance.

![OTP Conversion report showing 2.3M attempted, 2.2M status OK, 1.8M conversions, and 213 countries.](https://docs-resources.prod.twilio.com/c260c2504be3f81831c64fcd4b4088370b16898de3e5c19101474dd7bbcb9187.jpg)

## How to submit Message Feedback

Submitting Message Feedback to Twilio is a four-step process:

1. Send a Message with `ProvideFeedback` enabled
2. Store Message SID retrievable in response to the tracked user action
3. Track the user action performed in response to Message receipt
4. Send Message Feedback to confirm the tracked user action was performed

> \[!NOTE]
>
> As the use case details of your tracked user action may differ, the following step-by-step instructions focus on the correct usage of the [Message resource](/docs/messaging/api/message-resource) and its [Message Feedback](/docs/messaging/api/message-feedback-resource) subresource.
>
> For illustration purposes, the following steps assume that
>
> * You send a message to the recipient (user) containing a URL with a unique confirmation `id` as a query parameter.
> * The tracked user action is the opening of the URL by the message recipient.
> * You implemented a means of retrieving the unique Message SID for a sent confirmation message on the basis of the confirmation `id` query parameter.

## Step 1: Send a Message with ProvideFeedback enabled

Create a new Message with the `ProvideFeedback` parameter set to `True` to send the message underlying the uniquely trackable user action.

`ProvideFeedback` must be set to `True` at time of Message creation so that:

* Twilio starts

  * Capturing the necessary data to track delivery performance from Message creation and sending to the expected confirmation that the tracked user action was performed.
  * Incorporating the gathered information in the Message Insights OTP Conversion Report immediately.
* You can send Message Feedback in Step 4 to confirm the actual performance of the tracked user action.

> \[!WARNING]
>
> We recommend using the `ProvideFeedback` parameter *only* on OTP messages to ensure a clean set of OTP-related data in the OTP Conversion report.

Send the Message for which to provide Feedback

```js
// Download the helper library from https://www.twilio.com/docs/node/install
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";

// Find your Account SID and Auth Token at twilio.com/console
// and set the environment variables. See http://twil.io/secure
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = twilio(accountSid, authToken);

async function createMessage() {
  const message = await client.messages.create({
    body: "Open to confirm: https://www.example.com/confirm?id=1234567890",
    from: "+15557122661",
    provideFeedback: true,
    to: "+15558675310",
  });

  console.log(message.body);
}

createMessage();
```

```python
# Download the helper library from https://www.twilio.com/docs/python/install
import os
from twilio.rest import Client

# Find your Account SID and Auth Token at twilio.com/console
# and set the environment variables. See http://twil.io/secure
account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
client = Client(account_sid, auth_token)

message = client.messages.create(
    body="Open to confirm: https://www.example.com/confirm?id=1234567890",
    from_="+15557122661",
    provide_feedback=True,
    to="+15558675310",
)

print(message.body)
```

```csharp
// Install the C# / .NET helper library from twilio.com/docs/csharp/install

using System;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
using System.Threading.Tasks;

class Program {
    public static async Task Main(string[] args) {
        // Find your Account SID and Auth Token at twilio.com/console
        // and set the environment variables. See http://twil.io/secure
        string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
        string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");

        TwilioClient.Init(accountSid, authToken);

        var message = await MessageResource.CreateAsync(
            body: "Open to confirm: https://www.example.com/confirm?id=1234567890",
            from: new Twilio.Types.PhoneNumber("+15557122661"),
            provideFeedback: true,
            to: new Twilio.Types.PhoneNumber("+15558675310"));

        Console.WriteLine(message.Body);
    }
}
```

```java
// Install the Java helper library from twilio.com/docs/java/install

import com.twilio.type.PhoneNumber;
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;

public class Example {
    // Find your Account SID and Auth Token at twilio.com/console
    // and set the environment variables. See http://twil.io/secure
    public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
    public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");

    public static void main(String[] args) {
        Twilio.init(ACCOUNT_SID, AUTH_TOKEN);
        Message message = Message
                              .creator(new com.twilio.type.PhoneNumber("+15558675310"),
                                  new com.twilio.type.PhoneNumber("+15557122661"),
                                  "Open to confirm: https://www.example.com/confirm?id=1234567890")
                              .setProvideFeedback(true)
                              .create();

        System.out.println(message.getBody());
    }
}
```

```go
// Download the helper library from https://www.twilio.com/docs/go/install
package main

import (
	"fmt"
	"github.com/twilio/twilio-go"
	api "github.com/twilio/twilio-go/rest/api/v2010"
	"os"
)

func main() {
	// Find your Account SID and Auth Token at twilio.com/console
	// and set the environment variables. See http://twil.io/secure
	// Make sure TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN exists in your environment
	client := twilio.NewRestClient()

	params := &api.CreateMessageParams{}
	params.SetBody("Open to confirm: https://www.example.com/confirm?id=1234567890")
	params.SetFrom("+15557122661")
	params.SetProvideFeedback(true)
	params.SetTo("+15558675310")

	resp, err := client.Api.CreateMessage(params)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	} else {
		if resp.Body != nil {
			fmt.Println(*resp.Body)
		} else {
			fmt.Println(resp.Body)
		}
	}
}
```

```php
<?php

// Update the path below to your autoload.php,
// see https://getcomposer.org/doc/01-basic-usage.md
require_once "/path/to/vendor/autoload.php";

use Twilio\Rest\Client;

// Find your Account SID and Auth Token at twilio.com/console
// and set the environment variables. See http://twil.io/secure
$sid = getenv("TWILIO_ACCOUNT_SID");
$token = getenv("TWILIO_AUTH_TOKEN");
$twilio = new Client($sid, $token);

$message = $twilio->messages->create(
    "+15558675310", // To
    [
        "body" =>
            "Open to confirm: https://www.example.com/confirm?id=1234567890",
        "from" => "+15557122661",
        "provideFeedback" => true,
    ]
);

print $message->body;
```

```ruby
# Download the helper library from https://www.twilio.com/docs/ruby/install
require 'twilio-ruby'

# Find your Account SID and Auth Token at twilio.com/console
# and set the environment variables. See http://twil.io/secure
account_sid = ENV['TWILIO_ACCOUNT_SID']
auth_token = ENV['TWILIO_AUTH_TOKEN']
@client = Twilio::REST::Client.new(account_sid, auth_token)

message = @client
          .api
          .v2010
          .messages
          .create(
            body: 'Open to confirm: https://www.example.com/confirm?id=1234567890',
            from: '+15557122661',
            provide_feedback: true,
            to: '+15558675310'
          )

puts message.body
```

```bash
# Install the twilio-cli from https://twil.io/cli

twilio api:core:messages:create \
   --body "Open to confirm: https://www.example.com/confirm?id=1234567890" \
   --from +15557122661 \
   --provide-feedback \
   --to +15558675310
```

```bash
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json" \
--data-urlencode "Body=Open to confirm: https://www.example.com/confirm?id=1234567890" \
--data-urlencode "From=+15557122661" \
--data-urlencode "ProvideFeedback=true" \
--data-urlencode "To=+15558675310" \
-u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
```

```json
{
  "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "api_version": "2010-04-01",
  "body": "Open to confirm: https://www.example.com/confirm?id=1234567890",
  "date_created": "Thu, 24 Aug 2023 05:01:45 +0000",
  "date_sent": "Thu, 24 Aug 2023 05:01:45 +0000",
  "date_updated": "Thu, 24 Aug 2023 05:01:45 +0000",
  "direction": "outbound-api",
  "error_code": null,
  "error_message": null,
  "from": "+15557122661",
  "num_media": "0",
  "num_segments": "1",
  "price": null,
  "price_unit": null,
  "messaging_service_sid": "MGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "sid": "SMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "status": "queued",
  "subresource_uris": {
    "media": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Messages/SMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Media.json"
  },
  "to": "+15558675310",
  "uri": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Messages/SMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json"
}
```

## Step 2: Store Message SID retrievable in response to the tracked user action

Store the Message SID of the Message created in Step 1 such that you can retrieve it on the basis of the uniquely identifiable user action you are tracking.

For purposes of the illustrative scenario, the Message SID must be stored and retrievable on the basis of the unique `id` query parameter value contained in the URL sent with the Step 1 message.

## Step 3: Track the user action performed in response to Message receipt

Track the performance of the unique user action performed in response to the successful receipt of the Message sent in Step 1.

For purposes of the illustrative scenario, you know the tracked user action has been performed, when your backend route handler for the URL sent in Step 1 is called with the unique confirmation `id` as a query parameter.

## Step 4: Send Message Feedback to confirm the tracked user action was performed

Once you determine that the unique tracked user action in Step 3 has been performed by the message recipient, you

* Retrieve the unique Message SID of the specific Message underlying the tracked user action.
* Update the Message Feedback for the Message with `Outcome` parameter value `confirmed` to report to Twilio that the tracked user action was performed.

> \[!NOTE]
>
> Update the Message Feedback even if the Message is received with a delay once the conditions for confirmation are met. This ensures the Messaging Insights are current and message delivery optimizations are based on complete information.
>
> Do not update the Message Feedback if the tracked user action is not performed, this will result in the Message Feedback resource's `outcome` status correctly remaining `unconfirmed`.

Send Message Feedback to confirm performance of the tracked user action

```js
// Twilio Credentials
// To set up environmental variables, see http://twil.io/secure
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;

// require the Twilio module and create a REST client
const client = require('twilio')(accountSid, authToken);

const http = require('http');
const express = require('express');

const app = express();

app.get('/confirm', (req, res) => {
  const uniqueId = req.query.id;

  // Lookup constiable `uniqueId` in a database to find messageSid
  const messageSid = 'SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

  // Send Feedback to Twilio
  client
    .messages(messageSid)
    .feedback.create({
      outcome: 'confirmed',
    })
    .then(() => {
      // Handle remaining request normally
      res.send('Thank you!');
      res.end();
    })
    .catch(err => {
      res.status(500);
      res.send(err.toString());
    })
    .done();
});

http.createServer(app).listen(1337, () => {
  console.log('Express server listening on port 1337');
});
```

```py
import os
from flask import Flask
from twilio import Client

import logging

logging.basicConfig(level=logging.INFO)

app = Flask(__name__)


@app.route("/confirm", methods=['GET'])
def incoming_sms():
    # unique_id = request.values.get('id', None)
    # Use a unique id associated with your user to figure out the Message Sid
    # of the message that prompted this action
    message_sid = 'SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

    # Get your Account Sid and Auth Token from twilio.com/console
# To set up environmental variables, see http://twil.io/secure
    account_sid = os.environ['TWILIO_ACCOUNT_SID']
    auth_token = os.environ['TWILIO_AUTH_TOKEN']

    client = Client(account_sid, auth_token)
    client.messages(message_sid).feedback.create(outcome="confirmed")

    return ('Thank you!', 200)


if __name__ == "__main__":
    app.run(debug=True)
```

```cs
// Download the twilio-csharp library from twilio.com/docs/libraries/csharp
using System;
using System.Web.Mvc;
using Twilio.Rest.Api.V2010.Account.Message;
using Twilio;

public class ConfirmController : Controller
{
    // GET: Confirm
    public ActionResult Index()
    {
        // Lookup variable `id` in a database to find messageSid
        const string messageSid = "SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        // Find your Account Sid and Auth Token at twilio.com/console
        // To set up environmental variables, see http://twil.io/secure
        const string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
        const string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
        TwilioClient.Init(accountSid, authToken);

        // Send Feedback to Twilio
        FeedbackResource.Create(messageSid,
                                outcome: FeedbackResource.OutcomeEnum.Confirmed);

        // Handle remaining request normally
        return View();
    }
}
```

```java
import java.io.IOException;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.twilio.rest.api.v2010.account.message.Feedback;

@WebServlet(name = "Confirm", urlPatterns = {"/Confirm"})
public class Confirm extends HttpServlet {
  public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String uniqueId = request.getParameter("id");
    // Lookup variable `uniqueId` in a database to find messageSid
    String messageSid = "SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    Feedback feedback =
        Feedback.creator(messageSid).setOutcome(Feedback.Outcome.CONFIRMED).create();
  }
}
```

```php
<?php
// Get the PHP helper library from https://twilio.com/docs/libraries/php
require_once '/path/to/vendor/autoload.php'; // Loads the library

// Use the REST API Client to make requests to the Twilio REST API
use Twilio\Rest\Client;

$uniqueId = $_REQUEST['id'];
// Lookup variable `$uniqueId` in a database to find messageSid
$messageSid = 'SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

// Your Account SID and Auth Token from twilio.com/console
// To set up environmental variables, see http://twil.io/secure
$accountSid = getenv('TWILIO_ACCOUNT_SID'); 
$authToken = getenv('TWILIO_AUTH_TOKEN');

$client = new Client($accountSid, $authToken);
$client->messages($messageSid)->feedback->create(
    array("outcome" => "confirmed")
);

echo 'Thank you!';
```

```rb
# Get twilio-ruby from twilio.com/docs/ruby/install
require 'twilio-ruby'
require 'sinatra'

post '/confirm' do
  # Get your Account SID and Auth Token from twilio.com/console
# To set up environmental variables, see http://twil.io/secure
  account_sid = ENV['TWILIO_ACCOUNT_SID']
  auth_token = ENV['TWILIO_AUTH_TOKEN']

  # Initialize Twilio Client
  @client = Twilio::REST::Client.new(account_sid, auth_token)

  message_sid = 'SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

  @client.messages(message_sid)
         .feedback.create(outcome: 'confirmed')

  redirect '/thanks'
end
```

```bash
curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Feedback.json' \
--data-urlencode 'Outcome=confirmed'  \
-u ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_auth_token
```

```json
{
	"account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
	"message_sid": "SM6d4d807e10f24d83a1ab01da10ccc0f5",
	"outcome": "confirmed",
	"date_created": "Fri, 02 Sep 2016 18:19:59 +0000",
	"date_updated": "Fri, 02 Sep 2016 18:42:40 +0000",
	"uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/SMS/Messages/SM6d4d807e10f24d83a1ab01da10ccc0f5/Feedback.json"
}
```

## What's next?

Now that you have learned why, when and how to provide Message Feedback, you may wish to check out the following:

* View your reported Message Feedback information in the [Console](https://www.twilio.com/console/sms/insights/feedback) to help you monitor and understand your message deliverability.
* Looking for step-by-step instructions on tracking the delivery of your sent messages based on Twilio- and carrier-captured status data? Follow our guide to [Tracking the Message Status of Outbound Messages](/docs/messaging/guides/track-outbound-message-status) in the programming language of your choice.
