Protocol & OAuth for developers
How Telenow's MCP server speaks the protocol and signs clients in — for building your own MCP client, or debugging one. ChatGPT, Claude and Grok handle all of this for you.
Endpoint
POST https://api.telenow.ai/mcp
- Streamable HTTP, JSON responses, stateless. Every request is one JSON-RPC message in a
POST; the answer is one JSON body. No session id, no server-sent events.GETandDELETEanswer405withAllow: POST. - One message per request. JSON-RPC batches are refused with
400. Notifications are accepted with202. - Methods:
initializeandping(2025 revisions),server/discover(2026-07-28),tools/list,tools/call.
Protocol revisions
| Revision | How a request says it |
|---|---|
2026-07-28 | params._meta["io.modelcontextprotocol/protocolVersion"] and ["io.modelcontextprotocol/clientCapabilities"] on every request, mirrored in the MCP-Protocol-Version header; Mcp-Method names the method, and Mcp-Name the tool on tools/call. No handshake — server/discover describes the server. |
2025-11-25, 2025-06-18, 2025-03-26 | Open with initialize; later requests send MCP-Protocol-Version. A request without the header is read as 2025-03-26. |
A request is served in the revision it arrives in, so clients that try 2026-07-28 first and fall back to initialize work either way. Errors: -32022 for a revision the server does not speak (with the supported list), -32020 when a header disagrees with the body, -32601 for an unknown method, -32602 for bad parameters.
A modern tools/list result carries ttlMs and cacheScope: "private" — the list depends on the caller's permissions.
Authentication
Every request carries a bearer token:
Authorization: Bearer <token>
- an OAuth access token from signing in (below), or
- an organization API key (
vai_live_…, Authentication). A write-capable key gets every permission across its organization — Give agents tools included; a read-only key gets read access.
Without a valid token the answer is 401 with a pointer to the metadata:
WWW-Authenticate: Bearer resource_metadata="https://api.telenow.ai/.well-known/oauth-protected-resource/mcp", scope="calls:read calls:write agents:write agents:tools campaigns:write"
An expired or revoked token adds error="invalid_token". A tools/call for a tool the token's permissions do not cover is 403 with error="insufficient_scope" and a scope of what the token holds plus the permission it needs, so a client can ask the user to grant more.
Requests are limited to 3,000 per 15 minutes per connection or API key.
OAuth
Telenow is its own authorization server for assistants (OAuth 2.1).
| Document | URL |
|---|---|
| Protected resource metadata (RFC 9728) | https://api.telenow.ai/.well-known/oauth-protected-resource/mcp (also without /mcp) |
| Authorization server metadata (RFC 8414) | https://api.telenow.ai/.well-known/oauth-authorization-server |
From the authorization server metadata:
| Field | Value |
|---|---|
issuer | https://api.telenow.ai |
authorization_endpoint | Telenow's sign-in and consent page (/connect/authorize) |
token_endpoint | https://api.telenow.ai/oauth2/token |
registration_endpoint | https://api.telenow.ai/oauth2/register |
revocation_endpoint | https://api.telenow.ai/oauth2/revoke |
grant_types_supported | authorization_code, refresh_token |
code_challenge_methods_supported | S256 (PKCE is required) |
token_endpoint_auth_methods_supported | none, client_secret_basic, client_secret_post, private_key_jwt |
scopes_supported | calls:read, calls:write, agents:write, agents:tools, campaigns:write, and offline_access (accepted; every connection gets a refresh token anyway) |
client_id_metadata_document_supported | true |
authorization_response_iss_parameter_supported | true |
Identifying your client
Either:
- A Client ID Metadata Document — use an
httpsURL you control as theclient_id, serving your client's metadata (name, redirect URIs, auth method; up to 5 KB). Telenow fetches it when someone connects, so the consent page can show who vouches for the client; or - Dynamic Client Registration (RFC 7591) —
POST /oauth2/register. Redirect URIs must behttps, orhttponlocalhost/127.0.0.1for native apps (any port is accepted at sign-in). Without atoken_endpoint_auth_methodyou get a client secret, as the RFC defaults.
curl -s https://api.telenow.ai/oauth2/register -H 'Content-Type: application/json' -d '{
"client_name": "My call assistant",
"redirect_uris": ["https://app.example.com/oauth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}'
Signing in
- Send the user to the
authorization_endpointwithresponse_type=code,client_id,redirect_uri,scope,state,code_challengewithcode_challenge_method=S256(required, and must be sent) andresource=https://api.telenow.ai/mcp(RFC 8707). The user signs in, picks the permissions, organization, numbers and limits, and approves. - Telenow redirects back with
code, yourstate, andiss(RFC 9207) — check thatissishttps://api.telenow.ai. - Exchange the code at
/oauth2/tokenwith yourcode_verifierand the sameredirect_uriandresource. A code works once, for ten minutes; presenting it a second time revokes everything it issued. - You get an access token (one hour) and a refresh token. Each refresh returns a new pair; presenting a refresh token that was already used revokes the whole chain.
Tokens are opaque. Telenow stores only their hashes, and checks them on every request — disconnecting a connection in Telenow stops it at once.
What the user grants
A token gets the scopes your client asked for and the user left ticked on the consent page — never more. A request that names none of Telenow's scopes is offered all of them, and the user chooses. agents:tools (Give agents tools) is opt-in: it starts unticked and is granted only when the user ticks it.
Example: list the tools with an API key
curl -s https://api.telenow.ai/mcp \
-H 'Authorization: Bearer vai_live_…' \
-H 'Content-Type: application/json' \
-H 'MCP-Protocol-Version: 2025-06-18' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Example: call a tool (2026-07-28)
curl -s https://api.telenow.ai/mcp \
-H 'Authorization: Bearer vai_live_…' \
-H 'Content-Type: application/json' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: tools/call' \
-H 'Mcp-Name: list_calls' \
-d '{
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {
"name": "list_calls",
"arguments": { "direction": "inbound", "limit": 5 },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'
The result's structuredContent holds the JSON described in the Tools reference; content holds the same as text. A declined call — a Do-Not-Call number, a missing value — is a normal result with isError: true and the reason, not a JSON-RPC error.
Connecting from your own tools
- Claude Code:
claude mcp add --transport http telenow https://api.telenow.ai/mcp --header "Authorization: Bearer vai_live_…", or leave out the header and sign in when prompted. - The xAI API (remote MCP tool) and other agent frameworks: give the server URL and an
Authorizationheader with an API key. - A product of your own for many users: use OAuth, so each user connects their own organization and numbers, with their own limits, and can disconnect you on their own.