curl --request POST 'https://api.voicecheap.ai/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
--data-urlencode 'code=mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' \
--data-urlencode 'redirect_uri=https://assistant.example.com/oauth/callback' \
--data-urlencode 'code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk' \
--data-urlencode 'resource=https://api.voicecheap.ai/mcp'
const input = {
grant_type: 'authorization_code',
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
code: 'mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC',
redirect_uri: 'https://assistant.example.com/oauth/callback',
code_verifier: 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
resource: 'https://api.voicecheap.ai/mcp',
};
const response = await fetch('https://api.voicecheap.ai/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/token",
headers={"Content-Type":"application/x-www-form-urlencoded"},
data={"grant_type": "authorization_code", "client_id": "mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "code": "mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "redirect_uri": "https://assistant.example.com/oauth/callback", "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", "resource": "https://api.voicecheap.ai/mcp"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
OAuth i protokół MCP
Wymień kod autoryzacyjny lub wykonaj rotację tokenu odświeżania
Uzyskaj krótkotrwałe tokeny dostępu MCP, używając kodu autoryzacyjnego S256 klienta publicznego lub jego bieżącego tokenu odświeżania.
POST
/
oauth
/
token
curl --request POST 'https://api.voicecheap.ai/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
--data-urlencode 'code=mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' \
--data-urlencode 'redirect_uri=https://assistant.example.com/oauth/callback' \
--data-urlencode 'code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk' \
--data-urlencode 'resource=https://api.voicecheap.ai/mcp'
const input = {
grant_type: 'authorization_code',
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
code: 'mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC',
redirect_uri: 'https://assistant.example.com/oauth/callback',
code_verifier: 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
resource: 'https://api.voicecheap.ai/mcp',
};
const response = await fetch('https://api.voicecheap.ai/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/token",
headers={"Content-Type":"application/x-www-form-urlencoded"},
data={"grant_type": "authorization_code", "client_id": "mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "code": "mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "redirect_uri": "https://assistant.example.com/oauth/callback", "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", "resource": "https://api.voicecheap.ai/mcp"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Uzyskaj krótkotrwałe tokeny dostępu MCP, używając kodu autoryzacyjnego S256 klienta publicznego lub jego bieżącego tokenu odświeżania.
POST https://api.voicecheap.ai/oauth/token
Uwierzytelnianie
Brak uwierzytelniania klienta HTTP. Prześlij client_id w treści zakodowanej w URL. Każdy nagłówek Authorization, client_secret lub client_assertion jest odrzucany.Treść żądania
Pokaż grant_type: authorization_code
Pokaż grant_type: authorization_code
string
wymagane
client_id zwrócony przez rejestrację dynamiczną lub dokładny publiczny adres HTTPS dokumentu metadanych klienta.Minimalna długość: 1. Maksymalna długość: 2048. Wzorzec:
^[^\u0000-\u001F\u007F]+$.string
wymagane
Wymagana jest dokładna wartość odbiorcy zasobu: użyj wartości resource z metadanych chronionego zasobu tego środowiska. W produkcji jest to https://api.voicecheap.ai/mcp. Inny adres bazowy, ścieżka lub końcowy ukośnik są odrzucane.Format: “uri”.
field
wymagane
Wartość określona w pełnym schemacie danych wejściowych.Wymagana wartość: “authorization_code”.
string
wymagane
Jednorazowy kod zwracany dokładnie na zarejestrowany adres wywołania zwrotnego.Minimalna długość: 1. Maksymalna długość: 256. Wzorzec:
^mcp_code_[A-Za-z0-9_-]{43}$.string
wymagane
Musi dokładnie odpowiadać adresowi wywołania zwrotnego powiązanemu z tym kodem i rejestracją.Minimalna długość: 1. Maksymalna długość: 2048. Wzorzec:
^[^\u0000-\u001F\u007F]+$.string
wymagane
Oryginalny weryfikator PKCE użyty do obliczenia S256 code_challenge żądania autoryzacji.Minimalna długość: 43. Maksymalna długość: 128. Wzorzec:
^[A-Za-z0-9._~-]{43,128}$.Pokaż grant_type: refresh_token
Pokaż grant_type: refresh_token
string
wymagane
client_id zwrócony przez rejestrację dynamiczną lub dokładny publiczny adres HTTPS dokumentu metadanych klienta.Minimalna długość: 1. Maksymalna długość: 2048. Wzorzec:
^[^\u0000-\u001F\u007F]+$.string
wymagane
Wymagana jest dokładna wartość odbiorcy zasobu: użyj wartości resource z metadanych chronionego zasobu tego środowiska. W produkcji jest to https://api.voicecheap.ai/mcp. Inny adres bazowy, ścieżka lub końcowy ukośnik są odrzucane.Format: “uri”.
field
wymagane
Wartość określona w pełnym schemacie danych wejściowych.Wymagana wartość: “refresh_token”.
string
wymagane
Bieżący niewykorzystany token odświeżania klienta zarejestrowanego dla refresh_token.Minimalna długość: 1. Maksymalna długość: 256. Wzorzec:
^mcp_refresh_[A-Za-z0-9_-]{43}$.string
Rozdzielony spacjami podzbiór pierwotnych uprawnień, opcjonalnie zawierający offline_access. Nieobsługiwane nazwy, puste wartości i dodane uprawnienia są odrzucane; duplikaty są usuwane. Jawny scope zachowuje tylko wymienione uprawnienia produktu i tożsamości i może usunąć wszystkie uprawnienia produktu. Pomiń scope, aby zachować bieżącą autoryzację, z wyjątkiem uprawnień tożsamości, które są usuwane, gdy OpenID Connect jest niedostępny.Minimalna długość: 1. Maksymalna długość: 2048. Wzorzec:
^ *(?: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))* *$.Schemat techniczny
Schemat techniczny
{
"oneOf": [
{
"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."
},
"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"
]
},
"grant_type": {
"const": "authorization_code"
},
"code": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "^mcp_code_[A-Za-z0-9_-]{43}$",
"description": "Single-use code returned to the exact registered callback."
},
"redirect_uri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Must exactly equal the callback bound to this code and registration."
},
"code_verifier": {
"type": "string",
"minLength": 43,
"maxLength": 128,
"pattern": "^[A-Za-z0-9._~-]{43,128}$",
"description": "Original PKCE verifier used to calculate the authorization request's S256 code_challenge."
}
},
"required": [
"grant_type",
"client_id",
"resource",
"code",
"redirect_uri",
"code_verifier"
],
"not": {
"anyOf": [
{
"required": [
"client_secret"
]
},
{
"required": [
"client_assertion"
]
}
]
},
"additionalProperties": true
},
{
"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."
},
"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"
]
},
"grant_type": {
"const": "refresh_token"
},
"refresh_token": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "^mcp_refresh_[A-Za-z0-9_-]{43}$",
"description": "The current unconsumed refresh token from a client registered for refresh_token."
},
"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 subset of the original permissions, optionally including offline_access. Unsupported names, empty values, and added permissions are rejected; duplicates are removed. An explicit scope keeps only the listed product and identity permissions and may remove every product permission. Omit scope to retain the current grant, except identity permissions are dropped if OpenID Connect is unavailable.",
"examples": [
"account:read projects:read offline_access"
]
}
},
"required": [
"grant_type",
"client_id",
"resource",
"refresh_token"
],
"not": {
"anyOf": [
{
"required": [
"client_secret"
]
},
{
"required": [
"client_assertion"
]
}
]
},
"additionalProperties": true
}
]
}
Przykłady żądań
Stosuj udokumentowany sposób kodowania i uwierzytelniania dla tej ścieżki. Struktura odpowiedzi i reguły idempotencji API akcji dotyczą wyłącznie wywołań akcji.curl --request POST 'https://api.voicecheap.ai/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
--data-urlencode 'code=mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' \
--data-urlencode 'redirect_uri=https://assistant.example.com/oauth/callback' \
--data-urlencode 'code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk' \
--data-urlencode 'resource=https://api.voicecheap.ai/mcp'
const input = {
grant_type: 'authorization_code',
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
code: 'mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC',
redirect_uri: 'https://assistant.example.com/oauth/callback',
code_verifier: 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
resource: 'https://api.voicecheap.ai/mcp',
};
const response = await fetch('https://api.voicecheap.ai/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/token",
headers={"Content-Type":"application/x-www-form-urlencoded"},
data={"grant_type": "authorization_code", "client_id": "mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "code": "mcp_code_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "redirect_uri": "https://assistant.example.com/oauth/callback", "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", "resource": "https://api.voicecheap.ai/mcp"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Odpowiedź
Nieprzezroczysty token dostępu Bearer, jego pozostały czas ważności w sekundach, przyznane zakresy i token odświeżania, jeśli klient zarejestrował refresh_token. expires_in wynosi maksymalnie 900 i może być mniejsze pod koniec ważności przyznanego dostępu.HTTP 200
{
"access_token": "mcp_access_DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "mcp_refresh_EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
"scope": "account:read projects:read offline_access"
}
Nagłówki odpowiedzi
{
"Cache-Control": "no-store",
"Pragma": "no-cache"
}
Uwagi do operacji
- Rozpoznawane parametry skalarne muszą być podane jednokrotnie jako niepuste ciągi znaków bez znaków sterujących ASCII ani DEL. Powtórzone pola zapytania lub formularza są dekodowane jako tablice i odrzucane przy odczycie jako wartości skalarne.
- Użyj danych formularza URL-encoded, np. curl —data-urlencode dla każdego pola. Treść tokenu w formacie JSON jest odrzucana z invalid_request.
- Odbiorca zasobu jest wymagany dla obu typów grantu i musi dokładnie odpowiadać adresowi URL MCP środowiska, które wydało kod lub token odświeżania.
- Wymiana kodu autoryzacyjnego wymaga oryginalnego client_id, dokładnego redirect_uri i pasującego S256 code_verifier. Kody wygasają po 2 minutach i są jednorazowe.
- Punkt końcowy tokenów odrzuca każdy podany nagłówek Authorization, w tym Basic i Bearer, a także każde pole client_secret lub client_assertion w treści żądania.
- Tokeny dostępu są ważne maksymalnie 15 minut. Przy zarejestrowanym refresh_token połączenie i rodzina tokenów odświeżania wygasają 30 dni po zatwierdzeniu przez użytkownika; rotacja nie przedłuża tego terminu.
- To, czy zwracany jest refresh_token, zależy od zarejestrowanych typów przyznania uprawnień. Nie zależy od uwzględnienia offline_access w żądaniu autoryzacji. Zwracane scope zawiera dodatkowo offline_access zawsze, gdy zwracany jest token odświeżania.
- Aby odnowić, wyślij grant_type=refresh_token z client_id, bieżącym refresh_token, resource i opcjonalnie zawężonym scope. Atomowo zastąp zapisany token odświeżania nową wartością.
- Ponowne użycie zużytego kodu autoryzacyjnego lub zastąpionego tokenu odświeżania unieważnia całe połączenie, w tym wszystkie jego tokeny dostępu. Nie wymieniaj jednocześnie tego samego kodu ani tokenu odświeżania.
- Żądania odświeżenia nigdy nie dodają uprawnień. Przy jawnym scope zachowywane są tylko żądane uprawnienia produktu i tożsamości; offline_access nie zachowuje pominiętych uprawnień produktu.
- Tokeny są powiązane z użytkownikiem udzielającym zgody, klientem, odbiorcą zasobu, bieżącą własnością zespołu i aktywnym płatnym dostępem. Cofnięcie zgody lub zmiana własności wymaga ponownego połączenia.
- Publiczny limit tokenów wynosi 60 żądań na minutę na adres IP klienta i jest współdzielony przez procesy robocze. Treści URL-encoded są ograniczone do 32 KiB.
- Błędy OAuth zwracają error i error_description. Błędy parsera treści żądania mogą zostać zwrócone wcześniej w formacie błędu parsera integracji.
- openid i email to uprawnienia tożsamości, niezależne od uprawnień produktu, akceptowane tylko wtedy, gdy są ogłaszane. email wymaga openid. Podczas pierwszej autoryzacji żądanie zawierające wyłącznie zakresy tożsamości używa domyślnych uprawnień produktu pokazanych na ekranie zgody.
- client_id zwrócony przez rejestrację dynamiczną lub dokładny publiczny adres HTTPS dokumentu metadanych klienta.
- Po przyznaniu openid wymiana kodu autoryzacji zwraca również id_token podpisany algorytmem RS256. Sprawdź podpis, wystawcę, odbiorcę, czas wygaśnięcia i nonce, jeśli go podano. Przyznane zakresy tożsamości pojawiają się w scope odpowiedzi; odświeżanie nie wydaje nowego id_token.
- Jeśli OpenID Connect stanie się niedostępny, odświeżenie bez scope zachowa uprawnienia produktu i usunie uprawnienia tożsamości. Jawne żądanie niedostępnych zakresów tożsamości zwróci invalid_scope.
- Jeśli konto zostało usunięte lub wyłączone, endpoint tokenów zwraca invalid_grant z HTTP 400.
Błędy
| Status HTTP | Kod | Znaczenie lub sposób rozwiązania |
|---|---|---|
| 400 | invalid_request | Treść żądania lub rozpoznany parametr są nieprawidłowe, nieobecne, zduplikowane lub mają niewłaściwe kodowanie. |
| 400 | invalid_client | client_id jest nieznany, podano uwierzytelnianie HTTP klienta lub podano client_secret/client_assertion. |
| 400 | invalid_target | Brakuje wartości audience zasobu lub nie odpowiada ona dokładnie zasobowi MCP. |
| 400 | unsupported_grant_type | Typ przyznania uprawnień jest nieobsługiwany lub refresh_token nie został zarejestrowany dla tego klienta. |
| 400 | invalid_grant | Kod lub token jest nieprawidłowy, wygasły, unieważniony, użyty ponownie, niedopasowany lub nie jest już autoryzowany dla bieżącego konta lub zespołu. |
| 400 | invalid_scope | Żądanie odświeżenia próbuje dodać uprawnienia lub zawiera nieobsługiwane zakresy. |
| 413 | REQUEST_TOO_LARGE | Żądanie przekracza dozwolony rozmiar treści. Limit dla JSON wynosi 1 MiB, a dla formularzy zakodowanych w URL — 32 KiB. |
| 415 | UNSUPPORTED_CONTENT_TYPE | Parser treści integracji przyjmuje wyłącznie JSON lub formularze URL-encoded. Dana trasa może wymagać konkretnego z tych kodowań. |
| 429 | temporarily_unavailable | Osiągnięto publiczny limit częstotliwości żądań OAuth. Przed ponowieniem poczekaj na zakończenie bieżącego okna. |
| 500 | server_error | Autoryzacja jest tymczasowo niedostępna. Spróbuj ponownie później. |
Czy ta strona była pomocna?

