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://api.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://api.maton.ai/quo/{native-api-path}
Maton 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://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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://api.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://api.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": "{connection_id}",
"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://api.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://api.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', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
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://api.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 requests
response = requests.get(
'https://api.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 requests
response = requests.post(
'https://api.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://api.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://api.maton.ai/quo/v1/phone-numbershttps://api.maton.ai/openphone/v1/phone-numbers共 2 个版本