> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwairy.co/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth Authentication

> How the Qwairy MCP server authenticates connections using OAuth 2.1 with PKCE.

The Qwairy MCP server uses **OAuth 2.1 with PKCE** for secure authentication. Your credentials are never shared with AI clients — only a secure, scoped access token.

## How It Works

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Client as MCP Client
    participant Auth as Qwairy Auth
    participant MCP as Qwairy MCP

    User->>Client: Start conversation
    Client->>Auth: Authorization request (with PKCE)
    Auth->>User: Login prompt
    User->>Auth: Sign in with Qwairy account
    Auth->>Client: Authorization code
    Client->>Auth: Exchange code for token
    Auth->>Client: User-scoped access token
    Client->>MCP: API request with access token
    MCP->>Client: Data response
```

<Note>
  Most MCP clients handle this flow automatically. You just click "Authorize" when prompted — there's no team to pick and no manual token management. The connection is **user-scoped**: it can read every MCP-eligible workspace you belong to.
</Note>

## OAuth Endpoints

| Endpoint      | URL                                                            |
| ------------- | -------------------------------------------------------------- |
| Discovery     | `https://mcp.qwairy.co/.well-known/oauth-authorization-server` |
| Authorization | `https://auth.qwairy.co/authorize`                             |
| Token         | `https://auth.qwairy.co/token`                                 |
| Revocation    | `https://auth.qwairy.co/revoke`                                |

<Tip>
  Modern MCP clients automatically discover these endpoints from the server URL. You only need to provide `https://mcp.qwairy.co` — the client fetches the OAuth configuration via the discovery endpoint.
</Tip>

## Available Scopes

Request only the scopes you need:

| Scope               | Description                                                                                                                       |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `read:brands`       | List monitored brands                                                                                                             |
| `read:visibility`   | Access performance metrics                                                                                                        |
| `read:competitors`  | View competitor data                                                                                                              |
| `read:sources`      | Access source/citation data                                                                                                       |
| `read:prompts`      | List monitored prompts                                                                                                            |
| `read:answers`      | Read AI responses                                                                                                                 |
| `read:topics`       | View topic analytics                                                                                                              |
| `read:pitch-audits` | List and read Pitch Audit reports from accessible agency workspaces                                                               |
| `read:measure`      | Off-AI web analytics from connected integrations (Google Search Console, Bing Webmaster Tools, Google Analytics, AI crawler logs) |

<Note>
  If no scopes are specified, all read scopes are granted by default. A request that specifies only unrecognized scopes is rejected with `invalid_scope`.
</Note>

<Note>
  `read:measure` and `read:pitch-audits` are newer scopes. Existing OAuth connections must reconnect and re-authorize to gain access to their tools. Scopes are never expanded silently on an already-issued token.
</Note>

## Token Lifecycle

The OAuth flow issues a single **user-scoped access token** — the same kind of token as a Personal Access Token (`qw-usr-`). It is long-lived and has no refresh token: there is nothing to rotate, and clients simply reuse the bearer until it is revoked.

| Token                    | Duration        | Notes                                                                  |
| ------------------------ | --------------- | ---------------------------------------------------------------------- |
| Access Token (`qw-usr-`) | No fixed expiry | Revoke any time from **Settings → MCP**; re-authorize to get a new one |
| Authorization Code       | 5 minutes       | One-time use, PKCE-protected                                           |

## Personal Access Tokens

For headless clients that cannot run an interactive OAuth flow — automation platforms (n8n, Make), scripts, or custom agents — Qwairy supports **user-scoped Personal Access Tokens (PATs)**. Create and revoke them from your Qwairy account under **Settings → MCP**.

* A PAT is prefixed `qw-usr-` and is tied to **your user account**, not a single team.
* It can read every team you belong to that is on an MCP-eligible plan (Starter and above), so one token covers all your workspaces.
* Pass it as a bearer token: `Authorization: Bearer qw-usr-...`.
* Like the OAuth access token, a PAT has no fixed expiry. Revoke it from **Settings → MCP** when it is no longer needed.

<Note>
  PATs are read-only, like all Qwairy MCP access — they never expose write operations.
</Note>

### Choosing a team

Brand-scoped tools take a `brandId`, and Qwairy resolves the owning team automatically. Call `list_brands` first, then pass the `brandId` you want. `list_pitch_audits` is the other cross-workspace discovery tool: it lists only agency workspaces the token may access and accepts an optional `teamId` filter. Detail calls still resolve one audit from one authorized workspace, so data is never mixed across teams.

## PKCE Support

The server requires PKCE with the **S256** method only:

* **S256** — SHA-256 hash of the code verifier

The `plain` method is not supported. Most MCP clients use S256 by default, so no extra configuration is needed.

## Security Features

<AccordionGroup>
  <Accordion title="No Password Sharing">
    Your Qwairy password is never shared with any AI client. Authentication happens directly with Qwairy's auth server.
  </Accordion>

  <Accordion title="Scoped Access">
    Tokens are scoped to specific data types. An MCP client can only access what you've authorized.
  </Accordion>

  <Accordion title="Team Isolation">
    Both OAuth connections and Personal Access Tokens are user-scoped (`qw-usr-`): they span every MCP-eligible team you belong to. Each tool call still resolves to exactly one team from the requested `brandId` — Qwairy never mixes data across teams on a single call, so workspaces stay fully isolated even though one token reaches several.
  </Accordion>

  <Accordion title="Short-Lived Authorization Code">
    The authorization code is single-use and expires after 5 minutes, so the window to exchange it for a token is tight. The issued access token itself is revocable at any time (see below).
  </Accordion>

  <Accordion title="Revocation">
    You can revoke access at any time from your Qwairy account settings.
  </Accordion>
</AccordionGroup>

## Manual Token Exchange

For developers building custom MCP clients, here's the token exchange flow:

### 1. Start Authorization

```bash theme={null}
GET https://auth.qwairy.co/authorize
  ?response_type=code
  &client_id=mcp-client
  &redirect_uri=YOUR_CALLBACK_URL
  &scope=read:brands read:visibility
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
```

### 2. Exchange Code for Tokens

```bash theme={null}
POST https://auth.qwairy.co/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_FROM_CALLBACK
&redirect_uri=YOUR_CALLBACK_URL
&code_verifier=ORIGINAL_CODE_VERIFIER
```

**Response:**

```json theme={null}
{
  "access_token": "qw-usr-abc123...",
  "token_type": "Bearer",
  "scope": "read:brands read:visibility"
}
```

The access token is a long-lived, user-scoped token (`qw-usr-`). There is no `refresh_token` or `expires_in` — reuse the bearer until you revoke it.

### 3. Reuse or Revoke

The token does not expire and has no refresh step — reuse the bearer for subsequent requests. To rotate it, revoke the old one and run the authorization flow again:

```bash theme={null}
POST https://auth.qwairy.co/revoke
Content-Type: application/x-www-form-urlencoded

token=qw-usr-abc123...
```

## Troubleshooting

### "Invalid or expired token"

The token was revoked — user-scoped tokens do not expire on their own. Disconnect and reconnect to authorize a new one.

### "Insufficient scope"

The tool you're trying to use requires a scope that wasn't granted. Reconnect and authorize all requested scopes.

### "No eligible team"

Your account isn't a member of any team on an MCP-eligible plan (Starter and above), or the subscription has lapsed. Authorization is denied until at least one workspace is eligible.
