All three call POST /v1/assess with TonePerfect-Response: compact, so the app reads the stable compact schema. Start with a test key (tp_test_…): fixed example results, no credits spent. Swap in a live key once the screens work.

Next.js

App: Browser page with MediaRecorder. Key holder: Route handler app/api/assess/route.js.

Files: README.md · package.json · env.example · app/api/assess/route.js · app/page.js · app/layout.js · LICENSE

Expo + Cloudflare Worker

App: React Native app recording M4A with expo-av. Key holder: Cloudflare Worker.

Files: README.md · worker/src/index.js · worker/wrangler.toml · worker/package.json · app/App.js · app/package.json · LICENSE

Flutter + Firebase

App: Flutter app recording AAC with record. Key holder: Firebase HTTPS Function (2nd gen) with an ID-token check.

Files: README.md · pubspec.yaml · lib/main.dart · functions/index.js · functions/package.json · LICENSE

Before you ship

  • Put your own sign-in or a per-user limit in front of the proxy. Anyone who finds an open proxy URL can spend your credits.
  • Send one Idempotency-Key per recording attempt from the app, so a retry after a dropped connection is not charged twice.
  • Show error.message as it comes back; it is written for people. An assessable: false result is not a zero score.

These projects are not published to GitHub or app stores; they are served from this site so you can read every line first. Questions: nik@toneperfect.app.