# auth.md — JagCall agent authentication

JagCall runs AI voice and SMS agents on real phone numbers. This document tells
an automated client how to obtain and use credentials.

- Machine-readable metadata: [OAuth protected resource](https://jagcall.com/.well-known/oauth-protected-resource),
  [OAuth authorization server](https://jagcall.com/.well-known/oauth-authorization-server)
- API catalog: https://jagcall.com/.well-known/api-catalog
- Skills: https://jagcall.com/.well-known/agent-skills/index.json

## Audience

Credentials are issued to an **organization** (a JagCall workspace), never to a
bare agent identity. Every request is executed as that organization: it spends
that organization's prepaid balance and reads that organization's call records.
A human who controls the organization must therefore authorize you — there is
no self-service path that creates an organization for an agent.

Two protected resources accept the same credentials:

| Resource | URL |
| --- | --- |
| REST API v1 | https://jagcall.com/v1 |
| MCP server (Streamable HTTP) | https://jagcall.com/mcp |

## Path 1 — OAuth 2.1 with dynamic client registration (preferred)

Use this whenever a human is present to approve access. No pre-shared client
secret is required, and access is revocable.

1. **Register a client** — RFC 7591:

   ```http
   POST https://jagcall.com/api/oauth/register
   Content-Type: application/json

   { "client_name": "Your Agent",
     "redirect_uris": ["https://your.app/callback"],
     "grant_types": ["authorization_code", "refresh_token"],
     "response_types": ["code"],
     "token_endpoint_auth_method": "none",
     "scope": "orgs:read agents:read calls:read" }
   ```

   Registration is open and returns a `client_id` immediately. Public clients
   (`token_endpoint_auth_method: "none"`) are supported, as are
   `client_secret_post` and `client_secret_basic`.

2. **Send the user to consent** — authorization code + PKCE, `S256` only:

   ```
   https://jagcall.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…
     &scope=orgs%3Aread+calls%3Aread&state=…
     &code_challenge=…&code_challenge_method=S256
   ```

   The user signs in, picks the organization to grant, and approves the scopes.
   Requesting a scope does not guarantee it: read the granted `scope` off the
   token response and degrade rather than assuming.

3. **Exchange the code** at `POST https://jagcall.com/api/oauth/token` for an access token
   (prefix `jca_`) and a refresh token.

4. **Call the API** with `Authorization: Bearer jca_…`.

5. **Revoke** at `POST https://jagcall.com/api/oauth/revoke` when you are done, or when the user
   asks you to disconnect.

### Identity types

Client registration is **anonymous**: JagCall does not consume an agent identity
assertion, and authority comes entirely from the human's consent at step 2.
Nothing you assert about yourself grants access, so there is no ID-JAG or
verified-email flow to complete — do not attempt one.

## Path 2 — Organization API key (headless)

For automation with no interactive user, a human creates a long-lived key in the
dashboard at https://jagcall.com/api-keys and hands it to you out of band. Keys are
prefixed `jc_live_` and sent the same way:

```
Authorization: Bearer jc_live_…
```

Scopes are fixed when the key is minted; a key created with no scope selection
has full access. There is **no API that mints a key for you** — if you have no
key, ask your user to create one rather than probing for a registration
endpoint.

## Scopes

Format is `resource:action`. A dynamically-registered client may request:

- `orgs:read`
- `agents:read`
- `agents:write`
- `calls:read`
- `calls:write`
- `phone-numbers:read`
- `phone-numbers:write`
- `sms:read`
- `sms:write`
- `contacts:read`
- `contacts:write`
- `campaigns:read`
- `campaigns:write`
- `calendar:read`
- `calendar:write`
- `knowledge:read`
- `knowledge:write`
- `analytics:read`
- `billing:read`

Ask for the narrowest set that does the job. `403 forbidden` means the
credential lacks a scope — re-running the request will not fix it.

## Using the credential

- Send it in the `Authorization` header. Bearer is the only supported method;
  query-string credentials are not accepted.
- On a Corporate account one credential reaches several child organizations.
  Name the target with the `X-JagCall-Org` header — it is mandatory on parent
  accounts, and an id you do not own answers `404`, not `403`.
- `401` means the credential is missing, malformed, or revoked. Stop; do not
  retry with the same value.
- `402 insufficient_balance` means the organization is out of prepaid credit.
  Only a human can top it up.
- Rate limits are per organization. Respect `X-RateLimit-Reset`.

## Before you act

Placing a call, sending an SMS, and provisioning a phone number **spend real
money and contact real people.** Confirm intent with your user before the first
such request, and never repeat one to check whether it worked — read the call or
message record instead.

## Support

- MCP setup: https://jagcall.com/docs/mcp
- API reference: https://jagcall.com/docs/api-reference
- Contact a human: https://jagcall.com/contact
