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. GET and DELETE answer 405 with Allow: POST.
  • One message per request. JSON-RPC batches are refused with 400. Notifications are accepted with 202.
  • Methods: initialize and ping (2025 revisions), server/discover (2026-07-28), tools/list, tools/call.

Protocol revisions

RevisionHow a request says it
2026-07-28params._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-26Open 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).

DocumentURL
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:

FieldValue
issuerhttps://api.telenow.ai
authorization_endpointTelenow's sign-in and consent page (/connect/authorize)
token_endpointhttps://api.telenow.ai/oauth2/token
registration_endpointhttps://api.telenow.ai/oauth2/register
revocation_endpointhttps://api.telenow.ai/oauth2/revoke
grant_types_supportedauthorization_code, refresh_token
code_challenge_methods_supportedS256 (PKCE is required)
token_endpoint_auth_methods_supportednone, client_secret_basic, client_secret_post, private_key_jwt
scopes_supportedcalls:read, calls:write, agents:write, agents:tools, campaigns:write, and offline_access (accepted; every connection gets a refresh token anyway)
client_id_metadata_document_supportedtrue
authorization_response_iss_parameter_supportedtrue

Identifying your client

Either:

  • A Client ID Metadata Document — use an https URL you control as the client_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 be https, or http on localhost/127.0.0.1 for native apps (any port is accepted at sign-in). Without a token_endpoint_auth_method you 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

  1. Send the user to the authorization_endpoint with response_type=code, client_id, redirect_uri, scope, state, code_challenge with code_challenge_method=S256 (required, and must be sent) and resource=https://api.telenow.ai/mcp (RFC 8707). The user signs in, picks the permissions, organization, numbers and limits, and approves.
  2. Telenow redirects back with code, your state, and iss (RFC 9207) — check that iss is https://api.telenow.ai.
  3. Exchange the code at /oauth2/token with your code_verifier and the same redirect_uri and resource. A code works once, for ten minutes; presenting it a second time revokes everything it issued.
  4. 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 Authorization header 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.