Feature

Twilio Integration Plugin

Marketplace Product

Twilio Integration plugin has a deep integration with the Backendless to enable the following capabilities:

  • User registration with phone number confirmation;
  • Login with a phone number – two factor authentication (2FA);
  • Sending SMS/MMS API.

This marketplace product enables phone-based authentication for the Backendless login process and also provides some useful APIs, such as sending out an SMS/MMS message. The plugin is deeply integrated into Backendless to provide a seamless experience requiring minimal changes. The standard user registration and login workflows are extended by the plugin to take advantage of its ability to send out SMS and thus provide the 2FA capability.

Installation Instructions

  1. Login to Backendless Console and select your app. Open the Marketplace screen and install the “Twilio Integration plugin” from the Backendless marketplace. The plugin product is available under the All services menu. The product itself is available at no cost (free), however, keep in mind that your app must be on a paid plan in order to install a product from the Marketplace.
  2. The Twilio Integration Plugin product has required configuration parameters. When installing the product, you will be presented with a configuration popup window shown below. Keep the browser window open as you will return to it with the required information.
  3. In a separate browser window, navigate to https://twilio.com and login to your Twilio account. If you do not have an account, you will need to create one.
  4. In the Twilio dashboard, open the Settings section and scroll down to API Credentials.
  5. Copy the Account SID and the Auth Token values into the corresponding fields of the Twilio Service Configuration popup in Backendless Console (the one shown in Step 2 of these instructions).
  6. Enter a phone number in the Twilio Sender Phone Number field in Backendless Console. It must be a phone number registered with Twilio. There is an option to obtain a number from Twilio or transfer an existing number to Twilio.
  7. All other fields can remain as is for now. All the field values can be modified later. Click the TEST CONFIGURATION button which will validate all values.
  8. Click SAVE to save the configuration information and install the Twilio plugin.

Verifying Installation

Once the Twilio plugin is installed, you can verify it by navigating to the Business Logic section of Backendless Console:

The plugin consists of an API service and several API Event handlers which enable deep integration into the user registration and login workflows. The API service has several operations which you can test and try directly from Backendless Console. See below for information about the API operations provided with the Twilio API service.

Phone Number Column

The functionality of the Twilio Integration Plugin relies on a phone number present for every user. The phone number must be stored in the Users table. The name of the column where the phone number is stored is phoneNumber, however, you can change the name using the Service Configuration window shown above. To access the window, select the Business Logic section of Backendless Console, select the Twilio service and click the gear icon as shown below:

In the Service Configuration window see the User Phone Number Field property.

As you will see in the APIs documented below, the phone number is present in every registration and login request. The Twilio Integration Plugin uses the phone number column to send out a code which is used to confirm user’s identity.

Using the API

The API supported by the Twilio Integration Plugin can be used either via REST or using a Backendless SDK. To use the API with an SDK, generate the client-side library for the API as shown below:

  1. Click the Business Logic icon in Backendless Console and select the Twilio service.
  2. Click the Download Client SDK icon as shown below (the icon is shown in bright green color):
  3. Select the language of your choice to download the client SDK generated specifically for the Twilio service. The SDK includes all the methods provided by the Twilio Integration Plugin.

In case a language you would like to use does not appear in the list, you can still use the plugin. The information below describes all available options.

User Registration with Phone Verification

Consider the following scenario: your application’s registration form includes a field for the user’s phone number. When a user creates an account, they provide a phone number that you would like to verify before their account is enabled. They can then log in after the registration process is complete.

The process of user registration with phone number confirmation consists of the following steps:

  1. The application uses the standard registration API call, however, the client sends only the phone number. The response for the registration call includes a special value called "transactionId".
  2. The Twilio Integration Plugin sends out an SMS message containing a confirmation code. The message is sent to the phone number specified in the request (1).
  3. Your application asks the user to enter the confirmation code to complete the registration process.
  4. Once the user enters the confirmation code, the application sends out the second registration API call which now includes the following fields:
    • email or the property/column used as identity by your application.
    • password – user’s password
    • transactionId – this is the value from step 1.
    • transactionCode – this is the code sent to the user’s phone in step 2.
    • phoneNumber – this is the same number used in step 1. This is required as a security measure.
  5. The second registration API call returns a complete user object,

It is possible to configure your application to use the phoneNumber column as identity. In this case, the second registration API call will include only four properties (phoneNumber, password, transactioniId and transactionCode).

    First registration request:

    POST https://api.backendless.com/APP-ID/REST-API-KEY/users/register

    Request Body:

    {
      "phoneNumber":"PHONE-NUMBER-VALUE"
    }
    

    Response:

    {
      "transactionId": "TRANSACTION-ID-VALUE"
    }

    Second registration request:

    POST https://api.backendless.com/APP-ID/REST-API-KEY/users/register

    Request Body:

    {
      "email":"VALUE",
      "password":"VALUE",
      "phoneNumber":"MUST BE THE SAME VALUE AS IN FIRST CALL",
      "transactionId":"VALUE FROM THE FIRST CALL'S RESPONSE",
      "transactionCode":"VALUE SENT TO THE USER'S PHONE",
    }

    Response:

    {
        "userStatus": "ENABLED",
        "created": VALUE,
        "ownerId": "9693201D-C418-410B-FFAE-427D2D08B800",
        "phoneNumber": "USER'S PHONE NUMBER",
        "___class": "Users",
        "blUserLocale": "en",
        "updated": null,
        "objectId": "9693201D-C418-410B-FFAE-427D2D08B800",
        "email": "VALUE"
    }


    First registration request:

    BackendlessUser user = new BackendlessUser();
    user.setEmail("EMAIL-VALUE");
    user.setPassword("PASSWORD-VALUE");
    user.setProperty("phoneNumber", "PHONE-NUMBER-VALUE");
    Backendless.UserService.register(user, new AsyncCallback() {
       @Override
       public void handleResponse(BackendlessUser registeredUser) {
           String transactionId = (String) registeredUser.getProperty("transactionId");
       }
    
       @Override
       public void handleFault(BackendlessFault fault) {
           Log.e(TAG, fault.getMessage());
       }
    });

    Second registration request:

    BackendlessUser user = new BackendlessUser();
    user.setEmail("EMAIL-VALUE");
    user.setPassword("PASSWORD-VALUE");
    user.setProperty("phoneNumber", "MUST BE THE SAME VALUE AS IN FIRST CALL");
    user.setProperty("transactionId", "VALUE FROM THE FIRST CALLS RESPONSE");
    user.setProperty("transactionCode", "VALUE SENT TO THE USERS PHONE");
    Backendless.UserService.register(user, new AsyncCallback() {
       @Override
       public void handleResponse(BackendlessUser registeredUser) {
           Log.i(TAG, "User is registered");
       }
    
       @Override
       public void handleFault(BackendlessFault fault) {
           Log.e(TAG, fault.getMessage());
       }
    });


    First registration request:

    BackendlessUser user = new BackendlessUser();
    user.Email = "EMAIL-VALUE";
    user.Password = "PASSWORD-VALUE";
    user.SetProperty( "phoneNumber", "PHONE-NUMBER-VALUE" );
    
    Backendless.UserService.Register( user, new AsyncCallback<BackendlessUser>(
      registeredUser =>
      {
        String transactionId = (String) registeredUser.GetProperty( "transactionId" );
      },
      fault=>
      {
        //ERROR MESSAGE
      } ) );
    

    Second registration request:

     BackendlessUser user = new BackendlessUser();
     user.Email = "EMAIL-VALUE";
     user.Password = "PASSWORD-VALUE";
     user.SetProperty( "phoneNumber", "PHONE-NUMBER-VALUE" );
     user.SetProperty( "transactionId", "VALUE FROM THE FIRST CALLS RESPONSE" );
     user.SetProperty( "transactionCode", "VALUE SENT TO THE USERS PHONE" );
     Backendless.UserService.Register( user, new AsyncCallback<BackendlessUser>(
       registeredUser =>
       {
         //SUCCESSFUL OPERATION
       },
       fault=>
       {
         // ERROR MESSAGE
       } ) );
    


    First registration request:

    const { transactionId } = await Backendless.Users.register({
      phoneNumber: 'PHONE-NUMBER-VALUE'
    })

    Second registration request:

    const user = await Backendless.Users.register({
     email          : 'VALUE',
     password       : 'VALUE',
     phoneNumber    : 'MUST BE THE SAME VALUE AS IN FIRST CALL',
     transactionId  : 'VALUE FROM THE FIRST CALL\'S RESPONSE',
     transactionCode: 'VALUE SENT TO THE USER\'S PHONE',
    })


    First registration request:

    BackendlessUser user = new BackendlessUser()
     ..email = "EMAIL-VALUE"
     ..password = "PASSWORD-VALUE";
    user.setProperty("phoneNumber", "PHONE-NUMBER-VALUE");
    Backendless.userService.register(user).then((registeredUser) {
       String transactionId = registeredUser.getProperty("transactionId");
     }
    );

    Second registration request:

    BackendlessUser user = new BackendlessUser()
     ..email = "EMAIL-VALUE"
     ..password = "PASSWORD-VALUE";
    user.setProperty("phoneNumber", "MUST BE THE SAME VALUE AS IN FIRST CALL");
    user.setProperty("transactionId", "VALUE FROM THE FIRST CALLS RESPONSE");
    user.setProperty("transactionCode", "VALUE SENT TO THE USERS PHONE");
    Backendless.userService.register(user).then((registeredUser) {
     print("User is registered");
    });


    First registration request:

    let user = BackendlessUser()
    user.email = "EMAIL-VALUE"
    user.password = "PASSWORD-VALUE"
    user.properties["phoneNumber"] = "PHONE-NUMBER-VALUE"
            
    Backendless.shared.userService.registerUser(user: user, responseHandler: { registeredUser in
        let transactionId = registeredUser.properties["transactionId"] as? String
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
    

    Second registration request:

    let user = BackendlessUser()
    user.email = "EMAIL-VALUE"
    user.password = "PASSWORD-VALUE"
    user.properties["phoneNumber"] = "MUST BE THE SAME VALUE AS IN FIRST CALL"
    user.properties["transactionId"] = "VALUE FROM THE FIRST CALL\'S RESPONSE"
    User.properties["transactionCode"] = "VALUE SENT TO THE USER\'S PHONE"
     
    Backendless.shared.userService.registerUser(user: user, responseHandler: { registeredUser in
        // user is registered
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
    


    First registration request:

    BackendlessUser *user = [BackendlessUser new];
    user.email = @"EMAIL-VALUE";
    user.password = @"PASSWORD-VALUE";
        
    NSMutableDictionary *properties = user.properties.mutableCopy;
    properties[@"phoneNumber"] = @"PHONE-NUMBER-VALUE";
    [user setProperties:properties];
        
    [Backendless.shared.userService
     registerUserWithUser:user
     responseHandler:^(BackendlessUser *registeredUser) {
        NSString *transactionId = registeredUser.properties[@"transactionId"];
    }
     errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
    

    Second registration request:

    BackendlessUser *user = [BackendlessUser new];
    user.email = @"EMAIL-VALUE";
    user.password = @"PASSWORD-VALUE";
    
    NSMutableDictionary *properties = user.properties.mutableCopy;
    properties[@"phoneNumber"] = @"MUST BE THE SAME VALUE AS IN FIRST CALL";
    properties[@"transactionId"] = @"VALUE FROM THE FIRST CALL\'S RESPONSE";
    properties[@"transactionCode"] = @"VALUE SENT TO THE USER\'S PHONE";
    [user setProperties:properties];
        
    [Backendless.shared.userService
     registerUserWithUser: user
     responseHandler:^(BackendlessUser *registeredUser) {
        // user is registered
    }
    errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
    

    Login with Phone Number

    Consider a scenario where an application login form requires user’s phone number and their password. The user enters their phone number and the password. If the password is correct, the user receives a code via SMS and enters the code into the application to complete the login. This scenario is supported by the API documented below.

    The process consists of the following steps, which include two service API calls:

    1. Application initiates user login with phone number by making first API call to the Twilio Integration Plugin API service.
    2. Twilio Integration plugin verifies that a user with the provided phone number exists and sends a code to the user’s phone number.
    3. The API call response is a transactionId value.
    4. The second API call by the application completes the login by sending transactionId and code to the Twilio Integration Plugin service.
    5. If the code is confirmed, the second call’s response is the logged in user object.

      1. Request to initiate the login:
        POST https://api.backendless.com/APP-ID/REST-API-KEY/services/Twilio/loginWithPhoneNumber

        Request Body is just a string value of the phone number:

        "PHONE-NUMBER-VALUE"

        Response Body

        { "transactionId":"VALUE" }
      2. Request to confirm code:
        POST https://api.backendless.com/APP-ID/REST-API-KEY/services/Twilio/loginWithCode

        Request Body:

        { 
          "transactionId":"TRANSACTION ID VALUE FROM STEP1",
          "code" : "CODE SENT VIA SMS"  
        }

        Response Body:

        {
         "___jsonclass": "Users",
         "lastLogin": 1590527843419,
         "userStatus": "ENABLED",
         "created": 1590527533000,
         "ownerId": "CBFA6722-58AD-12BE-FF80-28999D7D0200",
         "socialAccount": "BACKENDLESS",
          "___class": "Users",
         "blUserLocale": "en",
         "user-token": "B83FA46D-A19A-A8CE-FF2B-976B7A1D8100",
         "updated":  null,
         "email": "VALUE",
         "objectId": "CBFA6722-58AD-12BE-FF80-28999D7D0200"
        }

        The user-token property value is important, as it identifies the session of the user with Backendless. The value must be passed to the server as an HTTP header with all subsequent API calls.


      Request to initiate the login:
      With Android Client SDK – downloaded from Backendless Console per the instructions above

      Twilio.getInstance().loginWithPhoneNumberAsync("PHONE_NUMBER", new AsyncCallback<Object>() {
         @Override
         public void handleResponse(Object response) {
             String transactionId = (String) ((Map) response).get("transactionId");
         }
      
         @Override
         public void handleFault(BackendlessFault fault) {
             Log.e(TAG, fault.getMessage());
         }
      });

      With Android SDK:

      Backendless.CustomService.invoke("Twilio", "loginWithPhoneNumber",
         new Object[]{"PHONE_NUMBER"}, new AsyncCallback<Map>() {
             @Override
             public void handleResponse(Map response) {
                 String transactionId = (String) response.get("transactionId");
             }
      
             @Override
             public void handleFault(BackendlessFault fault) {
                 Log.e("TAG", fault.getMessage());
             }
         });

      Request to confirm code:
      With Android Client SDK – downloaded from Backendless Console per the instructions above:

      Twilio.getInstance().loginWithCodeAsync("TRANSACTION ID VALUE FROM STEP1",
         "CODE SENT VIA SMS", new AsyncCallback<Object>() {
             @Override
             public void handleResponse(Object response) {
                 // user is logged in
             }
      
             @Override
             public void handleFault(BackendlessFault fault) {
                 Log.e("TAG", fault.getMessage());
             }
         });

      With SDK for Android:

      Backendless.CustomService.invoke("Twilio", "loginWithCode",
         new Object[]{"TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS"}, new AsyncCallback<Map>() {
             @Override
             public void handleResponse(Map response) {
                 // user is logged in
             }
      
             @Override
             public void handleFault(BackendlessFault fault) {
                 Log.e("TAG", fault.getMessage());
             }
         });


      Request to initiate the login:

      Backendless.CustomService.Invoke( "Twilio", "loginWithPhoneNumber", new Object[] { "PHONE NUMBER" }, new AsyncCallback>Dictionary<String, Object>>(
        callback =>
        {
          String transactionId = (String) callback[ "transactionId" ];
        },
        fault =>
        {
          //ERROR MESSAGE
        } ) );
      

      Request to confirm the code:

      Backendless.CustomService.Invoke( "Twilio", "loginWithCode", new Object[] { "TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS" }, new AsyncCallback<Object>(
        callback =>
        {
          //USER IS LOGGED
        },
        fault =>
        {
          //ERROR MESSAGE
        } ) );
      


      Request to initiate the login:

      // with JS Client SDK
      const { transactionId } = await Twilio.loginWithPhoneNumber('PHONE_NUMBER')
      
      // with JS-SDK
      const { transactionId } = await Backendless.CustomServices.invoke('Twilio', 'loginWithPhoneNumber', 'PHONE_NUMBER')
      

      Request to confirm code:

      // with JS Client SDK
      const loggedInUser = await Twilio.loginWithCode('TRANSACTION ID VALUE FROM STEP1', 'CODE SENT VIA SMS')
      
      // with JS-SDK
      const loggedInUser = await Backendless.CustomServices.invoke('Twilio', 'loginWithCode', {
       transactionId: 'TRANSACTION ID VALUE FROM STEP1',
       code         : 'CODE SENT VIA SMS'
      })
      


      Request to initiate the login:

      Backendless.customService.invoke("Twilio", "loginWithPhoneNumber",["PHONE_NUMBER"])
         .then((response) {
           String transactionId = response["transactionId"];
         });

      Request to confirm the code:

      Backendless.customService.invoke("Twilio", "loginWithCode",
           ["TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS"])
         .then((response) {
           print("User is logged in");
         });


      Request to initiate the login:

      // with Swift Client SDK - downloaded from Backendless Console per the instructions above
      Twilio.shared.loginWithPhoneNumber(phoneNumber: "PHONE_NUMBER", responseHandler: { transactionId in
          // received the dictionary ["transactionId": "TRANSACTION ID VALUE"]
      }, errorHandler: { fault in
          print("Error: \(fault.message ?? "")")
      })
      
      // with Swift-SDK
      Backendless.shared.customService.invoke(
          serviceName: "Twilio",
          method: "loginWithPhoneNumber",
          parameters: "PHONE_NUMBER",
          responseHandler: { transactionId in
              // received the dictionary ["transactionId": "TRANSACTION ID VALUE"]
      },
          errorHandler: { fault in
              print("Error: \(fault.message ?? "")")
      })
      

      Request to confirm code:

      // with Swift Client SDK - downloaded from Backendless Console per the instructions above
      Twilio.shared.loginWithCode(
          transactionId: "TRANSACTION ID VALUE FROM STEP1",
          code: "CODE SENT VIA SMS",
          responseHandler: { loggedInUser in
              // user is logged in
      },
          errorHandler: { fault in
              print("Error: \(fault.message ?? "")")
      })
       
      // with Swift-SDK
      Backendless.shared.customService.invoke(
          serviceName: "Twilio",
          method: "loginWithCode",
          parameters: ["transactionId": "TRANSACTION ID VALUE FROM STEP1",
                       "code": "CODE SENT VIA SMS"],
          responseHandler: { loggedInUser in
              // user is logged in
      },
          errorHandler: { fault in
              print("Error: \(fault.message ?? "")")
      })
      


      Request to initiate the login:

      // with Swift Client SDK - downloaded from Backendless Console per the instructions above
      [Twilio.sharedInstance loginWithPhoneNumber:@"PHONE_NUMBER" responseHandler:^(NSDictionary *transactionId) {
          // received the dictionary @{@"transactionId": @"TRANSACTION ID VALUE"}
          });
      } errorHandler:^(Fault *fault) {
          NSLog(@"Error: %@", fault.message);
      }];
      
      // with Swift-SDK
      [Backendless.shared.customService
       invokeWithServiceName:@"Twilio"
       method:@"loginWithPhoneNumber"
       parameters:@"PHONE_NUMBER"
       responseHandler:^(NSDictionary *transactionId) {
          // received the dictionary @{@"transactionId": @"TRANSACTION ID VALUE"}
      }
       errorHandler:^(Fault *fault) {
          NSLog(@"Error: %@", fault.message);
      }];
      

      Request to confirm code:

      // with Swift Client SDK - downloaded from Backendless Console per the instructions above
      [Twilio.sharedInstance
       loginWithCode:@"TRANSACTION ID VALUE FROM STEP1"
       code:@"CODE SENT VIA SMS"
       responseHandler:^(BackendlessUser *loggedInUser) {
          // user is logged in
          NSLog(@"%@", loggedInUser.properties);
      }
       errorHandler:^(Fault *fault) {
          NSLog(@"Error: %@", fault.message);
      }];
      
      // with Swift-SDK
      [Backendless.shared.customService
       invokeWithServiceName:@"Twilio"
       method:@"loginWithCode"
       parameters:@{@"transactionId": @"TRANSACTION ID VALUE FROM STEP1",
                    @"code": @"CODE SENT VIA SMS"}
       responseHandler:^(BackendlessUser *loggedInUser) {
          // user is logged in
      }
       errorHandler:^(Fault *fault) {
          NSLog(@"Error: %@", fault.message);
      }];
      

      Login with Phone Number And Password

      Consider a scenario where an application login form requires user’s phone number and their password. The user enters their phone number and the password. If the password is correct, the user receives a code via SMS and enters the code into the application to complete the login. This scenario is supported by the API documented below. The process consists of the following steps which include two service API calls:

      1. Application initiates user login with phone number and password by making the first API call to the Twilio Integration Plugin API service.
      2. Twilio Integration plugin verifies that a user with the provided phone number exists, and if the provided password is correct, sends a code to the user’s phone number.
      3. The API call response is a transactionId value.
      4. The second API call by the application completes the login by sending transactionId and the code from step 2 to the Twilio Integration Plugin service.
      5. If the code is confirmed, the second call’s response is the logged in user object.

        1. Request to initiate the login:
          POST https://api.backendless.com/APP-ID/REST-API-KEY/services/Twilio/loginWithPhoneNumberAndPassword

          Request Body:

          { 
            "phoneNumber":"VALUE",
            "password" : VALUE"
          }

          Response Body

          { "transactionId":"VALUE" }
        2. Request to confirm code:
          POST https://api.backendless.com/APP-ID/REST-API-KEY/services/Twilio/loginWithCode

          Request Body:

          { 
            "transactionId":"TRANSACTION ID VALUE FROM STEP1",
            "code" : "CODE SENT VIA SMS"  
          }

          Response Body:

          {
           "___jsonclass": "Users",
           "lastLogin": 1590527843419,
           "userStatus": "ENABLED",
           "created": 1590527533000,
           "ownerId": "CBFA6722-58AD-12BE-FF80-28999D7D0200",
           "socialAccount": "BACKENDLESS",
            "___class": "Users",
           "blUserLocale": "en",
           "user-token": "B83FA46D-A19A-A8CE-FF2B-976B7A1D8100",
           "updated":  null,
           "email": "VALUE",
           "objectId": "CBFA6722-58AD-12BE-FF80-28999D7D0200"
          }

          The user-token property value is important, as it identifies the session of the user with Backendless. The value must be passed to the server as an HTTP header with all subsequent API calls.


        Request to initiate the login:
        With Android Client SDK – downloaded from Backendless Console per the instructions above

        Twilio.getInstance().loginWithPhoneNumberAndPasswordAsync("PHONE_NUMBER", "PASSWORD",
           new AsyncCallback<Object>() {
               @Override
               public void handleResponse(Object response) {
                   String transactionId = (String) ((Map) response).get("transactionId");
               }
        
               @Override
               public void handleFault(BackendlessFault fault) {
                   Log.e("TAG", fault.getMessage());
               }
           });

        With Android SDK:

        Backendless.CustomService.invoke("Twilio", "loginWithPhoneNumberAndPassword",
           new Object[]{"PHONE_NUMBER", "PASSWORD"}, new AsyncCallback<Map>() {
               @Override
               public void handleResponse(Map response) {
                   String transactionId = (String) response.get("transactionId");
               }
        
               @Override
               public void handleFault(BackendlessFault fault) {
                   Log.e("TAG", fault.getMessage());
               }
           });

        Request to confirm the code:
        With Android Client SDK:

        Twilio.getInstance().loginWithCodeAsync("TRANSACTION ID VALUE FROM STEP1",
           "CODE SENT VIA SMS", new AsyncCallback<Object>() {
               @Override
               public void handleResponse(Object response) {
                   // user is logged in
               }
        
               @Override
               public void handleFault(BackendlessFault fault) {
                   Log.e("TAG", fault.getMessage());
               }
           });

        With SDK for Android:

        Backendless.CustomService.invoke("Twilio", "loginWithCode",
           new Object[]{"TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS"}, new AsyncCallback<Map>() {
               @Override
               public void handleResponse(Map response) {
                   // user is logged in
               }
        
               @Override
               public void handleFault(BackendlessFault fault) {
                   Log.e("TAG", fault.getMessage());
               }
           });


        Request to initiate the login:

        Backendless.CustomService.Invoke( "Twilio", "loginWithPhoneNumberAndPassword", new Object[] { "PHONE NUMBER", "PASSWORD" }, new AsyncCallback<Dictionary<String, Object>>(
          callback =>
          {
            String transactionId = (String) callback[ "transactionId" ];
          },
          fault =>
          {
            //ERROR MESSAGE
          } ) );
        

        Request to confirm the code:

        Backendless.CustomService.Invoke( "Twilio", "loginWithCode", new Object[] { "TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS" }, new AsyncCallback<Dictionary<String, Object>>(
          callback =>
          {
            //USER IS LOGGED
          },
          fault =>
          {
            //ERROR MESSAGE
          } ) );
        

        Request to initiate the login:

        // with JS Client SDK - downloaded from Backendless Console per the instructions above
        const { transactionId } = await Twilio.loginWithPhoneNumberAndPassword('PHONE_NUMBER', 'PASSWORD')
        
        // with JS-SDK
        const { transactionId } = await Backendless.CustomServices.invoke('Twilio', 'loginWithPhoneNumberAndPassword', {
         phoneNumber: 'VALUE',
         password   : 'VALUE'
        })

        Request to confirm the code:

        // with JS Client SDK - downloaded from Backendless Console per the instructions above
        const loggedInUser = await Twilio.loginWithCode('TRANSACTION ID VALUE FROM STEP1', 'CODE SENT VIA SMS')
        
        // with JS-SDK
        const loggedInUser = await Backendless.CustomServices.invoke('Twilio', 'loginWithCode', {
         transactionId: 'TRANSACTION ID VALUE FROM STEP1',
         code         : 'CODE SENT VIA SMS'
        })


        Request to initiate the login:

        Backendless.customService.invoke("Twilio", "loginWithPhoneNumberAndPassword",
             ["PHONE_NUMBER", "PASSWORD"])
           .then((response) {
             String transactionId = response["transactionId"];
           });

        Request to confirm the code:

        Backendless.customService.invoke("Twilio", "loginWithCode",
             ["TRANSACTION ID VALUE FROM STEP1", "CODE SENT VIA SMS"])
           .then((response) {
             print("User is logged in");
           });

        Request to initiate the login:

        // with Swift Client SDK - downloaded from Backendless Console per the instructions above
        Twilio.shared.loginWithPhoneNumberAndPassword(
            phoneNumber: "PHONE_NUMBER",
            password: "PASSWORD",
            responseHandler: { transactionId in
                // received the dictionary ["transactionId": "TRANSACTION ID VALUE"]
        },
            errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
        })
         
        // with Swift-SDK
        Backendless.shared.customService.invoke(
            serviceName: "Twilio",
            method: "loginWithPhoneNumberAndPassword",
            parameters: ["phoneNumber": "VALUE",
                         "password": "VALUE"],
            responseHandler: { transactionId in
                // received the dictionary ["transactionId": "TRANSACTION ID VALUE"]
        },
            errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
        })
        

        Request to confirm code:

        // with Swift Client SDK - downloaded from Backendless Console per the instructions above
        Twilio.shared.loginWithCode(
            transactionId: "TRANSACTION ID VALUE FROM STEP1",
            code: "CODE SENT VIA SMS",
            responseHandler: { loggedInUser in
                // user is logged in
        },
            errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
        })
         
        // with Swift-SDK
        Backendless.shared.customService.invoke(
            serviceName: "Twilio",
            method: "loginWithCode",
            parameters: ["transactionId": "TRANSACTION ID VALUE FROM STEP1",
                         "code": "CODE SENT VIA SMS"],
            responseHandler: { loggedInUser in
                // user is logged in
        },
            errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
        })
        

        Request to initiate the login:

        // with Swift Client SDK - downloaded from Backendless Console per the instructions above
        [Twilio.sharedInstance
         loginWithPhoneNumberAndPassword:@"PHONE_NUMBER"
         password:@"PASSWORD"
         responseHandler:^(NSDictionary *transactionId) {
            // received the dictionary @{@"transactionId": @"TRANSACTION ID VALUE"}
        }
         errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
        
        // with Swift-SDK
        [Backendless.shared.customService
         invokeWithServiceName:@"Twilio"
         method:@"loginWithPhoneNumberAndPassword"
         parameters:@{@"phoneNumber": @"VALUE",
                      @"password": @"VALUE"}
         responseHandler:^(NSDictionary *transactionId) {
            // received the dictionary @{@"transactionId": @"TRANSACTION ID VALUE"}
        }
         errorHandler:^(Fault *fault){
            NSLog(@"Error: %@", fault.message);
        }];
        

        Request to confirm code:

        // with Swift Client SDK - downloaded from Backendless Console per the instructions above
        [Twilio.sharedInstance
         loginWithCode:@"TRANSACTION ID VALUE FROM STEP1"
         code:@"CODE SENT VIA SMS"
         responseHandler:^(BackendlessUser *loggedInUser) {
            // user is logged in
        }
         errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
        
        // with Swift-SDK
        [Backendless.shared.customService
         invokeWithServiceName:@"Twilio"
         method:@"loginWithCode"
         parameters:@{@"transactionId": @"TRANSACTION ID VALUE FROM STEP1",
                      @"code": @"CODE SENT VIA SMS"}
         responseHandler:^(BackendlessUser *loggedInUser) {
            // user is logged in
        }
         errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
        

        Sending SMS/MMS API

        The API below provides a way to send SMS/MMS messages using Twilio Integration Plugin via your Twilio account.

          POST https://api.backendless.com/APP-ID/REST-API-KEY/services/Twilio/sendMessage

          Request Body:

          {
              "phoneNumber": "RECEPIENT NUMBER",
              "message": "TEXT MESSAGE",
              "mediaUrl": "IMAGE URL FOR MMS DELIVERY"
          }

          The mediaUrl is an optional parameter. If provided, it must contain an HTTPS URL for an image to be delivered in an MMS message.Response Body:Same response as documented by Twilio – https://www.twilio.com/docs/sms/send-messages


          with Android Client SDK – downloaded from Backendless Console per the instructions above

          Twilio.getInstance().sendMessageAsync("RECIPIENT NUMBER", "TEXT MESSAGE",
             "IMAGE URL FOR MMS DELIVERY", null, new AsyncCallback<Object>() {
                 @Override
                 public void handleResponse(Object response) {
                     // handle the result
                 }
          
                 @Override
                 public void handleFault(BackendlessFault fault) {
                     Log.e("TAG", fault.getMessage());
                 }
             });

          With SDK for Android:

          Backendless.CustomService.invoke("Twilio", "sendMessage",
             new Object[]{"RECIPIENT NUMBER", "TEXT MESSAGE",
                 "IMAGE URL FOR MMS DELIVERY"}, new AsyncCallback<Map>() {
                 @Override
                 public void handleResponse(Map response) {
                     // handle the result
                 }
          
                 @Override
                 public void handleFault(BackendlessFault fault) {
                     Log.e("TAG", fault.getMessage());
                 }
             });
             }

          Backendless.CustomService.Invoke( "Twilio", "sendMessage", new Object[] { "PHONE NUMBER", "TEXT MESSAGE", "IMAGE URL FOR MMS DELIVERY" }, new AsyncCallback<Dictionary<String, Object>>(
            callback =>
            {
              //Result
            },
            fault =>
            {
              //ERROR MESSAGE
            } ) );
          

          // with JS Client SDK - downloaded from Backendless Console per the instructions above
          const result = await Twilio.sendMessage({
           phoneNumber: 'RECEPIENT NUMBER',
           message    : 'TEXT MESSAGE',
           mediaUrl   : 'IMAGE URL FOR MMS DELIVERY',
          })
          
          // with JS-SDK
          const result = await Backendless.CustomServices.invoke('Twilio', 'sendMessage', {
           phoneNumber: 'RECEPIENT NUMBER',
           message    : 'TEXT MESSAGE',
           mediaUrl   : 'IMAGE URL FOR MMS DELIVERY',
          })

          Backendless.customService.invoke("Twilio", "sendMessage",
               ["RECIPIENT NUMBER", "TEXT MESSAGE", "IMAGE URL FOR MMS DELIVERY"])
             .then((response) {
               // handle the result
             });

          // with Swift Client SDK - downloaded from Backendless Console per the instructions above
          Twilio.shared.sendMessage(
              phoneNumber: "RECIPIENT NUMBER",
              message: "TEXT MESSAGE",
              mediaUrl: "IMAGE URL FOR MMS DELIVERY",
              options: EXTRA OPTIONS,
              responseHandler: { result in
                  // handle the result
          },
              errorHandler: { fault in
                  print("Error: \(fault.message ?? "")")
          })
           
          // with Swift-SDK
          Backendless.shared.customService.invoke(
              serviceName: "Twilio",
              method: "sendMessage",
              parameters: ["phoneNumber": "RECIPIENT NUMBER",
                           "message": "TEXT MESSAGE",
                           "mediaUrl": "IMAGE URL FOR MMS DELIVERY"],
              responseHandler: { result in
                  // handle the result
          },
              errorHandler: { fault in
                  print("Error: \(fault.message ?? "")")
          })
          

          // with Swift Client SDK - downloaded from Backendless Console per the instructions above
          [Twilio.sharedInstance
           sendMessage:@"RECIPIENT NUMBER"
           message:@"TEXT MESSAGE"
           mediaUrl:@"IMAGE URL FOR MMS DELIVERY"
           options:EXTRA OPTIONS
           responseHandler:^(NSDictionary *result) {
              // handle the result
          }
           errorHandler:^(Fault *fault){
              NSLog(@"Error: %@", fault.message);
          }];
              
          // with Swift-SDK
          [Backendless.shared.customService
           invokeWithServiceName:@"Twilio"
           method:@"sendMessage"
           parameters:@{@"phoneNumber": @"RECIPIENT NUMBER",
                        @"message": @"TEXT MESSAGE",
                        @"mediaUrl": @"IMAGE URL FOR MMS DELIVERY"}
           responseHandler:^(NSDictionary *result) {
              // handle the result
          }
           errorHandler:^(Fault *fault) {
              NSLog(@"Error: %@", fault.message);
          }];