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

# Hosts and authentication

> api.kupe.in vs x.kupe.in, x-api-key vs Bearer sk-kupe-, and GET /v1/me.

## Hosts

|                 | Kupe App              | Kupe Hub                     |
| --------------- | --------------------- | ---------------------------- |
| REST            | `https://api.kupe.in` | `https://x.kupe.in`          |
| Console         | `https://app.kupe.in` | `https://hub.kupe.in`        |
| Docs playground | —                     | Try it → `https://x.kupe.in` |

Do **not** put `/v1` in `KUPE_BASE_URL`. Paths on this site are `{base}/v1/...`.

## Headers (diff)

```diff theme={"theme":{"light":"github-light","dark":"github-dark"}}
- GET https://api.kupe.in/api/v1/me
- x-api-key: YOUR_APP_KEY
+ GET https://x.kupe.in/v1/me
+ Authorization: Bearer sk-kupe-YOUR_KEY
```

## Auth header

Kupe App accepted either `x-api-key: <opaque>` or a dashboard JWT.

Kupe Hub accepts one header on every `/v1` route:

```
Authorization: Bearer sk-kupe-YOUR_KEY
```

Project keys start with `sk-kupe-` and are scoped to **one org and one project**. Hub sessions may send a Supabase user JWT instead (`auth: jwt` on `GET /v1/me`). Voice **clone / patch / delete** require JWT. See [Authentication](/auth).

Kupe App keys are **not** valid on `x.kupe.in`. Create a new key in Hub: **project → API keys**.

<CodeGroup>
  ```bash Kupe App theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl https://api.kupe.in/api/v1/agents \
    -H "x-api-key: YOUR_APP_KEY"
  ```

  ```bash Kupe Hub theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl https://x.kupe.in/v1/me \
    -H "Authorization: Bearer sk-kupe-YOUR_KEY"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from kupe import Kupe

  client = Kupe()  # KUPE_API_KEY
  me = client.me()
  # {"auth": "api_key", "org_id": "org_...", "project_id": "prj_...", ...}
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Kupe } from "kupe-sdk";

  const me = await new Kupe().me();
  ```
</CodeGroup>

## Identity model

| Kupe App                                                              | Kupe Hub                                                                                   |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Almost everything is **user-scoped** (`request.state.user.id`)        | API keys are **org + project**. Nested routes need `org_id` and often `project_id`.        |
| `GET /api/v1/me` returns the Kupe App profile / consent / org context | `GET /v1/me` returns `auth`, `org_id`, `project_id`, `user_id`, `api_key_id`               |
| Org admin can act on other users via query `user_id`                  | Members are `GET/POST /v1/orgs/{org_id}/members`. Keys cannot impersonate another project. |
| `POST /api/v1/api-keys` creates a user key                            | `POST /v1/projects/{project_id}/api-keys`                                                  |

Resolve IDs once, then reuse them:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://x.kupe.in/v1/me \
  -H "Authorization: Bearer sk-kupe-YOUR_KEY"
```

## CORS and `create_call`

Kupe App `POST /api/call/create_call` is on the auth **exclude** list (middleware skips JWT/`x-api-key`), then the handler resolves identity itself (`x-api-key` or Bearer) **and** rejects unknown `Origin` via `validate_origin`. Browser apps had to be on the CORS allow-list (`api.kupe.in` plus verified custom domains).

Kupe outbound is a normal authenticated `/v1` route. Send the Bearer key; there is no Origin gate on the public API the way Kupe App implemented `create_call`.

## Public vs provider webhooks

Kupe App left telephony answer URLs unauthenticated on purpose (`/api/call/incoming_call`, `/api/call/plivo-incoming_call`, `/api/call/plivo-hangup`, Exotel/Elison variants). Kupe does the same for **provider** callbacks (`/v1/telephony/plivo/inbound`, `/v1/plivo/webhooks/ucc`, …). Those paths are **not** in the public OpenAPI and you should not call them from your app — Plivo/Twilio/Exotel call them.

Your app should only call documented `/v1/...` routes with a Bearer key.
