> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.voicecheap.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Start MCP account authorization

> Create a short-lived authorization request and redirect the user to the VoiceCheap sign-in and permission-consent page.

Create a short-lived authorization request and redirect the user to the VoiceCheap sign-in and permission-consent page.

```http theme={null}
GET https://api.voicecheap.ai/oauth/authorize
```

## Authentication

None on this route. The browser's subsequent consent flow requires the user to sign in, and approval requires eligible paid account access.

## Query parameters

<ParamField query="client_id" type="string" required>
  The client\_id returned by dynamic registration, or the exact public HTTPS URL of the client metadata document.

  Minimum length: 1. Maximum length: 2048. Pattern: `^[^\u0000-\u001F\u007F]+$`.
</ParamField>

<ParamField query="redirect_uri" type="string" required>
  Must exactly match one of this client's registered redirect\_uris.

  Minimum length: 1. Maximum length: 2048. Pattern: `^[^\u0000-\u001F\u007F]+$`.
</ParamField>

<ParamField query="response_type" type="field" required>
  Value defined by the complete input schema.

  Required value: "code".
</ParamField>

<ParamField query="response_mode" type="field">
  Value defined by the complete input schema.

  Required value: "query". Default: "query".
</ParamField>

<ParamField query="code_challenge" type="string" required>
  Base64url SHA-256 digest of the original code\_verifier, without padding.

  Minimum length: 43. Maximum length: 43. Pattern: `^[A-Za-z0-9_-]{43}$`.
</ParamField>

<ParamField query="code_challenge_method" type="field" required>
  Value defined by the complete input schema.

  Required value: "S256".
</ParamField>

<ParamField query="resource" type="string" required>
  Required exact resource audience: use the resource value from this environment's protected-resource metadata. In production this is [https://api.voicecheap.ai/mcp](https://api.voicecheap.ai/mcp). A different origin, path, or trailing slash is rejected.

  Format: "uri".
</ParamField>

<ParamField query="scope" type="string">
  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.

  Minimum length: 1. Maximum length: 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))* *$`.
</ParamField>

<ParamField query="state" type="string">
  Optional opaque value echoed on the final callback. Use a fresh unpredictable value and verify it in the client.

  Minimum length: 1. Maximum length: 2048. Pattern: `^[^\u0000-\u001F\u007F]+$`.
</ParamField>

<ParamField query="nonce" type="string">
  Optional opaque nonce echoed in the ID token when openid is granted. Verify it in the client.

  Minimum length: 1. Maximum length: 2048. Pattern: `^[^\u0000-\u001F\u007F]+$`.
</ParamField>

<ParamField query="prompt" type="string">
  Text value.

  Required value: "consent".
</ParamField>

<Accordion title="Technical schema">
  ```json theme={null}
  {
    "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
  }
  ```
</Accordion>

## Request examples

Follow the documented encoding and authentication for this route. The action API's envelope and idempotency rules apply only to action invocations.

<RequestExample>
  ```bash cURL theme={null}
  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'
  ```

  ```javascript JavaScript theme={null}
  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());
  ```

  ```python Python theme={null}
  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)
  ```
</RequestExample>

## Response

HTTP redirect to the VoiceCheap consent page. There is no JSON result body. The Location contains an opaque request value; the user then signs in and chooses permissions.

```http theme={null}
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
```

## Response headers

```json theme={null}
{
  "Cache-Control": "no-store",
  "Pragma": "no-cache",
  "Referrer-Policy": "no-referrer",
  "Location": "https://app.voicecheap.ai/mcp/authorize?request=mcp_req_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
}
```

## Operation notes

* Recognized scalar parameters must be supplied once as nonempty strings without ASCII control characters or DEL. Repeated query/form fields decode to arrays and are rejected when read as scalars.
* URL-encode every query value. The redirect\_uri must use exactly the spelling registered for this client.
* Use S256 only; plain PKCE and implicit/token responses are unsupported. Retain the original 43–128-character code\_verifier for the token exchange.
* The resource parameter is mandatory and must exactly equal the selected environment's MCP resource URL.
* The default product scope set is account:read usage:read projects:read transcription:read subtitles:read tts:read voices:read images:read thumbnails:read. A defaulted request is narrowed to scopes registered by the client; if no product scopes remain, authorization fails.
* A scope consisting only of offline\_access uses the default product scope selection. offline\_access is removed before showing or granting product permissions.
* Consent requests expire after 10 minutes and can be answered once. The user may approve only a nonempty subset of the requested product permissions.
* After approval, the client callback receives code, iss, and the original state when supplied. The authorization code expires after 2 minutes and can be exchanged once.
* After denial, the client callback receives error=access\_denied, error\_description, iss, and the original state when supplied; no code is issued.
* Invalid client metadata, redirect mismatches, PKCE, scopes, or resource produce OAuth JSON errors directly; they are not redirected to an unvalidated callback.
* The public authorization limit is 30 requests per minute per client IP, shared across workers.
* openid and email are identity permissions, separate from product permissions, and are accepted only when advertised. email requires openid. During initial authorization, a request containing only identity scopes uses the default product permissions shown on the consent screen.
* The client\_id returned by dynamic registration, or the exact public HTTPS URL of the client metadata document.
* For OpenID Connect, prompt=consent is supported. Other prompt values and max\_age are rejected; silent authorization and forced fresh sign-in are not supported.

## Errors

| HTTP status | Code | Meaning or recovery |
| - | - | - |
| 400 | `invalid_request` | The body or a recognized parameter is invalid, missing, duplicated, or has the wrong encoding. |
| 400 | `invalid_client` | The client\_id is unknown, or its client metadata document cannot be retrieved or validated. |
| 400 | `unsupported_response_type` | response\_type is not code. |
| 400 | `invalid_target` | resource is missing or is not exactly the environment's MCP URL. |
| 400 | `invalid_scope` | A requested product scope is unsupported, not registered, or leaves no usable permission. |
| 429 | `temporarily_unavailable` | The public OAuth request rate limit was reached. Wait for the current window before retrying. |
| 500 | `server_error` | Authorization is temporarily unavailable. Retry later. |
| 503 | `temporarily_unavailable` | Client metadata is temporarily unavailable. Retry later. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.