API reference

OAuth 2.0

Build third-party apps that access JagCall on behalf of your users with the standard authorization code flow.

Overview

JagCall implements the OAuth 2.0 Authorization Code flow, the industry standard for third-party integrations. Your application requests specific permissions from a JagCall user, who approves access through a consent screen, and then exchanges the authorization code for access and refresh tokens.

OAuth is ideal when your application acts on behalf of another JagCall user. For server-to-server access to your own account, use API keys instead.

Authorization code flow

  1. 1

    Register an OAuth client

    Before you can authenticate users, register your application as an OAuth client:

    1. Open Settings → General → OAuth Applications (requires superadmin role).
    2. Click Register App.
    3. Enter your application name and redirect URI.
    4. Copy the client_id and client_secret.

    Each app has a scope allowlist; a request can only be granted scopes on that list. MCP clients such as Claude can instead self-register with dynamic client registration (POST /api/oauth/register, RFC 7591), and public clients (token_endpoint_auth_method: none) have no secret and must use PKCE. Server metadata is published at /.well-known/oauth-authorization-server.

    Store your client secret securely

    The client_secret is shown only once and cannot be retrieved later.
  2. 2

    Redirect to the authorization URL

    Direct the user to the JagCall authorization endpoint with your client ID, redirect URI, requested scopes, and a CSRF state parameter. Add a PKCE challenge (S256 only; plain is rejected) — it is required for public clients and recommended for all:

    url
    https://jagcall.com/oauth/authorize?  response_type=code  &client_id=YOUR_CLIENT_ID  &redirect_uri=https://yourapp.com/callback  &scope=agents:read calls:read calls:write  &state=random_csrf_token  &code_challenge=BASE64URL_SHA256_OF_VERIFIER  &code_challenge_method=S256

    The redirect_uri must exactly match a registered URI. The one exception is a loopback http:// URI (127.0.0.1, localhost, [::1]), where the port may vary.

  3. 3

    User approves consent

    JagCall displays a consent screen showing your application name and the requested scopes. When the user approves, JagCall redirects back to your redirect_uri with an authorization code:

    redirect
    https://yourapp.com/callback?code=jc_auth_code_xxx&state=random_csrf_token

    Verify state

    Always confirm the state parameter matches the value you sent to prevent CSRF attacks. The authorization code expires in 10 minutes.
  4. 4

    Exchange the code for tokens

    Exchange the authorization code for an access token and refresh token. Send code_verifier if you sent a code_challenge; public clients omit client_secret. The token endpoint accepts JSON, form-encoded bodies, or HTTP Basic client authentication, and is limited to 30 requests per minute.

    curl
    curl -X POST https://jagcall.com/api/oauth/token \  -H "Content-Type: application/json" \  -d '{    "grant_type": "authorization_code",    "code": "AUTH_CODE_FROM_REDIRECT",    "client_id": "YOUR_CLIENT_ID",    "client_secret": "YOUR_CLIENT_SECRET",    "redirect_uri": "https://yourapp.com/callback",    "code_verifier": "YOUR_PKCE_CODE_VERIFIER"  }'
    response
    {  "access_token": "jca_xxx...",  "token_type": "Bearer",  "expires_in": 3600,  "refresh_token": "jcr_xxx...",  "scope": "agents:read calls:read calls:write"}
  5. 5

    Use the access token

    Include the access token as a Bearer token in API requests, just like an API key. Access tokens use the jca_ prefix and expire after 1 hour. They are limited to the granted scopes, act on the organization the user chose on the consent screen, and are subject to the same rate limits as API keys.

    curl
    curl -X GET https://jagcall.com/v1/agents \  -H "Authorization: Bearer jca_xxx..."  \  -H "Content-Type: application/json"
  6. 6

    Refresh the token

    When the access token expires, use the refresh token (prefix jcr_) to obtain a new one without user interaction:

    curl
    curl -X POST https://jagcall.com/api/oauth/token \  -H "Content-Type: application/json" \  -d '{    "grant_type": "refresh_token",    "refresh_token": "jcr_xxx...",    "client_id": "YOUR_CLIENT_ID",    "client_secret": "YOUR_CLIENT_SECRET"  }'
    response
    {  "access_token": "jca_new_xxx...",  "token_type": "Bearer",  "expires_in": 3600,  "refresh_token": "jcr_new_xxx...",  "scope": "agents:read calls:read calls:write"}

    Refresh tokens are single-use

    Each refresh response includes a new refresh token that must be stored for the next refresh cycle. Refresh tokens expire after 30 days without use. Presenting a refresh token that was already used revokes the whole grant, so store the new one before discarding the old one.

    invalid_grant means reconnect

    If a refresh returns invalid_grant (the token expired, was replayed, or the grant was destroyed, for example because the authorizing user was deleted), the grant cannot be recovered. Send the user through the authorization flow again. Call GET /v1/oauth/grant (needs workspaces:read) to check a grant's health and see refreshTokenExpiresInSeconds.
  7. 7

    Revoke when the user disconnects

    Revoke an access or refresh token when the user disconnects your integration:

    curl
    curl -X POST https://jagcall.com/api/oauth/revoke \  -H "Content-Type: application/json" \  -d '{    "token": "jcr_xxx...",    "client_id": "YOUR_CLIENT_ID",    "client_secret": "YOUR_CLIENT_SECRET"  }'

Available scopes

Request only the scopes your application needs. Users see the full list of requested permissions on the consent screen. Scopes are named resource:read or resource:write; a :write scope does not imply :read, so request both if you need both. A token missing a scope gets 403 from the endpoint.

ScopeDescription
agents:readList and view agent details
agents:writeCreate, update, and delete agents
calls:readList calls, transcripts, and recordings
calls:writeInitiate outbound calls and browser calls
phone-numbers:readList and view phone numbers; search available numbers
phone-numbers:writePurchase, update, and release numbers
sms:readList SMS conversations and messages
sms:writeSend SMS messages
contacts:readList and view contacts, contact notes, and follow-up tasks
contacts:writeCreate and update contacts; create, edit, and delete notes and tasks
campaigns:readList campaigns and their contacts
campaigns:writeCreate, edit, and run outbound campaigns
calendar:readView calendar integrations and availability
calendar:writeBook appointments on connected calendars
automations:readList automations and their runs
automations:writeCreate, edit, and delete automations
knowledge:readList and search knowledge bases
knowledge:writeCreate knowledge bases and add or remove documents
analytics:readView analytics and reports
billing:readView account balance and usage
webhooks:readList webhook endpoints and deliveries
webhooks:writeCreate, update, and delete webhook endpoints; rotate secrets; replay deliveries
integrations:writeSet integration credentials for the organization
workspaces:readList workspaces (child organizations) on a Corporate account
workspaces:writeCreate workspaces
workspaces:deleteDelete workspaces. Never granted by default; it must be added to an app on purpose

Access can be paused

JagCall can pause API access for a single OAuth app, an organization, or the whole platform. Paused credentials stop working immediately, including tokens already issued: requests return 403 (oauth_client_paused, organization_api_paused) or 503 (api_paused, with Retry-After), and the message includes the reason when one was given.

Token format reference

Token typePrefixExpiration
API keyjc_live_No expiry by default
OAuth access tokenjca_1 hour
OAuth refresh tokenjcr_30 days (renewed on each refresh)

Still need help?

Can't find what you're looking for? Send us a message and our team will get back to you.

Contact support