API documentation
Base URL: https://api-gigrunner.softrace.pt/api/v1
Full endpoint reference + integration guide for the GigRunner / Cue Companion app.
Integration guide (app)
For whoever implements the desktop/mobile client. The website handles registration/purchase; the app handles login, licence validation and AI credit consumption.
1. Setup
During development you can use the Playground to validate responses before wiring the app.
2. Login (once / when expired)
App login screen → POST /login with email and password.
POST /api/v1/login
{ "email": "user@email.com", "password": "…" }
→ 200
{
"user": { "id", "uuid", "name", "email" },
"license": { "valid", "status", "plan", "starts_at", "expires_at" },
"credits": { "ai": 50 },
"token": "1|…"
}
Persist securely on disk:
token, user.uuid, license, credits,
and last_validated_at = now.
If license.valid === false: only allow the “activate / buy licence” screen
(open the website in the browser). Do not unlock the full app.
3. Next startup (token already stored)
- Read token from disk. If missing → login screen.
- When online:
GET /license(orGET /meif you also want user + credits). - Update local cache (
license,creditsif present,last_validated_at). - If
valid === true→ normal app. Otherwise → block / ask to purchase. - If 401 → invalid token → clear cache → login.
The user does not log in again on every launch if the token is still valid.
4. Using AI (required)
Before every AI request to your infra / model:
POST /api/v1/credits/consume
Authorization: Bearer …
{ "type": "ai", "amount": 1, "reason": "cue_suggest" }
→ 200 { "ok": true, "balance": 49, "credits": { "ai": 49 } }
→ 402 { "message": "Insufficient credits.", "credits": { "ai": 0 } }
- 200 → update local balance → run AI.
- 402 → do not call AI; show “buy more credits” UI (link to site).
- Do not rely only on a cached balance to authorise spend — the server decides.
- Optional: send a unique
referenceper operation (e.g. job id) for auditing / future double-spend protection.
4b. Audio analysis (Magic Chords)
The app must not call Magic Chords directly. Use GigRunner URLs. The backend picks the provider (today: Magic Chords). Requires a valid licence; consumes 1 AI credit when creating the job.
POST /api/v1/audio/jobs
{ "url": "https://…/song.mp3", "kind": "analyze" }
or multipart: file + kind
→ 201 { "job": { "id", "status", "progress", … }, "credits" }
GET /api/v1/audio/jobs/{id} → poll until status=complete
GET /api/v1/audio/jobs/{id}/result → chords, tempo, key, …
kind: analyze (chords/tempo/key) or transcribe (lyrics).
Jobs take time — poll every 2–5s.
5. Offline (proposal)
Rehearsals may have no network. Suggested rule (agree on N):
- If cache has
license.valid === trueandnow - last_validated_at < N days(e.g. 7) → allow app use. - AI features require network (they need
/credits/consume). Offline → disable AI or show a clear message. - When back online → revalidate immediately (
GET /license+ optionalGET /credits). - If online the API says invalid/revoked → block even with an old cache.
6. What the app should do per error
7. Client checklist
- □ Configurable Base URL (dev / production)
- □ Login → store
token+uuid+license+credits - □ Startup with token →
GET /license(do not ask for password again) - □ Block app if
license.valid === false - □ Before generic AI →
POST /credits/consume; handle 402 - □ Audio analysis →
POST /audio/jobs+ poll +/result - □ “Register / Buy” link → website (browser)
- □ Optional logout →
POST /logout+ clear disk - □ Documented offline rule (N days; AI online only)
Test endpoints (activate-test, purchase-test) are for backend/playground —
the production app does not need to call them; purchase will be on the website.
Typical flow (summary)
- User creates an account on the website.
- Buys a licence (e.g. €10) → gets an active app + initial credits (e.g. 50 AI).
- In the app: login → token + license + credits.
- Before each AI use:
POST /credits/consume. If 402 → no balance, ask for top-up.
Authentication
Protected endpoints need the header:
Authorization: Bearer <token>
Accept: application/json
The token comes from POST /login or POST /register.
The end user never creates tokens by hand — that is the app’s job.
Endpoints
Response:
{ "ok": true, "app": "...", "env": "..." }
Body:
name, email, password, password_confirmationResponse 201:
{ user, license, credits, token }
Body:
email, passwordResponse:
{ user, license, credits, token }
Response:
{ user, license, credits }
Response:
{
"license": {
"valid": true,
"status": "active",
"plan": "trial",
"starts_at": "2026-09-27T00:00:00+01:00",
"expires_at": "2026-10-27T00:00:00+01:00"
}
}
valid: false, status: "inactive".
Response 201:
{ license }
Response:
{ license }
Model: licence vs credits
Licence = may use the app (gate).
Credits = balance per type (currently only ai) for paid actions.
You do not need to “check credits” in a loop. The rule is:
- On app open →
GET /license(and optionally offline cache). - Before each AI action →
POST /credits/consume(atomic on the server). - If 402 → show “no credits” and a link to buy a pack.
There is a ledger (credit_transactions) to audit purchases and consumption.
Response:
{ credits: { ai }, packs: [...] }
Body:
type (ai), amount? (default 1), reason?200:
{ ok, type, balance, credits }402: insufficient credits
Body:
{ "pack": "license_standard" }Packs:
license_standard (€10 → licence + 50 AI), ai_50, ai_200.
JSON:
{ "url": "https://…", "kind": "analyze"|"transcribe" }multipart:
file + kind201:
{ job, credits } · 402 no credits · 403 no licence
queued / processing / complete / failed) + progress.
409 if not yet
complete.
Response:
{ "ok": true }
Common errors
user object
{
"id": 1,
"uuid": "63f50785-…",
"name": "Name",
"email": "user@email.com"
}
The uuid is stable — the app can store it locally with the token and licence state.