@@ -152,15 +152,31 @@ export function detectSystem(): DetectionResult {
152152
153153/**
154154 * Validate an ElevenLabs API key.
155+ * Uses /v1/voices endpoint (requires only xi-api-key header, no specific scope)
156+ * instead of /v1/user (requires user_read permission, which many keys lack).
157+ * Also handles 401 with missing_permissions as "valid key, limited scope" —
158+ * TTS works fine with a known voice_id even without voices_read permission.
155159 */
156160export async function validateElevenLabsKey ( key : string ) : Promise < { valid : boolean ; error ?: string } > {
157161 try {
158- const res = await fetch ( "https://api.elevenlabs.io/v1/user " , {
162+ const res = await fetch ( "https://api.elevenlabs.io/v1/voices " , {
159163 headers : { "xi-api-key" : key } ,
160164 signal : AbortSignal . timeout ( 10000 ) ,
161165 } ) ;
162166
163167 if ( res . ok ) return { valid : true } ;
168+
169+ // 401 with missing_permissions means the key IS valid but lacks a specific scope.
170+ // TTS still works (doesn't need voices_read to use a known voice_id).
171+ if ( res . status === 401 ) {
172+ try {
173+ const body = await res . json ( ) ;
174+ if ( body ?. detail ?. status === "missing_permissions" ) {
175+ return { valid : true } ;
176+ }
177+ } catch { /* fall through to error */ }
178+ }
179+
164180 return { valid : false , error : `HTTP ${ res . status } ` } ;
165181 } catch ( e : any ) {
166182 return { valid : false , error : e . message || "Network error" } ;
0 commit comments