# Authentication
Source: https://www.quo.com/docs/mdx/api-reference/authentication
Learn how to gain API access.
## Prerequisites
Before you begin using the Quo API, ensure you have:
Need an account? Follow our [account creation
guide](https://support.openphone.com/hc/en-us/articles/1500009886621-How-to-create-an-OpenPhone-account).
Owner or admin privileges in your Quo workspace.
**US Messaging Registration Required:** To send text messages to US numbers
via the API, you must complete US Carrier Registration. Learn more
[here](https://support.openphone.com/hc/en-us/articles/15519949741463-Guide-to-US-carrier-registration-for-OpenPhone-customers).
## API key generation
The Quo API uses API keys for secure authentication. Follow these steps to get started:
Access your Quo account.
Navigate to the "API" tab under workspace settings. Remember, you need
workspace owner or admin privileges to access this tab.
Click "Generate API key" and provide a descriptive label. Each key provides
full API access.
Name your API key based on its intended use (e.g., "production-environment" or "testing-integration"). Spaces are not allowed in the API key name.
Include your API key in the Authorization header of each request: ` Authorization: YOUR_API_KEY`
The Quo API does not use a Bearer token for authentication.
## Security guidelines
Your API key carries the same privileges as your Quo account. Treat it
with the same level of security as your password.
### Best practices
* Keep your API keys confidential
* Don’t share your API keys in publicly accessible areas such as GitHub or client-side code
* Regularly rotate your API keys to enhance security
* If a key is compromised, revoke it immediately and generate a new one
### Revoking access
If a key is compromised or no longer needed:
1. Navigate to the "API" tab in Workspace Settings
2. Locate the specific key
3. Click the ellipsis (three dots) icon and select "Delete" to immediately revoke access
4. Generate a new key if needed
Deleting an API key only affects the integrations using that specific key.
Other keys and integrations will continue to function normally.
# Get a call by ID
Source: https://www.quo.com/docs/mdx/api-reference/calls/get-a-call-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/calls/{callId}
Get a call by its unique identifier.
# Get a summary for a call
Source: https://www.quo.com/docs/mdx/api-reference/calls/get-a-summary-for-a-call
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/call-summaries/{callId}
Retrieve a detailed summary of a specific call identified by its unique call ID. This endpoint supports summaries for both regular calls and calls handled by Sona. Call summaries are only available on business and scale plans.
# Get a transcription for a call
Source: https://www.quo.com/docs/mdx/api-reference/calls/get-a-transcription-for-a-call
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/call-transcripts/{id}
Retrieve a detailed transcript of a specific call identified by its unique call ID. This endpoint supports transcripts for both regular calls and calls handled by Sona. Call transcripts are only available on business and scale plans.
# Get a voicemail for a call
Source: https://www.quo.com/docs/mdx/api-reference/calls/get-a-voicemail-for-a-call
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/call-voicemails/{callId}
Retrieve a voicemail associated with a specific call. Returns null data fields while the voicemail is processing in our system. Returns competed data fields when the voicemail has finished processing.
# Get recordings for a call
Source: https://www.quo.com/docs/mdx/api-reference/calls/get-recordings-for-a-call
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/call-recordings/{callId}
Retrieve a list of recordings associated with a specific call. The results are sorted chronologically, with the oldest recording segment appearing first in the list.
# List calls
Source: https://www.quo.com/docs/mdx/api-reference/calls/list-calls
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/calls
Fetch a paginated list of calls associated with a specific Quo number and another number.
# Get contact custom fields
Source: https://www.quo.com/docs/mdx/api-reference/contact-custom-fields/get-contact-custom-fields
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/contact-custom-fields
Custom contact fields enhance your Quo contacts with additional information beyond standard details like name, company, role, emails and phone numbers. These user-defined fields let you capture business-specific data. While you can only create or modify these fields in Quo itself, this endpoint retrieves your existing custom properties. Use this information to accurately map and include important custom data when creating new contacts via the API.
# Create a contact
Source: https://www.quo.com/docs/mdx/api-reference/contacts/create-a-contact
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/contacts
Create a contact for a workspace.
# Delete a contact
Source: https://www.quo.com/docs/mdx/api-reference/contacts/delete-a-contact
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json delete /v1/contacts/{id}
Delete a contact by its unique identifier.
# Get a contact by ID
Source: https://www.quo.com/docs/mdx/api-reference/contacts/get-a-contact-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/contacts/{id}
Retrieve detailed information about a specific contact in your Quo workspace using the contact's unique identifier.
# List contacts
Source: https://www.quo.com/docs/mdx/api-reference/contacts/list-contacts
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/contacts
Retrieve a paginated list of contacts. You can optionally filter the results by providing external IDs and sources. When no external IDs are provided, all contacts for the organization are returned.
# Update a contact by ID
Source: https://www.quo.com/docs/mdx/api-reference/contacts/update-a-contact-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json patch /v1/contacts/{id}
Modify an existing contact in your Quo workspace using the contact's unique identifier. This endpoint replaces the contact rather than merging into it: any defaultFields.emails, defaultFields.phoneNumbers or customFields you omit from the request body is deleted on the contact. Always send the full set of fields, emails, and phone numbers you want the contact to have, not just the ones you're changing.
# List conversations
Source: https://www.quo.com/docs/mdx/api-reference/conversations/list-conversations
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/conversations
Fetch a paginated list of conversations of Quo conversations. Can be filtered by user and/or phone numbers. Defaults to all conversations in the Quo organization. Results are returned in descending order based on the most recent conversation.
# Mark conversation as done
Source: https://www.quo.com/docs/mdx/api-reference/conversations/mark-conversation-as-done
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/conversations/{conversationId}/mark-as-done
Mark a conversation as done, removing it from the inbox without sending a message. Returns the updated conversation.
# Mark conversation as open
Source: https://www.quo.com/docs/mdx/api-reference/conversations/mark-conversation-as-open
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/conversations/{conversationId}/mark-as-open
Mark a conversation as open, returning it to the inbox without sending a message. Returns the updated conversation.
# Mark conversation as read
Source: https://www.quo.com/docs/mdx/api-reference/conversations/mark-conversation-as-read
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/conversations/{conversationId}/mark-as-read
Mark a conversation as read, clearing its unread indicator without sending a message. Returns the updated conversation.
# API response codes
Source: https://www.quo.com/docs/mdx/api-reference/error-codes
Quo uses standard HTTP response codes to indicate request status.
## Response code categories
* 2xx: Success
* 4xx: Client-side errors
* 5xx: Server-side errors
Some 4xx errors include specific error codes for programmatic handling.
## Common response codes
| Code | Status | Description |
| - | - | - |
| `200` | OK | Request successful |
| `201` | Created | Resource successfully created |
| `202` | Accepted | Request accepted for processing |
| `204` | No Content | Request successful, no content returned |
| `400` | Bad Request | Invalid parameters |
| `401` | Unauthorized | Missing or invalid API key |
| `403` | Forbidden | Insufficient permissions or an account setting is not enabled |
| `404` | Not Found | Resource doesn't exist |
| `409` | Conflict | Conflict with another request |
| `422` | Unprocessable Entity | Request is well-formed but contains semantic errors |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Server Error | Quo-side issue |
For 429 errors, implement exponential backoff in your requests.
# Introduction
Source: https://www.quo.com/docs/mdx/api-reference/introduction
Welcome to the Quo API!
## Overview
The Quo API enables developers to integrate powerful communication features directly into their applications and workflows. Built on REST principles, our API provides a reliable and secure way to programmatically manage phone communications.
## Key features
Industry-standard REST API design with JSON responses for easy integration
API key-based authentication to ensure secure access to your account
All API responses are returned in standardized JSON format
Comprehensive documentation and [resources](/docs/mdx/guides) to accelerate development
## Next steps
Ready to dive deeper? Here are some helpful resources:
* [Authentication guide](/docs/mdx/api-reference/authentication) - Learn about securing your API requests
* [Send your first message](/docs/mdx/api-reference/send-your-first-message) - Quick start guide for messaging
* [Build with AI LLMs](/docs/mdx/guides/building-with-ai-llms) - Learn how to utilize this API alongside AI
## Need help?
Email our developer support team at [support+developers@quo.com](mailto:support+developers@quo.com).
Review the [Developer API Terms of Service](/docs/mdx/pricing-support/terms-of-service) that apply when using the Quo API.
# Get a message by ID
Source: https://www.quo.com/docs/mdx/api-reference/messages/get-a-message-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/messages/{id}
Get a message by its unique identifier.
# List messages
Source: https://www.quo.com/docs/mdx/api-reference/messages/list-messages
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/messages
Retrieve a chronological list of messages exchanged between your Quo number and specified participants, with support for filtering and pagination.
# Send a text message
Source: https://www.quo.com/docs/mdx/api-reference/messages/send-a-text-message
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/messages
Send a text message from your Quo number to a recipient.
# OpenAPI specification
Source: https://www.quo.com/docs/mdx/api-reference/openapi
View the raw OpenAPI specification for API version v1.
# Get a phone number by ID
Source: https://www.quo.com/docs/mdx/api-reference/phone-numbers/get-a-phone-number-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/phone-numbers/{phoneNumberId}
Get a phone number by its unique identifier.
# List phone numbers
Source: https://www.quo.com/docs/mdx/api-reference/phone-numbers/list-phone-numbers
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/phone-numbers
Retrieve the list of phone numbers and users associated with your Quo workspace.
# Rate limits
Source: https://www.quo.com/docs/mdx/api-reference/rate-limits
Quo implements rate limiting to ensure API stability and fair usage.
Each API key may make up to **10 requests per second.**
Exceeding this limit may result in `429` status code errors.
Implement request throttling in your application to stay within rate limits and optimize API usage.
# Send your first message
Source: https://www.quo.com/docs/mdx/api-reference/send-your-first-message
This is a step-by-step guide for sending your first text message.
## Ping!
Let's get started sending text messages via the Quo API.
**Have you completed Carrier Registration?** If you plan to send text messages to US numbers via the API, you will also need to complete US Carrier Registration. Learn more [here](https://support.openphone.com/hc/en-us/articles/15519949741463-Guide-to-US-carrier-registration-for-OpenPhone-customers).
### 1. Get phone numbers (optional)
Make a call to the `GET Phone Numbers` endpoint to retrieve `userId` and `from` for the desired number (the phone number from which you'd like to send a text message). This step is optional if you already know the `userId` and `from` for the desired sending number.
```
curl --request GET \
--url https://api.quo.com/v1/phone-numbers \
--header 'Authorization: YOUR_API_KEY'
```
### 2. Specify user ID (optional)
If you'd like to send the text message as a particular Quo member in your workspace, make sure to include this `userId` in your request body. If `userId` is not specified, the sender will default to the phone number owner.
### 3. Send your message
You are now ready to send your first text message! Once you send your text message, you will receive a 202 Success Message via the API. Nice!
```
curl --request POST \
--url https://api.quo.com/v1/messages \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"content": "",
"from": "",
"to": [
"+15555555555"
],
"userId": ""
}'
```
Be sure to format phone numbers in E.164 format (+1234567890).
### Summary
You are now able to send a text message to anyone in US or Canada. By using the API, you are able to programmatically send texts to your customers.
# Assign a user to a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/assign-a-user-to-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/assign
Assigns the provided user to the list of assignees for the task
# Change a task's due date
Source: https://www.quo.com/docs/mdx/api-reference/tasks/change-a-tasks-due-date
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/change-due-date
Sets the task's due date
# Complete a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/complete-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/complete
Marks the provided task as completed
# Create a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/create-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks
Create a task linked to a phone number, conversation, or conversation activity. Provide exactly one of `phoneNumberId`, `conversationId`, or `activityId`.
# Delete a task by ID
Source: https://www.quo.com/docs/mdx/api-reference/tasks/delete-a-task-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json delete /v1/tasks/{taskId}
Deletes the task with the provided task ID
# Gets a task by ID
Source: https://www.quo.com/docs/mdx/api-reference/tasks/gets-a-task-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/tasks/{taskId}
Retrieve a single task by its ID
# Link a task to a conversation
Source: https://www.quo.com/docs/mdx/api-reference/tasks/link-a-task-to-a-conversation
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/link-conversation
Links the task with the provided task ID to a conversation
# List tasks
Source: https://www.quo.com/docs/mdx/api-reference/tasks/list-tasks
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/tasks
Retrieve a list of tasks
# Remove a task's due date
Source: https://www.quo.com/docs/mdx/api-reference/tasks/remove-a-tasks-due-date
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/remove-due-date
Clears the task's due date
# Reopen a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/reopen-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/reopen
Reopens the provided task
# Unassign a user from a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/unassign-a-user-from-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/unassign
Removes the provided user from the assignee list of a task
# Unlink a task from a conversation
Source: https://www.quo.com/docs/mdx/api-reference/tasks/unlink-a-task-from-a-conversation
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/tasks/{taskId}/unlink-conversation
Unlinks the provided conversation from the task
# Update a task
Source: https://www.quo.com/docs/mdx/api-reference/tasks/update-a-task
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json put /v1/tasks/{taskId}
Updates the task's title and description
# Get a user by ID
Source: https://www.quo.com/docs/mdx/api-reference/users/get-a-user-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/users/{userId}
Retrieve detailed information about a specific user in your Quo workspace using the user's unique identifier.
# List users
Source: https://www.quo.com/docs/mdx/api-reference/users/list-users
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/users
Retrieve a paginated list of users in your Quo workspace.
# Create a new webhook for call summaries
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/create-a-new-webhook-for-call-summaries
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/webhooks/call-summaries
Creates a new webhook that triggers on events from call summaries.
# Create a new webhook for call transcripts
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/create-a-new-webhook-for-call-transcripts
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/webhooks/call-transcripts
Creates a new webhook that triggers on events from call transcripts.
# Create a new webhook for calls
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/create-a-new-webhook-for-calls
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/webhooks/calls
Creates a new webhook that triggers on events from calls.
# Create a new webhook for messages
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/create-a-new-webhook-for-messages
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json post /v1/webhooks/messages
Creates a new webhook that triggers on events from messages.
# Delete a webhook by ID
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/delete-a-webhook-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json delete /v1/webhooks/{id}
Delete a webhook by its unique identifier.
# Get a webhook by ID
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/get-a-webhook-by-id
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/webhooks/{id}
Get a webhook by its unique identifier.
# Lists all webhooks
Source: https://www.quo.com/docs/mdx/api-reference/webhooks/lists-all-webhooks
https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json get /v1/webhooks
List all webhooks for a user.
# Building with AI LLMs
Source: https://www.quo.com/docs/mdx/guides/building-with-ai-llms
Learn how to use AI Language Models to build applications with the Quo API.
## Introduction
Building with Large Language Models (LLMs) can significantly accelerate your Quo API integration development. This guide will help you effectively use LLMs to create applications with our API.
While we provide examples using Claude, the principles and practices outlined here apply to any capable LLM platform.
## Getting started
### Documentation setup
Before beginning development with an LLM, gather and prepare the necessary documentation:
Get our [OpenAPI specification](https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-v1-prod.json) for detailed endpoint information.
**Tip**: Right-click and select "Save Link As..." to download the file
Download and extract our [complete documentation package](https://openphone-public-api-prod.s3.us-west-2.amazonaws.com/public/openphone-public-api-llm-ready-docs-prod.zip)
Provide these resources to your LLM to help it understand Quo API capabilities
## Development process
### Working with LLMs
Start by clearly describing your integration objectives to the LLM
Share relevant API documentation and specifications
Let the LLM help break complex features into manageable tasks
Generate and review code one step at a time
### Best practices
* Start with core functionality
* Iterate to add features
* Test each component thoroughly
* Move forward only after validation
* Never share API keys with LLMs
* Keep sensitive data out of prompts
* Validate all generated code
* Follow security best practices
* Follow Quo API rate limits
* Implement proper error handling
* Monitor API usage
* Optimize API calls
## Example interactions
Here's a practical example of how to instruct an LLM to help build with our API:
```text theme={null}
I want to build an application that:
1. Displays a list of all my phone numbers
2. Allows me to select a number to send a message with
3. Lets me input an external phone number to send a message to
4. Sends a message to the external phone number
Please help me implement this using the Quo API.
```
```text theme={null}
Help me create a system to:
1. Sync contacts from my CRM
2. Update contact details automatically
3. Track message history per contact
4. Generate contact activity reports
```
## Integration patterns
Automate message handling and responses
Sync and manage contact information
Process call summaries and recording data
Manage scheduling and reminders
## Implementation checklist
Thoroughly review all LLM-generated code
Test extensively in a development environment
Implement comprehensive error handling and logging
Deploy with appropriate monitoring systems
Continuously improve based on usage and feedback
Need more detailed guidance? Check out our comprehensive [API Reference](/docs/api-reference/introduction) for detailed endpoint documentation and examples.
# External contacts
Source: https://www.quo.com/docs/mdx/guides/contacts
Learn about working with contacts imported via the Quo API or native integrations.
## Understanding external contacts
External contacts are contacts that originate from outside the Quo app: either created through the Quo API or synced via native integrations (such as CRM or other connected platforms). These contacts allow you to centralize contact records from multiple sources within your Quo workspace.
### Key characteristics
External contacts are separate from contacts created directly in the Quo app, with their own specific behaviors depending on how they were created.
External contacts can come from two sources: the Quo API or native integrations and each has different editability rules within the Quo app.
### Editability by source
Contacts created via the Quo API can be updated directly within the Quo app. You can also continue managing them programmatically through API endpoints.
Contacts synced from native integrations remain read-only within Quo. Any changes must be made in the source system and synced back to Quo.
### Important behaviors and limitations
When you create a contact using the `POST /contacts` endpoint, it's essential to save the `id` returned in the response. This `id` will be required for all future API operations involving the contact.
After creating an API contact, it will only appear in the Quo app—whether in the conversation list, contact list, or search results—if there's an associated conversation with a matching phone number.
## Contact field structure
Quo organizes contact information using two distinct field types. Understanding these is crucial for effective contact management.
Every contact in Quo includes these predefined fields:
* First Name
* Last Name
* Role
* Company
* Emails
* Phone Numbers
These fields maintain consistent properties across all contacts and form the foundation of contact information.
Custom fields allow for flexible, user-defined contact properties. Supported data types include:
* Address
* Boolean
* Date
* Multi-select
* Number
* String
* URL
**Managing Custom Fields:** Custom field definitions can only be modified within the Quo app. The API does not currently support creating or editing custom field definitions.
## Creating and managing contacts via API
Follow these steps to effectively create and manage contacts through the API:
First, call the `GET /contact-custom-fields` endpoint to retrieve your workspace's custom contact field definitions.
Structure your contact data according to both default and custom fields:
```json theme={null}
{
"defaultFields": {
"firstName": "John",
"lastName": "Doe",
"phoneNumbers": [
{
"name": "primary",
"value": "+1234567890"
}
]
},
"customFields": {
// Include any custom field values here
}
}
```
Use the `POST /contacts` endpoint to create the contact and store the returned contact ID for future operations.
Update contacts either within the Quo app or programmatically using the `PATCH /contacts/:id` endpoint with the saved contact ID.
Always validate phone numbers are in E.164 format (+1234567890) before creating or updating contacts to ensure proper functionality.
# Sync your contacts
Source: https://www.quo.com/docs/mdx/guides/sync-contacts
Implement a one-way contact sync from Google Sheets to Quo using Javascript.
## Overview
This guide provides a foundation for implementing a one-way sync from Google Sheets to Quo using JavaScript. You may need to adjust some details based on your specific requirements and environment. Remember to thoroughly test the implementation to ensure data integrity.
## Development guide
##### 1.1 Quo API.
* Obtain your Quo API key from the Quo dashboard.
##### 1.2 Google Sheets API
* Enable the Google Sheets API in your Google Cloud Console.
* Create service account credentials and download the JSON key file.
##### 1.3 Google Sheets
* Create a Google Sheet in the following format:
| contactId | firstName | lastName | phone | email |
| - | - | - | - | - |
| | Jane | Doe | (555) 555-5555 | [jane@example.com](mailto:jane@example.com) |
* Share your Google Sheet with the service account email address.
##### 2.1 Ensure you have Node.js installed on your system.
##### 2.2 Create a new Node.js project and initialize it
```bash theme={null}
mkdir quo-sync
cd quo-sync
npm init -y
```
##### 2.3 Install required packages
```bash theme={null}
npm install googleapis axios dotenv node-cron
```
##### 2.4 Create a .env file to store environment variables
```bash theme={null}
QUO_API_KEY=your_quo_api_key
GOOGLE_APPLICATION_CREDENTIALS=path/to/your/credentials.json
GOOGLE_SHEET_ID=your_google_sheet_id
```
##### 3.1 Create a new file named `sync.js` and add the setup functions
```js theme={null}
require("dotenv").config();
const { google } = require("googleapis");
const axios = require("axios");
const cron = require("node-cron");
const API_BASE_URL = "https://api.quo.com/v1";
const quo = axios.create({
baseURL: API_BASE_URL,
headers: {
Authorization: process.env.QUO_API_KEY,
"Content-Type": "application/json",
},
});
const googleAuth = new google.auth.GoogleAuth({
keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
```
##### 3.2 Add the Quo API helper functions
```js theme={null}
async function createQuoContact(contactData) {
const response = await quo.post("/contacts", contactData);
return response.data.data;
}
async function updateQuoContact(contactId, contactData) {
const response = await quo.patch(`/contacts/${contactId}`, contactData);
return response.data.data;
}
```
##### 3.3 Add the Google Sheets to Quo contacts mapping function
```js theme={null}
function mapFields(sheetRow) {
if (!sheetRow.firstName) {
console.warn("Missing required firstName in row: ", sheetRow);
return;
}
return {
defaultFields: {
firstName: sheetRow.firstName,
lastName: sheetRow.lastName,
phoneNumbers: sheetRow.phone
? [{ name: "primary", value: sheetRow.phone }]
: undefined,
emails: sheetRow.email
? [{ name: "primary", value: sheetRow.email }]
: undefined,
},
};
}
```
##### 3.4 Add the Google Sheets helper functions
```js theme={null}
async function getGoogleSheetsData() {
const sheets = google.sheets({ version: "v4", googleAuth });
const response = await sheets.spreadsheets.values.get({
spreadsheetId: process.env.GOOGLE_SHEET_ID,
range: "Sheet1!A1:Z", // First sheet and all initial columns
});
const rows = response.data.values;
const headers = rows[0]; // First row contains headers
return rows.slice(1).map((row) => { // Skip the first row and map the contact data
const contact = {};
headers.forEach((header, index) => {
contact[header] = row[index];
});
return contact;
});
}
async function updateSheetWithContactId(rowNumber, contactId) {
const sheets = google.sheets({ version: "v4", googleAuth });
await sheets.spreadsheets.values.update({
spreadsheetId: process.env.GOOGLE_SHEET_ID,
range: `Sheet1!A${rowNumber + 2}`, // +2 to account for 1-based index and header row
valueInputOption: "RAW",
resource: { values: [[contactId]] },
});
}
```
##### 3.4 Finally, tie it all together
```js theme={null}
async function syncContacts() {
const sheetContacts = await getGoogleSheetsData();
for (const [rowNumber, sheetRow] of sheetContacts.entries()) {
const mappedContact = mapFields(sheetRow);
if (sheetRow.contactId) {
await updateQuoContact(sheetRow.contactId, mappedContact);
} else {
const { id } = await createQuoContact(mappedContact);
await updateSheetWithContactId(rowNumber, id);
}
}
console.log("Sync completed successfully");
}
// Run sync every hour
cron.schedule("0 * * * *", syncContacts);
console.log("Sync process started. Running every hour.");
```
```bash theme={null}
node sync.js
```
This will start the sync process, which will run every hour.
## Considerations and Optimizations
* Implement deletion logic to remove contacts from Quo that are no longer present in the Google Sheet.
* Implement pagination for fetching Quo contacts if you have a large number of contacts.
* Implement more robust error handling and retry mechanisms.
* Implement logging for auditing and troubleshooting purposes.
* Consider using a database to store the state of the sync process and to track changes between syncs.
* Consider implementing rate-limiting and an incremental sync to reduce API calls and processing time.
* For production use, consider deploying this script to a cloud platform like Heroku or AWS Lambda for better reliability and scalability.
# Connect to Quo MCP
Source: https://www.quo.com/docs/mdx/mcp/connect
Connect Claude, ChatGPT, another MCP-compatible client, or a custom MCP client to Quo.
Quo MCP is a hosted remote server. You do not need to download a package, run a local process, create a Quo API key, or manage server infrastructure.
## Connection endpoint
Use the Streamable HTTP endpoint:
```text theme={null}
https://mcp.quo.com/mcp
```
Use the complete URL, including `/mcp`. `https://mcp.quo.com` on its own is not an MCP transport endpoint.
## Official Claude connector
Quo's official Claude connector is named **Quo**.
In Claude, open **Settings → Connectors**, then browse the connector directory. You can also open the [Quo connector listing](https://claude.ai/directory/connectors/quo).
Find **Quo** and choose **Connect**.
Sign in to the Quo account you want to connect and approve access. Return to Claude after authorization completes.
## Official ChatGPT connector
Quo's official ChatGPT connector is named **Quo MCP**.
In ChatGPT, open **Settings → Connectors** and browse the available connectors.
Find **Quo MCP** and choose **Connect**.
Sign in to the Quo account you want to connect and approve access. Return to ChatGPT after authorization completes.
Connector availability can depend on your AI client's plan and your organization's admin settings.
## Other MCP clients
If your client supports remote MCP servers, add the Streamable HTTP endpoint:
```text theme={null}
https://mcp.quo.com/mcp
```
Many clients accept a configuration similar to this:
```json theme={null}
{
"mcpServers": {
"quo": {
"url": "https://mcp.quo.com/mcp"
}
}
}
```
The exact property names vary by client. Some clients use `serverUrl` instead of `url`, or provide a form where you paste only the server URL. Follow your client's instructions for adding a remote Streamable HTTP server.
After saving the connection, the client should open a browser to complete OAuth authorization. If it does not, look for an **Authenticate**, **Connect**, or **Sign in** action in the client's MCP settings.
## Authentication and dynamic client registration
Quo MCP uses the OAuth 2.1 Authorization Code flow with S256 PKCE. Compatible clients can discover the OAuth endpoints and register dynamically, so most third-party client developers do not need to request a static client ID or secret from Quo.
Start discovery from the MCP resource URL. Quo publishes protected-resource and authorization-server metadata that identify the authorization, token, and registration endpoints.
If the client is not preconfigured, register it at the advertised `registration_endpoint` using [RFC 7591 dynamic client registration](https://datatracker.ietf.org/doc/html/rfc7591). Persist the returned client credentials and reuse them for future authorizations.
Generate a high-entropy code verifier and its SHA-256 challenge. Quo accepts `S256` only; the `plain` challenge method is not supported.
Open the advertised authorization endpoint with `response_type=code`, the registered redirect URI, state, and the S256 code challenge.
For a dynamically registered third-party client, Quo first shows the client-reported name and redirect host. The user then signs in to Quo and approves the connection.
Exchange the authorization code with the original verifier. Store the resulting tokens securely, save each rotated refresh token atomically, and reauthorize if the server returns `invalid_grant`.
### OAuth endpoints
| Purpose | Endpoint |
| - | - |
| Authorization server metadata | `https://mcp.quo.com/.well-known/oauth-authorization-server` |
| Dynamic client registration | `https://mcp.quo.com/register` |
| Authorization | `https://mcp.quo.com/authorize` |
| Token exchange, refresh, and revocation | `https://mcp.quo.com/token` |
Clients should read the discovery metadata instead of hard-coding every OAuth endpoint. The metadata is the source of truth if the server configuration changes.
### Dynamic registration requirements
* Register no more than five redirect URIs.
* Use an absolute HTTPS redirect URI. Loopback hosts such as `localhost` and `127.0.0.1` may use HTTP for local clients.
* Do not include user information or a URL fragment in a redirect URI.
* Send the exact registered redirect URI during authorization and token exchange.
* Keep `client_name` at 120 characters or fewer. Quo displays this self-reported value to the user for third-party consent.
* Public clients should use S256 PKCE and can register with `token_endpoint_auth_method` set to `none`.
A third-party client's name is self-reported and does not mean Quo has verified the client. Approve a connection only when you started it and recognize the redirect host shown on the consent page.
## Troubleshooting
### The client opens the documentation site
Confirm that the server URL includes `/mcp`. The correct Streamable HTTP endpoint is `https://mcp.quo.com/mcp`.
### The client never opens a sign-in window
Confirm that the client supports OAuth for remote MCP servers. Then look for an **Authenticate** or **Connect** action in its MCP settings. Local-only clients that support only stdio cannot connect directly to the hosted endpoint.
### Authorization fails or repeatedly restarts
* Disconnect Quo in the MCP client, add it again, and complete a fresh authorization.
* For a custom client, reuse the registered `client_id`, send the exact registered redirect URI, and use S256 PKCE.
* Persist the newest refresh token after every successful refresh. A stale or revoked grant requires a new authorization.
### The wrong Quo workspace is connected
Disconnect the integration in the MCP client. In your browser, sign in to the intended Quo account and then reconnect.
## Next steps
* Review the [complete tool list](/docs/mdx/mcp/tools).
* Apply the [security best practices](/docs/mdx/mcp/security) before enabling write actions.
# Quo MCP
Source: https://www.quo.com/docs/mdx/mcp/overview
Connect an MCP-compatible AI client to Quo and work with conversations, messages, contacts, calls, and tasks in plain language.
Quo MCP is a hosted [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets an AI client work with your Quo workspace through a curated set of tools.
After you authorize a connection, the client can use those tools to read communication history and take actions you approve. Quo hosts the server, handles authentication, and sends each request to the Quo account you connected.
**Remote server URL:** `https://mcp.quo.com/mcp`
## What you can do
Fetch message history, call transcripts, missed calls, and voicemail transcripts from your Quo inboxes.
Send a one-to-one text, start a shared group conversation, or broadcast the same text as separate private messages.
List and inspect contacts, create new contacts, and update existing contact details.
List, create, assign, complete, and update tasks linked to an inbox, conversation, call, or message.
See [Supported tools](/docs/mdx/mcp/tools) for the complete tool list, parameters, and limits.
## How Quo MCP works
Use Quo's official Claude or ChatGPT connector, or add the remote server URL to another MCP-compatible client.
Your browser opens Quo's OAuth flow. Sign in to the Quo account you want to use and review the requested access.
The client selects the appropriate Quo MCP tools and can combine several tool calls to complete a task.
The connection can access only the Quo data available to the account that authorized it. Your Quo password is entered only on Quo's sign-in page and is not shared with the MCP client.
## Choose a connection path
| If you are… | Start here |
| - | - |
| Using Claude or ChatGPT | Install Quo's official connector for the simplest setup. |
| Using another MCP-compatible client | Add `https://mcp.quo.com/mcp` as a remote Streamable HTTP server. |
| Building an MCP client | Use Quo's OAuth discovery metadata, dynamic client registration, Authorization Code flow, and S256 PKCE. |
[Connect to Quo MCP](/docs/mdx/mcp/connect) covers all three paths.
## Quo MCP or the Quo API?
| Use Quo MCP when… | Use the [Quo API](/docs/2026-03-30/introduction) when… |
| - | - |
| A person works with Quo through an MCP-compatible AI client. | You are building a product, backend integration, or deterministic workflow. |
| You want the client to choose and combine supported tools from a plain-language request. | You need direct control over endpoints, payloads, retries, and event-driven webhooks. |
| Interactive OAuth authorization fits your workflow. | API-key authentication and versioned API contracts fit your architecture. |
The two interfaces can complement each other. Use MCP for conversational work and the API for application logic that needs a stable, programmatic contract.
## Next steps
Install an official connector or configure another MCP client.
Review the complete, current tool surface and its limits.
Protect customer data and review actions before they run.
Follow updates to Quo's developer platform.
# Security best practices
Source: https://www.quo.com/docs/mdx/mcp/security
Use Quo MCP safely with trusted clients, explicit action confirmation, and careful handling of customer data.
Quo MCP can read customer communications and perform real-world actions. Treat an MCP connection like access you would grant to a teammate who can view your inbox and text customers.
## Use the official server
Connect only to Quo's hosted Streamable HTTP endpoint: `https://mcp.quo.com/mcp`.
Verify the hostname before authorizing. Do not enter your Quo credentials into a page opened from an MCP URL on another domain.
## Connect only clients you trust
A connected client can ask Quo MCP to access anything exposed by the available tools and permitted by the Quo account that authorized the connection. The client may also send tool results to its own model provider, logs, plugins, or other connected systems.
Before connecting a client:
* Verify its publisher, download source, privacy policy, and data-retention settings.
* Review its enabled MCP tools and approval settings.
* Prefer Quo's official Claude or ChatGPT connector when using those products.
* Do not connect a shared client under a Quo account with broader access than its users need.
For a dynamically registered third-party client, Quo shows the client-reported name and the redirect host before forwarding you to Quo authorization. The name is self-reported and is not proof that Quo verified the client. Continue only if you started the connection and recognize the destination.
## Require confirmation for write actions
Configure your MCP client to ask before any action that changes data or contacts someone outside your workspace.
| Action | Tools | What to confirm |
| - | - | - |
| Send a message | `send-message`, `send-group-message`, `send-bulk-messages` | Sender, every recipient, each recipient's final content, cost, and shared-thread visibility |
| Create or change a contact | `create-contact`, `update-contact` | Correct contact, fields to change, and fields to clear |
| Create or change a task | `create-task`, `update-task` | Title, assignee, due date, completion state, and linked record |
Message sends are irreversible and use prepaid messaging credits. A bulk send can partially succeed, so review its result before retrying; otherwise, recipients who succeeded on the first attempt may receive a duplicate.
`send-group-message` exposes every recipient's phone number to everyone in the shared thread. Use `send-bulk-messages` when recipients should receive separate private messages.
## Treat tool results as untrusted content
Messages, call transcripts, voicemail transcripts, contact fields, and task descriptions can contain instructions written by an external person. Those instructions are data, not authorization.
This matters because an attacker can place text in customer-controlled content that attempts to redirect the AI client—for example, telling it to ignore your request, disclose other conversations, or send a message. This is a form of prompt injection.
To reduce the risk:
* Tell the client to treat all Quo content as untrusted input.
* Do not let retrieved content authorize another tool call.
* Review proposed writes and any data leaving Quo.
* Limit a task to the smallest relevant inbox, contact, and date range.
* Start a new client session if retrieved content appears to be steering the assistant.
## Understand account-limited access
Quo MCP uses the Quo account selected during OAuth authorization. It can access only what that account can access, but it does not create an additional permission boundary inside that account.
Use a Quo user with the narrowest practical workspace and inbox access. If a client is used by several people, do not assume the client will reproduce Quo's per-user controls outside Quo.
## Protect OAuth credentials
During authorization, your Quo password is entered only on Quo's hosted sign-in page. The MCP client receives OAuth tokens rather than your Quo password.
If you are building a client:
* Store access tokens, refresh tokens, and dynamic-registration credentials in a secure credential store.
* Never log tokens or include them in prompts, URLs, source code, or error messages.
* Persist the newest rotated refresh token atomically and serialize refreshes for a grant.
* Use the exact registered redirect URI and S256 PKCE.
* Treat `invalid_grant` as a signal to discard the failed grant and ask the user to authorize again, not as a retry loop.
## End or repair a connection
Use the MCP client's **Disconnect**, **Remove**, or **Clear authentication** action to stop using a connection. Reconnect and complete OAuth again if:
* You authorized the wrong Quo account.
* The client reports an expired or revoked grant.
* You no longer trust how the client stored its credentials.
* You see activity you do not recognize.
If you suspect unauthorized activity, stop using the client and contact [Quo developer support](mailto:support+developers@quo.com).
## Related guidance
* [Connect to Quo MCP](/docs/mdx/mcp/connect)
* [Supported tools](/docs/mdx/mcp/tools)
# Supported tools
Source: https://www.quo.com/docs/mdx/mcp/tools
Review the current tools, limits, and usage patterns available through Quo MCP.
Quo MCP currently exposes the same complete toolset to Claude, ChatGPT, and other connected MCP clients. A client can call several tools in one task—for example, discover an inbox, fetch its recent messages, and create follow-up tasks from the results.
Tool names and parameters are stable identifiers. How you select or approve a tool call depends on your MCP client.
## Workspace discovery
| Tool | What it does | Key notes |
| - | - | - |
| `list-users` | Lists workspace members with their ID, name, email, and role. | Supports `pageToken`; 1–50 results per page. Use the returned `US…` ID in tools that accept a `userId`. |
| `list-inboxes` | Lists Quo phone numbers with their phone-number ID, E.164 number, inbox name, and assigned users. | Optionally filter by `userId`. Use this before a fetch or send tool when you do not know the inbox number. |
Example prompts:
* “Which Quo numbers can I use, and who is assigned to each one?”
* “Find Jamie's user ID and show the inboxes assigned to them.”
## Messages and conversations
| Tool | What it does | Key notes |
| - | - | - |
| `fetch-messages` | Fetches message history for an inbox, either for one contact or across the inbox. | Returns message text and any MMS attachments with their media type and URL. Filter by participant, date range, or sending team member. See [Fetch modes and pagination](#fetch-modes-and-pagination). |
| `send-message` | Sends an SMS from a Quo inbox to one recipient. | The send is irreversible. The recipient must be a valid phone number; international E.164 numbers are supported. |
| `send-group-message` | Starts one shared SMS thread with 2–10 recipients. | Every recipient can see every other recipient's phone number and all replies. |
| `send-bulk-messages` | Sends SMS to 2–40 recipients as separate one-to-one messages, with identical or recipient-specific content. | Recipients do not see one another. The tool reports per-recipient failures and may partially succeed. |
### Choose the correct send tool
| Desired result | Tool | Recipients see one another? |
| - | - | - |
| Text one person | `send-message` | Not applicable |
| Put 2–10 people in one shared thread | `send-group-message` | Yes |
| Broadcast the same text privately to 2–40 people | `send-bulk-messages` | No |
| Send personalized text privately to 2–40 people | `send-bulk-messages` | No |
### Bulk message modes
`send-bulk-messages` accepts one of two input modes:
* **Same message:** Provide `to` as an array of 2–40 phone numbers and `content` as the message sent to every recipient.
* **Personalized messages:** Provide `messages` as an array of 2–40 objects, each containing that recipient's `to` phone number and `content`.
Use exactly one mode per call. Do not combine `messages` with `to` or `content`, and include each recipient only once. Use `send-message` when sending to one person.
All three send tools:
* Require a `from` number that belongs to the connected Quo workspace. The tool accepts its E.164 number or `PN…` phone-number ID.
* Accept message content up to 1,600 characters.
* Use prepaid messaging credits and count as API-powered messages. See [API pricing](/docs/mdx/pricing-support/pricing-overview).
* Cannot undo or recall a message after it is sent.
Confirm the sender, recipients, message content, and whether the recipients should share a thread before sending. In particular, never substitute `send-group-message` for a private broadcast.
Example prompts:
* “Show me yesterday's messages across the support inbox and summarize the open questions.”
* “Draft a reply to +14165550100, but ask me to approve it before sending.”
* “Send this service notice privately to these 12 customers so none of them can see the other numbers.”
* “Send each attendee their own confirmation time from this list as a private message.”
## Calls and voicemail
| Tool | What it does | Key notes |
| - | - | - |
| `fetch-call-transcripts` | Fetches completed call transcripts for a Quo inbox. | Requires the Quo Business plan and calls where transcription was enabled. Filter by participant, date range, or team member. |
| `fetch-missed-calls` | Fetches missed incoming calls and includes voicemail status, transcript, and recording URL when a voicemail exists. | Returns only incoming calls with status `missed`, `no-answer`, or `abandoned`; this filter cannot be changed. |
Voicemail processing is asynchronous. If a voicemail has an `in-progress` status, its duration, transcript, and recording URL may be empty; retry after processing completes. A missed call with no voicemail is still returned.
Call and message results include activity IDs (`AC…`). Multi-contact results can also include conversation IDs (`CN…`). Use these IDs to link a follow-up task to the exact call, message, or conversation.
Example prompts:
* “What calls did we miss yesterday, and which callers left a voicemail?”
* “Summarize this week's completed call transcripts for the sales inbox.”
* “Find Pat's call from this morning and create a task linked to that call.”
## Contacts
| Tool | What it does | Key notes |
| - | - | - |
| `list-contacts` | Lists contacts with their ID, name, company, role, email, and phone number. | Optionally filter by external IDs or sources. Supports `pageToken`; 1–50 results per page. |
| `get-contact` | Fetches one contact by ID. | Includes default fields, source metadata, and workspace custom fields. |
| `create-contact` | Creates a contact and returns its new contact ID. | `firstName` is required. Last name, company, role, email, and phone number are optional. |
| `update-contact` | Updates an existing contact's default fields. | Omit a field to leave it unchanged; pass `null` to clear it. Use `get-contact` first when the current value matters. |
Phone numbers are normalized to E.164. Email values must be valid email addresses.
Example prompts:
* “Look up the contact with +14165550100 and show their custom fields.”
* “Create a contact for Priya Shah at Northwind, then show me the new contact ID.”
* “Clear the outdated role on this contact without changing any other field.”
## Tasks
| Tool | What it does | Key notes |
| - | - | - |
| `list-tasks` | Lists workspace tasks with status, title, due date, assignee, linked record, creation time, and revision. | Supports `pageToken`; 1–100 results per page. |
| `create-task` | Creates a task linked to an inbox, conversation, or specific activity. | Title and description are required. Provide exactly one link target: `inboxPhoneNumber`, `conversationId`, or `activityId`. |
| `update-task` | Changes a task's content, assignee, due date, completion status, or conversation link. | Performs one change type per call. Call it again for each additional change. |
When creating a task, use the most specific link available:
1. Use an `activityId` (`AC…`) for one particular call or message.
2. Use a `conversationId` (`CN…`) for the whole thread with a contact.
3. Use an inbox number or `PN…` ID only when the task is not about a specific activity or conversation.
`update-task` can update the title and description together as one change. Assignment, unassignment, due-date changes, completion or reopening, and conversation linking or unlinking are separate change types.
Example prompts:
* “List the open tasks and show who each is assigned to.”
* “Create a follow-up task for tomorrow linked to this missed call and assign it to Alex.”
* “Mark task `TK…` complete.”
## Fetch modes and pagination
The fetch tools can search a specific contact deeply or discover activity across an inbox broadly.
### One contact
Set `participantPhoneNumber` to query one contact.
* `fetch-messages`, `fetch-call-transcripts`, and `fetch-missed-calls` return up to 100 matching records per page.
* If the response includes a next-page token, call the same tool with the same filters plus `pageToken`.
* A `pageToken` can be used only when `participantPhoneNumber` is set.
### Whole inbox
Omit `participantPhoneNumber` to discover active conversations across the inbox.
* `fetch-messages` returns up to 10 in-window messages per contact across up to 50 contacts. It flags contacts with more messages so the client can re-query that contact in one-contact mode.
* `maxResults` applies to one-contact message queries and is ignored by the whole-inbox message view.
* Call tools aggregate matching calls across discovered participants, up to the requested result limit.
* If a response indicates that older conversations remain, continue discovery with the returned `conversationPageToken`.
The tool response includes the token and guidance needed for the next call. Treat page tokens as opaque and do not modify them.
## Dates, times, and phone numbers
* Pass `createdAfter`, `createdBefore`, and task due dates as ISO 8601 datetimes with `Z` or an explicit offset, such as `2026-08-21T14:00:00-04:00`.
* Fetch results use UTC timestamps. A client should convert relative requests such as “yesterday” from the user's local timezone to the corresponding UTC range, then present results in the user's timezone.
* Use E.164 for external phone numbers, such as `+14165550100`. International numbers are supported.
* When a tool accepts an inbox, use either its E.164 number or its `PN…` ID. Run `list-inboxes` to discover valid values.
## Related guidance
* [Connect to Quo MCP](/docs/mdx/mcp/connect)
* [Security best practices](/docs/mdx/mcp/security)
# API pricing
Source: https://www.quo.com/docs/mdx/pricing-support/pricing-overview
Understand Quo API messaging costs and learn how to minimize message segments.
## Our pricing philosophy
At Quo, we believe in transparent and fair pricing. Sometimes it can be hard to understand the true cost of something when it's buried in the fine print. We are committed to helping you clearly understand and manage your costs when using our platform.
## Core pricing model
Our pricing is based on message segments, making it easy to calculate costs:
\$0.01 per segment
\$0.01 + country-specific rate per segment
Rates vary by destination country
For detailed international rates by country, see our [International Pricing Guide](https://www.quo.com/rates).
## Understanding message segments
A **segment** is the basic unit we use to calculate SMS billing. Every message you send is divided into one or more segments based on two factors:
1. **Message length** (character count)
2. **Character type** (standard or special characters)
## How character types affect segment size
### Standard GSM characters
Messages using only standard GSM-7 characters fit **up to 160 characters per segment**.
Standard characters include:
* Letters (A-Z, a-z)
* Numbers (0-9)
* Spaces
* Basic punctuation (. , ! ? - etc.)
### Special/Non-GSM characters
**If your message contains even one special character, the entire message is billed at the 70-character limit** — not just the portion with special characters. This often results in more segments and higher costs than you might expect.
Messages containing **any** special characters fit only **up to 70 characters per segment**.
Special characters include:
* Accented letters (é, ñ, ü)
* Curly/smart quotes (" " ' ')
* Emojis (😊, 🚀, ✨)
* Many international characters
## Tools and optimization
### Segment calculator
Use our [Segment Calculator](https://twiliodeved.github.io/message-segment-calculator/) tool to estimate costs before sending messages. This helps you optimize your message length and content to avoid unexpected charges.
### Smart encoding
The Quo API automatically enables **smart encoding** to minimize segment usage and reduce costs wherever possible. This feature works behind the scenes to choose the most efficient encoding for your messages.
## Tips for minimizing costs
Standard Latin alphabet characters, numbers, and basic punctuation use 160 characters per segment.
Some special characters, such as é, ñ, and ß, reduce segment capacity to 70 characters.
Emojis switch a message to Unicode encoding, which reduces the number of characters that fit in each segment.
Each line break counts toward your message length.
Long URLs can span multiple segments. A shorter destination URL leaves more room for the rest of your message.
Widely understood abbreviations can reduce character count without making a message harder to understand.
## API message types
You're only charged for outgoing API-powered messages, which include:
Messages sent through direct API calls
Messages sent via applications built with our API
## How our billing works
We use a credit-based system for all API messaging charges:
Add funds to your account through the "Plans & Billing" tab
Credits are automatically deducted when messages are sent
Monitor your balance and enable auto-recharge to prevent service interruptions
## Important service notes
### Requirements & limitations
1. An active Quo subscription is required for API access.
2. MMS is not supported in the current API version.
3. Partial credits cannot be used for sending messages; the API will return an error.
If your credit balance is insufficient for a message's full cost, the API will return an error and the message won't be sent.
### Support & assistance
Need help understanding our pricing or managing your costs? Our team is here to help. Email us at [support+developers@quo.com](mailto:support+developers@quo.com) with any questions.
# Quo Experts
Source: https://www.quo.com/docs/mdx/quo-experts
# Changelog
Source: https://www.quo.com/docs/changelog
### Improved
**Group threads in message history.** `fetch-messages` now handles group threads. In whole-inbox results, each group thread appears as its own conversation with its members listed and each message's sender shown. To fetch a group thread's full history, pass its members as `participantPhoneNumbers`.
**Complete inbox reads.** When you ask about a period, such as yesterday or this week, a whole-inbox `fetch-messages` call now reads every message in that window and includes every conversation in it. Whole-inbox queries also need far fewer API requests, so large inboxes return faster. See [Fetch modes and pagination](/docs/2026-03-30/mcp/tools#fetch-modes-and-pagination) for details.
### Added
**Read and write contact notes.** Quo MCP adds three tools for the notes on a contact. `list-contact-notes` lists a contact's notes, including their author and any attachment URLs. `create-contact-note` adds a note of up to 2,000 characters, and `@` followed by a teammate's user ID mentions them. `update-contact-note` replaces a note's text. Adding or editing a note doesn't send anything to the contact. See [Supported tools](/docs/2026-03-30/mcp/tools#contact-notes) for details.
### Added
**Read messages across your workspace.** [`GET /messages`](/docs/2026-03-30/messages/list-messages) now returns a paginated message history across all conversations, newest first. Unlike v1, you don't need to specify a Quo phone number or conversation participants before retrieving messages.
* **Find the messages that matter.** Filter by sender, exact recipient set, delivery status, direction, Quo phone number, sending user, and creation time. This makes it easier to build workspace-wide reporting or retrieve undelivered and failed messages.
* **Retrieve a single message.** [`GET /messages/{messageId}`](/docs/2026-03-30/messages/get-a-message-by-id) returns the message's text, conversation ID, delivery status, and attached media. Both read endpoints include media URLs and MIME types when present.
Message sending remains available through the [v1 API](/docs/mdx/api-reference/messages/send-a-text-message).
### Added
**Discover the numbers your integration can use.** [`GET /phone-numbers`](/docs/2026-03-30/phone-numbers/list-phone-numbers) and [`GET /phone-numbers/{phoneNumberId}`](/docs/2026-03-30/phone-numbers/get-a-phone-number-by-id) are now available in the `2026-03-30` API. The list endpoint supports cursor pagination and filtering by an E.164 phone number.
* **Find a user's assigned numbers.** [`GET /users/{userId}/phone-numbers`](/docs/2026-03-30/user-phone-numbers/list-a-users-phone-numbers) provides a dedicated, paginated list of the numbers assigned to a workspace user.
* **Search available numbers.** [`GET /phone-numbers/available`](/docs/2026-03-30/phone-numbers/get-available-phone-numbers) lets you find numbers by country, area code, city, region, a matching substring, or toll-free availability. This search is new to the API; it does not purchase or provision a number.
**Moving from v1.** Phone number responses use `phoneNumber` instead of `number`. Calling and messaging restrictions are included with `include=restrictions`, and assigned members are retrieved through [`GET /phone-numbers/{phoneNumberId}/users`](/docs/2026-03-30/phone-numbers/list-the-members-assigned-to-a-phone-number) rather than embedded in each number.
### Added
**Call history with summaries and voicemails in one request.** [`GET /calls`](/docs/2026-03-30/calls/list-calls) and [`GET /calls/{callId}`](/docs/2026-03-30/calls/get-a-call-by-id) are now available in the `2026-03-30` API. Add `include=summary,voicemail` to either endpoint to retrieve that context alongside the call, without separate requests for its summary or voicemail.
* **Find the calls you need.** List calls across the workspace or filter by Quo phone number, user or AI agent (`actorId`), participant, direction, status, and creation time. Results are paginated, newest first.
* **Understand who handled each call.** Responses include group-call participants and their actor IDs, who answered or initiated the call, whether AI handled it, and routing and forwarding details.
* **Retrieve recordings and transcripts.** [`GET /calls/{callId}/recordings`](/docs/2026-03-30/calls/list-recordings-for-a-call) and [`GET /calls/{callId}/transcripts`](/docs/2026-03-30/calls/list-transcripts-for-a-call) return paginated segments in the order they occurred, including multiple segments when recording was paused and resumed.
**Discover and sync conversations.** [`GET /conversations`](/docs/2026-03-30/conversations/list-conversations) returns a paginated list of workspace conversations. Filter by one or more Quo phone number IDs and by creation or update time, and sort by `createdAt` or `updatedAt` in either direction. Use `updatedAt[gte]` to retrieve conversations changed since your last sync. Responses include participants, the latest activity ID and timestamp, and mute and snooze timestamps.
### Added
**More ways to manage contacts.** The `2026-03-30` API now supports creating, updating, and deleting contacts. You can also read a contact's shares and manage notes and properties attached to a contact. The existing list and get-by-ID endpoints remain available.
| Area | New endpoints |
| - | - |
| Contacts | [`POST /contacts`](/docs/2026-03-30/contacts/create-a-contact), [`PATCH /contacts/{contactId}`](/docs/2026-03-30/contacts/update-a-contact-by-id), [`DELETE /contacts/{contactId}`](/docs/2026-03-30/contacts/delete-a-contact) |
| Shares | [`GET /contacts/{contactId}/shares`](/docs/2026-03-30/contact-shares/list-contact-shares) |
| Notes | [`GET /contacts/{contactId}/notes`](/docs/2026-03-30/contact-notes/list-notes-for-a-contact), [`POST /contacts/{contactId}/notes`](/docs/2026-03-30/contact-notes/create-a-contact-note), [`GET /contacts/{contactId}/notes/{noteId}`](/docs/2026-03-30/contact-notes/get-a-contact-note-by-id), [`PATCH /contacts/{contactId}/notes/{noteId}`](/docs/2026-03-30/contact-notes/update-a-contact-note), [`DELETE /contacts/{contactId}/notes/{noteId}`](/docs/2026-03-30/contact-notes/delete-a-contact-note) |
| Properties | [`GET /contacts/{contactId}/properties`](/docs/2026-03-30/contact-properties/list-contact-properties), [`POST /contacts/{contactId}/properties`](/docs/2026-03-30/contact-properties/create-a-contact-property), [`GET /contacts/{contactId}/properties/{propertyId}`](/docs/2026-03-30/contact-properties/get-a-contact-property-by-id), [`PATCH /contacts/{contactId}/properties/{propertyId}`](/docs/2026-03-30/contact-properties/update-a-contact-property), [`DELETE /contacts/{contactId}/properties/{propertyId}`](/docs/2026-03-30/contact-properties/delete-a-contact-property) |
### Added
**Consistent collection queries.** Collection endpoints now share these conventions where sorting and filtering are supported:
* **Sort:** Use `sort=field:asc` or `sort=field:desc` with a single field.
* **Filter:** Use `field=value` for an exact match or `field[operator]=value` for another comparison. Filters on different fields combine with AND; `[in]` matches any value in a comma-separated set.
* **Contacts:** [`GET /contacts`](/docs/2026-03-30/contacts/list-contacts) now accepts `externalId` and `source`, including `externalId[in]` and `source[in]` for multiple values.
See [Sorting and filtering](/docs/2026-03-30/sorting-and-filtering) for syntax and operators, and each endpoint's reference for its supported fields.
### Added
**Send feedback to the Quo product team.** The new `submit-feedback` tool passes along feedback about your phone-line or communications experience. It is opt-in: the client sends feedback only after you explicitly ask it to or accept its offer, and a submission cannot be edited or withdrawn. See [Supported tools](/docs/2026-03-30/mcp/tools#feedback) for details.
### Added
**Skip done conversations.** `fetch-messages` and `fetch-call-transcripts` now accept `excludeDoneConversations`. Set it to `true` on a whole-inbox query to leave out conversations that are currently marked done or snoozed. It defaults to `false`, so existing calls are unchanged, and queries for one contact always return that contact's full history. See [Supported tools](/docs/2026-03-30/mcp/tools#whole-inbox) for details.
### Improved
**Custom fields on contacts.** `create-contact` and `update-contact` now support your workspace's custom fields. Set custom field values when you create a contact, and set or clear them on an existing contact. See [Supported tools](/docs/2026-03-30/mcp/tools#contacts) for details.
### Added
**Get the organization.** [`GET /organization`](/docs/2026-03-30/organization/get-the-organization) retrieves information about your Quo workspace, including its name, subscription status, and creation/update timestamps.
### Improved
**Webhook delivery correlation.** Delivery list and detail results now include the payload `id` as `eventId` and the primary resource ID as `resourceId`. Filter the delivery list by `resourceId` to find every delivery for one business resource. Deliveries created before September 2, 2026 return `null` for both fields, and the `resourceId` filter does not match them. Test deliveries also return `null`. The delivery `id` matches the `webhook-id` header and identifies detail and retry requests.
### Added
**New endpoints supported for tasks in `2026-03-30`**:
* [`GET /tasks`](/docs/2026-03-30/tasks/list-tasks) — List tasks
* [`GET /tasks/{taskId}`](/docs/2026-03-30/tasks/get-a-task-by-task-id) — Get a task by ID
* [`POST /tasks`](/docs/2026-03-30/tasks/create-task) — Create a task
* [`PATCH /tasks/{taskId}`](/docs/2026-03-30/tasks/update-task) — Update a task's title and/or description
* [`DELETE /tasks/{taskId}`](/docs/2026-03-30/tasks/delete-task) — Delete a task
* [`PATCH /tasks/{taskId}/status`](/docs/2026-03-30/tasks/update-the-status-for-a-task) — Update a task's status
* [`POST /tasks/{taskId}/conversations`](/docs/2026-03-30/tasks/link-a-task-to-a-conversation) — Link a task to a conversation
* [`DELETE /tasks/{taskId}/conversations`](/docs/2026-03-30/tasks/unlink-a-task-from-a-conversation) — Unlink a task from a conversation
* [`POST /tasks/{taskId}/users`](/docs/2026-03-30/tasks/assign-a-task-to-a-user) — Assign a task to a user
* [`DELETE /tasks/{taskId}/users`](/docs/2026-03-30/tasks/unassign-a-task-to-a-user) — Unassign a task from a user
* [`PATCH /tasks/{taskId}/due-date`](/docs/2026-03-30/tasks/update-the-due-date-of-a-task) — Set a task's due date
* [`DELETE /tasks/{taskId}/due-date`](/docs/2026-03-30/tasks/delete-the-due-date-of-a-task) — Remove a task's due date
### Added
**Undelivered message event for webhooks.** Create and update webhook endpoints now support the `message.undelivered` event. It fires when an outbound message could not be delivered to the recipient or was blocked. An `undelivered` message is terminal and cannot be retried, unlike a `message.failed` event. See [Webhook event payloads](/docs/2026-03-30/webhooks-event-payloads#message-undelivered) for the schema.
### Improved
**Message attachments in fetch results.** `fetch-messages` now returns MMS attachments alongside message text, including each attachment's media type and URL. Connected agents can present images and files as clickable links, including messages that contain an attachment without accompanying text.
**Larger bulk sends.** `send-bulk-messages` now supports 2–40 recipients per call, up from 20. Every recipient still receives a separate one-to-one message and cannot see the other recipients.
**Personalized bulk sends.** `send-bulk-messages` can now send different content to each recipient using `messages: [{to, content}]`. For an identical message to everyone, continue using `to` with `content`; provide one mode or the other, not both. See [Supported tools](/docs/2026-03-30/mcp/tools#bulk-message-modes) for details.
### Added
**`media` in message responses.** [`GET /v1/messages`](/docs/mdx/api-reference/messages/list-messages) and [`GET /v1/messages/{id}`](/docs/mdx/api-reference/messages/get-a-message-by-id) now include a `media` array on each message. Each item has a `url` for the attached media and a `type` for its MIME type.
### Added
**Phone menu event for webhooks.** Create and update webhook endpoints now support the `call.menu.selected` event, which fires when a caller reaches a routing decision in a phone menu (IVR). See [Webhook event payloads](/docs/2026-03-30/webhooks-event-payloads#call-menu-selected) for the schema.
### Added
**Get a contact by ID.** [`GET /contacts/{contactId}`](/docs/2026-03-30/contacts/get-a-contact-by-id) retrieves detailed information about a specific contact in your Quo workspace using the contact's unique identifier.
### Added
**Task events for webhooks.** Create and update webhook endpoints now support `events`: `task.created`, `task.updated`, `task.deleted`, `task.completed`, `task.reopened`, `task.assigned`, `task.unassigned`, `task.overdue`, `task.linked`, `task.unlinked`, `task.duedate.updated`, `task.duedate.removed`.
### Added
**List contacts.** [`GET /contacts`](/docs/2026-03-30/contacts/list-contacts) returns a paginated list of contacts. Filter by external ID or source to narrow results.
### Added
**Webhook API.** The webhook API is now available in this version. Subscribe an HTTPS endpoint to real-time message, call, and contact events, with signed and automatically retried deliveries. See the [overview](/docs/2026-03-30/webhooks-overview) and [quickstart](/docs/2026-03-30/webhooks-quickstart) to get started.
**Managing subscriptions.** [`POST /webhooks`](/docs/2026-03-30/webhooks/create-a-new-webhook) creates a subscription; companion endpoints list, update, and delete subscriptions, rotate the signing secret, inspect deliveries, retry a delivery, and send a test event. A workspace can have up to 50 webhooks.
**Events.** Message (`received`, `delivered`, `failed`), the full call lifecycle, and contact (`updated`, `deleted`) events are supported. See [Webhook event payloads](/docs/2026-03-30/webhooks-event-payloads) for every schema.
### Added
**`conversationId` in message responses.** All messages endpoints now include `conversationId` in the response body.
**Group messaging support.** [`GET /v1/messages`](/docs/mdx/api-reference/messages/list-messages) now supports retrieving group conversation messages via the `participants` array.
### Added
**Mark a conversation as done.** [`POST /conversations/{conversationId}/mark-as-done`](/docs/2026-03-30/conversations/mark-conversation-as-done) removes a conversation from the inbox without sending a message and returns the updated conversation.
**Mark a conversation as open.** [`POST /conversations/{conversationId}/mark-as-open`](/docs/2026-03-30/conversations/mark-conversation-as-open) moves a conversation back to the inbox without sending a message and returns the updated conversation.
### Added
**Mark a conversation as done.** [`POST /v1/conversations/{conversationId}/mark-as-done`](/docs/mdx/api-reference/conversations/mark-conversation-as-done) removes a conversation from the inbox without sending a message and returns the updated conversation.
**Mark a conversation as open.** [`POST /v1/conversations/{conversationId}/mark-as-open`](/docs/mdx/api-reference/conversations/mark-conversation-as-open) moves a conversation back to the inbox without sending a message and returns the updated conversation.
### Added
**Group messages.** [`POST /v1/messages`](/docs/mdx/api-reference/messages/send-a-text-message) now accepts up to 10 phone numbers in the `to` array, sending a single group message to all recipients at once. Sending to a single recipient is unchanged.
### Added
**Mark a conversation as read.** [`POST /conversations/{conversationId}/mark-as-read`](/docs/2026-03-30/conversations/mark-conversation-as-read) clears a conversation's unread indicator without sending a message and returns the updated conversation.
**Retry a failed message.** [`POST /messages/{messageId}/retry`](/docs/2026-03-30/messages/retry-a-failed-message) re-attempts delivery of a message in a failed state. Messages that have permanently failed with an error code, and messages in an undelivered state, cannot be retried.
### Added
**Mark a conversation as read.** [`POST /v1/conversations/{conversationId}/mark-as-read`](/docs/mdx/api-reference/conversations/mark-conversation-as-read) clears a conversation's unread indicator without sending a message and returns the updated conversation.
### Added
**Tasks API.** A new set of endpoints for managing tasks is now available.
| Method | Endpoint | Description |
| - | - | - |
| `GET` | [`/v1/tasks`](/docs/mdx/api-reference/tasks/list-tasks) | List tasks with cursor-based pagination. |
| `POST` | [`/v1/tasks`](/docs/mdx/api-reference/tasks/create-a-task) | Create a task linked to a phone number, conversation, or activity. |
| `GET` | [`/v1/tasks/{taskId}`](/docs/mdx/api-reference/tasks/gets-a-task-by-id) | Get a task by ID. |
| `PUT` | [`/v1/tasks/{taskId}`](/docs/mdx/api-reference/tasks/update-a-task) | Update a task's title and description. |
| `DELETE` | [`/v1/tasks/{taskId}`](/docs/mdx/api-reference/tasks/delete-a-task-by-id) | Delete a task by ID. |
| `POST` | [`/v1/tasks/{taskId}/complete`](/docs/mdx/api-reference/tasks/complete-a-task) | Mark a task as completed. |
| `POST` | [`/v1/tasks/{taskId}/reopen`](/docs/mdx/api-reference/tasks/reopen-a-task) | Reopen a completed task. |
| `POST` | [`/v1/tasks/{taskId}/assign`](/docs/mdx/api-reference/tasks/assign-a-user-to-a-task) | Assign a user to a task. |
| `POST` | [`/v1/tasks/{taskId}/unassign`](/docs/mdx/api-reference/tasks/unassign-a-user-from-a-task) | Remove a user from a task's assignees. |
| `POST` | [`/v1/tasks/{taskId}/change-due-date`](/docs/mdx/api-reference/tasks/change-a-tasks-due-date) | Set a task's due date. |
| `POST` | [`/v1/tasks/{taskId}/remove-due-date`](/docs/mdx/api-reference/tasks/remove-a-tasks-due-date) | Clear a task's due date. |
| `POST` | [`/v1/tasks/{taskId}/link-conversation`](/docs/mdx/api-reference/tasks/link-a-task-to-a-conversation) | Link a task to a conversation. |
| `POST` | [`/v1/tasks/{taskId}/unlink-conversation`](/docs/mdx/api-reference/tasks/unlink-a-task-from-a-conversation) | Unlink a conversation from a task. |
### Added
**List users.** `GET /users` returns a paginated list of users in your Quo workspace.
**Get user by ID.** `GET /users/{userId}` retrieves detailed information about a specific workspace user.
### Added
**Call lifecycle events.** Five call lifecycle events are now supported by the beta webhook API, completing event parity with the legacy webhook system:
| Event type | When it fires |
| - | - |
| [`call.ringing`](/docs/mdx/beta/webhooks-event-payloads#call-ringing) | A call started ringing (incoming or outgoing). |
| [`call.answered`](/docs/mdx/beta/webhooks-event-payloads#call-answered) | A call connected. Also fires when an outgoing call reaches voicemail. |
| [`call.forwarded`](/docs/mdx/beta/webhooks-event-payloads#call-forwarded) | An incoming call was forwarded. Includes `forwardedFrom` and `forwardedTo` phone numbers. |
| [`call.missed`](/docs/mdx/beta/webhooks-event-payloads#call-missed) | An incoming call ended without being answered. |
| [`call.voicemail.completed`](/docs/mdx/beta/webhooks-event-payloads#call-voicemail-completed) | A voicemail finished processing. Correlate with the source call via `resource.callId`. |
`call.answered`, `call.forwarded`, `call.missed`, and `call.voicemail.completed` are new event types with no equivalent in the legacy webhook system.
The beta webhook API now covers the full event surface of the legacy system, plus `call.answered`, `call.forwarded`, `call.missed`, and `call.voicemail.completed` — four event types with no equivalent in legacy. See the [beta webhook overview](/docs/mdx/beta/webhooks-overview) and [event payload reference](/docs/mdx/beta/webhooks-event-payloads) for details.
### Added
**Beta webhook API (open beta).** A new webhook API is available in open beta. See the [overview](/docs/mdx/beta/webhooks-overview) and [quickstart](/docs/mdx/beta/webhooks-quickstart) to get started.
**Unified create endpoint.** `POST /webhooks` replaces the four legacy create endpoints (`/v1/webhooks/messages`, `/v1/webhooks/calls`, `/v1/webhooks/call-summaries`, `/v1/webhooks/call-transcripts`). Message, call, and contact event types can be combined in a single subscription. Up to 50 webhooks per workspace.
**Supported event types at launch.**
| Event type | When it fires |
| - | - |
| [`message.received`](/docs/mdx/beta/webhooks-event-payloads#message-received) | An inbound message was received. |
| [`message.delivered`](/docs/mdx/beta/webhooks-event-payloads#message-delivered) | An outbound message was delivered. |
| [`call.completed`](/docs/mdx/beta/webhooks-event-payloads#call-completed) | A call ended. Includes final status and duration. |
| [`call.recording.completed`](/docs/mdx/beta/webhooks-event-payloads#call-recording-completed) | A call recording finished processing. |
| [`call.summary.completed`](/docs/mdx/beta/webhooks-event-payloads#call-summary-completed) | An AI call summary finished generating. |
| [`call.transcript.completed`](/docs/mdx/beta/webhooks-event-payloads#call-transcript-completed) | A call transcript finished processing. |
| [`contact.updated`](/docs/mdx/beta/webhooks-event-payloads#contact-updated) | A contact was created or updated. |
| [`contact.deleted`](/docs/mdx/beta/webhooks-event-payloads#contact-deleted) | A contact was deleted. |
Call lifecycle events (`call.ringing`, `call.answered`, `call.forwarded`, `call.missed`, `call.voicemail.completed`) were not yet available at launch — they were added on May 26, 2026.
**Payload envelope.** All events share a common `data.resource` / `data.context` / `data.links` structure. `data.resource` contains the primary business object; `data.context` contains routing metadata (phone number, conversation, participants, contact lookup). See [Webhook event payloads](/docs/mdx/beta/webhooks-event-payloads).
**Payload versioning.** Each subscription pins a payload version at creation via the `x-quo-api-version` header. Existing subscriptions are unaffected by future version changes. See [Versioning policy](/docs/mdx/beta/webhooks-overview#versioning-policy) for the current version.
**Signing.** Deliveries use Standard-Webhooks-compatible headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`) and a `whsec_...` base64 secret, compatible with the Svix SDK. This scheme is not interchangeable with the legacy `OpenPhone-Signature` header — update signature verification before routing beta traffic to an existing endpoint. See [Validate webhook signatures](/docs/mdx/beta/webhooks-signature-validation).
**Delivery inspection.** Send a signed test delivery (`POST /webhooks/:id/events/test`), browse delivery history and per-attempt responses (`GET /webhooks/:id/events`, `GET /webhooks/:id/events/:eventId`), and trigger manual retries (`POST /webhooks/:id/events/:eventId/retry`). See [Webhook API reference](/docs/mdx/beta/webhooks-api-reference).
**Signing secret rotation.** `POST /webhooks/:id/rotate` issues a new `whsec_...` key for an existing subscription without changing its event subscriptions or `apiVersion`.
For a side-by-side comparison with the legacy system and a no-downtime migration walkthrough, see [Migrating from legacy](/docs/mdx/beta/webhooks-differences-from-current).
### API versioning
The Quo API now uses header-based versioning. Specify your target version by passing the `Quo-Api-Version` header on every request:
```
Quo-Api-Version: 2026-03-30
```
**Version policy.** A new version is only introduced when a breaking change is necessary — for example, removing a field, changing a field's type, or altering existing behavior in an incompatible way. New endpoints, new optional fields, and bug fixes are added to the current version without a new version being published. See [Versioning](/docs/2026-03-30/versioning) for the full contract.
### Minor Changes
* Adds a property `externalId` to the contact model. Adds `externalId` and `source` as optional parameters to the Create Contact (`POST /contacts`) request.
* Adds `externalId` and `source` as optional parameters to the Update Contact (`PATCH /contacts/:id`) request.
* Added a route to list contacts (`GET /contacts`).
### Patch Changes
* Fixed an issue where creating or updating a contact with an invalid custom field would result in 500 error. Sending an invalid custom field will now result in a 400 "Invalid Custom Field Item" error.
### Patch Changes
* Fixed an issue where paginated endpoints would return a string token for the next page at the end of paginated results. Now, they will correctly return the next page token as `null`.
* Added a callout that the `totalItems` result field for the paginated endpoints is not functioning as expected and is not returning the true total items count.
### Patch Changes
* Fixes an issue where phone numbers in various routes were expected to be in E164 format, but the format was not being validated correctly.
## 1.1.0
### Minor Changes
* Adds a property, `restrictions`, to the objects in the response from list phone numbers (`GET /phone-numbers`). The new property contains information about regional restrictions for outbound calling and messaging from a phone number.
### Patch Changes
* Fixed an issue with list calls (`GET /calls`) where sending an empty participants param resulted in a 500 response. Sending an empty participants param will now result in a descriptive 400 response.
* Fixes an issue where attempting to send a message to an international number would result in a 500 response if international messaging is not enabled in the workspace. With this fix, the 500 error response changed to a 403 with a descriptive message
* Fixes a bug where the GET call recordings endpoint sometimes returned an empty array.
* Fixes an issue that was preventing some call records from returning successfully from `GET /calls`
* Fixes an issue where getting a contact by id would result in a 500 instead of a 404 when contact is not found. Now this will respond in a 404 with a descriptive message.
* Fixes an issue where sending a message that contained only whitespace (`' '`, `'\n'`, etc.) resulted in a 500 error response. Now, this will respond with 400 and a validation error message instead.
### Patch Changes
* Fixes an issue with List Calls (`GET /calls`) where the user ID applied by default when the user ID parameter was not sent was being set to the workspace owner instead of the phone number owner.
## 1.0.0
### Major Changes
OpenPhone's Public API v1 release 🚀
Changes from the beta version include:
* The `since` query parameter on "list calls" and "list messages" has been deprecated. It used to incorrectly behave as a `createdBefore`. Please use `createdAfter` instead, or `createdBefore` to maintain current functionality.
* The `phoneNumberId` field for "send text message" has been deprecated. Please use `from` instead.
* `/v0` endpoints have been deprecated. Please use `/v1` instead.