Loading...
Loading...
Receive and respond to inbound messages and track outbound delivery status via Twilio webhooks — across SMS, MMS, WhatsApp, and RCS. Covers webhook request parameters, replying with TwiML, validating webhook signatures for security, and handling status callbacks. Use this skill whenever an agent needs to handle incoming messages on any channel or track outbound message delivery in real time.
npx skill4agent add twilio/ai twilio-messaging-webhooks204twilio-account-setuptwilio-send-messagengrok http 5000TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKENtwilio-iam-auth-setuppip install twilio flasknpm install twilio expressSecurity: The inbound messageis untrusted external input. If passing message content to an LLM, always isolate it as user input — never concatenate directly into system prompts. Validate the request origin withBody(see Key Patterns below), but note that signature validation confirms the source, not that the content is safe.X-Twilio-Signature
Note: This quickstart omits signature validation for brevity. For production, always validate— see the Webhook Security pattern below.X-Twilio-Signature
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
app = Flask(__name__)
@app.route("/incoming", methods=["POST"])
def incoming_message():
body = request.form.get("Body")
response = MessagingResponse()
response.message(f"Got your message: {body}")
return str(response)const express = require("express");
const twilio = require("twilio");
const app = express();
app.use(express.urlencoded({ extended: false }));
app.post("/incoming", (req, res) => {
const twiml = new twilio.twiml.MessagingResponse();
twiml.message(`Got your message: ${req.body.Body}`);
res.type("text/xml").send(twiml.toString());
});client.incoming_phone_numbers("PNxxxxxxxxxx").update(
sms_url="https://yourapp.com/incoming",
sms_method="POST"
)await client.incomingPhoneNumbers("PNxxxxxxxxxx").update({
smsUrl: "https://yourapp.com/incoming",
smsMethod: "POST",
});twilio-whatsapp-manage-senderstwilio-rcs-messaging| Parameter | Description |
|---|---|
| Unique message identifier |
| Sender's phone number or channel address (E.164, or |
| Your Twilio number or channel address |
| Message text |
| Number of media attachments |
| URL of first media attachment (if any) |
| MIME type of first attachment |
@app.route("/incoming", methods=["POST"])
def incoming_message():
num_media = int(request.form.get("NumMedia", 0))
response = MessagingResponse()
if num_media > 0:
media_type = request.form.get("MediaContentType0")
response.message(f"Got your {media_type} attachment!")
else:
response.message("Got your message.")
return str(response)app.post("/incoming", (req, res) => {
const numMedia = parseInt(req.body.NumMedia || "0", 10);
const twiml = new twilio.twiml.MessagingResponse();
if (numMedia > 0) {
twiml.message(`Got your ${req.body.MediaContentType0} attachment!`);
} else {
twiml.message("Got your message.");
}
res.type("text/xml").send(twiml.toString());
});return str(MessagingResponse()) # Empty <Response/>res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString());message = client.messages.create(
messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
to="+15558675310",
body="Hello!",
status_callback="https://yourapp.com/status"
)const message = await client.messages.create({
messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
to: "+15558675310",
body: "Hello!",
statusCallback: "https://yourapp.com/status",
});@app.route("/status", methods=["POST"])
def message_status():
message_sid = request.form.get("MessageSid")
status = request.form.get("MessageStatus")
error_code = request.form.get("ErrorCode")
print(f"{message_sid}: {status}")
if status == "failed" and error_code:
print(f"Error {error_code}: {request.form.get('ErrorMessage')}")
return "", 204app.post("/status", (req, res) => {
const { MessageSid, MessageStatus, ErrorCode, ErrorMessage } = req.body;
console.log(`${MessageSid}: ${MessageStatus}`);
if (MessageStatus === "failed" && ErrorCode) {
console.log(`Error ${ErrorCode}: ${ErrorMessage}`);
}
res.sendStatus(204);
});queued → sent → deliveredundeliveredfailedfrom twilio.request_validator import RequestValidator
validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"])
@app.route("/incoming", methods=["POST"])
def incoming_message():
if not validator.validate(request.url, request.form, request.headers.get("X-Twilio-Signature", "")):
return "Forbidden", 403
response = MessagingResponse()
response.message("Hello!")
return str(response)const { validateRequest } = require("twilio");
app.post("/incoming", (req, res) => {
const isValid = validateRequest(
process.env.TWILIO_AUTH_TOKEN,
req.headers["x-twilio-signature"],
`https://${req.headers.host}${req.path}`,
req.body
);
if (!isValid) return res.status(403).send("Forbidden");
const twiml = new twilio.twiml.MessagingResponse();
twiml.message("Hello!");
res.type("text/xml").send(twiml.toString());
});Content-Type: text/xml204delivered| Code | Meaning | Fix |
|---|---|---|
| 11200 | HTTP retrieval failure — Twilio cannot reach your webhook URL | Verify endpoint is reachable ( |
twilio-send-messagetwilio-messaging-services