Documentação da API

Base URL: https://api-gigrunner.softrace.pt/api/v1
Referência completa dos endpoints + guia de integração para a app GigRunner / Cue Companion.

Guia de integração (app)

Destinado a quem implementa o cliente desktop/mobile. O site trata de registo/compra; a app trata de login, validar licença e consumir créditos AI.

1. Setup

Base URL
https://api-gigrunner.softrace.pt/api/v1
Formato
JSON · Content-Type: application/json · Accept: application/json
Auth
Authorization: Bearer <token>

Em desenvolvimento podes usar o Playground para validar as respostas antes de ligar a app.

2. Login (uma vez / quando expirar)

Ecrã de login na app → POST /login com email e 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|…"
}

Guardar em disco (seguro): token, user.uuid, license, credits, e last_validated_at = now.

Se license.valid === false: deixa entrar só ao ecrã “activa / compra licença” (abre o site no browser). Não desbloqueies a app completa.

3. Arranque seguinte (já tem token)

  1. Ler token do disco. Se não houver → ecrã de login.
  2. Com rede: GET /license (ou GET /me se quiseres user + credits também).
  3. Actualizar cache local (license, credits se vierem, last_validated_at).
  4. Se valid === true → app normal. Caso contrário → bloquear / pedir compra.
  5. Se 401 → token inválido → limpar cache → login.

O utilizador não volta a fazer login em cada abertura se o token ainda for válido.

4. Usar AI (obrigatório)

Antes de cada pedido AI à tua infra / modelo:

POST /api/v1/credits/consume
Authorization: Bearer …
{ "type": "ai", "amount": 1, "reason": "cue_suggest" }

→ 200  { "ok": true, "balance": 49, "credits": { "ai": 49 } }
→ 402  { "message": "Créditos insuficientes.", "credits": { "ai": 0 } }
  • 200 → actualiza saldo local → executa a AI.
  • 402 → não chames a AI; UI “compra mais créditos” (link ao site).
  • Não confies só no saldo em cache para autorizar o gasto — o servidor decide.
  • Opcional: envia reference único por operação (ex. id do job) para auditar / evitar double-spend no futuro.

4b. Análise de áudio (Magic Chords)

A app não chama a Magic Chords directamente. Usa as URLs GigRunner. O backend escolhe o provider (hoje: Magic Chords). Requer licença válida; consome 1 crédito AI ao criar o job.

POST /api/v1/audio/jobs
{ "url": "https://…/song.mp3", "kind": "analyze" }
  ou multipart: file + kind

→ 201 { "job": { "id", "status", "progress", … }, "credits" }

GET /api/v1/audio/jobs/{id}           → poll até status=complete
GET /api/v1/audio/jobs/{id}/result    → acordes, tempo, key, …

kind: analyze (acordes/tempo/key) ou transcribe (letras). Os jobs demoram — faz poll a cada 2–5s.

5. Offline (proposta)

Ensaios podem não ter rede. Regra sugerida (acordar valor de N):

  • Se há cache com license.valid === true e now - last_validated_at < N dias (ex.: 7) → permite usar a app.
  • Funções AI requerem rede (porque precisam de /credits/consume). Sem rede → desactivar AI ou mensagem clara.
  • Quando voltar a rede → revalidar logo (GET /license + opcional GET /credits).
  • Se online a API disser inválida/revogada → bloquear mesmo com cache antigo.

6. O que a app deve fazer por erro

401
Limpar token → ecrã login
402
Sem créditos AI → UI compra / top-up
422
Mostrar mensagem de validação (login incorrecto, etc.)
5xx / rede
Retry suave; se arranque, aplicar regra offline

7. Checklist para o cliente

  • □ Base URL configurável (dev / produção)
  • □ Login → guardar token + uuid + license + credits
  • □ Arranque com token → GET /license (não pedir password outra vez)
  • □ Bloquear app se license.valid === false
  • □ Antes de AI genérica → POST /credits/consume; tratar 402
  • □ Análise áudio → POST /audio/jobs + poll + /result
  • □ Link “Registar / Comprar” → site (browser)
  • □ Logout opcional → POST /logout + limpar disco
  • □ Regra offline documentada (N dias; AI só online)

Endpoints de teste (activate-test, purchase-test) são para o backend/playground — a app de produção não precisa de os chamar; a compra será no site.

Fluxo típico (resumo)

  1. Utilizador cria conta no site.
  2. Compra licença (ex.: 10€) → fica com app activa + créditos iniciais (ex.: 50 AI).
  3. Na app: login → token + license + credits.
  4. Antes de cada uso AI: POST /credits/consume. Se 402 → sem saldo, pedir top-up.

Autenticação

Endpoints protegidos precisam do header:
Authorization: Bearer <token>
Accept: application/json

O token vem em POST /login ou POST /register. O utilizador final nunca gera tokens à mão — isso é só a app.

Endpoints

GET /api/v1/health público
Verifica se a API está online.
Resposta: { "ok": true, "app": "...", "env": "..." }
POST /api/v1/register público
Cria conta.
Body: name, email, password, password_confirmation
Resposta 201: { user, license, credits, token }
POST /api/v1/login público
Login da app.
Body: email, password
Resposta: { user, license, credits, token }
GET /api/v1/me Bearer
Dados da conta + licença + créditos.
Resposta: { user, license, credits }
GET /api/v1/license Bearer
Estado da licença (o que a app valida ao abrir).
Resposta:
{
  "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"
  }
}
Sem licença: valid: false, status: "inactive".
POST /api/v1/license/activate-test Bearer · teste
Cria uma licença trial de 30 dias (enquanto não há pagamento).
Resposta 201: { license }
POST /api/v1/license/revoke Bearer · teste
Revoga a licença actual (para testar bloqueio na app).
Resposta: { license }

Modelo: licença vs créditos

Licença = pode usar a app (porta de entrada).
Créditos = saldo por tipo (agora só ai) para acções pagas.

Não precisas de “verificar créditos” em ciclo. A regra é:

  • Ao abrir a app → GET /license (e opcionalmente cache offline).
  • Antes de cada acção AI → POST /credits/consume (atómico no servidor).
  • Se 402 → mostra “sem créditos” e link para comprar pack.

Há um ledger (credit_transactions) para auditar compras e consumos.

GET /api/v1/credits Bearer
Saldos + packs disponíveis.
Resposta: { credits: { ai }, packs: [...] }
POST /api/v1/credits/consume Bearer
Consome créditos AI.
Body: type (ai), amount? (default 1), reason?
200: { ok, type, balance, credits }
402: créditos insuficientes
POST /api/v1/credits/purchase-test Bearer · teste
Simula compra de pack (até existir Stripe).
Body: { "pack": "license_standard" }
Packs: license_standard (10€ → licença + 50 AI), ai_50, ai_200.
POST /api/v1/audio/jobs Bearer
Cria análise/transcrição. Provider actual: Magic Chords. Consome 1 crédito AI.
JSON: { "url": "https://…", "kind": "analyze"|"transcribe" }
multipart: file + kind
201: { job, credits } · 402 sem créditos · 403 sem licença
GET /api/v1/audio/jobs/{id} Bearer
Estado do job (queued / processing / complete / failed) + progresso.
GET /api/v1/audio/jobs/{id}/result Bearer
Resultado completo do provider (acordes, tempo, key, …).
409 se ainda não estiver complete.
POST /api/v1/logout Bearer
Revoga o token actual.
Resposta: { "ok": true }

Erros comuns

401
Token em falta ou inválido
402
Créditos insuficientes (consume)
422
Validação (password errada, pack inválido, etc.)

Objecto user

{
  "id": 1,
  "uuid": "63f50785-…",
  "name": "Nome",
  "email": "user@email.com"
}

O uuid é estável — a app pode guardá-lo localmente junto com o token e o estado da licença.