curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
OAuth et protocole MCP
Démarrer l’autorisation du compte MCP
Créez une demande d’autorisation à durée de validité courte et redirigez l’utilisateur vers la page VoiceCheap de connexion et de consentement aux autorisations.
GET
/
oauth
/
authorize
curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Créez une demande d’autorisation à durée de validité courte et redirigez l’utilisateur vers la page VoiceCheap de connexion et de consentement aux autorisations.
GET https://api.voicecheap.ai/oauth/authorize
Authentification
Aucune sur cette route. Le parcours de consentement qui suit dans le navigateur exige la connexion de l’utilisateur, et l’approbation exige un accès éligible à un compte payant.Paramètres de requête
string
requis
Le client_id renvoyé par l’enregistrement dynamique ou l’URL HTTPS publique exacte du document de métadonnées du client.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^[^\u0000-\u001F\u007F]+$.string
requis
Doit correspondre exactement à l’une des redirect_uris enregistrées pour ce client.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^[^\u0000-\u001F\u007F]+$.field
requis
Valeur définie par le schéma d’entrée complet.Valeur requise: “code”.
field
Valeur définie par le schéma d’entrée complet.Valeur requise: “query”. Valeur par défaut: “query”.
string
requis
Empreinte SHA-256 encodée en Base64url du code_verifier d’origine, sans remplissage.Longueur minimale: 43. Longueur maximale: 43. Motif:
^[A-Za-z0-9_-]{43}$.field
requis
Valeur définie par le schéma d’entrée complet.Valeur requise: “S256”.
string
requis
Audience de ressource exacte requise : utilisez la valeur resource des métadonnées de la ressource protégée de cet environnement. En production, il s’agit de https://api.voicecheap.ai/mcp. Une origine, un chemin ou une barre oblique finale différents sont refusés.Format: “uri”.
string
Permissions de scopes_supported séparées par des espaces, pouvant inclure offline_access. Les noms non pris en charge et les valeurs composées uniquement d’espaces sont rejetés ; les doublons sont supprimés. Omettre scope ou ne fournir aucune permission produit, par exemple openid seul ou offline_access seul, sélectionne les permissions de lecture par défaut également enregistrées pour ce client. email nécessite openid ; email seul est rejeté. Les permissions produit demandées doivent être enregistrées pour ce client. Les permissions d’identité doivent être explicitement demandées et approuvées par l’utilisateur ; omettre scope ne demande aucune permission d’identité.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^ *(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email)(?: +(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email))* *$.string
Valeur opaque facultative renvoyée à l’identique lors du rappel final. Utilisez une nouvelle valeur imprévisible et vérifiez-la dans le client.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^[^\u0000-\u001F\u007F]+$.string
Nonce opaque facultatif repris dans le token d’identité quand openid est accordé. Vérifiez-le côté client.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^[^\u0000-\u001F\u007F]+$.string
Valeur textuelle.Valeur requise: “consent”.
Schéma technique
Schéma technique
{
"type": "object",
"properties": {
"client_id": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "The client_id returned by dynamic registration, or the exact public HTTPS URL of the client metadata document."
},
"redirect_uri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Must exactly match one of this client's registered redirect_uris."
},
"response_type": {
"const": "code"
},
"response_mode": {
"const": "query",
"default": "query"
},
"code_challenge": {
"type": "string",
"minLength": 43,
"maxLength": 43,
"pattern": "^[A-Za-z0-9_-]{43}$",
"description": "Base64url SHA-256 digest of the original code_verifier, without padding."
},
"code_challenge_method": {
"const": "S256"
},
"resource": {
"type": "string",
"format": "uri",
"description": "Required exact resource audience: use the resource value from this environment's protected-resource metadata. In production this is https://api.voicecheap.ai/mcp. A different origin, path, or trailing slash is rejected.",
"examples": [
"https://api.voicecheap.ai/mcp"
]
},
"scope": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^ *(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email)(?: +(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email))* *$",
"description": "Space-delimited permissions from scopes_supported, optionally including offline_access. Unsupported names and whitespace-only values are rejected; duplicates are removed. Omitting scope or supplying no product scopes, such as openid alone or offline_access alone, selects the default read scopes intersected with this client's registered product scopes. email requires openid; email alone is rejected. Requested product permissions must be registered for this client. Identity permissions must be explicitly requested and approved by the user; omitting scope requests no identity permissions.",
"examples": [
"account:read projects:read offline_access"
]
},
"state": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Optional opaque value echoed on the final callback. Use a fresh unpredictable value and verify it in the client."
},
"nonce": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Optional opaque nonce echoed in the ID token when openid is granted. Verify it in the client."
},
"prompt": {
"type": "string",
"const": "consent"
}
},
"required": [
"client_id",
"redirect_uri",
"response_type",
"code_challenge",
"code_challenge_method",
"resource"
],
"additionalProperties": true
}
Exemples de requêtes
Suivez l’encodage et l’authentification documentés pour cette route. L’enveloppe et les règles d’idempotence de l’API d’actions s’appliquent uniquement aux invocations d’actions.curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Réponse
Redirection HTTP vers la page de consentement VoiceCheap. Il n’y a pas de corps de résultat JSON. Location contient une valeur request opaque ; l’utilisateur se connecte ensuite et choisit les autorisations.HTTP/1.1 302 Found
Cache-Control: no-store
Pragma: no-cache
Referrer-Policy: no-referrer
Location: https://app.voicecheap.ai/mcp/authorize?request=mcp_req_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
En-têtes de réponse
{
"Cache-Control": "no-store",
"Pragma": "no-cache",
"Referrer-Policy": "no-referrer",
"Location": "https://app.voicecheap.ai/mcp/authorize?request=mcp_req_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
}
Notes de l’opération
- Les paramètres scalaires reconnus doivent être fournis une seule fois, sous forme de chaînes non vides sans caractères de contrôle ASCII ni DEL. Les champs de requête ou de formulaire répétés sont décodés en tableaux et rejetés lorsqu’ils sont lus comme des scalaires.
- Encodez chaque valeur de requête au format URL. redirect_uri doit utiliser exactement la forme enregistrée pour ce client.
- Utilisez uniquement S256 ; le PKCE plain et les réponses implicit/token ne sont pas pris en charge. Conservez le code_verifier d’origine de 43–128 caractères pour l’échange de jeton.
- Le paramètre resource est obligatoire et doit correspondre exactement à l’URL de ressource MCP de l’environnement sélectionné.
- L’ensemble de portées produit par défaut est account:read usage:read projects:read transcription:read subtitles:read tts:read voices:read images:read thumbnails:read. Une requête utilisant les valeurs par défaut est restreinte aux portées enregistrées par le client ; s’il ne reste aucune portée produit, l’autorisation échoue.
- Une portée composée uniquement de offline_access utilise la sélection par défaut des portées du produit. offline_access est retiré avant l’affichage ou l’octroi des autorisations du produit.
- Les demandes de consentement expirent après 10 minutes et n’acceptent qu’une seule réponse. L’utilisateur ne peut approuver qu’un sous-ensemble non vide des autorisations produit demandées.
- Après approbation, l’URL de rappel du client reçoit code, iss et la valeur state d’origine si elle a été fournie. Le code d’autorisation expire après 2 minutes et ne peut être échangé qu’une seule fois.
- Après un refus, l’URL de rappel du client reçoit error=access_denied, error_description, iss et le state d’origine s’il a été fourni ; aucun code n’est émis.
- Les métadonnées client invalides, les divergences de redirection et les erreurs de PKCE, de portées ou de resource produisent directement des erreurs JSON OAuth ; elles ne sont pas redirigées vers une URL de rappel non validée.
- La limite publique d’autorisation est de 30 requêtes par minute par adresse IP cliente, partagée entre les workers.
- openid et email sont des permissions d’identité, distinctes des permissions produit, acceptées uniquement lorsqu’elles sont annoncées. email nécessite openid. Lors de l’autorisation initiale, une demande contenant seulement des permissions d’identité utilise les permissions produit par défaut affichées sur l’écran de consentement.
- Le client_id renvoyé par l’enregistrement dynamique ou l’URL HTTPS publique exacte du document de métadonnées du client.
- Pour OpenID Connect, prompt=consent est pris en charge. Les autres valeurs de prompt et max_age sont refusées ; l’autorisation silencieuse et la connexion récente forcée ne sont pas prises en charge.
Erreurs
| Statut HTTP | Code | Signification ou solution |
|---|---|---|
| 400 | invalid_request | Le corps ou un paramètre reconnu est invalide, manquant, en double ou mal encodé. |
| 400 | invalid_client | Le client_id est inconnu ou son document de métadonnées ne peut pas être récupéré ou validé. |
| 400 | unsupported_response_type | response_type n’est pas code. |
| 400 | invalid_target | resource est absent ou ne correspond pas exactement à l’URL MCP de l’environnement. |
| 400 | invalid_scope | Une portée du produit demandée n’est pas prise en charge, n’est pas enregistrée ou ne laisse aucune autorisation utilisable. |
| 429 | temporarily_unavailable | La limite de fréquence des requêtes OAuth publiques a été atteinte. Attendez la fin de la fenêtre en cours avant de réessayer. |
| 500 | server_error | L’autorisation est temporairement indisponible. Réessayez plus tard. |
| 503 | temporarily_unavailable | Les métadonnées du client sont temporairement indisponibles. Réessayez plus tard. |
Cette page vous a-t-elle été utile ?

