# Calling the Nech.PL APIs — `audio`

Base URL: **`https://api.nech.pl/audio/v1`** · Auth: **`Authorization: Bearer <token>`**
(your platform token, provisioned for your tenant). Interactive docs +
schema: `https://api.nech.pl/audio/v1/docs` · `…/openapi.json` (snapshot:
[`openapi.json`](./openapi.json)).

Every analysis is a `multipart` POST with one `file` field (an audio clip) and
returns JSON. Identical audio is **cached** — the `X-Nech-Cache: hit|miss`
header (and `result._cache` in the TS client) tells you which.

## Official TypeScript client — `@nech/api` (generated)

The official client is **generated** from this spec, so it can never drift from
the API. One package, per-domain subpath imports (you only bundle what you use).
Source + build: [`nech-api/`](./nech-api/).

```bash
echo '@nech:registry=https://npm.nech.pl' >> .npmrc   # the @nech scope is private
npm i @nech/api
```

```ts
import { AudioApi, Configuration } from "@nech/api/audio";

const audio = new AudioApi(new Configuration({
  basePath: "https://api.nech.pl/audio/v1",
  accessToken: process.env.NECH_TOKEN,
}));

const clip = await fetch(trackUrl).then(r => r.blob());      // Blob/File
const mood = await audio.analyzeEmotion({ file: clip });     // method = operationId
const loud = await audio.loudness({ file: clip });
```

### Vercel Function example

```ts
// app/api/mood/route.ts  (Next.js App Router, Node runtime)
import { AudioApi, Configuration } from "@nech/api/audio";

export async function POST(req: Request) {
  const clip = await req.blob();
  const audio = new AudioApi(new Configuration({
    basePath: "https://api.nech.pl/audio/v1",
    accessToken: process.env.NECH_TOKEN,
  }));
  const res = await audio.analyzeEmotion({ file: clip });
  return Response.json(res);
}
```

> **Interim:** until Verdaccio (`npm.nech.pl`) is deployed (see
> [`../VERDACCIO.md`](../VERDACCIO.md)), the zero-dep drop-in
> [`nech.ts`](./nech.ts) — `new NechAPI({token}).audio.emotion(clip)` — is the
> working client. It's superseded by `@nech/api` once the registry is live.

## Scopes

Access is by the platform's hierarchical scope convention. The scope a route
needs is **derived from its path**: `/audio/v1/analyze/emotion` requires
`api:audio:analyze:emotion`, `/audio/v1/grade` requires `api:audio:grade`, etc.
A grant of **`api:audio:*`** covers the whole audio API, and **`api:*`** covers
every API on the platform — so most tokens just carry one of those. (Full
convention: the platform's `RECIPE.md` / `scopes.py`.)

## Endpoints (required scope in parens)

| Method | Path | Returns | required scope |
|---|---|---|---|
| GET | `/healthz` | status + engine availability | — (public) |
| POST | `/analyze/emotion` | valence/arousal + top emotions | `api:audio:analyze:emotion` |
| POST | `/features` | ~35-dim feature stack | `api:audio:features` |
| POST | `/analyze/samples` | per-sample EDA + measured role | `api:audio:analyze:samples` |
| POST | `/grade` | mechanical loop quality (tier + flags) | `api:audio:grade` |
| POST | `/onsets` | onset times + tempo | `api:audio:onsets` |
| POST | `/waveform` | `[min,max]` peaks + RMS envelope | `api:audio:waveform` |
| POST | `/spectrum` | downsampled spectrogram (FFT-as-a-service) | `api:audio:spectrum` |
| POST | `/loudness` | BS.1770 LUFS + peak + gain-to-target | `api:audio:loudness` |
| POST | `/naming` | convention-compliant sample name | `api:audio:naming` |
| POST | `/analyze` | emotion + features + sample in one call | `api:audio:analyze` |
| POST | `/separate` | **503** until a GPU runner | `api:audio:separate` |

(All covered by `api:audio:*` or `api:*`.)

## curl

```bash
curl -s -H "Authorization: Bearer $NECH_TOKEN" \
  -F file=@clip.wav https://api.nech.pl/audio/v1/loudness | jq
```

## Regenerating the spec + client

`openapi.json` is a checked-in snapshot of the live app; `@nech/api` is generated
from it. Both are reproducible:

```bash
# 1. refresh the snapshot from the app (adds the public server block)
~/.virtualenvs/fourier/bin/python clients/refresh_openapi.py
# 2. regenerate + bundle the TS client
cd clients/nech-api && ./codegen.sh
```

`test_openapi_snapshot.py` guards the snapshot against route drift. For an
ad-hoc client in another language, point any generator at `openapi.json`:

```bash
npx @openapitools/openapi-generator-cli generate \
  -i armada/api/clients/openapi.json -g python -o ./generated   # e.g. Python (nech_api, later)
```
