Skip to content
CloudVNO

Receive Inbound SMS

Handle inbound SMS messages on your CloudVNO phone numbers using webhooks.

How Inbound SMS Works

When someone sends an SMS to your CloudVNO phone number, CloudVNO forwards the message to your configured webhook URL via HTTP POST.

Configure Your Webhook

  1. In the dashboard, go to Phone Numbers
  2. Click on the number you want to configure
  3. Under Messaging, set the Webhook URL for inbound messages
  4. Set the HTTP method (POST recommended)

Or configure via API:

number = client.incoming_phone_numbers("+14155551234").update(
    sms_url="https://yourapp.com/sms-webhook",
    sms_method="POST"
)

Handle the Webhook

CloudVNO sends these parameters to your webhook:

ParameterDescription
MessageSidUnique ID for this message
FromSender's phone number
ToYour CloudVNO number
BodyMessage text
NumMediaNumber of media attachments
MediaUrl0URL of first media attachment (if any)
from flask import Flask, request, Response

app = Flask(__name__)

@app.route("/sms-webhook", methods=["POST"])
def sms_webhook():
    from_number = request.form["From"]
    body = request.form["Body"]

    print(f"Received SMS from {from_number}: {body}")

    # Auto-reply
    if body.strip().upper() == "HELLO":
        return Response(
            '<?xml version="1.0" encoding="UTF-8"?>'
            '<Response><Message>Hi there! How can we help?</Message></Response>',
            mimetype="text/xml"
        )

    return Response("", status=204)
import express from 'express';
const app = express();
app.use(express.urlencoded({ extended: false }));

app.post('/sms-webhook', (req, res) => {
  const { From, Body } = req.body;
  console.log(`SMS from ${From}: ${Body}`);
  res.status(204).send();
});

Webhook Security

Validate that incoming requests are genuinely from CloudVNO by checking the X-CloudVNO-Signature header:

from cloudvno.security import validate_signature

@app.route("/sms-webhook", methods=["POST"])
def sms_webhook():
    signature = request.headers.get("X-CloudVNO-Signature")
    valid = validate_signature(
        auth_token=os.environ["CLOUDVNO_AUTH_TOKEN"],
        signature=signature,
        url=request.url,
        params=request.form
    )
    if not valid:
        return Response("Forbidden", status=403)
    # Handle message...