# Authentication
URL: https://musicapi.com/docs/get-started/authentication.md

> The credentials MusicAPI accepts, which route takes which, the Client-Secret-only routes, DevTokens with and without sub, one-time tokens, and how to keep the secret on your server.

Product: MusicAPI Core and MusicAPI Managed Transfers. Section: Get started.

Every API call carries one credential in the `Authorization` header. Which credentials a route accepts depends on its prefix.

## The credentials

| Credential            | Header                                               | Where it may live                           |
| --------------------- | ---------------------------------------------------- | ------------------------------------------- |
| Client Secret (Basic) | `Authorization: Basic base64(clientId:clientSecret)` | Your backend only                           |
| Client ID token       | `Authorization: Token <clientId>`                    | Anywhere. It is public.                     |
| One-time token        | `Authorization: Token <oneTimeToken>`                | Your app, for 30 seconds after a sign-in    |
| DevToken              | `Authorization: DevToken <ES256 JWT>`                | Your frontend. Your backend signs it.       |
| Docs session          | `Authorization: DocsSession <token>`                 | This docs site. You never send it yourself. |

Make API keys under **API & webhooks** in the dashboard. Each key is a Client ID and a Client Secret. The values show only once, when you create the key. Make one key per backend, so you can revoke one without touching the others.

## Which route takes which

| Route                                               | Basic                | Client ID token | One-time token | DevToken           | Docs session |
| --------------------------------------------------- | -------------------- | --------------- | -------------- | ------------------ | ------------ |
| `/public/*` (catalog, connected users)              | Yes                  | Yes             | Yes            | Yes                | Yes          |
| `/api/{integrationUserUUID}/*` (the user's library) | Yes                  | **No**          | **No**         | Yes                | Yes          |
| `DELETE /public/integrations/user/{uuid}`           | Yes                  | No              | No             | No                 | No           |
| `/public/transfer-sessions/*`                       | Yes                  | No              | No             | Only without `sub` | No           |
| `GET /public/transfers/*` (Match Results)           | Yes                  | No              | No             | No                 | Yes          |
| `/app/*` (hosted sign-in state)                     | No credential needed |                 |                |                    |              |

A call without any credential gets `401`. A credential that a route refuses gets `403`.

> **Token works on /public only:** `Authorization: Token <clientId>` is for the catalog and the connected-user reads under `/public`. A call to `/api/{integrationUserUUID}/…` with a Client ID token gets `401`. Use Basic from your backend, or a DevToken from your frontend.

## Client Secret (Basic)

The Client ID and the Client Secret together give full access to your account. Send them only from your backend.

```bash
curl "https://api.musicapi.com/public/search/introspection" \
  -u "$MUSICAPI_CLIENT_ID:$MUSICAPI_CLIENT_SECRET"
```

```js
const auth = Buffer.from(`${process.env.MUSICAPI_CLIENT_ID}:${process.env.MUSICAPI_CLIENT_SECRET}`).toString("base64");
await fetch("https://api.musicapi.com/public/search/introspection", {
  headers: { Authorization: `Basic ${auth}` },
});
```

```python
import os, requests

requests.get(
    "https://api.musicapi.com/public/search/introspection",
    auth=(os.environ["MUSICAPI_CLIENT_ID"], os.environ["MUSICAPI_CLIENT_SECRET"]),
)
```

Only Basic sees the raw music-service tokens (`authData`) and only Basic can delete a connected user.

## Client ID token

The Client ID is public. Use it from a browser or an app for the catalog endpoints.

```bash
curl -X POST "https://api.musicapi.com/public/search" \
  -H "Authorization: Token $MUSICAPI_CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"type": "track", "track": "Numb", "artist": "Linkin Park", "sources": ["spotify", "appleMusic"]}'
```

## DevToken

A DevToken is a short-lived JWT that your backend signs with your own key. Your frontend then calls `/api/{integrationUserUUID}/…` directly, and your Client Secret stays on the server.

1. **Make a key pair**

   Make a P-256 key pair with OpenSSL (the commands are below). Keep `private.ec.key` on your backend.
2. **Upload the public key**

   Open **Settings** in the dashboard and add `public.pem` under **Dev Token JWT Public EC Keys**. Save, and note the **Key ID** of the key.
3. **Sign tokens on your backend**

   Algorithm `ES256`, the Key ID in `kid`, your Client ID in `iss`, and a short `exp`.

```bash
openssl ecparam -name prime256v1 -genkey -noout -out private.ec.key
openssl ec -in private.ec.key -pubout -out public.pem
```

```js
import jwt from "jsonwebtoken";

export function signDevToken({ privateKey, keyId, integrationUserUUID }) {
  const now = Math.floor(Date.now() / 1000);
  return jwt.sign(
    {
      iss: process.env.MUSICAPI_CLIENT_ID,
      sub: integrationUserUUID, // optional: limits the token to this one user
      iat: now,
      exp: now + 60 * 60,
    },
    privateKey,
    { algorithm: "ES256", keyid: keyId }
  );
}
```

```bash
curl "https://api.musicapi.com/api/3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11/playlists" \
  -H "Authorization: DevToken $DEV_TOKEN"
```

### With and without `sub`

- **With `sub`**: the token works only for the connected user in `sub`. A call for any other `integrationUserUUID` gets `403`. Give the browser of each user a token with their own `sub`.
- **Without `sub`**: the token works for every connected user of your account. Keep such a token on your servers, for example for `POST /public/transfer-sessions`, which refuses a token with `sub`.

A DevToken never deletes a connected user and never reads raw service tokens.

## One-time tokens

Add `requestOneTimeToken=true` to the [sign-in URL](https://musicapi.com/docs/connect-users/start-sign-in.md). The callback then carries a `oneTimeToken`. It lives for **30 seconds** and is bound to that one sign-in.

Use it as `Authorization: Token <oneTimeToken>` to read the connected user once, with the raw service tokens. This lets an app without a backend get the tokens. See [Raw tokens and refresh](https://musicapi.com/docs/connect-users/raw-tokens-and-refresh.md).

## Keep the secret on your server

- Never put the Client Secret in a browser bundle, a mobile app, a URL, a log or a public repository.
- Read it from your secret store at run time: `MUSICAPI_CLIENT_SECRET`.
- Frontend code uses a DevToken with `sub`, or the Client ID token for the catalog.
- Lost a secret, or leaked one? Create a new key under **API & webhooks**, deploy it, then revoke the old key. Both halves of a revoked key stop working at once.

## Tenant isolation

Every lookup by `integrationUserUUID`, sign-in id or transfer id stays inside your account. A uuid of another account answers `404`, the same answer as a uuid that does not exist.
