{"openapi":"3.1.0","info":{"title":"GalaxyTranslate API","version":"0.1.0","summary":"Real-time call translation, TTS, ASR, and conferencing.","description":"**Staging** API surface for GalaxyTranslate.  Wraps the FreeSWITCH +\ntranslation-engine stack so partner applications can:\n\n- Initiate outbound SIP calls (`POST /v1/calls`)\n- Inspect / hang up active channels\n- List + control conference rooms\n- Synthesize speech (Google or Piper) inline or into a conference\n- Transcribe uploaded audio (sync or long-running)\n- Translate text between languages\n- (TODO) Run NLM completions / summaries\n\nAll `/v1/*` routes require `Authorization: Bearer <STAGING_API_KEY>`.\n\nPublic surface lives at `api.galaxytranslate.com/api/*` (Cloudflare tunnel).","contact":{"name":"GalaxyTranslate Ops"}},"servers":[{"url":"https://api.galaxytranslate.com/api","description":"Configured environment"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"]},"TranscriptResult":{"type":"object","properties":{"transcript":{"type":"string"},"confidence":{"type":"number","nullable":true},"languageCode":{"type":"string","nullable":true},"words":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"speaker_tag":{"type":"integer"},"start_s":{"type":"number","nullable":true},"end_s":{"type":"number","nullable":true}}}}}}}},"security":[{"bearerAuth":[]}],"tags":[{"name":"Health","description":"Liveness + downstream probes (no auth)"},{"name":"Calls","description":"Initiate, list, inspect, hang up SIP calls"},{"name":"Conferences","description":"Conference rooms — dial, play, say, kick"},{"name":"TTS","description":"Text-to-speech (Google / Piper)"},{"name":"ASR","description":"Speech-to-text via Google STT"},{"name":"Translate","description":"Text translation via Google Translate"},{"name":"NLM","description":"Natural Language Model — Surfiai (deepseek-r1:1.5b by default)"}],"paths":{"/v1/health":{"get":{"tags":["Health"],"summary":"Combined gateway + downstream health","security":[],"responses":{"200":{"description":"Health snapshot"}}}},"/v1/calls":{"post":{"tags":["Calls"],"summary":"Initiate an outbound call","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","example":"sofia/external/15551234567@sip.didlogic.net"},"gateway":{"type":"string","example":"didlogic"},"destination_number":{"type":"string","example":"15551234567"},"from":{"type":"string","example":"+18883163088"},"from_name":{"type":"string","example":"Galaxy Translate"},"extension":{"type":"string","example":"galaxytranslate_auto_4200"},"context":{"type":"string","example":"galaxytranslate"},"variables":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"responses":{"200":{"description":"Originate accepted, returns Job-UUID"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"ESL error"}}},"get":{"tags":["Calls"],"summary":"List active channels","responses":{"200":{"description":"Channel list"}}}},"/v1/calls/{uuid}":{"get":{"tags":["Calls"],"summary":"One channel's status","parameters":[{"name":"uuid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Channel dump"},"404":{"description":"Not found"}}}},"/v1/calls/{uuid}/hangup":{"post":{"tags":["Calls"],"summary":"Hang up a channel","parameters":[{"name":"uuid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"cause":{"type":"string","example":"NORMAL_CLEARING"}}}}}},"responses":{"200":{"description":"Hung up"}}}},"/v1/conferences":{"get":{"tags":["Conferences"],"summary":"List all rooms","responses":{"200":{"description":"Room list"}}}},"/v1/conferences/{room}":{"get":{"tags":["Conferences"],"summary":"One room's members","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Room dump"}}},"delete":{"tags":["Conferences"],"summary":"Dismiss the conference (kick all)","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dismissed"}}}},"/v1/conferences/{room}/dial":{"post":{"tags":["Conferences"],"summary":"Dial an extra leg into the room","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"destination":{"type":"string","example":"sofia/external/..."},"caller_id_number":{"type":"string"},"caller_id_name":{"type":"string"}},"required":["destination"]}}}},"responses":{"200":{"description":"Dial submitted"}}}},"/v1/conferences/{room}/play":{"post":{"tags":["Conferences"],"summary":"Play an audio file/URL into the room","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","example":"/usr/share/freeswitch/sounds/.../foo.wav"}},"required":["file"]}}}},"responses":{"200":{"description":"Playback queued"}}}},"/v1/conferences/{room}/say":{"post":{"tags":["Conferences"],"summary":"TTS-say text into the room","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"},"lang_code":{"type":"string","example":"en-US"},"voice":{"type":"string","example":"en-US-Neural2-F"},"speaker":{"type":"string","description":"Speaker tag for translation context"}},"required":["text","lang_code"]}}}},"responses":{"200":{"description":"Synth + playback queued"}}}},"/v1/conferences/{room}/kick":{"post":{"tags":["Conferences"],"summary":"Kick a member by ID","parameters":[{"name":"room","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"integer"}},"required":["member_id"]}}}},"responses":{"200":{"description":"Kicked"}}}},"/v1/tts":{"post":{"tags":["TTS"],"summary":"Synthesize speech and return audio inline","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","example":"Hello world"},"lang_code":{"type":"string","example":"en-US"},"voice":{"type":"string","example":"en-US-Neural2-F"},"gender":{"type":"string","enum":["MALE","FEMALE","NEUTRAL"]},"format":{"type":"string","enum":["MP3","LINEAR16","OGG_OPUS"],"default":"MP3"},"sample_rate":{"type":"integer"},"speaking_rate":{"type":"number","default":1},"provider":{"type":"string","enum":["google","piper"],"default":"google"}},"required":["text","lang_code"]}}}},"responses":{"200":{"description":"Audio (base64) + metadata"},"500":{"description":"TTS failed"}}}},"/v1/tts/play-room":{"post":{"tags":["TTS"],"summary":"Synthesize and play into a conference room","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"room":{"type":"string","example":"4200"},"text":{"type":"string"},"lang_code":{"type":"string","example":"fr-CA"},"voice":{"type":"string"},"speaker":{"type":"string"}},"required":["room","text","lang_code"]}}}},"responses":{"200":{"description":"Engine accepted the request"}}}},"/v1/asr":{"post":{"tags":["ASR"],"summary":"Transcribe audio (multipart upload OR base64 JSON)","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"audio":{"type":"string","format":"binary"},"lang_code":{"type":"string","example":"en-US"},"diarize":{"type":"boolean"},"punctuation":{"type":"boolean"},"model":{"type":"string","example":"phone_call"},"alternative_lang_codes":{"type":"string","description":"JSON array of BCP-47 codes"}}}},"application/json":{"schema":{"type":"object","properties":{"audio_base64":{"type":"string"},"encoding":{"type":"string","example":"MP3"},"sample_rate":{"type":"integer","example":8000},"lang_code":{"type":"string","example":"en-US"},"diarize":{"type":"boolean"},"punctuation":{"type":"boolean"},"model":{"type":"string"},"alternative_lang_codes":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Transcript","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"transcript":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptResult"}}}}}}},"500":{"description":"ASR failed"}}}},"/v1/translate":{"post":{"tags":["Translate"],"summary":"Translate text","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"source":{"type":"string","example":"fr"},"target":{"type":"string","example":"en"}},"required":["text","target"]}}}},"responses":{"200":{"description":"Translations"}}}},"/v1/nlm/complete":{"post":{"tags":["NLM"],"summary":"Chat completion via Surfiai","description":"Optional `context` wraps the prompt as live-call operator suggestion (matches WP plugin behaviour).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"context":{"type":"string"},"model":{"type":"string","example":"deepseek-r1:1.5b"}},"required":["prompt"]}}}},"responses":{"200":{"description":"Completion result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"provider":{"type":"string"},"model":{"type":"string"},"reply":{"type":"string"}}}}}},"502":{"description":"Surfiai upstream error"},"503":{"description":"Surfiai not configured"}}}},"/v1/nlm/summarize":{"post":{"tags":["NLM"],"summary":"Summarize a call transcript","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"transcript":{"type":"string"},"max_sentences":{"type":"integer","default":4},"model":{"type":"string"}},"required":["transcript"]}}}},"responses":{"200":{"description":"Summary"},"503":{"description":"Surfiai not configured"}}}},"/v1/nlm/extract":{"post":{"tags":["NLM"],"summary":"Extract structured fields from a transcript","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"transcript":{"type":"string"},"fields":{"type":"array","items":{"type":"string"},"example":["caller_name","callback_phone","reason_for_call"]},"model":{"type":"string"}},"required":["transcript"]}}}},"responses":{"200":{"description":"JSON object with extracted fields (Surfiai reply is JSON-as-string)"},"503":{"description":"Surfiai not configured"}}}},"/v1/nlm/intent":{"post":{"tags":["NLM"],"summary":"Classify caller intent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"transcript":{"type":"string"},"candidates":{"type":"array","items":{"type":"string"},"example":["sales","support","billing"]},"model":{"type":"string"}},"required":["transcript"]}}}},"responses":{"200":{"description":"Single label string in `reply`"},"503":{"description":"Surfiai not configured"}}}}}}