Quo
Quo API integration with managed OAuth. Manage calls, messages, contacts, and conversations for your business phone system. Use this skill when users want to send SMS, list calls, manage contacts, or
Quo API integration with managed OAuth. Manage calls, messages, contacts, and conversations for your business phone system. Use this skill when users want to send SMS, list calls, manage contacts, or
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Access the Quo API with managed OAuth authentication. Send SMS messages, manage calls and contacts, and retrieve call recordings and transcripts.
# List phone numbers python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/quo/v1/phone-numbers') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('User-Agent', 'Maton/1.0') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
https://gateway.maton.ai/quo/{native-api-path}
Replace
{native-api-path} with the actual Quo API endpoint path. The gateway proxies requests to api.openphone.com and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header and a User-Agent header:
Authorization: Bearer $MATON_API_KEY User-Agent: Maton/1.0
Environment Variable: Set your API key as
MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Manage your Quo OAuth connections at
https://ctrl.maton.ai.
python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=quo&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
python <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'quo'}).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
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:
{ "connection": { "connection_id": "21fd90f9-5935-43cd-b6c8-bde9d915ca80", "status": "ACTIVE", "creation_time": "2025-12-08T07:20:53.488460Z", "last_updated_time": "2026-01-31T20:03:32.593153Z", "url": "https://connect.maton.ai/?session_token=...", "app": "quo", "metadata": {} } }
Open the returned
url in a browser to complete OAuth authorization.
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
If you have multiple Quo 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/quo/v1/phone-numbers') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('User-Agent', 'Maton/1.0') req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
If omitted, the gateway uses the default (oldest) active connection.
GET /quo/v1/phone-numbers
Optional query parameter:
userId - Filter by user ID (pattern: ^US(.*)$)Response:
{ "data": [ { "id": "PN123abc", "number": "+15555555555", "formattedNumber": "(555) 555-5555", "name": "Main Line", "users": [ { "id": "US123abc", "email": "user@example.com", "firstName": "John", "lastName": "Doe", "role": "admin" } ], "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:00:00Z" } ] }
GET /quo/v1/users?maxResults=50
Query parameters:
maxResults (required) - Results per page (1-50, default: 10)pageToken - Pagination tokenResponse:
{ "data": [ { "id": "US123abc", "email": "user@example.com", "firstName": "John", "lastName": "Doe", "role": "owner", "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:00:00Z" } ], "totalItems": 10, "nextPageToken": null }
GET /quo/v1/users/{userId}
POST /quo/v1/messages Content-Type: application/json{ "content": "Hello, world!", "from": "PN123abc", "to": ["+15555555555"] }
Request body:
content (required) - Message text (1-1600 characters)from (required) - Phone number ID (PN*) or E.164 formatto (required) - Array with single recipient in E.164 formatuserId - User ID (defaults to phone owner)setInboxStatus - Set to "done" to mark conversation completeResponse (202):
{ "id": "AC123abc", "to": ["+15555555555"], "from": "+15555555555", "text": "Hello, world!", "phoneNumberId": "PN123abc", "direction": "outgoing", "userId": "US123abc", "status": "queued", "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:00:00Z" }
GET /quo/v1/messages?phoneNumberId=PN123abc&participants[]=+15555555555&maxResults=100
Query parameters:
phoneNumberId (required) - Phone number IDparticipants (required) - Array of participant phone numbers in E.164 formatmaxResults (required) - Results per page (1-100, default: 10)userId - Filter by user IDcreatedAfter - ISO 8601 timestampcreatedBefore - ISO 8601 timestamppageToken - Pagination tokenGET /quo/v1/messages/{messageId}
GET /quo/v1/calls?phoneNumberId=PN123abc&participants[]=+15555555555&maxResults=100
Query parameters:
phoneNumberId (required) - Phone number IDparticipants (required) - Array with single participant phone number in E.164 format (max 1)maxResults (required) - Results per page (1-100, default: 10)userId - Filter by user IDcreatedAfter - ISO 8601 timestampcreatedBefore - ISO 8601 timestamppageToken - Pagination tokenResponse:
{ "data": [ { "id": "AC123abc", "phoneNumberId": "PN123abc", "userId": "US123abc", "direction": "incoming", "status": "completed", "duration": 120, "participants": ["+15555555555"], "answeredAt": "2022-01-01T00:00:00Z", "completedAt": "2022-01-01T00:02:00Z", "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:02:00Z" } ], "totalItems": 50, "nextPageToken": "..." }
GET /quo/v1/calls/{callId}
GET /quo/v1/call-recordings/{callId}
Response:
{ "data": [ { "id": "REC123abc", "duration": 120, "startTime": "2022-01-01T00:00:00Z", "status": "completed", "type": "voicemail", "url": "https://..." } ] }
Recording status values:
absent, completed, deleted, failed, in-progress, paused, processing, stopped, stopping
GET /quo/v1/call-summaries/{callId}
GET /quo/v1/call-transcripts/{callId}
GET /quo/v1/call-voicemails/{callId}
GET /quo/v1/contacts?maxResults=50
Query parameters:
maxResults (required) - Results per page (1-50, default: 10)externalIds - Array of external identifierssources - Array of source indicatorspageToken - Pagination tokenResponse:
{ "data": [ { "id": "CT123abc", "externalId": null, "source": null, "defaultFields": { "company": "Acme Corp", "firstName": "Jane", "lastName": "Doe", "role": "Manager", "emails": [{"name": "work", "value": "jane@example.com", "id": "EM1"}], "phoneNumbers": [{"name": "mobile", "value": "+15555555555", "id": "PH1"}] }, "customFields": [], "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:00:00Z", "createdByUserId": "US123abc" } ], "totalItems": 100, "nextPageToken": "..." }
GET /quo/v1/contacts/{contactId}
POST /quo/v1/contacts Content-Type: application/json{ "defaultFields": { "firstName": "Jane", "lastName": "Doe", "company": "Acme Corp", "phoneNumbers": [{"name": "mobile", "value": "+15555555555"}], "emails": [{"name": "work", "value": "jane@example.com"}] } }
PATCH /quo/v1/contacts/{contactId} Content-Type: application/json{ "defaultFields": { "company": "New Company" } }
DELETE /quo/v1/contacts/{contactId}
GET /quo/v1/contact-custom-fields
GET /quo/v1/conversations?maxResults=100
Query parameters:
maxResults (required) - Results per page (1-100, default: 10)phoneNumbers - Array of phone number IDs or E.164 numbers (1-100 items)userId - Filter by user IDcreatedAfter - ISO 8601 timestampcreatedBefore - ISO 8601 timestampupdatedAfter - ISO 8601 timestampupdatedBefore - ISO 8601 timestampexcludeInactive - Boolean to exclude inactive conversationspageToken - Pagination tokenResponse:
{ "data": [ { "id": "CV123abc", "phoneNumberId": "PN123abc", "name": "Jane Doe", "participants": ["+15555555555"], "assignedTo": "US123abc", "lastActivityAt": "2022-01-01T00:00:00Z", "createdAt": "2022-01-01T00:00:00Z", "updatedAt": "2022-01-01T00:00:00Z" } ], "totalItems": 50, "nextPageToken": "..." }
Quo uses token-based pagination. Include
maxResults to set page size and use pageToken to retrieve subsequent pages.
GET /quo/v1/contacts?maxResults=50&pageToken=eyJsYXN0SWQiOi...
Response includes pagination info:
{ "data": [...], "totalItems": 150, "nextPageToken": "eyJsYXN0SWQiOi..." }
When
nextPageToken is null, you've reached the last page.
const response = await fetch( 'https://gateway.maton.ai/quo/v1/phone-numbers', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}`, 'User-Agent': 'Maton/1.0' } } ); const data = await response.json();
import os import requestsresponse = requests.get( 'https://gateway.maton.ai/quo/v1/phone-numbers', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'User-Agent': 'Maton/1.0' } ) data = response.json()
import os import requestsresponse = requests.post( 'https://gateway.maton.ai/quo/v1/messages', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'User-Agent': 'Maton/1.0', 'Content-Type': 'application/json' }, json={ 'content': 'Hello from Quo!', 'from': 'PN123abc', 'to': ['+15555555555'] } ) data = response.json()
PNUSAC+15555555555)User-Agent header (e.g., User-Agent: Maton/1.0). Requests without this header will be blocked.curl -g when URLs contain brackets (participants[]) to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| Status | Meaning |
|---|---|
| 400 | Bad request (e.g., too many participants, invalid format) |
| 401 | Invalid or missing Maton API key |
| 402 | Insufficient credits for SMS |
| 403 | Not authorized for this phone number |
| 404 | Resource not found |
| 429 | Rate limited |
| 500 | Server error |
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
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
quo. For example:https://gateway.maton.ai/quo/v1/phone-numbershttps://gateway.maton.ai/openphone/v1/phone-numbersNo automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.