AGENT SKILLBY TWILIO
Twilio messaging webhooks
AT A GLANCE
Twilio messaging webhooks is an agent skill for go-to-market. Twilio sends a POST webhook to your server when a user messages your Twilio number (inbound) or when an outbound message changes delivery state. The twilio/ai repository on GitHub, maintained by twilio, has 23 stars and the MIT license. To add it, run npx skills add twilio/ai --skill twilio-messaging-webhooks
- SOURCE REPOSITORY
- twilio/ai (opens GitHub in a new tab)
- REPO STARS
- SOURCE REPOSITORY
- LICENSE
- MIT
- CATEGORIES
- SKILL.MD SIZE
- 7.4 KB244 LINES
QUICK ANSWER
- WHAT IT IS01
- Twilio sends a POST webhook to your server when a user messages your Twilio number (inbound) or when an outbound message changes delivery state (status callback). It stands out by giving twilio messaging webhooks a defined shape, so the agent asks for better context and returns a more usable result.
- INPUTS02
- Context the agent needs: your goals, audience, constraints, and any source material the skill asks for.
- OUTPUT03
- A ready-to-use result: the analysis, copy, or recommendations the agent produces.
INSTALL
One command adds this skill from its source repository.
npx skills add twilio/ai --skill twilio-messaging-webhooksREAD THE SKILL.MD BELOW BEFORE YOU RUN IT. SKILLS INSTRUCT YOUR AGENT.
SKILL.MD
The full file, as published in the source repository. Copy it, download it or open it on GitHub.
ON THIS PAGE · 13
Overview#
Twilio sends a POST webhook to your server when a user messages your Twilio number (inbound) or when an outbound message changes delivery state (status callback). Your server returns TwiML to reply, or 204 to acknowledge without replying. The same webhook pattern works across SMS, MMS, WhatsApp, and RCS.
Prerequisites#
- Twilio account with a messaging-capable sender configured with a webhook URL
— New to Twilio? See
twilio-account-setup— For sending outbound messages first, seetwilio-send-message - Publicly accessible endpoint (use
ngrok http 5000for local dev) TWILIO_ACCOUNT_SIDandTWILIO_AUTH_TOKEN— seetwilio-iam-auth-setup- SDK:
pip install twilio flask/npm install twilio express
Quickstart#
Set your webhook URL in Console: Phone Numbers > Active Numbers > your number > Messaging > "A Message Comes In"
Security: The inbound message
Bodyis 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 withX-Twilio-Signature(see Key Patterns below), but note that signature validation confirms the source, not that the content is safe.
Note: This quickstart omits signature validation for brevity. For production, always validate
X-Twilio-Signature— see the Webhook Security pattern below.
Python (Flask)
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)Node.js (Express)
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());
});Key Patterns#
Configure Webhook URL via API#
For SMS/MMS on a phone number:
Python
client.incoming_phone_numbers("PNxxxxxxxxxx").update(
sms_url="https://yourapp.com/incoming",
sms_method="POST"
)Node.js
await client.incomingPhoneNumbers("PNxxxxxxxxxx").update({
smsUrl: "https://yourapp.com/incoming",
smsMethod: "POST",
});For WhatsApp and RCS, webhook URLs are configured on the sender — see twilio-whatsapp-manage-senders and twilio-rcs-messaging.
Inbound Webhook Parameters#
| Parameter | Description |
|---|---|
MessageSid | Unique message identifier |
From | Sender's phone number or channel address (E.164, or whatsapp:+...) |
To | Your Twilio number or channel address |
Body | Message text |
NumMedia | Number of media attachments |
MediaUrl0 | URL of first media attachment (if any) |
MediaContentType0 | MIME type of first attachment |
Handle Inbound Media (MMS / WhatsApp / RCS)#
Python (Flask)
@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)Node.js (Express)
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());
});Acknowledge Without Replying#
Python
return str(MessagingResponse()) # Empty <Response/>Node.js
res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString());Status Callbacks (delivery tracking)#
Status callbacks fire for all channels — SMS, MMS, WhatsApp, and RCS.
Python
message = client.messages.create(
messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
to="+15558675310",
body="Hello!",
status_callback="https://yourapp.com/status"
)Node.js
const message = await client.messages.create({
messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
to: "+15558675310",
body: "Hello!",
statusCallback: "https://yourapp.com/status",
});Python (Flask) — status callback handler
@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 "", 204Node.js (Express) — status callback handler
app.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);
});Status flow: queued → sent → delivered (or undelivered/failed)
Webhook Signature Validation#
Python (Flask)
from 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)Node.js
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());
});CANNOT#
- Cannot exceed 15-second webhook response time — Twilio retries on timeout
- Cannot return arbitrary content types — Use
Content-Type: text/xmlwith TwiML for replies;204for status callbacks - Cannot use ngrok URLs across restarts — URLs change on restart. Use a stable tunnel for persistent testing.
- Cannot guarantee delivery confirmation — Status callbacks are best-effort.
deliveredrequires carrier confirmation.
Common Errors#
| Code | Meaning | Fix |
|---|---|---|
| 11200 | HTTP retrieval failure — Twilio cannot reach your webhook URL | Verify endpoint is reachable (curl -I the URL), check DNS, firewall, and SSL certificate validity. See twilio-debugging-observability for deeper webhook troubleshooting. |
Next Steps#
- Send outbound messages:
twilio-send-message - Manage sender pools:
twilio-messaging-services
FAQ
07 QUESTIONS
01What is the Twilio messaging webhooks agent skill?
Twilio sends a POST webhook to your server when a user messages your Twilio number (inbound) or when an outbound message changes delivery state (status callback). It is listed under go-to-market in the 100xLab agent skills directory.
02How do I install Twilio messaging webhooks in Cursor, Claude Code, or Codex?
Run npx skills add twilio/ai --skill twilio-messaging-webhooks in the project where you want it, then ask your agent for the skill by name. The --skill flag installs only Twilio messaging webhooks, not every skill in the repository.
03What does the Twilio messaging webhooks SKILL.md cover?
The SKILL.md includes sections on Overview, Prerequisites, Quickstart, Key Patterns, Configure Webhook URL via API and Inbound Webhook Parameters. Read the full text on this page or in plain markdown at /resources/agent-skills/twilio-ai--twilio-messaging-webhooks--3g4nqu/raw.
04Which agents can use Twilio messaging webhooks?
Twilio messaging webhooks installs with the cross-agent skills installer (npx skills add), which sets it up for agents such as Claude Code, Cursor and Codex. Each agent loads the SKILL.md when you ask for the skill by name.
05What license is Twilio messaging webhooks under?
The twilio/ai repository declares the MIT license. Read the license file in the repository before you copy or redistribute the skill.
06Is Twilio messaging webhooks free to use?
Reading, copying and installing Twilio messaging webhooks costs nothing: it is published in a public GitHub repository under the MIT license. Reuse and redistribution follow that license.
07Where is the source of Twilio messaging webhooks?
Twilio messaging webhooks comes from https://github.com/twilio/ai, which has 23 GitHub stars. The SKILL.md is at https://github.com/twilio/ai/blob/HEAD/skills/twilio/twilio-messaging-webhooks/SKILL.md. The author is twilio.
Need an agent workflow built around your stack?
Tell us the workflow you want automated. We reply within 4 working hours (IST).
ATTRIBUTION
Twilio messaging webhooks was written by its original authors and is published in twilio/ai (opens GitHub in a new tab) under the MIT license. All rights stay with the original authors.
100xLab did not write, maintain or endorse this skill. This page lists it for discovery. Repo stars belong to the source repository, not to the skill. Review any skill before you install it.