How the Frontend is Wired
The Ooki Music frontend is 100% static HTML/CSS/JS. All data currently comes from js/ooki-data.js — a mock data service layer. Your job is to replace each mock function with real fetch() calls to your REST API. No framework changes needed.
The frontend uses no framework (no React/Vue/Angular). It's vanilla HTML + CSS + JS. Any backend stack works — Node.js, Python, Go, PHP, etc.
Copy This Exact Structure to Your Server
All HTML files use relative paths. Preserve this structure exactly on your web server or CDN. Do not move files between folders without updating all relative path references.
ooki/
├── index.html ← landing page
├── artists.html ← browse all artists
├── search.html ← global search results
├── store.html ← digital store
├── sonic-capsules.html ← Sonic Capsules
├── artist-covers.html ← covers gallery
├── portals.html ← sign-in hub
├── investor.html ← investor pitch
├── artist.html ← public artist profile
├── listener.html ← public listener profile
├── developer.html ← developer overview
├── handoff.html ← this handoff package
├── dev-handoff.html ← API spec & integration guide
├── admin/
│ ├── index.html ← admin hub
│ ├── overview.html
│ ├── users.html
│ ├── permissions.html
│ ├── platform-analytics.html
│ ├── songwriters.html
│ └── merchant.html ← store/merchant management
├── app/
│ ├── artist-dashboard.html
│ ├── listener-dashboard.html
│ └── app-analytics.html
├── artist/
│ ├── kira.html ← live example
│ └── template.html ← dynamic template (?artist=slug)
├── auth/
│ ├── login.html
│ ├── signup.html
│ ├── reset.html
│ ├── admin-login.html ← admin only
│ └── 2fa.html ← TOTP verification
├── css/
│ └── ooki-system.css ← entire design system
├── developers/
│ ├── index.html
│ ├── api-keys.html
│ ├── sandbox.html
│ └── webhooks.html
├── guides/
│ ├── index.html
│ ├── artist-guide.html
│ ├── listener-guide.html
│ ├── developer.html ← this page
│ └── user-guide.html
├── images/
│ ├── ooki-logo-wordmark.png
│ └── ooki-vinyl-icon.png
└── js/
├── ooki-shell.js ← global UI controller (DO NOT rename)
└── ooki-data.js ← mock data → REPLACE with API callsSwapping ooki-data.js for Real API Calls
Every function in js/ooki-data.js is marked with a // TODO: replace with API call comment. Keep the same function names and return shapes — the HTML pages call these functions directly.
// MOCK — delete this entire block and replace const OokiService = { getTracks: async (filter = {}) => { // TODO: replace with API call await delay(200); return { data: MOCK_TRACKS, total: MOCK_TRACKS.length }; }, getArtists: async (filter = {}) => { // TODO: replace with API call await delay(200); return { data: MOCK_ARTISTS, total: MOCK_ARTISTS.length }; } };
const API_BASE = 'https://api.ooki.music/v1'; async function apiFetch(path, options = {}) { const token = localStorage.getItem('ooki-token'); const res = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json', ...(token ? { 'Authorization': `Bearer ${token}` } : {}) }, ...options }); if (!res.ok) throw new Error(await res.text()); return res.json(); } const OokiService = { getTracks: (filter = {}) => apiFetch(`/tracks?${new URLSearchParams(filter)}`), getArtists: (filter = {}) => apiFetch(`/artists?${new URLSearchParams(filter)}`), getArtist: (slug) => apiFetch(`/artists/${slug}`), getAlbums: (filter = {}) => apiFetch(`/albums?${new URLSearchParams(filter)}`), uploadTrack: (data) => apiFetch('/tracks', { method: 'POST', body: JSON.stringify(data) }) };
Keep the same return shape: All pages expect { data: [], total: number } from list endpoints and a single object from detail endpoints. Your API should return this format or add a thin adapter wrapper in ooki-data.js.
JWT + Google SSO + Admin 2FA
The auth pages are fully built — they just need their form actions and API endpoints wired up.
# currently. Set action="https://api.ooki.music/v1/auth/login" and method="POST", or intercept with fetch() in JS, store the returned JWT in localStorage.setItem('ooki-token', token), and redirect based on role.googleSignInBtn. Call google.accounts.id.initialize({ client_id: GOOGLE_CLIENT_ID, callback: handleCredential }).{ requires_2fa: true, temp_token: "..." }. Store temp_token, redirect to auth/2fa.html. The 2FA page submits the TOTP code + temp_token to POST /auth/2fa/verify.ooki-token (JWT) and ooki-refresh-token in localStorage. On 401 responses, call POST /auth/refresh with the refresh token and retry the request. All Sign Out buttons call localStorage.clear() + redirect to index.html.const Auth = { login: async (email, password) => { const res = await apiFetch('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }) }); if (res.token) localStorage.setItem('ooki-token', res.token); return res; }, logout: () => { localStorage.clear(); window.location.href = '../index.html'; }, isLoggedIn: () => !!localStorage.getItem('ooki-token'), getRole: () => { const token = localStorage.getItem('ooki-token'); if (!token) return null; try { return JSON.parse(atob(token.split('.')[1])).role; } catch { return null; } } };
Wiring the Store & Artist Payouts
The store UI is complete. You need to load Stripe.js, create checkout sessions server-side, and connect artist payout accounts via Stripe Connect.
</body>: <script src="https://js.stripe.com/v3/"></script> and init: const stripe = Stripe('pk_live_...')POST /api/checkout with { product_id, price, user_id }. Your server creates a Stripe Checkout Session and returns { session_id }. Then call stripe.redirectToCheckout({ sessionId }).POST /api/connect/onboard which returns a Stripe onboarding URL. Artists keep 85% — your backend splits via Stripe's transfer API.Never put Stripe secret keys in frontend JS. All payment session creation must happen on your backend. Only the publishable key (pk_live_...) belongs in the frontend.
Event System Integration
The webhooks dashboard is built at developers/webhooks.html. Register your endpoint URL and Ooki will POST events to it. Your backend must verify the X-Ooki-Signature header using HMAC-SHA256.
| Event | Trigger | Payload (key fields) |
|---|---|---|
track.published | Artist publishes a track | track_id, artist_id, title, genre |
track.removed | Track taken down | track_id, reason |
user.signup | New user registered | user_id, role, email, plan |
user.subscribed | User upgrades to Pro | user_id, plan, amount |
payment.success | Store purchase completed | order_id, user_id, product_id, amount |
artist.verified | Artist account verified by admin | artist_id, verified_by |
capsule.unlocked | User unlocks a Sonic Capsule | capsule_id, user_id, artist_id |
const crypto = require('crypto'); app.post('/api/webhooks', (req, res) => { const sig = req.headers['x-ooki-signature']; const expected = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.rawBody) .digest('hex'); if (sig !== `sha256=${expected}`) { return res.status(401).send('Invalid signature'); } const { event, data } = req.body; switch (event) { case 'payment.success': handlePayment(data); break; case 'user.signup': sendWelcomeEmail(data); break; } res.sendStatus(200); });
Core Endpoints Your Backend Must Implement
These are the endpoints the frontend calls. Build them in any language/framework. Full spec is in dev-handoff.html.
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/login | Returns JWT + refresh token + user role |
| POST | /auth/signup | Creates user, returns JWT |
| POST | /auth/refresh | Returns new JWT from refresh token |
| POST | /auth/reset-password | Sends reset email |
| POST | /auth/2fa/verify | Validates TOTP code, returns full JWT |
| POST | /auth/google | Google SSO token exchange |
| Method | Endpoint | Description |
|---|---|---|
| GET | /tracks | List tracks — ?genre=&artist=&search=&page=&limit= |
| GET | /tracks/:id | Single track detail |
| POST | /tracks | Upload new track (multipart/form-data) |
| PATCH | /tracks/:id | Update track metadata |
| DELETE | /tracks/:id | Remove track |
| GET | /artists | List artists — ?genre=&search=&verified=&page= |
| GET | /artists/:slug | Artist profile + discography |
| POST | /artists/:id/follow | Follow/unfollow artist |
| Method | Endpoint | Description |
|---|---|---|
| GET | /store/products | List albums/EPs/singles for sale |
| POST | /checkout | Create Stripe Checkout Session |
| GET | /store/downloads/:id | Signed download URL for purchased track |
| GET | /payouts | Artist payout history |
| Method | Endpoint | Description |
|---|---|---|
| GET | /admin/users | All users — ?role=&status=&search=&page= |
| PATCH | /admin/users/:id/status | Ban/restore user |
| GET | /admin/analytics | Platform KPIs — DAU, streams, revenue |
| GET | /admin/songwriters | ISRC, splits, publishing rights |
Required Configuration
Set these on your server/hosting platform. Never commit them to Git.
| Variable | Example Value | Required? | Used In |
|---|---|---|---|
| OOKI_API_BASE_URL | https://api.ooki.music/v1 | Required | ooki-data.js API_BASE |
| JWT_SECRET | super-secret-256bit-key | Required | Backend JWT signing |
| GOOGLE_CLIENT_ID | 1234567890.apps.googleusercontent.com | Required | auth/*.html Google SSO |
| STRIPE_PK | pk_live_abc123... | Required | store.html, merchant.html |
| STRIPE_SK | sk_live_abc123... | Required | Backend checkout sessions |
| ADMIN_2FA_SECRET | BASE32ENCODED... | Required | auth/2fa.html TOTP |
| CDN_URL | https://cdn.ooki.music | Required | Audio/image CDN base URL |
| SENDGRID_API_KEY | SG.abc123... | Required | Transactional emails |
| WEBHOOK_SECRET | whsec_abc123... | Optional | developers/webhooks.html |
| SENTRY_DSN | https://[email protected]/123 | Optional | Error monitoring |
| POSTHOG_KEY | phc_abc123 | Optional | Product analytics |
Get it Live in 3 Ways
The frontend is purely static — it deploys anywhere. Pick the option that matches your infrastructure.
ooki/ folder to a GitHub repo. Enable Pages under Settings → Pages → Deploy from branch (main / root). Free, automatic SSL.aws s3 sync . s3://your-bucket --delete. Point CloudFront distribution at the bucket. Enable OAC for private bucket. Invalidate on deploy: aws cloudfront create-invalidation --distribution-id YOUR_ID --paths "/*"amplify.yml handles build + deploy automatically on every push to main. Zero config needed.version: 1 frontend: phases: build: commands: - echo "Static site — no build step required" artifacts: baseDirectory: / files: - '**/*' cache: paths: []
Before You Go Live
Run through this list before flipping the switch to production.
All 15 checks passed? You're ready to go live. Push to your deployment branch and ship it.