Galactic Radio MusicGen API

v1.0.0 — AI-powered music generation with Google Lyria 3

Get raw markdown for LLM context

Authentication

The API supports three authentication methods:

  • Firebase OAuth — Send a Firebase ID token as Authorization: Bearer <token>. Used by the web frontend after Google sign-in.
  • API Key — Send your key as X-API-Key: grm_xxx header. Get a key via POST /api/v1/users/register or OAuth sign-in.
  • Local Dev Bypass — In development mode, requests from localhost are auto-authenticated as admin. No headers needed.

Users

POST /api/v1/users/register No Auth

Register a new user. Returns an API key (shown once).

Request Body

FieldTypeRequiredDescription
emailstringYesUser email
passwordstringYesMin 6 characters
display_namestringNoDisplay name
curl -X POST http://localhost:8216/api/v1/users/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret123"}'

Response (201)

{
  "uid": "abc123",
  "email": "user@example.com",
  "api_key": "grm_xxxxxxxxxxxx",
  "credits_balance": 3,
  "message": "Account created. Store this API key securely..."
}
POST /api/v1/users/oauth-signin Bearer Token

OAuth sign-in callback. Provisions profile and API key for new users.

curl -X POST http://localhost:8216/api/v1/users/oauth-signin \
  -H "Authorization: Bearer <firebase_id_token>"

Response (200)

{
  "uid": "firebase_uid",
  "email": "user@gmail.com",
  "display_name": "John Doe",
  "api_key": "grm_xxxxxxxxxxxx",  // only for new users
  "credits_balance": 3,
  "is_new_user": true
}
GET /api/v1/users/me Auth Required

Get current user profile.

curl http://localhost:8216/api/v1/users/me \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "uid": "abc123",
  "email": "user@example.com",
  "display_name": "John",
  "bio": "Music enthusiast",
  "avatar_url": "https://...",
  "role": "user",
  "credits_balance": 3,
  "api_key_count": 1,
  "created_at": "2026-04-17T12:00:00+00:00"
}
PATCH /api/v1/users/me Auth Required

Update profile fields (display_name, bio).

Request Body

FieldTypeRequiredDescription
display_namestringNoDisplay name (max 100)
biostringNoShort bio (max 500)
curl -X PATCH http://localhost:8216/api/v1/users/me \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "DJ Nebula", "bio": "Making cosmic beats"}'
GET /api/v1/users/me/stats Auth Required

Get aggregated play statistics for the current user's songs.

curl http://localhost:8216/api/v1/users/me/stats \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "total_songs": 12,
  "total_plays": 150,
  "plays_by_location": {"web-player": 100, "discord-bot": 50},
  "top_songs": [
    {"song_id": "song_abc123", "title": "Hit Track", "play_count": 42}
  ]
}
POST /api/v1/users/api-keys Auth Required

Generate a new API key. The raw key is returned once.

curl -X POST http://localhost:8216/api/v1/users/api-keys \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "api_key": "grm_new_key_here",
  "message": "Store this key securely — it will not be shown again"
}
DELETE /api/v1/users/api-keys/{key_id} Auth Required

Revoke an API key by its hash prefix.

curl -X DELETE http://localhost:8216/api/v1/users/api-keys/abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"
POST /api/v1/users/me/avatar Auth Required

Upload a custom avatar image. Max 2MB, JPEG or PNG only.

curl -X POST http://localhost:8216/api/v1/users/me/avatar \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -F "file=@avatar.jpg"

Response (200)

{
  "uid": "abc123",
  "email": "user@example.com",
  "display_name": "John",
  "avatar_url": "https://storage.googleapis.com/.../avatar.jpg",
  ...
}

Songs

POST /api/v1/songs/generate Auth Required

Generate a new song via Google Lyria 3 API. Costs credits.

Request Body

FieldTypeRequiredDescription
titlestringYesSong title (1-200 chars)
descriptionstringNoDescription (max 500)
lyricsstringNoLyrics to use (max 5000)
genrestringNoGenre (e.g. "ambient electronic")
moodstringNoMood (e.g. "ethereal", "upbeat")
artist_namestringNoArtist name to display
artist_urlstringNoArtist profile URL (max 500)
generation_typestringNofull_song (default) or jingle
audio_formatstringNomp3 (default)
bpmintNoTempo 40-300
key_scalestringNoMusical key (e.g. "C minor")
instrumentsstring[]NoInstruments to feature
duration_hintstringNoe.g. "short", "30 seconds"
metaobjectNoArbitrary metadata (station_id, etc.)
curl -X POST http://localhost:8216/api/v1/songs/generate \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Cosmic Dawn",
    "genre": "ambient electronic",
    "mood": "ethereal",
    "artist_name": "DJ Nebula",
    "generation_type": "full_song"
  }'

Response (201)

Returns the full Song object.

GET /api/v1/songs/{song_id} Auth Required

Get a song by ID.

curl http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"
GET /api/v1/songs Auth Required

List and search songs with filtering, search, and shuffle.

Query Parameters

ParamTypeDefaultDescription
mineboolfalseOnly show my songs
created_bystring[][]Filter by user UID(s) — repeat for multiple
genrestringSubstring match, case-insensitive
moodstringSubstring match, case-insensitive
qstringFree-text search (title, desc, genre, mood, artist, lyrics)
shuffleboolfalseRandomize results instead of newest-first
statusstringcompletedFilter by status
limitint201-100
offsetint0Pagination offset
# Search for country songs about love
curl "http://localhost:8216/api/v1/songs?q=love&genre=country&limit=10" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# 5 random songs for radio shuffle
curl "http://localhost:8216/api/v1/songs?shuffle=true&limit=5" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "songs": [ ... ],  // array of Song objects
  "total": 42,
  "limit": 20,
  "offset": 0
}
POST /api/v1/songs/{song_id}/play Auth Required

Record a play event for analytics.

Request Body

FieldTypeRequiredDescription
locationstringYesWhere played (e.g. "web-player", "discord-bot")
curl -X POST http://localhost:8216/api/v1/songs/song_abc123/play \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"location": "web-player"}'
PATCH /api/v1/songs/{song_id} Auth Required

Update song metadata. Owner or admin only. At least one field required.

Request Body

FieldTypeRequiredDescription
titlestringNoSong title (1-200 chars)
artist_namestringNoArtist name (max 100)
artist_urlstringNoArtist profile URL (max 500)
descriptionstringNoDescription (max 500)
genrestringNoGenre (max 100)
moodstringNoMood (max 100)
curl -X PATCH http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "genre": "jazz"}'

Response (200)

Returns the updated Song object.

POST /api/v1/songs/{song_id}/art Auth Required

Upload cover art for a song. Max 2MB, JPEG or PNG only. Owner or admin only.

curl -X POST http://localhost:8216/api/v1/songs/song_abc123/art \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -F "file=@cover.jpg"

Response (200)

{
  "cover_art_url": "https://storage.googleapis.com/.../cover.jpg",
  "song_id": "song_abc123"
}
DELETE /api/v1/songs/{song_id} Auth Required

Delete a song. Only the owner or an admin can delete.

curl -X DELETE http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Credits

GET /api/v1/credits/balance Auth Required

Get current credits balance.

curl http://localhost:8216/api/v1/credits/balance \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{"credits_balance": 3}
POST /api/v1/credits/purchase Auth Required

Purchase credits (AP2 placeholder — returns payment_required status).

Request Body

FieldTypeRequiredDescription
amountintYesNumber of credits to purchase (> 0)
curl -X POST http://localhost:8216/api/v1/credits/purchase \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount": 10}'

Response (200)

{
  "status": "payment_required",
  "ap2_metadata": {
    "protocol": "ap2",
    "provider": "galactic-radio-musicgen",
    "amount_requested": 10,
    "currency": "credits",
    "message": "AP2 payment integration coming soon"
  }
}

A2A Protocol (Agent-to-Agent)

The A2A protocol allows other AI agents to discover and interact with this service programmatically.

GET /.well-known/agent.json No Auth

Agent discovery card. Returns service capabilities, skills, and authentication requirements.

curl http://localhost:8216/.well-known/agent.json

Skills

  • generate_music — Generate a song using Lyria 3
  • search_songs — Search the song catalog
  • get_song — Retrieve a specific song
  • purchase_credits — Purchase credits (AP2 placeholder)
POST /a2a Auth Required

JSON-RPC 2.0 endpoint for agent task execution. Supports tasks/send method.

curl -X POST http://localhost:8216/a2a \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/send",
    "id": 1,
    "params": {
      "skill_id": "search_songs",
      "input": {"q": "ambient", "limit": 5}
    }
  }'

Response (200)

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "task-uuid",
    "status": {"state": "completed"},
    "artifacts": [{
      "parts": [{
        "type": "data",
        "data": {"songs": [...], "total": 5}
      }]
    }]
  }
}

System

GET /api/v1/health No Auth

Health check. Returns service status.

curl http://localhost:8216/api/v1/health

Response (200)

{
  "status": "ok",
  "version": "1.0.0",
  "environment": "development",
  "services": {
    "firestore": "connected",
    "music_gen": "configured"
  }
}
GET /api/v1/info No Auth

Server name, version, and environment.

curl http://localhost:8216/api/v1/info
GET /api/v1/config Admin Only

View current runtime config (models, credits, storage).

curl http://localhost:8216/api/v1/config \
  -H "X-API-Key: grm_admin_key"
PATCH /api/v1/config Admin Only

Update runtime config values (in-memory, not persisted to .env).

curl -X PATCH http://localhost:8216/api/v1/config \
  -H "X-API-Key: grm_admin_key" \
  -H "Content-Type: application/json" \
  -d '{"lyria_pro_model": "lyria-3-pro-v2", "credits_per_full_song": 2}'

Radio Station / Agent Usage

Agents and radio bots can use the search/filter/shuffle endpoints to build playlists:

# Get 5 random songs for a radio set
curl "http://localhost:8216/api/v1/songs?shuffle=true&limit=5" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Get electronic songs matching a mood
curl "http://localhost:8216/api/v1/songs?genre=electronic&mood=chill&limit=10" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Search by keyword
curl "http://localhost:8216/api/v1/songs?q=space+adventure&shuffle=true&limit=3" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Generate a custom jingle for a station break
curl -X POST http://localhost:8216/api/v1/songs/generate \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Station Break Jingle",
    "description": "Upbeat 30-second station identification",
    "generation_type": "jingle",
    "meta": {"station_id": "galactic-fm", "requested_by_agent": "dj-bot"}
  }'

# Record that a song was played
curl -X POST http://localhost:8216/api/v1/songs/song_abc123/play \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"location": "galactic-fm-stream"}'

Song JSON Schema

Every song endpoint returns this shape:

{
  "song_id": "song_abc123def456gh",
  "title": "Cosmic Dawn",
  "artist_name": "DJ Nebula",
  "artist_url": "https://example.com/artist",
  "cover_art_url": "https://storage.googleapis.com/.../cover.jpg",
  "description": "Ambient track for sunrise",
  "lyrics_input": null,
  "lyrics_generated": "Stars fade into morning light...",
  "genre": "ambient electronic",
  "mood": "ethereal",
  "duration_seconds": null,
  "audio_url": "https://storage.googleapis.com/.../song.mp3",
  "audio_path": "songs/uid/song_id/song.mp3",
  "audio_format": "mp3",
  "file_size_bytes": 4521984,
  "model_used": "lyria-3-pro-preview",
  "generation_prompt": "Create a song titled...",
  "generation_type": "full_song",
  "cost_credits": 1,
  "play_count": 42,
  "play_count_by_location": {"web-player": 30, "discord-bot": 12},
  "status": "completed",
  "error_message": null,
  "created_by": "user_uid",
  "meta": {"station_id": "galactic-fm"},
  "created_at": "2026-04-17T12:00:00+00:00",
  "updated_at": "2026-04-17T12:00:00+00:00"
}

Status values: pending, generating, completed, failed