Skip to main content
VoiceInfra runs an OAuth 2.1 authorization server. If you are building an application that acts on behalf of VoiceInfra customers — reading their agents, placing calls on their behalf, syncing their call data — this is how your app gets access. An OAuth client is not the same as an API key: If you only need to call the API from your own systems, an API key is simpler and is probably what you want.

Two ways to get a client

Self-registration is deliberately open — that is what lets Claude and ChatGPT connect without you creating credentials first — so its clients are treated as unverified. A trusted client is one VoiceInfra has created for a named app.

Requesting a trusted client

A trusted client is created for you by VoiceInfra, so that a server-side app gets a secret it can hold safely. Contact hello@voiceinfra.ai with:
  • Your app’s name
  • The redirect URIs the app will use — at least one, up to five, and all https apart from localhost during development
  • The permissions you need, and why
  • Any logo, policy, and terms URLs you want shown on the consent screen, so customers can see who they are approving
We create the client and return a client_id and client_secret.
The client_secret is shown once. Only a hash is stored, so we cannot recover it — if it is lost, the client has to be registered again. Keep it server-side; never ship it in a mobile app or browser JavaScript.

The authorization flow

Your app sends the customer to VoiceInfra, they approve, and you get back an authorization code to exchange for a token.

1. Start the authorization request

Send the user to the authorization endpoint with your client ID, redirect URI, requested scopes, a state, and a PKCE challenge:
string
required
Must be S256. PKCE is required — the plain method was removed in OAuth 2.1.
string
The resource you want a token for. Clients must name it (RFC 8707), which prevents a token issued for one resource being replayed against another.

2. The user decides

VoiceInfra shows your app’s name, links, and the permissions you asked for — grouped into readable areas, with anything that spends credit called out separately. The customer picks which workspace the app may act in, and can allow change access to some areas and not others. Your app’s ability to change anything is still bounded by the customer’s role in that workspace.

3. Receive the decision

The customer returns to your redirect URI with either a code or an error (access_denied if they refused). The response carries an iss parameter (RFC 9207) — verify it matches the issuer to protect against mix-up attacks.

4. Exchange the code for a token

The response contains an access_token, a refresh_token, expires_in, and the granted scope.
Compare the granted scope to what you asked for. A customer can approve some areas and not others, so a request for five permissions may come back with two. Handle the narrower grant rather than assuming full access.

5. Refresh

Access tokens expire. Exchange the refresh token at the same endpoint with grant_type=refresh_token. Refresh tokens rotate — store the new one each time.

Revoking

Revoking invalidates the token. Customers can also disconnect your app themselves from Connected apps, which revokes the whole grant immediately — see Connected Apps.
A customer disconnecting your app takes effect straight away, including on tokens that have not yet expired. Your app must handle a 401 by sending the user through the authorization flow again, not by retrying.

Discovery

The server publishes its configuration, so clients do not need it hard-coded:

Building an MCP client instead?

If your app speaks MCP, you do not need to implement any of the above. Point it at the customer’s MCP address, choose OAuth, and the registration and approval flow happens for you — see MCP Server.