Commit b225ba76 by PLN (Algolia)

docs(douanier): Shipow onboarding one-pager (HTML template → branded PDF)

A shareable getting-started for the hydra-live-hexa Studio: what the Audio
Intelligence API does, base URL + bearer auth, the full endpoint list, a curl
quickstart and the zero-dep TS/Vercel snippet, plus caching/limits/errors and
support. Branded to the Nech.PL APIs / Ship's Bridge look; A4, print-clean.

onboarding.html is the committed template (token placeholder __NECHPL_TOKEN__);
render a per-tenant PDF with chromium --headless --print-to-pdf after sed-filling
the key. The rendered PDF carries a live token, so clients/*.pdf is gitignored —
never commit it; deliver it to the tenant over a private channel.
parent 4197696d
...@@ -2,3 +2,5 @@ _cache/ ...@@ -2,3 +2,5 @@ _cache/
__pycache__/ __pycache__/
*.pyc *.pyc
.pytest_cache/ .pytest_cache/
# rendered onboarding PDFs carry a live token — never commit (template is onboarding.html)
clients/*.pdf
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nech.PL APIs — Audio Intelligence · Getting Started</title>
<style>
:root{
--ink:#0f1419; --muted:#5b6675; --line:#e3e8ef; --bg:#ffffff;
--brand:#0b7285; --brand2:#0f8aa0; --accent:#c2255c; --code:#0d1b2a;
--chip:#eef4f6; --ok:#1f7a4d;
}
@page{ size:A4; margin:14mm 15mm; }
*{ box-sizing:border-box; }
html{ -webkit-print-color-adjust:exact; print-color-adjust:exact; }
body{ font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
color:var(--ink); margin:0; font-size:10.5pt; line-height:1.5; }
code,pre,.mono{ font-family:"SFMono-Regular",ui-monospace,Menlo,Consolas,monospace; }
h1{ font-size:21pt; margin:0 0 2pt; letter-spacing:-.3px; }
h2{ font-size:12.5pt; margin:18pt 0 6pt; color:var(--brand); border-bottom:1px solid var(--line); padding-bottom:3pt; }
h3{ font-size:10.5pt; margin:10pt 0 3pt; }
p{ margin:5pt 0; } a{ color:var(--brand2); text-decoration:none; }
.sub{ color:var(--muted); font-size:10pt; margin-top:0; }
.header{ display:flex; justify-content:space-between; align-items:flex-start;
border-bottom:2px solid var(--brand); padding-bottom:8pt; }
.badge{ background:var(--brand); color:#fff; border-radius:6px; padding:5pt 9pt;
font-size:8.5pt; font-weight:600; letter-spacing:.4px; text-align:right; }
.lede{ font-size:11pt; color:var(--ink); margin:10pt 0 2pt; }
.cred{ background:linear-gradient(180deg,#f4fafb,#eef6f8); border:1px solid #cfe6ea;
border-left:4px solid var(--brand); border-radius:8px; padding:10pt 12pt; margin:10pt 0; }
.cred .k{ font-size:8.5pt; color:var(--muted); text-transform:uppercase; letter-spacing:.6px; }
.cred .tok{ font-size:11pt; color:var(--code); word-break:break-all; margin-top:2pt; font-weight:600; }
.warn{ color:var(--accent); font-size:9pt; margin-top:6pt; }
pre{ background:var(--code); color:#e7eef5; border-radius:7px; padding:9pt 11pt;
font-size:8.6pt; line-height:1.45; overflow:hidden; white-space:pre-wrap; word-break:break-word; }
pre .c{ color:#7f93a8; } pre .s{ color:#9ae6b4; } pre .k{ color:#f6c177; }
table{ border-collapse:collapse; width:100%; font-size:9pt; margin:6pt 0; }
th,td{ text-align:left; padding:4pt 7pt; border-bottom:1px solid var(--line); vertical-align:top; }
th{ color:var(--muted); font-weight:600; font-size:8.5pt; text-transform:uppercase; letter-spacing:.4px; }
td .mono{ font-size:8.4pt; color:var(--code); }
.chip{ background:var(--chip); border-radius:4px; padding:1pt 5pt; font-size:8pt; color:var(--brand); }
.two{ display:flex; gap:14pt; } .two>div{ flex:1; }
.foot{ margin-top:16pt; border-top:1px solid var(--line); padding-top:7pt;
color:var(--muted); font-size:8.5pt; display:flex; justify-content:space-between; }
ul{ margin:4pt 0 4pt 0; padding-left:16pt; } li{ margin:2pt 0; }
.ok{ color:var(--ok); font-weight:600; }
</style>
</head>
<body>
<div class="header">
<div>
<h1>Audio Intelligence API</h1>
<p class="sub">Nech.PL APIs · <span class="mono">api.nech.pl/audio/v1</span> · Getting Started</p>
</div>
<div class="badge">NECH.PL<br>APIs</div>
</div>
<p class="lede">Stem-aware audio analysis as simple HTTP calls — emotion (valence/arousal),
a ~35-dim feature stack, per-sample role, loop grading, onsets/tempo, render-ready
waveform &amp; spectrogram (FFT-as-a-service), BS.1770 loudness, and naming. Upload a
clip, get JSON. Identical audio is cached server-side, so repeats are free.</p>
<div class="cred">
<div class="k">Your API key — keep it secret</div>
<div class="tok mono">__NECHPL_TOKEN__</div>
<div class="warn">Send only over a private channel. Anyone with this key can call the API as you.
Lost/leaked? Ping ParVagues and it's revoked &amp; reissued in seconds.</div>
</div>
<h2>1 · Talk to it</h2>
<div class="two">
<div>
<h3>Base URL &amp; auth</h3>
<ul>
<li>Base: <span class="mono">https://api.nech.pl/audio/v1</span></li>
<li>Header: <span class="mono">Authorization: Bearer &lt;key&gt;</span></li>
<li>Body: <span class="mono">multipart/form-data</span>, one <span class="mono">file</span> field (audio)</li>
<li>Returns: JSON. Cache state in <span class="mono">X-Douanier-Cache: hit|miss</span></li>
</ul>
<h3>Your access</h3>
<p>Your key carries scope <span class="chip mono">api:*</span> — full access to every
Nech.PL API, present and future. Health checks, the spec, and docs are public (no key).</p>
</div>
<div>
<h3>First call (curl)</h3>
<pre>B=https://api.nech.pl/audio/v1
<span class="c"># is it up? (no key needed)</span>
curl $B/<span class="k">healthz</span>
<span class="c"># who am I?</span>
curl -H <span class="s">"Authorization: Bearer $KEY"</span> $B/<span class="k">me</span>
<span class="c"># analyse a clip</span>
curl -H <span class="s">"Authorization: Bearer $KEY"</span> \
-F file=@track.wav $B/<span class="k">analyze/emotion</span></pre>
</div>
</div>
<h2>2 · Endpoints</h2>
<table>
<tr><th>Method &amp; path</th><th>Returns</th></tr>
<tr><td><span class="mono">POST /analyze/emotion</span></td><td>valence/arousal + top emotions</td></tr>
<tr><td><span class="mono">POST /features</span></td><td>~35-dim feature stack (spectral, MFCC, chroma/key, rhythm)</td></tr>
<tr><td><span class="mono">POST /analyze/samples</span></td><td>per-sample EDA + <i>measured</i> role (percs/bass/melodic/tops/atmos)</td></tr>
<tr><td><span class="mono">POST /analyze</span></td><td>emotion + features + sample role in one call</td></tr>
<tr><td><span class="mono">POST /grade</span></td><td>mechanical loop quality: 0–1 + S/A/B/C/D tier + flags</td></tr>
<tr><td><span class="mono">POST /onsets</span></td><td>onset times + tempo + onset rate</td></tr>
<tr><td><span class="mono">POST /waveform</span></td><td><span class="mono">[min,max]</span> peaks + 0–1 RMS envelope (audio-reactive visuals)</td></tr>
<tr><td><span class="mono">POST /spectrum</span></td><td>downsampled spectrogram, bands×frames 0–1 (FFT-as-a-service)</td></tr>
<tr><td><span class="mono">POST /loudness</span></td><td>BS.1770 LUFS + sample/true peak + gain-to-target (−14/−9)</td></tr>
<tr><td><span class="mono">POST /naming</span></td><td>convention-compliant sample name from the measured role</td></tr>
<tr><td><span class="mono">POST /separate</span></td><td><span class="chip">503</span> stem separation — GPU runner pending; code against it now</td></tr>
<tr><td><span class="mono">GET&nbsp; /healthz · /openapi.json · /docs</span></td><td>public: liveness · spec · interactive docs</td></tr>
</table>
<p class="sub">Interactive docs &amp; full schema: <span class="mono">https://api.nech.pl/audio/v1/docs</span>
· <span class="mono">/openapi.json</span> — generate a client in any language from the spec.</p>
<h2>3 · TypeScript / Vercel (zero deps)</h2>
<p>Drop <span class="mono">douanier.ts</span> (ships with this kit) into your project — it uses only
global <span class="mono">fetch</span>/<span class="mono">FormData</span>, so it runs on Node 18+, Vercel Functions and the Edge.</p>
<pre><span class="k">import</span> { Douanier } <span class="k">from</span> <span class="s">"./douanier"</span>;
<span class="k">const</span> dz = <span class="k">new</span> Douanier({ token: process.env.<span class="k">NECHPL_TOKEN</span>! });
<span class="k">const</span> clip = <span class="k">await</span> fetch(trackUrl).then(r =&gt; r.blob());
<span class="k">const</span> mood = <span class="k">await</span> dz.emotion(clip); <span class="c">// { emotion: { valence, arousal, top, … } }</span>
<span class="k">const</span> spec = <span class="k">await</span> dz.spectrum(clip, { bands: 64, frames: 200 });
<span class="k">if</span> (mood._cache === <span class="s">"hit"</span>) { <span class="c">/* came free from the cache */</span> }</pre>
<div class="two">
<div>
<h2>4 · Good to know</h2>
<ul>
<li><b>Caching is your friend.</b> Same bytes → same result, served instantly. Hash-addressed; nothing to manage.</li>
<li><b>Limits.</b> Uploads up to ~40&nbsp;MB; a burst rate-limit + monthly quota apply (generous — ask if you hit them).</li>
<li><b>Errors.</b> <span class="mono">401</span> bad/missing key · <span class="mono">403</span> out of scope/quota
(<span class="mono">X-Reason</span>) · <span class="mono">413</span> too big · <span class="mono">503</span> compute pending (separation).</li>
<li><b>Privacy.</b> Audio is processed transiently and the cache is evictable — no long-term storage of your files.</li>
</ul>
</div>
<div>
<h2>5 · Support</h2>
<p>This is hand-built by <b>ParVagues</b> for the hydra-live-hexa Studio. Anything weird,
a number that looks off, an endpoint you wish existed — just shout. Fast iterations,
that's the point.</p>
<p class="ok">Welcome aboard. ⛵</p>
</div>
</div>
<div class="foot">
<span>Nech.PL APIs · Audio Intelligence v1 · api.nech.pl</span>
<span>Generated __GENERATED__</span>
</div>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment