Twilio
Access the Twilio API with managed OAuth authentication. Send SMS messages, make voice calls, manage phone numbers, and work with Twilio resources.
Quick Start
# List all accounts
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/twilio/2010-04-01/Accounts.json')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/twilio/2010-04-01/Accounts/{AccountSid}/{resource}.json
The gateway proxies requests to api.twilio.com and automatically injects your OAuth token.
Important: Most Twilio endpoints require your Account SID in the path. You can get your Account SID from the /Accounts.json endpoint.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Twilio OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=twilio&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'twilio'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
json
{
"connection": {
"connection_id": "ebe566b1-3eaf-4926-bc92-0d8d47445f12",
"status": "ACTIVE",
"creation_time": "2026-02-09T23:18:44.243582Z",
"last_updated_time": "2026-02-09T23:19:55.176687Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "twilio",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Twilio connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/twilio/2010-04-01/Accounts.json')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'ebe566b1-3eaf-4926-bc92-0d8d47445f12')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Accounts
List Accounts
GET /twilio/2010-04-01/Accounts.json
Response:
json
{
"accounts": [
{
"sid": "ACf5d980cd4b3f7604a464afaec191fc60",
"friendly_name": "My first Twilio account",
"status": "active",
"date_created": "Mon, 09 Feb 2026 20:19:55 +0000",
"date_updated": "Mon, 09 Feb 2026 20:20:05 +0000"
}
]
}
Get Account
GET /twilio/2010-04-01/Accounts/{AccountSid}.json
Messages (SMS/MMS)
List Messages
GET /twilio/2010-04-01/Accounts/{AccountSid}/Messages.json
Query Parameters:
- PageSize - Number of results per page (default: 50)
- To - Filter by recipient phone number
- From - Filter by sender phone number
- DateSent - Filter by date sent
Response:
json
{
"messages": [
{
"sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"body": "Hello!",
"from": "+15551234567",
"to": "+15559876543",
"status": "delivered",
"date_sent": "Mon, 09 Feb 2026 21:00:00 +0000"
}
],
"page": 0,
"page_size": 50
}
Get Message
GET /twilio/2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json
Send Message
POST /twilio/2010-04-01/Accounts/{AccountSid}/Messages.json
Content-Type: application/x-www-form-urlencoded
To=+15559876543&From=+15551234567&Body=Hello%20from%20Twilio!
Required Parameters:
- To - Recipient phone number (E.164 format)
- From - Twilio phone number or messaging service SID
- Body - Message text (max 1600 characters)
Optional Parameters:
- MessagingServiceSid - Use instead of From for message routing
- MediaUrl - URL of media to send (MMS)
- StatusCallback - Webhook URL for status updates
Response:
json
{
"sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"body": "Hello from Twilio!",
"from": "+15551234567",
"to": "+15559876543",
"status": "queued",
"date_created": "Mon, 09 Feb 2026 21:00:00 +0000"
}
Update Message (Redact)
POST /twilio/2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json
Content-Type: application/x-www-form-urlencoded
Body=
Setting Body to empty string redacts the message content.
Delete Message
DELETE /twilio/2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json
Returns 204 No Content on success.
Calls (Voice)
List Calls
GET /twilio/2010-04-01/Accounts/{AccountSid}/Calls.json
Query Parameters:
- PageSize - Results per page
- Status - Filter by status (queued, ringing, in-progress, completed, etc.)
- To - Filter by recipient
- From - Filter by caller
Response:
json
{
"calls": [
{
"sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"from": "+15551234567",
"to": "+15559876543",
"status": "completed",
"duration": "60",
"direction": "outbound-api"
}
],
"page": 0,
"page_size": 50
}
Get Call
GET /twilio/2010-04-01/Accounts/{AccountSid}/Calls/{CallSid}.json
Make Call
POST /twilio/2010-04-01/Accounts/{AccountSid}/Calls.json
Content-Type: application/x-www-form-urlencoded
To=+15559876543&From=+15551234567&Url=https://example.com/twiml
Required Parameters:
- To - Recipient phone number
- From - Twilio phone number
- Url - TwiML application URL
Optional Parameters:
- StatusCallback - Webhook URL for call status updates
- StatusCallbackEvent - Events to receive (initiated, ringing, answered, completed)
- Timeout - Seconds to wait for answer (default: 60)
- Record - Set to true to record the call
Update Call
POST /twilio/2010-04-01/Accounts/{AccountSid}/Calls/{CallSid}.json
Content-Type: application/x-www-form-urlencoded
Status=completed
Use Status=completed to end an in-progress call.
Delete Call
DELETE /twilio/2010-04-01/Accounts/{AccountSid}/Calls/{CallSid}.json
Phone Numbers
List Incoming Phone Numbers
GET /twilio/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json
Response:
json
{
"incoming_phone_numbers": [
{
"sid": "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"phone_number": "+15551234567",
"friendly_name": "My Number",
"capabilities": {
"voice": true,
"sms": true,
"mms": true
}
}
]
}
Get Phone Number
GET /twilio/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{PhoneNumberSid}.json
Update Phone Number
POST /twilio/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{PhoneNumberSid}.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=Updated%20Name&VoiceUrl=https://example.com/voice
Delete Phone Number
DELETE /twilio/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{PhoneNumberSid}.json
Applications
List Applications
GET /twilio/2010-04-01/Accounts/{AccountSid}/Applications.json
Response:
json
{
"applications": [
{
"sid": "APxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"friendly_name": "My App",
"voice_url": "https://example.com/voice",
"sms_url": "https://example.com/sms"
}
]
}
Get Application
GET /twilio/2010-04-01/Accounts/{AccountSid}/Applications/{ApplicationSid}.json
Create Application
POST /twilio/2010-04-01/Accounts/{AccountSid}/Applications.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=My%20App&VoiceUrl=https://example.com/voice
Response:
json
{
"sid": "APxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"friendly_name": "My App",
"voice_url": "https://example.com/voice",
"date_created": "Tue, 10 Feb 2026 00:20:15 +0000"
}
Update Application
POST /twilio/2010-04-01/Accounts/{AccountSid}/Applications/{ApplicationSid}.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=Updated%20App%20Name
Delete Application
DELETE /twilio/2010-04-01/Accounts/{AccountSid}/Applications/{ApplicationSid}.json
Returns 204 No Content on success.
Queues
List Queues
GET /twilio/2010-04-01/Accounts/{AccountSid}/Queues.json
Response:
json
{
"queues": [
{
"sid": "QUxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"friendly_name": "Support Queue",
"current_size": 0,
"max_size": 1000,
"average_wait_time": 0
}
]
}
Create Queue
POST /twilio/2010-04-01/Accounts/{AccountSid}/Queues.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=Support%20Queue&MaxSize=100
Update Queue
POST /twilio/2010-04-01/Accounts/{AccountSid}/Queues/{QueueSid}.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=Updated%20Queue%20Name
Delete Queue
DELETE /twilio/2010-04-01/Accounts/{AccountSid}/Queues/{QueueSid}.json
Addresses
List Addresses
GET /twilio/2010-04-01/Accounts/{AccountSid}/Addresses.json
Create Address
POST /twilio/2010-04-01/Accounts/{AccountSid}/Addresses.json
Content-Type: application/x-www-form-urlencoded
FriendlyName=Office&Street=123%20Main%20St&City=San%20Francisco&Region=CA&PostalCode=94105&IsoCountry=US&CustomerName=Acme%20Inc
Usage Records
List Usage Records
GET /twilio/2010-04-01/Accounts/{AccountSid}/Usage/Records.json
Query Parameters:
- Category - Filter by usage category (calls, sms, etc.)
- StartDate - Start date (YYYY-MM-DD)
- EndDate - End date (YYYY-MM-DD)
Response:
json
{
"usage_records": [
{
"category": "sms",
"description": "SMS Messages",
"count": "100",
"price": "0.75",
"start_date": "2026-02-01",
"end_date": "2026-02-28"
}
]
}
Pagination
Twilio uses page-based pagination:
GET /twilio/2010-04-01/Accounts/{AccountSid}/Messages.json?PageSize=50&Page=0
Parameters:
- PageSize - Results per page (default: 50)
- Page - Page number (0-indexed)
Response includes:
json
{
"messages": [...],
"page": 0,
"page_size": 50,
"first_page_uri": "/2010-04-01/Accounts/{AccountSid}/Messages.json?PageSize=50&Page=0",
"next_page_uri": "/2010-04-01/Accounts/{AccountSid}/Messages.json?PageSize=50&Page=1",
"previous_page_uri": null
}
Use next_page_uri to fetch the next page of results.
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/twilio/2010-04-01/Accounts.json',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
const accountSid = data.accounts[0].sid;
console.log(`Account SID: ${accountSid}`);
Python
import os
import requests
# Get account SID
response = requests.get(
'https://gateway.maton.ai/twilio/2010-04-01/Accounts.json',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
account_sid = response.json()['accounts'][0]['sid']
print(f"Account SID: {account_sid}")
Python (Send SMS)
import os
import requests
account_sid = 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
response = requests.post(
f'https://gateway.maton.ai/twilio/2010-04-01/Accounts/{account_sid}/Messages.json',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/x-www-form-urlencoded'
},
data={
'To': '+15559876543',
'From': '+15551234567',
'Body': 'Hello from Python!'
}
)
message = response.json()
print(f"Message SID: {message['sid']}")
print(f"Status: {message['status']}")
Python (Make Call)
import os
import requests
account_sid = 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
response = requests.post(
f'https://gateway.maton.ai/twilio/2010-04-01/Accounts/{account_sid}/Calls.json',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/x-www-form-urlencoded'
},
data={
'To': '+15559876543',
'From': '+15551234567',
'Url': 'https://demo.twilio.com/docs/voice.xml'
}
)
call = response.json()
print(f"Call SID: {call['sid']}")
print(f"Status: {call['status']}")
Notes
- All endpoints require the
/2010-04-01/API version prefix - Most endpoints require your Account SID in the path
- Request bodies use
application/x-www-form-urlencodedformat (not JSON) - Phone numbers must be in E.164 format (+15551234567)
- SIDs are unique identifiers:
- Account SIDs start with
AC - Message SIDs start with
SM(SMS) orMM(MMS) - Call SIDs start with
CA - Phone Number SIDs start with
PN - Application SIDs start with
AP - Queue SIDs start with
QU
- Account SIDs start with
- POST is used for both creating and updating resources
- DELETE returns 204 No Content on success
- IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|---|---|
| 400 | Missing Twilio connection or bad request |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Twilio API |
Twilio error responses include:
json
{
"code": 20404,
"message": "The requested resource was not found",
"more_info": "https://www.twilio.com/docs/errors/20404",
"status": 404
}
Troubleshooting: Invalid API Key
When you receive an "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF