API Plateforme ePV ePV Platform API ePV Platform API API Piattaforma ePV

Documentation complète de l'API pour les services de transcription audio et de modèles de langage. Créez des applications d'IA puissantes avec un traitement vocal en temps réel et une génération de texte intelligente. Comprehensive API documentation for audio transcription and Large Language Model services. Build powerful AI applications with real-time speech processing and intelligent text generation. Umfassende API-Dokumentation für Audio-Transkriptions- und Sprachmodell-Services. Erstellen Sie leistungsstarke KI-Anwendungen mit Echtzeit-Sprachverarbeitung und intelligenter Textgenerierung. Documentazione completa dell'API per i servizi di trascrizione audio e modelli linguistici. Crea applicazioni AI potenti con elaborazione vocale in tempo reale e generazione intelligente di testo.

Dernière Mise à Jour (5 Janvier 2026): Latest Update (January 5, 2026): Letztes Update (5. Januar 2026): Ultimo Aggiornamento (5 Gennaio 2026): Alignement complet de la documentation sur les endpoints FastAPI actuels (modèles, lexique, santé) et mise à jour des paramètres de transcription. Full alignment with the current FastAPI surface (models, lexicon, health) plus refreshed transcription parameters. Aktualisierte Dokumentation für alle FastAPI-Endpunkte (Modelle, Lexikon, Health) sowie neue Transkriptionsparameter. Documentazione allineata agli endpoint FastAPI attuali (modelli, lessico, health) con parametri di trascrizione aggiornati.

Introduction Introduction Einführung Introduzione

L'API de la plateforme ePV fournit des services de transcription audio et de LLM de pointe conçus pour les applications d'entreprise. Notre plateforme combine la reconnaissance vocale optimisée pour le français avec des modèles de langage puissants "rate_limiting": { The ePV Platform API provides state-of-the-art audio transcription and LLM services designed for enterprise applications. Our platform combines French-optimized speech recognition with powerful language models to deliver accurate, scalable AI solutions. Die ePV Platform API bietet hochmoderne Audio-Transkriptions- und LLM-Services für Unternehmensanwendungen. Unsere Plattform kombiniert französisch-optimierte Spracherkennung mit leistungsstarken Sprachmodellen, um präzise, skalierbare KI-Lösungen zu liefern. L'API della piattaforma ePV fornisce servizi di trascrizione audio e LLM all'avanguardia progettati per applicazioni aziendali. La nostra piattaforma combina il riconoscimento vocale ottimizzato per il francese con potenti modelli linguistici per fornire soluzioni AI accurate e scalabili.

Haute Précision High Accuracy Hohe Genauigkeit Alta Precisione

Modèles Whisper optimisés pour le français avec diarisation des locuteurs pour une transcription précise multi-locuteurs. French-optimized Whisper models with speaker diarization for precise multi-speaker transcription. Französisch-optimierte Whisper-Modelle mit Sprecher-Diarisierung für präzise Multi-Sprecher-Transkription. Modelli Whisper ottimizzati per il francese con diarizzazione degli speaker per trascrizione precisa multi-parlante.

Traitement en Temps Réel Real-time Processing Echtzeit-Verarbeitung Elaborazione in Tempo Reale

Streaming WebSocket pour la transcription en direct avec suivi automatique de l'utilisation et gestion des sessions. WebSocket streaming for live transcription with automatic usage tracking and session management. WebSocket-Streaming für Live-Transkription mit automatischer Nutzungsüberwachung und Sitzungsverwaltung. Streaming WebSocket per trascrizione live con monitoraggio automatico dell'utilizzo e gestione delle sessioni.

Sécurité Entreprise Enterprise Security Unternehmenssicherheit Sicurezza Aziendale

Authentification basée sur des tokens avec journalisation d'audit complète. Token-based authentication with comprehensive audit logging. Token-basierte Authentifizierung mit umfassender Audit-Protokollierung. Autenticazione basata su token con logging di audit completo.

Authentification Authentifizierung Autenticazione

Toutes les requêtes API nécessitent une authentification utilisant des tokens Bearer. Incluez votre token API dans l'en-tête Authorization : Alle API-Anfragen erfordern Authentifizierung mit Bearer-Tokens. Fügen Sie Ihr API-Token in den Authorization-Header ein: Tutte le richieste API richiedono autenticazione usando token Bearer. Includi il tuo token API nell'header Authorization:

Authorization: Bearer epv_1234567890abcdef...

Vous pouvez obtenir des tokens API depuis le tableau de bord de la plateforme ePV à Sie können API-Tokens vom ePV Platform Dashboard erhalten unter Puoi ottenere token API dal dashboard della piattaforma ePV su dashboard.mayorsoftware.ch

URLs de Base Basis-URLs URL di Base

Services de Transcription : Transkriptions-Services: Servizi di Trascrizione:
https://api.mayorsoftware.ch/v1
Transcription WebSocket : WebSocket-Transkription: Trascrizione WebSocket:
wss://api.mayorsoftware.ch/v1/asr

Transcription WebSocket en Temps Réel WebSocket Live-Transkription Trascrizione WebSocket in Tempo Reale

Transcription Audio en Temps Réel Echtzeit-Audio-Transkription Trascrizione Audio in Tempo Reale
WS wss://api.mayorsoftware.ch/v1/asr

Établir une connexion WebSocket pour la transcription audio en temps réel avec support de la diarisation des locuteurs. Stellen Sie eine WebSocket-Verbindung für Echtzeit-Audio-Transkription mit Unterstützung für Sprecher-Diarisierung her. Stabilire una connessione WebSocket per la trascrizione audio in tempo reale con supporto per la diarizzazione degli speaker.

Paramètres de Connexion Verbindungsparameter Parametri di Connessione

Paramètre Parameter Parametro Type Typ Tipo Requis Erforderlich Richiesto Description Beschreibung Descrizione
token query requis erforderlich richiesto Token API pour l'authentification (paramètre d'URL) API-Token für Authentifizierung (URL-Parameter) Token API per autenticazione (parametro URL)
model / model_id query optionnel optional opzionale Identifiant de modèle retourné par /v1/models (défaut : modèle standard) Model identifier as returned by /v1/models (defaults to the standard engine)
enable_diarization query optionnel optional opzionale Activer la diarisation des locuteurs (défaut: false) Sprecher-Diarisierung aktivieren (Standard: false) Abilitare diarizzazione speaker (predefinito: false)

Limites de Taux (Rate Limiting) Rate Limiting Rate-Limiting Rate Limiting

Pour protéger le service contre la surcharge, les limites suivantes s'appliquent par token API: To protect the service against overload, the following limits apply per API token: Zum Schutz des Dienstes vor Überlastung gelten folgende Limits pro API-Token: Per proteggere il servizio dal sovraccarico, si applicano i seguenti limiti per token API:

  • Maximum 3 connexions WebSocket simultanées par token Maximum 3 simultaneous WebSocket connections per token Maximal 3 gleichzeitige WebSocket-Verbindungen pro Token Massimo 3 connessioni WebSocket simultanee per token
  • Maximum 10 nouvelles connexions par minute par token Maximum 10 new connections per minute per token Maximal 10 neue Verbindungen pro Minute pro Token Massimo 10 nuove connessioni al minuto per token

Si ces limites sont dépassées, la connexion WebSocket sera fermée avec le code 1008 (Policy Violation). If these limits are exceeded, the WebSocket connection will be closed with code 1008 (Policy Violation). Wenn diese Limits überschritten werden, wird die WebSocket-Verbindung mit Code 1008 (Richtlinienverletzung) geschlossen. Se questi limiti vengono superati, la connessione WebSocket verrà chiusa con codice 1008 (Violazione delle Policy).

Protocole WebSocket WebSocket Protocol WebSocket-Protokoll Protocollo WebSocket

Séquence de Connexion Connection Sequence Verbindungssequenz Sequenza di Connessione

  1. Connexion WebSocket
    wss://api.mayorsoftware.ch/v1/asr?token=epv_xxx&enable_diarization=true
  2. Message de configuration du serveur (Server → Client)

    Le serveur envoie immédiatement après la connexion :

    {
      "type": "config",
      "useAudioWorklet": false
    }
  3. Message de configuration des signatures vocales (Client → Server)

    OBLIGATOIRE si enable_diarization=true : Le client DOIT envoyer les signatures vocales dans les 5 secondes, sinon la diarisation sera désactivée :

    {
      "type": "config",
      "voice_signatures": [
        {
          "name": "Alice",
          "embedding": [0.123, -0.456, 0.789, ...]  // 512 dimensions
        },
        {
          "name": "Bob",
          "embedding": [0.321, 0.654, -0.987, ...]
        }
      ]
    }

    Note : Si aucune signature n'est disponible mais que la diarisation est activée, envoyez un tableau vide : "voice_signatures": []

  4. Accusé de réception des signatures (Server → Client)

    Le serveur confirme la réception :

    {
      "type": "config_ack",
      "signatures_received": 2
    }
  5. Envoi des données audio

    Le client peut maintenant envoyer des données audio binaires en continu.

Types de Messages Message Types Nachrichtentypen Tipi di Messaggi

1️⃣ Audio Data (Client → Server) - Binary

Envoyez les données audio brutes (WebM/Opus) directement via WebSocket. Pas besoin de JSON ou de base64. Send raw audio data (WebM/Opus) directly via WebSocket. No JSON or base64 encoding needed. Senden Sie rohe Audiodaten (WebM/Opus) direkt über WebSocket. Kein JSON oder Base64-Kodierung erforderlich. Invia dati audio grezzi (WebM/Opus) direttamente tramite WebSocket. Non è necessaria la codifica JSON o base64.

// Send binary audio data directly
ws.send(audioBlobData);

2️⃣ Transcription Result (Server → Client) - JSON

{
  "status": "active_transcription",
  "lines": [
    {
      "speaker": 0,
      "text": "Transcribed text segment",
      "start": "00:00:00.000",
      "end": "00:00:03.200"
    }
  ],
  "buffer_transcription": "",
  "buffer_diarization": "",
  "remaining_time_transcription": 0.0,
  "remaining_time_diarization": 0.0,
  "token_usage": {
    "tokens_consumed": 0.5,
    "base_tokens": 0.5,
    "diarization_tokens": 0.0,
    "voice_signature_tokens": 0.0,
    "llm_tokens": 0.0,
    "duration_minutes": 0.5,
    "billing_metadata": {}
  }
}

3️⃣ End Session (Client → Server) - JSON

Pour terminer proprement une session de transcription, envoyez ce message de contrôle : To properly end a transcription session, send this control message: Um eine Transkriptionssitzung ordnungsgemäß zu beenden, senden Sie diese Kontrollnachricht: Per terminare correttamente una sessione di trascrizione, invia questo messaggio di controllo:

{
  "type": "end_session"
}

Important : Ceci permet au serveur de traiter tout l'audio restant dans le buffer et d'envoyer les derniers résultats avant de fermer la connexion. Le serveur répondra avec un message ready_to_stop quand tout est terminé. Important: This allows the server to process any remaining audio in the buffer and send final results before closing the connection. The server will respond with a ready_to_stop message when complete.

4️⃣ Ready to Stop (Server → Client) - JSON

Le serveur envoie ce message après avoir traité tous les résultats suite à un end_session : The server sends this message after processing all results following an end_session:

{
  "type": "ready_to_stop"
}

Le client peut maintenant fermer la connexion WebSocket en toute sécurité avec ws.close(). The client can now safely close the WebSocket connection with ws.close().

Note: Les champs buffer_transcription, buffer_diarization, remaining_time_transcription, remaining_time_diarization et token_usage sont optionnels et peuvent ne pas être présents dans toutes les réponses.

Cas Spéciaux Special Cases Sonderfälle Casi Speciali

Diarisation désactivée (enable_diarization=false) Diarization disabled (enable_diarization=false)

Le serveur NE DEMANDE PAS de message de configuration avec les signatures vocales. Vous pouvez commencer à envoyer l'audio immédiatement après avoir reçu le message {"type": "config", "useAudioWorklet": false}. The server DOES NOT REQUEST a configuration message with voice signatures. You can start sending audio immediately after receiving the {"type": "config", "useAudioWorklet": false} message.

Timeout du message de configuration Configuration message timeout

Si enable_diarization=true et qu'aucun message de configuration n'est reçu dans les 5 secondes, la diarisation sera DÉSACTIVÉE. Les signatures vocales sont obligatoires pour utiliser la diarisation. If enable_diarization=true and no configuration message is received within 5 seconds, diarization will be DISABLED. Voice signatures are required to use diarization.

Reconnexion Reconnection

Pour démarrer une nouvelle session après une déconnexion, vous devez établir une nouvelle connexion WebSocket et recommencer le processus complet (y compris l'envoi du message de configuration si nécessaire). To start a new session after disconnection, you must establish a new WebSocket connection and restart the complete process (including sending the configuration message if needed).

Exemples de Code Code Examples Codebeispiele Esempi di Codice

const apiToken = 'epv_your_token_here';
const enableDiarization = true;
const voiceSignatures = [  // Get from /generate_signatures endpoint
  { name: 'Alice', embedding: [/* 512 dimensions */] },
  { name: 'Bob', embedding: [/* 512 dimensions */] }
];

const wsUrl = `wss://api.mayorsoftware.ch/v1/asr?token=${encodeURIComponent(apiToken)}&enable_diarization=${enableDiarization}`;

const ws = new WebSocket(wsUrl);
let mediaRecorder = null;
let configReceived = false;

ws.onopen = () => {
  console.log('WebSocket connected, waiting for server config...');
};

ws.onmessage = async (event) => {
  const data = JSON.parse(event.data);
  
  // 1. Handle initial server config
  if (data.type === 'config' && !configReceived) {
    console.log('Received server config:', data);
    configReceived = true;
    
    // 2. Send voice signatures if diarization is enabled
    if (enableDiarization && voiceSignatures.length > 0) {
      console.log('Sending voice signatures...');
      ws.send(JSON.stringify({
        type: 'config',
        voice_signatures: voiceSignatures
      }));
    } else if (enableDiarization) {
      // Send empty array if no signatures but diarization enabled
      ws.send(JSON.stringify({
        type: 'config',
        voice_signatures: []
      }));
    }
    
    // 3. Start audio capture after config is sent (or skipped)
    if (!enableDiarization || voiceSignatures) {
      startAudioCapture();
    }
    return;
  }
  
  // 4. Handle signature acknowledgment
  if (data.type === 'config_ack') {
    console.log(`Signatures confirmed: ${data.signatures_received} received`);
    startAudioCapture();
    return;
  }
  
  // 5. Handle ready_to_stop signal
  if (data.type === 'ready_to_stop') {
    console.log('Server ready to stop, closing connection...');
    ws.close();
    return;
  }
  
  // 6. Handle transcription results
  if (data.status === 'active_transcription' && data.lines) {
    data.lines.forEach(line => {
      if (line.text && line.text.trim()) {
        console.log(`Speaker ${line.speaker}: ${line.text}`);
      }
    });
    
    // Check for token usage information
    if (data.token_usage) {
      console.log(`Tokens consumed: ${data.token_usage.tokens_consumed}`);
    }
  }
};

function startAudioCapture() {
  console.log('Starting audio capture...');
  navigator.mediaDevices.getUserMedia({ audio: true })
    .then(stream => {
      mediaRecorder = new MediaRecorder(stream, {
        mimeType: 'audio/webm;codecs=opus'
      });
      
      mediaRecorder.ondataavailable = (event) => {
        if (event.data.size > 0 && ws.readyState === WebSocket.OPEN) {
          // Send raw binary audio data
          ws.send(event.data);
        }
      };
      
      mediaRecorder.start(250); // Send data every 250ms
      console.log('Audio capture started');
    })
    .catch(err => console.error('Audio capture error:', err));
}

function stopRecording() {
  console.log('⏹️ Stopping recording...');
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
  }
  
  // Send end_session message for graceful shutdown
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'end_session' }));
    console.log('Sent end_session, waiting for server confirmation...');
  }
}

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
  console.log(`WebSocket disconnected (code: ${event.code}, reason: ${event.reason || 'none'})`);
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
  }
};

// Usage: Call stopRecording() when user wants to end the session
// setTimeout(() => stopRecording(), 60000); // Example: stop after 60 seconds
import asyncio
import websockets
import json
import pyaudio

async def transcribe_live():
    api_token = "epv_your_token_here"
    uri = f"wss://api.mayorsoftware.ch/v1/asr?token={api_token}"
    
    async with websockets.connect(uri) as websocket:
        # Audio configuration
        chunk = 1024
        format = pyaudio.paInt16
        channels = 1
        rate = 16000
        
        audio = pyaudio.PyAudio()
        stream = audio.open(
            format=format,
            channels=channels,
            rate=rate,
            input=True,
            frames_per_buffer=chunk
        )
        
        async def send_audio():
            while True:
                # Read and send raw audio data
                data = stream.read(chunk)
                await websocket.send(data)
                await asyncio.sleep(0.05)
        
        async def receive_transcription():
            async for message in websocket:
                data = json.loads(message)
                if data.get("status") == "active_transcription" and data.get("lines"):
                    for line in data["lines"]:
                        if line.get("text") and line["text"].strip():
                            print(f"Speaker {line['speaker']}: {line['text']}")
                    
                    # Check for token usage information
                    if data.get("token_usage"):
                        tokens = data["token_usage"].get("tokens_consumed", 0)
                        print(f"Tokens consumed: {tokens}")
        
        await asyncio.gather(send_audio(), receive_transcription())

asyncio.run(transcribe_live())

Catalogue des Modèles Model Catalog Modellkatalog Catalogo Modelli

Lister les moteurs disponibles List available transcription engines Verfügbare Transkriptions-Engines Elenco dei motori di trascrizione
GET https://api.mayorsoftware.ch/v1/models

Retourne la liste des identifiants de modèle actifs, le modèle par défaut et les métadonnées détaillées. Disponible également via /models (alias sans préfixe /v1). Returns the active model identifiers, the current default model, and metadata describing each engine. Also reachable at /models (alias without the /v1 prefix).

Response Format

{
  "models": ["standard", "baseline"],
  "default": "standard",
  "details": [
    {
      "id": "standard",
      "label": "Whisper Large V3 Turbo",
      "description": "Primary production model",
      "backend": "faster-whisper",
      "model_size": "large-v3-turbo",
      "language": "fr",
      "release": "2024-12-15",
      "dataset": "SwissParliament",
      "quantization": "float16",
      "training_stats": { "wer": 5.1 },
      "adapter_rank": 16,
      "lora_alpha": 8,
      "available": true,
      "notes": "Optimised for livestreams"
    }
  ]
}

Keys inside details are optional and mirror the metadata loaded from the model registry. Any dynamically registered model appears even if it lacks metadata, in which case only id, label and available are populated.

Detail Fields

Field Type Description
id string Identifier to pass via model_id on transcription endpoints
backend string Engine backend (faster-whisper, simulstreaming, ...)
model_size string Underlying Whisper size or descriptor (e.g., large-v3-turbo)
available boolean Indicates if the engine is currently loaded and ready
notes string Free-form operational notes (quantisation, lora rank, etc.)

Gestion du Lexique Lexicon Management Lexikonverwaltung Gestione Lessico

Ces endpoints permettent de diagnostiquer et relancer la structure de trie utilisée pour le post-traitement lexical. Use these endpoints to inspect and reload the contextual lexicon trie that powers automatic corrections.

Lexicon Status
GET https://api.mayorsoftware.ch/lexicon/status

Returns the configuration currently in memory, whether the trie is enabled, and diagnostics for fuzzy/LLM helpers.

Response Example

{
  "enabled": true,
  "path": "/app/lexicon/lexicon_trie.json",
  "min_weight": 0.35,
  "max_span": 6,
  "loaded": true,
  "children": 742,
  "sentence_case": true,
  "allow_case_only": false,
  "allowed_sources": ["gazette", "manual"],
  "fuzzy": {
    "enabled": true,
    "buckets": 128,
    "similarity": 0.92,
    "min_weight": 0.25,
    "bucket_prefix": "fuzzy_",
    "bucket_len_slack": 2,
    "debug": false,
    "spacy": {
      "enabled": true,
      "model": "fr_core_news_md",
      "loaded": true,
      "error": null,
      "delta": 0.17
    }
  },
  "llm": {
    "enabled": false,
    "model": null,
    "max_spans": 16,
    "max_candidates": 4,
    "timeout": 3.0,
    "cache_max": 256,
    "cache_size": 0,
    "phonetic_index": 0,
    "debug": false
  }
}
Reload Lexicon
POST https://api.mayorsoftware.ch/lexicon/reload

Reloads the trie file and optionally overrides runtime settings. Useful when deploying a fresh lexicon without restarting.

JSON Body

Field Type Required Description
path string optional Override path to the trie JSON file before reloading
min_weight number optional Override minimum weight accepted for replacements
max_span integer optional Maximum number of tokens per lexicon span (defaults to config value)
enabled boolean optional Enable or disable lexicon post-processing entirely

Every field is optional. The endpoint returns the same structure as /lexicon/status along with {"success": true} when the reload succeeds.

Generate Voice Signatures

Generate Speaker Embeddings
POST https://api.mayorsoftware.ch/v1/generate_signatures

Upload audio samples for each speaker and generate neural embeddings (512-dimensional vectors). These embeddings can then be used for speaker identification in transcription requests.

Request Parameters

Parameter Type Required Description
audio_files file[] required Audio files containing speaker voice samples (minimum 1 second each)
speaker_names string required JSON array of speaker names matching the audio files order

Response Format

{
  "success": true,
  "signatures": [
    {
      "name": "John Doe",
      "embedding": [0.123, -0.456, 0.789, ...] // 512-dimensional vector
    },
    {
      "name": "Jane Smith",
      "embedding": [0.321, 0.654, -0.987, ...]
    }
  ],
  "count": 2
}

Code Examples

const formData = new FormData();
formData.append('audio_files', johnAudioFile);
formData.append('audio_files', janeAudioFile);
formData.append('speaker_names', JSON.stringify(['John Doe', 'Jane Smith']));

const response = await fetch('https://api.mayorsoftware.ch/v1/generate_signatures', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`
  },
  body: formData
});

const data = await response.json();
console.log(`Generated ${data.count} signatures`);

// Save signatures for later use in transcription
localStorage.setItem('voiceSignatures', JSON.stringify(data.signatures));
import requests

# Prepare audio files
files = [
    ('audio_files', ('john.wav', open('john.wav', 'rb'), 'audio/wav')),
    ('audio_files', ('jane.wav', open('jane.wav', 'rb'), 'audio/wav'))
]

data = {
    'speaker_names': '["John Doe", "Jane Smith"]'
}

response = requests.post(
    'https://api.mayorsoftware.ch/v1/generate_signatures',
    headers={'Authorization': f'Bearer {token}'},
    files=files,
    data=data
)

result = response.json()
print(f"Generated {result['count']} signatures")

# Save signatures for later use
signatures = result['signatures']

Mode de Raffinement des Signatures Signature Refinement Mode Signatur-Verfeinerungsmodus Modalità Raffinamento Firme

Qu'est-ce que le Raffinement Adaptatif? What is Adaptive Refinement?

Le mode de raffinement des signatures améliore automatiquement l'identification des locuteurs en adaptant les signatures vocales aux conditions acoustiques de l'enregistrement. Le système collecte les segments audio avec la meilleure correspondance et crée des signatures "affinées" spécifiques à la session, combinant les signatures originales (35%) avec les données de l'enregistrement (65%). Signature refinement mode automatically improves speaker identification by adapting voice signatures to the acoustic conditions of the recording. The system collects the best-matching audio segments and creates session-specific "refined" signatures that blend the original signatures (35%) with recording data (65%).

Modes Disponibles Available Modes

"off" (default)

Désactivé - Utilise uniquement les signatures originales fournies. Aucune adaptation n'est effectuée. Recommandé pour des enregistrements courts ou des tests rapides. Disabled - Uses only the original signatures provided. No adaptation is performed. Recommended for short recordings or quick tests.

"on" (recommended)

Activé - Effectue deux passes de diarisation: d'abord avec les signatures originales pour collecter les meilleures correspondances (similarité > 0.3), puis avec les signatures affinées. Améliore considérablement la précision sur des enregistrements de plus de 2 minutes. Enabled - Performs two diarization passes: first with original signatures to collect best matches (similarity > 0.3), then with refined signatures. Significantly improves accuracy on recordings longer than 2 minutes.

💡 Configuration automatique: 💡 Automatic configuration:
  • Utilise jusqu'à 5 frames par locuteur Uses up to 5 frames per speaker
  • Durée minimale requise: 1.5 secondes Minimum duration required: 1.5 seconds
  • Mélange: 35% signature originale + 65% données de session Blend: 35% original signature + 65% session data

"compare" (diagnostic)

Mode comparaison - Identique à "on" mais retourne également les segments de la passe initiale dans baseline_segments. Utile pour évaluer l'amélioration apportée par le raffinement. Consomme plus de mémoire. Comparison mode - Same as "on" but also returns segments from the initial pass in baseline_segments. Useful for evaluating the improvement from refinement. Consumes more memory.

Comment Ça Marche How It Works

  1. Passe 1 (Collecte) - Diarisation avec signatures originales. Le système collecte les frames audio avec similarité ≥ 0.3 (haute confiance). Pass 1 (Collection) - Diarization with original signatures. The system collects audio frames with similarity ≥ 0.3 (high confidence).
  2. Adaptation - Pour chaque locuteur avec au moins 1.5s de données haute confiance, crée une signature affinée en combinant l'embedding original et l'embedding de session. Adaptation - For each speaker with at least 1.5s of high-confidence data, creates a refined signature by blending the original embedding with the session embedding.
  3. Passe 2 (Raffinement) - Re-diarisation complète avec les signatures affinées, mieux adaptées à l'acoustique spécifique de l'enregistrement. Pass 2 (Refinement) - Complete re-diarization with refined signatures, better adapted to the specific acoustics of the recording.
⚠️ Important ⚠️ Important

Le raffinement nécessite environ 2× le temps de traitement d'une diarisation simple. Pour des enregistrements courts (<2 min), l'amélioration peut être marginale. Refinement requires approximately 2× the processing time of simple diarization. For short recordings (<2 min), the improvement may be marginal.

Format de Réponse Response Format

Lorsque le raffinement est activé, la réponse inclut l'objet signature_refinement: When refinement is enabled, the response includes the signature_refinement object:

{
  "text": "Full transcription text...",
  "segments": [...],
  "signature_refinement": {
    "mode": "on",
    "applied": true,
    "iterations": 2,
    "stats": [
      {
        "speaker_id": 1,
        "speaker_name": "Alice",
        "num_frames": 5,
        "total_duration": 7.5,
        "avg_similarity": 0.4523,
        "source": "refined"
      },
      {
        "speaker_id": 2,
        "speaker_name": "Bob",
        "num_frames": 0,
        "total_duration": 0.0,
        "avg_similarity": 0.0,
        "source": "original"
      }
    ]
  }
}

Champs de la Réponse Response Fields

Champ Field Type Description
mode string Mode utilisé: "off", "on" ou "compare" Mode used: "off", "on" or "compare"
applied boolean true si au moins une signature a été affinée et utilisée. false si le raffinement a été sauté (données insuffisantes ou mode "off") true if at least one signature was refined and used. false if refinement was skipped (insufficient data or mode "off")
iterations integer Nombre de passes de diarisation (1 ou 2) Number of diarization passes (1 or 2)
stats array Statistiques par locuteur (voir détails ci-dessous) Per-speaker statistics (see details below)

Champs de stats stats Fields

Champ Field Type Description
speaker_id integer Identifiant unique du locuteur Unique speaker identifier
speaker_name string Nom du locuteur Speaker name
num_frames integer Nombre de frames audio utilisées pour l'adaptation (0 = signature originale non modifiée) Number of audio frames used for adaptation (0 = original signature unchanged)
total_duration float Durée totale d'audio haute confiance utilisée (en secondes) Total duration of high-confidence audio used (in seconds)
avg_similarity float Similarité moyenne des frames collectées (0.0-1.0) Average similarity of collected frames (0.0-1.0)
source string "refined" si affiné, if refined, "original" si signature non modifiée if signature unchanged

Recommandations d'Utilisation Usage Recommendations

✅ Utiliser "on" pour: ✅ Use "on" for:
  • Enregistrements de réunions ou conférences (> 5 minutes) Meeting or conference recordings (> 5 minutes)
  • Environnements bruyants ou qualité audio variable Noisy environments or variable audio quality
  • Locuteurs avec plusieurs interventions longues Speakers with multiple long interventions
  • Production où la précision prime sur la vitesse Production where accuracy is more important than speed
❌ Utiliser "off" pour: ❌ Use "off" for:
  • Enregistrements courts (< 2 minutes) Short recordings (< 2 minutes)
  • Traitement en temps réel ou streaming Real-time processing or streaming
  • Tests rapides ou développement Quick tests or development
  • Contraintes de latence strictes Strict latency constraints
🔬 Utiliser "compare" pour: 🔬 Use "compare" for:
  • Évaluation de la performance du raffinement Evaluating refinement performance
  • Débogage de problèmes de diarisation Debugging diarization issues
  • Analyse comparative avant/après Before/after comparative analysis

Synchronous Transcription

Upload and Transcribe Audio File
POST https://api.mayorsoftware.ch/v1/transcribe/sync

Upload an audio file and receive the complete transcription synchronously. Best for smaller files.

Alias disponible via /transcribe/sync lorsque le proxy supprime le préfixe /v1.

Request Parameters

Parameter Type Required Description
audio file required Audio file (WAV, MP3, M4A, FLAC, OGG)
diarization string optional Enable speaker diarization ("true"/"false", default: "false"). Requires valid voice_signatures otherwise it is silently disabled.
use_voice_signatures string optional Explicitly enable the signature-guided pipeline ("true"/"false", default: "false")
voice_signatures string optional JSON string of pre-generated embeddings from /generate_signatures (e.g., '[{"name":"John","embedding":[...]}]')
speaker_names string optional (Deprecated) Speaker names are now embedded in voice_signatures JSON
voice_similarity_threshold string optional Similarity threshold for voice matching (0.0-1.0, default: 0.18)
model_id string optional Override the model to use (value from /v1/models). Defaults to the backend default id.
signature_refinement_mode string optional Adaptive diarization refinement: "off" (default), "on", or "compare". Ignored when diarization/signature matching is disabled.

Response Format

{
  "text": "Full transcription text",
  "segments": [
    {
      "text": "First segment",
      "speaker": 0,
      "start": "00:00:00.000",
      "end": "00:00:03.200"
    }
  ],
  "filename": "audio.wav",
  "duration": 15.3,
  "language": "fr",
  "diarization_enabled": true,
  "voice_signatures_used": false,
  "diarization_summary": {
    "total_segments": 5,
    "unique_speakers": 2,
    "speaker_durations": {
      "0": 8.5,
      "1": 6.8
    }
  },
  "speaker_info": {},
  "processing_mode": "sync",
  "token_usage": {
    "tokens_consumed": 0.5,
    "base_tokens": 0.25,
    "diarization_tokens": 0.125,
    "voice_signature_tokens": 0.0,
    "llm_tokens": 0.0,
    "duration_minutes": 0.255,
    "billing_metadata": {
      "filename": "audio.wav",
      "duration_seconds": 15.3,
      "num_segments": 5,
      "unique_speakers": 2,
      "processing_mode": "sync"
    }
  }
}

Code Examples

const transcribeFile = async (audioFile) => {
  // Step 1: Generate voice signatures (if using known speakers)
  const sigFormData = new FormData();
  sigFormData.append('audio_files', speakerAudioFile1); // Sample from speaker 1
  sigFormData.append('audio_files', speakerAudioFile2); // Sample from speaker 2
  sigFormData.append('speaker_names', JSON.stringify(['Alice', 'Bob']));

  const sigResponse = await fetch('https://api.mayorsoftware.ch/generate_signatures', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer epv_your_token_here'
    },
    body: sigFormData
  });

  const { signatures } = await sigResponse.json();
  // signatures = [{name: 'Alice', embedding: [...]}, {name: 'Bob', embedding: [...]}]

  // Step 2: Transcribe with voice signatures
  const formData = new FormData();
  formData.append('audio', audioFile);
  formData.append('diarization', 'true');
  formData.append('use_voice_signatures', 'true');
  formData.append('voice_signatures', JSON.stringify(signatures)); // Send as JSON string
  formData.append('voice_similarity_threshold', '0.18');

  try {
    const response = await fetch('https://api.mayorsoftware.ch/v1/transcribe/sync', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer epv_your_token_here'
      },
      body: formData
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();
    console.log('Transcription:', result.text);
    console.log('Duration:', result.duration);
    console.log('Language:', result.language);
    
    // Process segments with speaker information
    result.segments.forEach(segment => {
      console.log(`Speaker ${segment.speaker} (${segment.start}-${segment.end}): ${segment.text}`);
    });
    
    // Display diarization summary
    if (result.diarization_summary) {
      console.log(`Total segments: ${result.diarization_summary.total_segments}`);
      console.log(`Unique speakers: ${result.diarization_summary.unique_speakers}`);
    }
    
    // Display token usage
    if (result.token_usage) {
      console.log(`Tokens consumed: ${result.token_usage.tokens_consumed}`);
    }

  } catch (error) {
    console.error('Transcription failed:', error);
  }
};

// Usage with file input
document.getElementById('fileInput').addEventListener('change', (event) => {
  const file = event.target.files[0];
  if (file) {
    transcribeFile(file);
  }
});
import requests

def transcribe_audio_file(file_path, enable_diarization=True):
    url = 'https://api.mayorsoftware.ch/v1/transcribe/sync'
    
    headers = {
        'Authorization': 'Bearer epv_your_token_here'
    }
    
    files = {
        'audio': open(file_path, 'rb')
    }
    
    data = {
        'diarization': str(enable_diarization).lower()
    }
    
    try:
        response = requests.post(url, headers=headers, files=files, data=data)
        response.raise_for_status()
        
        result = response.json()
        
        print(f"Transcription: {result['text']}")
        print(f"Duration: {result['duration']}s")
        print(f"Language: {result['language']}")
        
        # Print diarization summary
        if result.get('diarization_summary'):
            summary = result['diarization_summary']
            print(f"Speakers: {summary['unique_speakers']}")
            print(f"Total segments: {summary['total_segments']}")
        
        # Print segments with timestamps
        for segment in result['segments']:
            print(f"Speaker {segment['speaker']} ({segment['start']}-{segment['end']}): {segment['text']}")
        
        # Print token usage
        if result.get('token_usage'):
            print(f"Tokens consumed: {result['token_usage']['tokens_consumed']}")
            
        return result
        
    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")
        return None
    finally:
        files['audio'].close()

# Usage
result = transcribe_audio_file('meeting_recording.wav')

Asynchronous Transcription - Submit

Submit Audio for Background Processing
POST https://api.mayorsoftware.ch/v1/transcribe/submit

Submit an audio file for asynchronous processing. Returns a task ID to track progress and retrieve results.

Alias: /transcribe/submit (sans préfixe /v1).

Request Parameters

Parameter Type Required Description
audio file required Audio file (WAV, MP3, M4A, FLAC, OGG)
diarization string optional Enable speaker diarization ("true"/"false", default: "false"). Requires voice_signatures to stay enabled.
voice_signatures string optional JSON string of pre-generated embeddings from /generate_signatures (e.g., '[{"name":"John","embedding":[...]}]')
voice_similarity_threshold string optional Cosine similarity threshold for voice matching (default: "0.18", range: "0.0"-"1.0")
speaker_names string optional (Deprecated) Speaker names are now embedded in voice_signatures JSON
use_voice_signatures string optional Enable voice signature matching ("true"/"false")
model_id string optional Model identifier from /v1/models
signature_refinement_mode string optional "off" (default), "on", or "compare" to control adaptive signature refinement when diarization is active.

Response Format

{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "message": "Task submitted successfully. Use /transcribe/status/{task_id} to check progress.",
  "model_id": "standard",
  "token_usage": {
    "tokens_consumed": 0.5,
    "base_tokens": 0.25,
    "diarization_tokens": 0.125,
    "voice_signature_tokens": 0.0,
    "llm_tokens": 0.0,
    "duration_minutes": 0.255,
    "billing_metadata": {
      "filename": "audio.wav",
      "task_id": "550e8400-e29b-41d4-a716-446655440000",
      "processing_mode": "async",
      "estimated": true
    }
  }
}

Code Examples

const submitAsyncTranscription = async (audioFile, signatures = null) => {
  // Step 1: Generate voice signatures if needed
  let voiceSignatures = signatures;
  if (!voiceSignatures && speakerAudioSamples) {
    const sigFormData = new FormData();
    speakerAudioSamples.forEach(sample => {
      sigFormData.append('audio_files', sample);
    });
    sigFormData.append('speaker_names', JSON.stringify(['Alice', 'Bob']));

    const sigResponse = await fetch('https://api.mayorsoftware.ch/generate_signatures', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer epv_your_token_here'
      },
      body: sigFormData
    });

    const sigResult = await sigResponse.json();
    voiceSignatures = sigResult.signatures;
  }

  // Step 2: Submit transcription task
  const formData = new FormData();
  formData.append('audio', audioFile);
  formData.append('diarization', 'true');
  if (voiceSignatures) {
    formData.append('use_voice_signatures', 'true');
    formData.append('voice_signatures', JSON.stringify(voiceSignatures));
    formData.append('voice_similarity_threshold', '0.18');
  }

  try {
    const response = await fetch('https://api.mayorsoftware.ch/v1/transcribe/submit', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer epv_your_token_here'
      },
      body: formData
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();
    console.log('Task submitted:', result.task_id);
    console.log('Status:', result.status);
    console.log('Message:', result.message);
    
    // Display estimated token usage
    if (result.token_usage) {
      console.log(`Estimated tokens: ${result.token_usage.tokens_consumed}`);
    }
    
    return result.task_id;

  } catch (error) {
    console.error('Submission failed:', error);
    return null;
  }
};

// Usage
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (event) => {
  const file = event.target.files[0];
  if (file) {
    const taskId = await submitAsyncTranscription(file);
    if (taskId) {
      // Poll for results
      pollTranscriptionStatus(taskId);
    }
  }
});
import requests
import json

def generate_signatures(audio_files, speaker_names):
    """Generate voice signatures from audio samples."""
    url = 'https://api.mayorsoftware.ch/generate_signatures'
    
    headers = {
        'Authorization': 'Bearer epv_your_token_here'
    }
    
    files = [('audio_files', open(f, 'rb')) for f in audio_files]
    data = {
        'speaker_names': json.dumps(speaker_names)
    }
    
    try:
        response = requests.post(url, headers=headers, files=files, data=data)
        response.raise_for_status()
        return response.json()['signatures']
    finally:
        for _, file_obj in files:
            file_obj.close()

def submit_async_transcription(file_path, signatures=None):
    url = 'https://api.mayorsoftware.ch/v1/transcribe/submit'
    
    headers = {
        'Authorization': 'Bearer epv_your_token_here'
    }
    
    files = {
        'audio': open(file_path, 'rb')
    }
    
    data = {
        'diarization': 'true'
    }
    
    # Add voice signatures if provided
    if signatures:
        data['use_voice_signatures'] = 'true'
        data['voice_signatures'] = json.dumps(signatures)
        data['voice_similarity_threshold'] = '0.18'
    
    try:
        response = requests.post(url, headers=headers, files=files, data=data)
        response.raise_for_status()
        
        result = response.json()
        
        print(f"Task submitted: {result['task_id']}")
        print(f"Status: {result['status']}")
        print(f"Message: {result['message']}")
        
        # Display estimated token usage
        if result.get('token_usage'):
            print(f"Estimated tokens: {result['token_usage']['tokens_consumed']}")
        
        return result['task_id']
        
    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")
        return None
    finally:
        files['audio'].close()

# Usage
task_id = submit_async_transcription('large_meeting.mp3')
if task_id:
    print(f"Track your task at: https://api.mayorsoftware.ch/v1/transcribe/status/{task_id}")

Check Transcription Status

Get Task Status
GET https://api.mayorsoftware.ch/v1/transcribe/status/{task_id}

Check the processing status of an asynchronous transcription task.

Disponible également via /transcribe/status/{task_id}.

Response Format

{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "progress": 100,
  "current_step": "Completed successfully",
  "created_at": "2025-11-07T10:30:00Z",
  "started_at": "2025-11-07T10:30:01Z",
  "completed_at": "2025-11-07T10:31:45Z",
  "filename": "audio.wav",
  "duration": 104.5,
  "error": null
}

Status Values

Status Description
queued Job is waiting to be processed
processing Job is currently being processed
completed Job completed successfully
failed Job failed with error
cancelled Job was cancelled by user

Get Transcription Result

Retrieve Completed Transcription
GET https://api.mayorsoftware.ch/v1/transcribe/result/{task_id}

Retrieve the transcription result for a completed task.

Disponible également via /transcribe/result/{task_id}.

Response Format

{
  "text": "Complete transcription text",
  "segments": [
    {
      "text": "Segment text",
      "speaker": 0,
      "start": "00:00:00.000",
      "end": "00:00:03.200"
    }
  ],
  "filename": "audio.wav",
  "duration": 120.5,
  "language": "fr",
  "diarization_enabled": true,
  "voice_signatures_used": 2,
  "model_id": "standard",
  "diarization_summary": {
    "total_segments": 25,
    "unique_speakers": 3,
    "speaker_durations": {
      "0": 45.2,
      "1": 38.7,
      "2": 36.6
    }
  },
  "speaker_info": {},
  "processing_mode": "background",
  "token_usage": {
    "tokens_consumed": 2.5,
    "base_tokens": 2.0,
    "diarization_tokens": 0.5,
    "voice_signature_tokens": 0.5,
    "llm_tokens": 0.0,
    "duration_minutes": 2.008,
    "billing_metadata": {
      "filename": "audio.wav",
      "duration": 120.5,
      "num_segments": 25,
      "unique_speakers": 3,
      "processing_mode": "background",
      "model_id": "standard"
    }
  },
  "signature_refinement": {
    "mode": "on",
    "iterations": 2,
    "matches": [
      {
        "speaker": "Alice",
        "score": 0.88
      }
    ]
  }
}

Cancel Transcription Task

Cancel Processing Task
DELETE https://api.mayorsoftware.ch/v1/transcribe/cancel/{task_id}

Cancel a queued or processing transcription task.

Alias: /transcribe/cancel/{task_id}.

Response Format

{
  "message": "Task cancelled successfully",
  "task_id": "550e8400-e29b-41d4-a716-446655440000"
}

Reference Transcripts

Lookup training reference text
GET https://api.mayorsoftware.ch/reference-transcript/{filename}

Provide a filename (with or without extension) to retrieve the authoritative transcription stored in the training manifests. The service searches both train_manifest.csv and val_manifest.csv inside the training volume.

Path Parameters

Parameter Type Description
filename string Base filename or audio_path used inside the manifest (case-sensitive)

Response Example

{
  "filename": "session_01.wav",
  "reference_text": "Mesdames et Messieurs, bienvenue...",
  "duration": 123.4,
  "source": "Swiss Parliament"
}

HTTP 404 is returned when the filename cannot be found in either manifest file. Any unexpected error triggers a 500 response with the error message in detail.

Statistiques Détaillées Detailed Statistics Detaillierte Statistiken Statistiche Dettagliate

Obtenir les statistiques de connexion Get connection statistics Verbindungsstatistiken abrufen Ottieni statistiche di connessione
GET /stats

Endpoint de monitoring détaillé qui retourne les connexions actives par token et les limites de rate limiting. Utile pour le debugging et l'analyse de l'utilisation du service. Detailed monitoring endpoint that returns active connections per token and rate limiting thresholds. Useful for debugging and service usage analysis. Detaillierter Überwachungsendpunkt, der aktive Verbindungen pro Token und Rate-Limiting-Schwellenwerte zurückgibt. Nützlich für Debugging und Nutzungsanalyse des Dienstes. Endpoint di monitoraggio dettagliato che restituisce connessioni attive per token e soglie di rate limiting. Utile per il debugging e l'analisi dell'utilizzo del servizio.

Réponse Succès (200 OK) Success Response (200 OK) Erfolgreiche Antwort (200 OK) Risposta di Successo (200 OK)

{
  "active_connections_per_token": {
    "abc123...": 2,
    "def456...": 1
  },
  "total_active_connections": 3,
  "rate_limits": {
    "max_concurrent_per_token": 3,
    "max_per_minute_per_token": 10
  }
}

Description des Champs Field Descriptions Feldbeschreibungen Descrizioni dei Campi

Champ Field Feld Campo Type Type Typ Tipo Description Description Beschreibung Descrizione
active_connections_per_token object Mapping token → nombre de connexions actives Mapping token → number of active connections Zuordnung Token → Anzahl aktiver Verbindungen Mappa token → numero di connessioni attive
total_active_connections integer Nombre total de connexions actives (tous tokens) Total number of active connections (all tokens) Gesamtzahl aktiver Verbindungen (alle Tokens) Numero totale di connessioni attive (tutti i token)
rate_limiting object Configuration des limites appliquées (max simultané, max/minute) Current throttling thresholds (max concurrent, max per minute) Aktuelle Drosselwerte (gleichzeitig & pro Minute) Limiti di throttling attivi (simultanei e al minuto)
max_concurrent_per_token integer Maximum de connexions simultanées par token (3) Maximum concurrent connections per token (3) Maximale gleichzeitige Verbindungen pro Token (3) Massimo connessioni simultanee per token (3)
max_per_minute_per_token integer Maximum de nouvelles connexions par minute par token (10) Maximum new connections per minute per token (10) Maximale neue Verbindungen pro Minute pro Token (10) Massimo nuove connessioni al minuto per token (10)

Rate Limiting Rate Limiting Rate-Limiting Rate Limiting

Le service applique des limites strictes pour éviter la surcharge: The service applies strict limits to prevent overload: Der Dienst wendet strenge Limits an, um Überlastung zu vermeiden: Il servizio applica limiti rigorosi per prevenire il sovraccarico:

  • 3 connexions simultanées maximum par token API Maximum 3 simultaneous connections per API token Maximal 3 gleichzeitige Verbindungen pro API-Token Massimo 3 connessioni simultanee per token API
  • 10 nouvelles connexions maximum par minute par token Maximum 10 new connections per minute per token Maximal 10 neue Verbindungen pro Minute pro Token Massimo 10 nuove connessioni al minuto per token
  • Code d'erreur WebSocket 1008 si les limites sont dépassées WebSocket error code 1008 if limits are exceeded WebSocket-Fehlercode 1008 bei Überschreitung der Limits Codice errore WebSocket 1008 se i limiti vengono superati

Vérification de Santé Health Check Health Check Verifica di Salute

Service heartbeat
GET https://api.mayorsoftware.ch/health

Lightweight readiness probe used by orchestration, returns the number of active WebSocket sessions and unique API tokens currently connected.

Response Example

{
  "status": "healthy",
  "active_connections": 2,
  "unique_tokens": 2
}

LLM Chat Completion

Generate Text with Language Models
POST https://api.mayorsoftware.ch/v1/llm/chat/completions

Chat completion endpoint powered by Qwen3 and other models. Supports streaming and non-streaming responses.

Alias sans préfixe : /llm/chat/completions.

Request Parameters

Parameter Type Required Description
messages array required Array of message objects with role and content
model string optional Model name: "qwen3" (default)
profile_type string optional Response style: "standard" (default), "creative", or "precise"
temperature float optional Randomness (0.0-2.0, default: 1.0)
max_tokens integer optional Maximum response tokens (omit for unlimited)
stream boolean optional Enable streaming response (default: false)

Request Format

{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": "Summarize this meeting transcript..."
    }
  ],
  "model": "qwen3",
  "profile_type": "standard",
  "temperature": 1.0,
  "max_tokens": 2048,
  "stream": false
}

Response Format

{
  "id": "chatcmpl-7890123456",
  "model": "qwen3",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "Generated response text..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 45,
    "completion_tokens": 123,
    "total_tokens": 168
  }
}

JavaScript Example (Non-Streaming)

const generateResponse = async (messages) => {
  try {
    const response = await fetch('https://api.mayorsoftware.ch/v1/llm/chat/completions', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer epv_your_token_here',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messages: messages,
        model: 'qwen3',
        profile_type: 'standard',
        temperature: 1.0,
        stream: false
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();
    return result.choices[0].message.content;

  } catch (error) {
    console.error('LLM request failed:', error);
    return null;
  }
};

// Usage
const messages = [
  { role: 'system', content: 'You are a helpful assistant.' },
  { role: 'user', content: 'Write a short story about AI.' }
];
const response = await generateResponse(messages);
console.log(response);

JavaScript Example (Streaming)

const streamResponse = async (messages) => {
  try {
    const response = await fetch('https://api.mayorsoftware.ch/v1/llm/chat/completions', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer epv_your_token_here',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messages: messages,
        model: 'qwen3',
        profile_type: 'standard',
        temperature: 1.0,
        stream: true
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';
    let fullText = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value, { stream: true });
      buffer += chunk;
      const lines = buffer.split('\n');
      buffer = lines.pop() || '';

      for (const line of lines) {
        if (line.startsWith('data: ') && !line.includes('[DONE]')) {
          try {
            const data = JSON.parse(line.slice(6));
            if (data.choices && data.choices[0].delta.content) {
              const content = data.choices[0].delta.content;
              fullText += content;
              console.log(content); // Display token by token
            }
          } catch (e) {
            // Ignore parsing errors
          }
        }
      }
    }

    return fullText;

  } catch (error) {
    console.error('Streaming failed:', error);
    return null;
  }
};