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 retrieve call recordings/transcripts. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
Access the Quo API with managed OAuth authentication. Send SMS messages, manage calls and contacts, and retrieve call recordings and transcripts.
```bash
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`:
```bash
export MATON_API_KEY="YOUR_API_KEY"
```
1. Sign in or create an account at [maton.ai](https://maton.ai)
2. Go to [maton.ai/settings](https://maton.ai/settings)
3. Copy your API key
Manage your Quo OAuth connections at `https://ctrl.maton.ai`.
```bash
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
```
```bash
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
```
```bash
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": "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.
```bash
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:
```bash
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.
#### List Phone Numbers
```bash
GET /quo/v1/phone-numbers
```
Optional query parameter:
**Response:**
```json
{
"data": [
{
"id": "PN123abc",
"number": "+15555555555",
"formattedNumber": "(555) 555-5555",
"name": "Main Line",
"users": [
{
"id": "US123abc",
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"role": "admin"
}
],
"createdAt": "2022-01-01T00:00:00Z",
"updatedAt": "2022-01-01T00:00:00Z"
}
]
}
```
#### List Users
```bash
GET /quo/v1/users?maxResults=50
```
Query parameters:
**Response:**
```json
{
"data": [
{
"id": "US123abc",
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"role": "owner",
"createdAt": "2022-01-01T00:00:00Z",
"updatedAt": "2022-01-01T00:00:00Z"
}
],
"totalItems": 10,
"nextPageToken": null
}
```
#### Get User by ID
```bash
GET /quo/v1/users/{userId}
```
#### Send Text Message
```bash
POST /quo/v1/messages
Content-Type: application/json
{
"content": "Hello, world!",
"from": "PN123abc",
"to": ["+15555555555"]
}
```
Request body:
**Response (202):**
```json
{
"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"
}
```
#### List Messages
```bash
GET /quo/v1/messages?phoneNumberId=PN123abc&participants[]=+15555555555&maxResults=100
```
Query parameters:
#### Get Message by ID
```bash
GET /quo/v1/messages/{messageId}
```
#### List Calls
```bash
GET /quo/v1/calls?phoneNumberId=PN123abc&participants[]=+15555555555&maxResults=100
```
Query parameters:
**Response:**
```json
{
"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 Call by ID
```bash
GET /quo/v1/calls/{callId}
```
#### Get Call Recordings
```bash
GET /quo/v1/call-recordings/{callId}
```
**Response:**
```json
{
"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 Call Summary
```bash
GET /quo/v1/call-summaries/{callId}
```
#### Get Call Transcript
```bash
GET /quo/v1/call-transcripts/{callId}
```
#### Get Call Voicemail
```bash
GET /quo/v1/call-voicemails/{callId}
```
#### List Contacts
```bash
GET /quo/v1/contacts?maxResults=50
```
Query parameters:
**Response:**
```json
{
"data": [
{
"id": "CT123abc",
"externalId": null,
"source": null,
"defaultFields": {
"company": "Acme Corp",
"firstName": "Jane",
"lastName": "Doe",
"role": "Manager",
"emails": [{"name": "work", "value": "[email protected]", "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 Contact by ID
```bash
GET /quo/v1/contacts/{contactId}
```
#### Create Contact
```bash
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": "[email protected]"}]
}
}
```
#### Update Contact
```bash
PATCH /quo/v1/contacts/{contactId}
Content-Type: application/json
{
"defaultFields": {
"company": "New Company"
}
}
```
#### Delete Contact
```bash
DELETE /quo/v1/contacts/{contactId}
```
#### Get Contact Custom Fields
```bash
GET /quo/v1/contact-custom-fields
```
#### List Conversations
```bash
GET /quo/v1/conversations?maxResults=100
```
Query parameters:
**Response:**
```json
{
"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.
```bash
GET /quo/v1/contacts?maxResults=50&pageToken=eyJsYXN0SWQiOi...
```
Response includes pagination info:
```json
{
"data": [...],
"totalItems": 150,
"nextPageToken": "eyJsYXN0SWQiOi..."
}
```
When `nextPageToken` is `null`, you've reached the last page.
```javascript
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();
```
```python
import os
import requests
response = 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()
```
```python
import os
import requests
response = 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()
```
| 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 |
**When you receive an "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:**
1. Check that the `MATON_API_KEY` environment variable is set:
```bash
echo $MATON_API_KEY
```
2. Verify the API key is valid by listing connections:
```bash
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
```
Leave a review
No reviews yet. Be the first to review this skill!