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

# Authentication

> API key and OAuth 2.1 authentication methods

Borough supports two authentication methods: **API keys** (simplest) and **OAuth 2.1** (for MCP clients and third-party integrations).

## API Key (Bearer Token)

Include your API key in the `Authorization` header:

```
Authorization: Bearer BOROUGH-<your-license-key>
```

Keys use the format `BOROUGH-<uuid>` and are provisioned automatically when you [subscribe to a plan](/borough/tiers-and-pricing).

```
BOROUGH-a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

Keys are validated against Polar.sh on every request — there is no key cache — so revocation takes effect immediately: the next request made with a revoked or disabled key returns `401`. The one delayed path is OAuth 2.1 access tokens (below), which are issued after validation and stay usable until they expire, at most one hour later.

## OAuth 2.1 (PKCE)

Borough implements OAuth 2.1 with PKCE for secure delegated access. This is the recommended method for MCP clients and third-party apps.

### Endpoints

| Endpoint                                  | Method | Description                                                                             |
| ----------------------------------------- | ------ | --------------------------------------------------------------------------------------- |
| `/oauth/register`                         | POST   | Dynamic client registration ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)) |
| `/oauth/authorize`                        | GET    | Consent page (redirects user)                                                           |
| `/oauth/authorize`                        | POST   | Submit API key, receive auth code                                                       |
| `/oauth/token`                            | POST   | Exchange code for tokens                                                                |
| `/.well-known/oauth-authorization-server` | GET    | Server metadata ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414))             |
| `/.well-known/oauth-protected-resource`   | GET    | Resource metadata ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728))           |

### Flow

1. **Register your client** (optional for localhost apps):
   ```bash theme={null}
   curl -X POST https://borough.qwady.app/oauth/register \
     -H "Content-Type: application/json" \
     -d '{"client_name":"My App","redirect_uris":["https://myapp.com/callback"]}'
   ```
   Unregistered clients may only use `localhost` redirect URIs.

2. **Redirect user to authorize:**
   ```
   GET /oauth/authorize?response_type=code
     &client_id=<client_id>
     &redirect_uri=<redirect_uri>
     &code_challenge=<S256_challenge>
     &code_challenge_method=S256
     &state=<random_state>
   ```
   The user enters their Borough API key (or continues with Free tier) and is redirected back with an authorization code.

3. **Exchange code for tokens:**
   ```bash theme={null}
   curl -X POST https://borough.qwady.app/oauth/token \
     -d "grant_type=authorization_code&code=<code>&client_id=<client_id>&code_verifier=<verifier>&redirect_uri=<redirect_uri>"
   ```

4. **Use the access token:**
   ```
   Authorization: Bearer <access_token>
   ```

### Token lifetimes

| Token               | Lifetime  |
| ------------------- | --------- |
| Access token        | 1 hour    |
| Refresh token       | 30 days   |
| Auth code           | 5 minutes |
| Client registration | 30 days   |

Use the refresh token to get new access tokens without re-authorization:

```bash theme={null}
curl -X POST https://borough.qwady.app/oauth/token \
  -d "grant_type=refresh_token&refresh_token=<token>&client_id=<client_id>"
```

The refresh flow re-validates your API key, so tier changes and revocations take effect on the next refresh.

## Free-tier access

Search endpoints (`/v1/search/rentals`, `/v1/search/sales`), areas (`/v1/areas`), market endpoints, and photo delivery (`/v1/photos/{key}`) work without authentication and use Borough's Free tier limits. The public `/v1/health` endpoint is also available without authentication, but it is outside Borough's metered quota and rate-limit middleware. Unauthenticated metered requests are subject to:

* 10 requests/minute rate limit (IP-based)
* 100 requests/month quota
* Maximum 10 results per page

Public Borough pages live on `borough.qwady.app`, while service integrations continue to use `/v1/*`, `/mcp`, `/.well-known/*`, `/oauth/*`, and `/v1/webhooks/*`. Public docs live separately at `qwady.wiki/borough`.

## Error responses

| Code              | Status | Description                                                |
| ----------------- | ------ | ---------------------------------------------------------- |
| `MISSING_API_KEY` | 401    | No `Authorization` header provided on a protected endpoint |
| `INVALID_API_KEY` | 401    | Key doesn't match any active subscription                  |
| `EXPIRED_API_KEY` | 401    | Subscription has ended                                     |
| `TIER_RESTRICTED` | 403    | Endpoint requires a higher tier                            |

On 401 responses, the `WWW-Authenticate` header includes a link to the protected-resource metadata for automatic OAuth discovery.

## Security best practices

* Store keys in environment variables, never in client-side code
* Rotate keys by creating a new subscription if compromised
* Use the minimum tier needed for your use case
* For OAuth, always use PKCE with S256 code challenges
* Register your client to restrict allowed redirect URIs
