# auth.md

> Agent authentication and registration for KaiCalls (https://www.kaicalls.com)

KaiCalls exposes an OAuth 2.0-protected [MCP](https://modelcontextprotocol.io)
resource. An agent (Claude, ChatGPT, or any OAuth 2.1 + PKCE client) can
register itself and obtain a scoped access token without any manual API-key
provisioning.

## Protected resource

- **Resource:** `https://www.kaicalls.com/api/mcp`
- **Protected Resource Metadata (RFC 9728):** `/.well-known/oauth-protected-resource/api/mcp`
  (the root `/.well-known/oauth-protected-resource` describes the site origin)
- **Authorization Server Metadata (RFC 8414):** `/.well-known/oauth-authorization-server`
- **Issuer:** `https://www.kaicalls.com`

A request to the resource without a valid bearer token receives `401` with a
`WWW-Authenticate: Bearer resource_metadata="https://www.kaicalls.com/.well-known/oauth-protected-resource/api/mcp"`
header, per RFC 9728 §5.1.

## Who this is for

AI agents acting for a business owner who wants KaiCalls to answer their
business calls. Two cases:

- **The owner has no KaiCalls account yet** → use the verified-email signup
  below. It creates the account only after the owner reads back a code sent to
  their inbox.
- **The owner already has an account** → use OAuth (Dynamic Client
  Registration or a Client ID Metadata Document). The owner signs in and
  approves the scopes in their browser.

## Registration

### 1. Verified-email signup (new account) — `identity_assertion` + `verified_email`

1. `POST https://www.kaicalls.com/api/v1/signup` with JSON `{ "business_name", "email" }`
   (optional: `business_type`, `city`, `state`, `website`). KaiCalls
   emails a 6-digit code to that address and returns
   `{ "status": "otp_sent", "challenge_id", "expires_in_seconds" }`.
2. Ask the owner for the code from their email. Never guess or retry codes.
3. Claim: `POST https://www.kaicalls.com/api/v1/signup` with the same fields plus
   `otp_code` and `challenge_id`. The response returns an `api_key`
   **once**, plus `business_id` and a one-time sign-in link for the owner.

What the owner gets: a browser preview of their secretary. A live phone line
needs the owner to choose a plan and pay in the dashboard; an agent cannot
skip that step. Signup is rate limited.

**Using the credential:** send `Authorization: Bearer <api_key>` to
`https://www.kaicalls.com/api/v1/*` (REST) or `https://www.kaicalls.com/api/mcp` (MCP). Store it as a
secret; it is not shown again.

**Optional: sign in with AgentID first.** If your agent has an
[AgentID](https://www.agentid.com) identity, open
`https://www.kaicalls.com/api/agentid/start` in the agent's browser. After AgentID
redirects back, the JSON response carries an `agentid_assertion` (valid 30
minutes). Send it as `agentid_assertion` in both signup requests. It
records which agent, and which owner, made the account. It does not replace
the owner's emailed code.

### 2. OAuth (existing account)

Either method gives a `client_id` for the standard OAuth 2.1
authorization-code + PKCE flow against `authorization_endpoint` /
`token_endpoint` in the Authorization Server metadata above.

1. **OAuth 2.0 Dynamic Client Registration (RFC 7591)** — `POST` your client
   metadata (at minimum `redirect_uris`) to the registration endpoint and
   receive a `client_id` back. No pre-approval, no shared secret required for
   public (PKCE) clients.
2. **Client ID Metadata Document (CIMD, draft-ietf-oauth-client-id-metadata-document)**
   — present an `https://` URL to your own client-metadata JSON document as
   the `client_id` directly in the authorization request. KaiCalls fetches and
   validates it (SSRF-guarded, 5s timeout, 5KB cap) on first use — no
   registration call needed at all.

The resulting access token is an MCP bearer credential: send it as
`Authorization: Bearer <token>` to `https://www.kaicalls.com/api/mcp`.

```json agent_auth
{
  "agent_auth": {
    "skill": "https://www.kaicalls.com/auth.md",
    "register_uri": "https://www.kaicalls.com/api/v1/signup",
    "claim_uri": "https://www.kaicalls.com/api/v1/signup",
    "identity_types_supported": [
      "identity_assertion"
    ],
    "identity_assertion": {
      "assertion_types_supported": [
        "verified_email"
      ],
      "credential_types_supported": [
        "api_key"
      ],
      "claim_uri": "https://www.kaicalls.com/api/v1/signup"
    },
    "credential_use": {
      "bearer_methods_supported": [
        "header"
      ],
      "resource": "https://www.kaicalls.com/api/v1",
      "documentation": "https://www.kaicalls.com/docs/api"
    },
    "registration_methods": [
      {
        "method": "verified_email_signup",
        "identity_type": "identity_assertion",
        "assertion_type": "verified_email",
        "register_uri": "https://www.kaicalls.com/api/v1/signup",
        "claim_uri": "https://www.kaicalls.com/api/v1/signup",
        "http_method": "POST",
        "content_type": "application/json",
        "required_fields": ["business_name", "email"],
        "claim_fields": ["otp_code", "challenge_id"],
        "credential_type": "api_key"
      },
      {
        "method": "agentid_sign_in",
        "identity_type": "identity_assertion",
        "assertion_type": "agentid",
        "provider": "https://auth.agentid.com",
        "start_uri": "https://www.kaicalls.com/api/agentid/start",
        "callback_uri": "https://www.kaicalls.com/api/agentid/callback",
        "returns": "agentid_assertion",
        "use_with": "https://www.kaicalls.com/api/v1/signup",
        "replaces_owner_email_code": false
      },
      {
        "method": "oauth_dynamic_client_registration",
        "protocol": "RFC 7591",
        "register_uri": "https://www.kaicalls.com/api/oauth/register",
        "http_method": "POST",
        "content_type": "application/json",
        "required_fields": ["redirect_uris"],
        "optional_fields": ["client_name", "token_endpoint_auth_method"],
        "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
        "authorization_server": "https://www.kaicalls.com",
        "authorization_endpoint": "https://www.kaicalls.com/api/oauth/authorize",
        "token_endpoint": "https://www.kaicalls.com/api/oauth/token",
        "response_types_supported": ["code"],
        "grant_types_supported": ["authorization_code", "refresh_token"],
        "code_challenge_methods_supported": ["S256"],
        "scopes_supported": ["agents:read","agents:write","calls:read","calls:write","numbers:read","numbers:write","webhooks:read","webhooks:write","billing:read","billing:write","leads:write","sms:read","sms:write","emails:read","emails:write","events:read","evals:read","evals:write","knowledge:read","knowledge:write","products:read","products:write","support:write","workspaces:read","workspaces:write"]
      },
      {
        "method": "client_id_metadata_document",
        "protocol": "draft-ietf-oauth-client-id-metadata-document",
        "description": "Present an https:// URL to a CIMD JSON document as client_id; no separate registration call required.",
        "authorization_endpoint": "https://www.kaicalls.com/api/oauth/authorize"
      }
    ]
  }
}
```

## Scopes

- `agents:read`
- `agents:write`
- `calls:read`
- `calls:write`
- `numbers:read`
- `numbers:write`
- `webhooks:read`
- `webhooks:write`
- `billing:read`
- `billing:write`
- `leads:write`
- `sms:read`
- `sms:write`
- `emails:read`
- `emails:write`
- `events:read`
- `evals:read`
- `evals:write`
- `knowledge:read`
- `knowledge:write`
- `products:read`
- `products:write`
- `support:write`
- `workspaces:read`
- `workspaces:write`

An unscoped request defaults to `agents:read calls:read calls:write`.

## Notes for automated scanners

- `POST /api/oauth/register` **creates a real, rate-limited OAuth client
  record** and is safe to call for registration testing (10/hour per IP, 200/hour
  global) — but treat it as a real side-effecting endpoint, not a no-op probe.
- `POST /api/v1/signup` **sends a real email to a real person**. Do not
  call it during a passive scan; read this file and the metadata instead.
- A minted OAuth token is a normal MCP bearer credential — see
  `/.well-known/oauth-protected-resource` for scopes and
  `https://www.kaicalls.com/docs/api` for the tool catalog.

## More discovery documents

- MCP server card: `/.well-known/mcp/server-card.json`
- A2A agent card: `/.well-known/agent-card.json`
- Agent Identity & Discovery (AID): `/.well-known/aid.json`
- API catalog (RFC 9727 linkset): `/.well-known/api-catalog`
- Markdown site summary: `/.well-known/markdown`
