Skip to main content
The Plivo Verify API enables programmatic user authentication via 2FA (Two-Factor Authentication) using SMS and voice calls. With just a few simple steps, you can easily integrate OTP-based verification into your applications.

Quick Start

1

Set Up an Application in Plivo Console

To start sending OTPs, create a Verify application in the Plivo Console.Configure these settings:
SettingDescription
AliasFriendly name for your application
Brand nameYour brand name shown in messages
Code lengthNumber of digits in the OTP (4-8)
ExpiryTime window before the OTP expires
AttemptsMaximum delivery attempts per session
TemplateMessage template (e.g., “Your brandverificationcodeis{brand} verification code is “)
Android AppHashHash string for automatic SMS verification on Android
Once created, you’ll receive an Application UUID to use in API requests.
2

Create a Session to Send OTPs

Use the Create Session API to send an OTP:
import plivo

client = plivo.RestClient('<auth_id>', '<auth_token>')

response = client.verify_session.create(
    recipient='<destination_number>',
    app_uuid='<verify_app_uuid>',
    channel='sms',
    url='https://your-domain.com/callback',
    method='POST'
)

print(f"Session UUID: {response.session_uuid}")
Set a callback URL to receive real-time delivery status updates.
3

Validate the OTP

When the user enters the OTP, validate it using the Validate Session API:
import plivo

client = plivo.RestClient('<auth_id>', '<auth_token>')

response = client.verify_session.validate(
    session_uuid='<session_uuid>',
    otp='<otp_code>'
)

print(f"Status: {response.message}")
4

Handle the Response

If the OTP is valid, allow the user to proceed. If invalid, prompt them to re-enter or resend the OTP.

Channels: SMS vs Voice

The Verify API supports two delivery channels:
ChannelWhen to Use
SMSDefault choice. Fast, familiar, works on all phones.
VoiceUsers without SMS capability, accessibility needs, or as fallback when SMS fails.

Using Voice OTP

To send OTP via voice call, set channel to voice:
response = client.verify_session.create(
    recipient='+14155551234',
    app_uuid='<verify_app_uuid>',
    channel='voice',  # Voice call instead of SMS
    url='https://your-domain.com/callback'
)
The user receives an automated voice call that reads out their OTP code.

Channel Fallback Strategy

For critical verifications, implement fallback:
# Try SMS first
response = client.verify_session.create(
    recipient=phone,
    app_uuid=app_uuid,
    channel='sms'
)

# If SMS fails or times out, retry with voice
if response.status == 'failed':
    response = client.verify_session.create(
        recipient=phone,
        app_uuid=app_uuid,
        channel='voice'
    )

Sessions

A session represents a single verification interaction with a user. Each session can have multiple delivery attempts. Example: You send an OTP at 10:00 AM with a 10-minute expiry. This creates a session that expires at 10:10 AM. All delivery attempts during this window are part of the same session and deliver the same OTP.

Session Lifecycle

  1. Created - OTP generated and first delivery attempted
  2. In Progress - Waiting for user to enter OTP (additional attempts possible)
  3. Verified - User entered correct OTP
  4. Expired - Session timed out without successful validation

Fraud Shield

Fraud Shield protects your applications from SMS pumping attacks by monitoring traffic in real-time and blocking suspicious messages.

Protection Levels

LevelDescription
HighStrongest filtering, best for high-risk scenarios. May have more false positives
MediumBalanced filtering (default). Fewer false positives
LowMinimal filtering for apps with higher fraud tolerance
Configure in Console > Verify > App Settings > Fraud Shield. Blocked messages return error code 452 (Potential SMS Pumping).

Preventing False Positives

  1. Skip fraud check for trusted users - Set check_fs=false for known good users
  2. Create separate apps - Use different apps for Sign Up, Sign In, and Password Reset
  3. Use a dedicated low-protection app - For trusted user segments, create an app with Fraud Shield set to Low or Disabled

Signature Validation

Validate that webhook requests originate from Plivo using signature verification.

HTTP Headers

All Plivo requests include:
  • X-Plivo-Signature-V2 - Generated using account/subaccount Auth Token
  • X-Plivo-Signature-Ma-V2 - Always generated using main account Auth Token
  • X-Plivo-Signature-V2-Nonce - Unique nonce for the request

Generating the Signature

Calculate HMAC with:
  • Key: Your Plivo Auth Token
  • Message: Base URI + X-Plivo-Signature-V2-Nonce (e.g., https://yourdomain.com/callback/05429567804466091622)
  • Hash function: SHA256

Example

from flask import Flask, request
import plivo

app = Flask(__name__)

@app.route('/receive_callback/', methods=['POST'])
def validate():
    signature = request.headers.get('X-Plivo-Signature-V2')
    nonce = request.headers.get('X-Plivo-Signature-V2-Nonce')
    uri = request.url
    auth_token = "<auth_token>"

    is_valid = plivo.utils.validate_signature(uri, nonce, signature, auth_token)
    print(f"Signature valid: {is_valid}")

    return "OK"

Translation Support

Send verification messages in your users’ preferred languages.
  1. Contact Plivo support to configure translations for your SMS templates
  2. Pass the locale parameter when creating a session

Locale Format

Combine language code (ISO 639-1) and optional region code (ISO 3166-1):
  • en - English
  • en_US - English (US)
  • es - Spanish
  • fr_FR - French (France)
If the specified locale is unavailable or invalid, the default locale (en) is used.

When to Use Multiple Applications

Most businesses need just one Verify application. Consider multiple applications when:
  • Different OTP lengths are needed (e.g., 4-digit for login, 6-digit for checkout)
  • Different channels are required (SMS-only for logins, SMS+voice for payments)
  • Different Fraud Shield settings are needed per use case

Reporting

View and analyze your verification traffic on the Plivo console at Verify > Logs.

Session Logs

FieldDescription
DateWhen the session was created
UUIDUnique session identifier
Application AliasName of the application used
RecipientDestination phone number
Statusin-progress, verified, or expired
AttemptsNumber of delivery attempts
CountryDestination country
Total ChargeSession cost including channel fees

Filters

Filter results by subaccount, date range, status, country, phone number, or application alias.
Session data is retained for 90 days.