OAuth for connectors
How Claude.ai and other MCP clients that cannot hold an API key connect to VortexDeployer through OAuth, and how a grant is tied to one of your keys.
Some MCP clients, like Claude.ai custom connectors, cannot send a fixed API key. They sign in with OAuth instead. OAuth is a standard way for an app to get access to your account without ever seeing your password.
VortexDeployer runs an OAuth 2.1 server for these clients. It uses the authorization code flow with PKCE. At the end, the client holds an access token that starts with vdo_. It works anywhere an API key works: the MCP server, every REST route and the live streams.
| What | Address |
|---|---|
| Issuer | https://api.vortexdeployer.com/api/v1 |
| Server info (RFC 8414) | https://api.vortexdeployer.com/.well-known/oauth-authorization-server |
| Resource info (RFC 9728) | https://api.vortexdeployer.com/.well-known/oauth-protected-resource |
| Register a client (RFC 7591) | POST https://api.vortexdeployer.com/api/v1/oauth/register |
| Ask for access | GET https://api.vortexdeployer.com/api/v1/oauth/authorize |
| Get tokens | POST https://api.vortexdeployer.com/api/v1/oauth/token |
| Revoke a token (RFC 7009) | POST https://api.vortexdeployer.com/api/v1/oauth/revoke |
The two .well-known files live at the root of the API host, not under /api/v1. They also answer with the resource path added at the end, like https://api.vortexdeployer.com/.well-known/oauth-protected-resource/api/v1/mcp. Copies answer under https://api.vortexdeployer.com/api/v1/oauth/.well-known/… too.
One grant, one key#
When you approve a client, you pick one of your API keys. The grant is tied to that key. The client's token then acts as that key in every way:
- It has the key's scopes, its max per buy, its daily cap, its wallet list and its IP list.
- It shares the key's rate limits.
- Its calls show under that key on the API page, Usage tab.
- Revoke the key and every token made from it stops working on its next call.
OAuth has no scopes of its own here. A scope value in the request is ignored.
The agent spends real money as the key allows. Make a new key for it first. Use a read-only key to look around, or a trade key with a low max per buy and a daily cap. See API overview & keys.
Connect Claude.ai#
- In the app, open API and make a key for the agent.
- In Claude.ai, add a custom connector with the address
https://api.vortexdeployer.com/api/v1/mcp. - Claude registers itself and sends you to the Connect an AI agent page. Sign in if asked.
- The page names the app that asks and the site your browser returns to. Under Act as this API key, pick the key.
- Click Approve as key name. To refuse, click Deny.
Claude's tools now act as that key. To end access, remove the connector in Claude.ai or revoke the key in the app.
If you have no active key, the page says No active API keys. Make one in the app, then connect again.
Build an OAuth client#
This part is for developers of MCP clients. The flow is plain OAuth 2.1, so the OAuth helpers in the MCP SDKs work as they are.
1. Find the server#
Call POST https://api.vortexdeployer.com/api/v1/mcp with no token. The 401 answer has a header that points to the resource info:
WWW-Authenticate: Bearer resource_metadata="https://api.vortexdeployer.com/.well-known/oauth-protected-resource"That file names the server in authorization_servers. Then read https://api.vortexdeployer.com/.well-known/oauth-authorization-server for the addresses above. It lists S256 as the only PKCE method and none as the only client sign-in method.
2. Register once#
POST https://api.vortexdeployer.com/api/v1/oauth/register
Content-Type: application/json
{ "client_name": "My agent", "redirect_uris": ["https://myapp.example/oauth/callback"] }client_name— up to 128 characters. DefaultMCP client.redirect_uris— 1 to 16 addresses your user comes back to. Each must behttps, orhttponlocalhost,127.0.0.1or[::1]. No#part.
The answer is 201 with your client_id. No client secret is given. Every client is public, and PKCE proves the code is yours.
3. Send the user to approve#
Make a random code_verifier. Its code_challenge is base64url(sha256(verifier)), 43 characters. Then send the browser to:
https://api.vortexdeployer.com/api/v1/oauth/authorize?response_type=code
&client_id=…&redirect_uri=…&state=…
&code_challenge=…&code_challenge_method=S256redirect_urimust match a registered one exactly.stateis optional, up to 1,024 characters. It comes back to you unchanged.- A bad request answers
400on the spot. It never sends the browser to your address.
The user then sees the Connect an AI agent page in the app. It stays open for 10 minutes. On approve, the browser comes back with code and state. On deny, it comes back with error=access_denied.
4. Trade the code for tokens#
The body is form-encoded, up to 8 KB:
POST https://api.vortexdeployer.com/api/v1/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=…&redirect_uri=…&client_id=…&code_verifier=…{ "access_token": "vdo_…", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "vdr_…" }A code works once and lasts 2 minutes. The client, the redirect address and the verifier must all match.
5. Use and refresh#
Send Authorization: Bearer vdo_… to https://api.vortexdeployer.com/api/v1/mcp or any REST route. On a stream, put it in token on each subscribe.
The access token lasts 1 hour. Before then, get a new pair:
POST https://api.vortexdeployer.com/api/v1/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=vdr_…&client_id=…A refresh token lasts 30 days and works once. Each refresh gives a new one. If an old refresh token is sent again, the server takes that as a leak. It revokes every token of that key and client, and the user must approve again.
6. Revoke#
POST https://api.vortexdeployer.com/api/v1/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=vdo_…It always answers 200, even for a token it does not know.
Errors#
These addresses answer in the OAuth style, not the API's usual shape:
{ "error": "invalid_grant", "error_description": "the grant is invalid, expired or already used" }error | Status | When |
|---|---|---|
invalid_client_metadata | 400 | The register body is wrong. |
invalid_redirect_uri | 400 | A redirect address breaks the rules in step 2. |
invalid_request | 400 | A field is missing or wrong, or the client or redirect address is unknown. |
invalid_grant | 400 | The code or refresh token is wrong, expired, used, or sent with the wrong verifier. |
temporarily_unavailable | 429 | Too many tries from your IP address. Wait a minute. |
The limits per IP address and minute:
- Register: 10 clients.
- Ask for access: 30 requests.
- Get tokens: failed tries share the API's budget for bad keys, 30 a minute. Over it, every call to
/oauth/tokengets429withRetry-After: 60.