# Transcription en direct

> Envoyez l'audio du micro ou d'un onglet à transcript1 par WebSocket et recevez des lignes de sous-titres, avec traduction en temps réel en option, pendant que les gens parlent.

Canonical: https://transcript1.com/fr/docs/live
Language: fr
Last updated: 2026-11-03

La transcription en direct transforme un flux audio en lignes de sous-titres pendant que les gens parlent encore. Votre client envoie de l'audio PCM brut par WebSocket, et transcript1 renvoie des lignes qui commencent par une version provisoire avant de devenir définitives. Ajoutez `translate_to` et chaque ligne arrive aussi avec sa traduction. À la fin de la session, le résultat est enregistré comme une transcription ordinaire.

Il n'existe pas d'offre distincte pour le direct : la transcription en direct utilise le même solde de minutes que le reste. La section [Facturation](https://transcript1.com/fr/docs/live#billing) explique comment le temps de connexion est compté.

## Quand choisir le direct

| Choisissez le direct si | Choisissez [une transcription classique](https://transcript1.com/fr/docs/getting-started) si |
|---|---|
| L'audio est encore en cours de production : un appel, une conférence, un live, une vidéo que vous regardez. | Vous avez un lien ou un fichier terminé. |
| Il vous faut des sous-titres une à deux secondes après la parole. | Vous voulez la meilleure précision et pouvez attendre le résultat. |
| Vous voulez une traduction en temps réel à côté de chaque ligne. | Vous voulez traduire une transcription terminée, ou dans plusieurs langues. |

Le direct n'accepte que de l'audio, en PCM brut. Il n'accepte ni liens vidéo, ni fichiers importés, ni WebM, Opus ou MP3. Capturez l'audio dans votre client et convertissez-le d'abord en PCM. La section [Capturer l'audio dans un navigateur](https://transcript1.com/fr/docs/live#capture-audio-in-a-browser) montre comment faire.

## Démarrage rapide avec le SDK

Installez le SDK 1.5.0 ou une version plus récente : `npm i @transcript1/sdk@latest`. `t1.live.start` crée la session, se connecte et se résout une fois la session démarrée : vous pouvez donc envoyer de l'audio tout de suite.

```javascript
import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
import { Transcript1 } from '@transcript1/sdk';

const t1 = new Transcript1(); // reads TRANSCRIPT1_API_KEY

const live = await t1.live.start({ language: 'en', translateTo: 'es', sampleRate: 16000 });

live.on('line', line => {
  const mark = line.final ? 'final  ' : 'partial';
  console.log(mark, line.text, '→', line.translation?.text ?? '');
});
live.on('warning', warning => console.warn(warning.code));

// speech.pcm: 16 kHz, mono, signed 16-bit little-endian, no header
for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
  live.send(chunk);
  await sleep(100); // 3,200 bytes are 100 ms of audio: send at real-time speed
}

const result = await live.end();
console.log(result.reason, result.transcriptionId, result.minutesCharged);
```

Les lignes se mettent à jour en place. Remplacez une ligne que vous avez déjà quand une autre avec le même `id` arrive, ou lisez-les toutes depuis `live.lines`. `end()` termine la session, attend les dernières lignes et enregistre la transcription. Toutes les méthodes et tous les événements sont décrits dans [SDK Node.js](https://transcript1.com/fr/docs/sdk#live-transcription).

## Démarrage rapide avec un WebSocket brut

Sans le SDK, il y a deux étapes : créer une session en HTTPS, puis ouvrir le WebSocket avec le token à usage unique reçu en retour.

### Créer une session

```bash
curl -X POST https://api.transcript1.com/v1/live/sessions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"language": "en", "translate_to": "es", "sample_rate": 16000}'
```

L'API répond `201` :

```json
{
  "id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "status": "waiting",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD/stream",
  "token": "Zk3x...",
  "token_expires_at": "2026-11-02T12:01:00Z",
  "rate": 1
}
```

`transcription_id` vaut `null` quand `save` est `false`. Le token est à usage unique et expire après 60 secondes : connectez-vous tout de suite. `rate` est le nombre de minutes de solde débitées par minute entamée de cette session ; voir [Facturation](https://transcript1.com/fr/docs/live#billing).

Le corps de la requête accepte ces champs. Tous sont facultatifs, et le scope requis est `transcriptions:write` :

| Champ | Valeur par défaut | Règle |
|---|---|---|
| `language` | `auto` | `auto`, ou un code à deux lettres pris en charge par la transcription en direct. |
| `translate_to` | aucune | Un code à deux lettres. Une seule langue cible par session. Voir [Traduire en direct](https://transcript1.com/fr/docs/live#translate-live). |
| `diarize` | `false` | Identifie les locuteurs. Certaines configurations ne le permettent pas, par exemple avec `language: "auto"`, et répondent `422`. Consultez les [capacités](https://transcript1.com/fr/docs/live#capabilities). |
| `audio_format` | `pcm_s16le` | La seule valeur. Le champ existe pour pouvoir ajouter d'autres formats plus tard. |
| `sample_rate` | `16000` | Un entier de 8000 à 48000. Mono uniquement. |
| `save` | `true` | Avec `false`, rien n'est conservé à la fin de la session. Voir [Transcriptions enregistrées](https://transcript1.com/fr/docs/live#saved-transcripts). |

### Se connecter et diffuser

Ouvrez le WebSocket avec le token comme second sous-protocole. Cela fonctionne dans les navigateurs et sous Node 22+ sans en-têtes personnalisés. Attendez `session.started` avant d'envoyer de l'audio : les trames binaires envoyées plus tôt sont ignorées, et vous recevez un avertissement `audio_dropped`.

```javascript
import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';

const session = await fetch('https://api.transcript1.com/v1/live/sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TRANSCRIPT1_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ language: 'en', translate_to: 'es', sample_rate: 16000 }),
}).then(response => response.json());

const ws = new WebSocket(session.websocket_url, ['t1-live', session.token]);
const lines = new Map();

ws.addEventListener('message', async message => {
  const event = JSON.parse(message.data);

  if (event.type === 'session.started') {
    for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
      ws.send(chunk);
      await sleep(100);
    }
    ws.send(JSON.stringify({ type: 'end' }));
  } else if (event.type === 'line') {
    lines.set(event.line.id, event.line); // the full current state: replace by id
    console.log(event.line.final, event.line.text, event.line.translation?.text);
  } else if (event.type === 'session.ended') {
    console.log('ended:', event.reason, event.minutes_charged);
  }
});

ws.addEventListener('close', event => console.log('closed', event.code));
```

Node 20 n'a pas de `WebSocket` intégré : utilisez le paquet `ws`, qui a la même forme `new WebSocket(url, protocols)`.

Pour un token valide, le serveur répond avec le sous-protocole `t1-live` et rien d'autre. Un mauvais token ne fait jamais échouer la poignée de main : le socket s'ouvre, vous recevez un événement `error` et le socket se ferme avec un code 4xxx, de sorte qu'un navigateur affiche la vraie raison plutôt qu'un 1006 générique. Voir [Codes de fermeture](https://transcript1.com/fr/docs/live#close-codes).

## Capturer l'audio dans un navigateur

L'audio en direct doit être du PCM mono signé sur 16 bits, little-endian, à la `sample_rate` de la session. Les navigateurs fournissent des échantillons en virgule flottante : convertissez-les dans un `AudioWorklet`. Cet extrait capture le micro en PCM 16 kHz par blocs de 100 ms et l'envoie avec le point d'entrée navigateur du SDK. `getAccessToken` renvoie un token d'accès OAuth ; ne mettez jamais de clé API dans un navigateur. Voir [Sécurité](https://transcript1.com/fr/docs/live#security).

```javascript
import { createLiveClient } from '@transcript1/sdk/live';

const workletSource = `
class Pcm16Processor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.buffer = new Int16Array(1600); // 100 ms at 16 kHz
    this.filled = 0;
  }

  process(inputs) {
    const channel = inputs[0][0];
    if (!channel) return true;
    for (let i = 0; i < channel.length; i++) {
      const sample = Math.max(-1, Math.min(1, channel[i]));
      this.buffer[this.filled++] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
      if (this.filled === this.buffer.length) {
        this.port.postMessage(this.buffer.slice());
        this.filled = 0;
      }
    }
    return true;
  }
}
registerProcessor('pcm16', Pcm16Processor);
`;

const client = createLiveClient({ getAccessToken: () => myApp.getOAuthAccessToken() });

const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1 } });
const context = new AudioContext({ sampleRate: 16000 });
const moduleUrl = URL.createObjectURL(new Blob([workletSource], { type: 'text/javascript' }));
await context.audioWorklet.addModule(moduleUrl);

// the context's real rate: a browser that cannot do 16 kHz picks its own
const live = await client.start({ language: 'auto', sampleRate: context.sampleRate });
live.on('line', renderLine);

const microphone = context.createMediaStreamSource(stream);
const capture = new AudioWorkletNode(context, 'pcm16');
capture.port.onmessage = event => live.send(event.data); // an Int16Array
const mute = context.createGain();
mute.gain.value = 0;
microphone.connect(capture).connect(mute).connect(context.destination);

async function stop() {
  stream.getTracks().forEach(track => track.stop());
  await context.close();
  return live.end();
}
```

Le worklet est écrit pour 16 kHz. Si le contexte tourne à une autre fréquence, les blocs ne durent plus 100 ms, mais le PCM reste correct puisque `sampleRate` correspond au contexte. Pour capturer le son d'un onglet ou d'une vidéo plutôt que le micro, créez le `MediaStream` avec `getDisplayMedia` ou à partir du `captureStream()` d'un élément média, puis branchez-le de la même façon.

## Traduire en direct

Ajoutez `translate_to` à la session et transcript1 traduit chaque ligne au fil de la parole.

- Une seule langue cible par session. Ajoutez ensuite d'autres langues à la transcription enregistrée avec l'[endpoint de traduction](https://transcript1.com/fr/docs/live#saved-transcripts).
- Chaque `line` a un original (`text`, `final`) et un objet `translation` (`language`, `text`, `final`). `translation` vaut `null` quand `translate_to` n'est pas défini.
- L'original et la traduction deviennent définitifs indépendamment : une ligne peut avoir un original définitif alors que sa traduction change encore. Affichez une ligne comme définitive quand les deux le sont.
- Une ligne déjà dans la langue cible utilise son propre texte comme traduction, et elle est définitive dès qu'elle se clôt.
- Une ligne se clôt quand le locuteur marque une pause, quand le locuteur change, quand une pause entre deux mots dépasse 0.9 seconde, quand la ligne atteint deux rangées de sous-titres (84 caractères) ou 15 secondes, ou quand vous envoyez `finalize`. Avec la traduction activée, une ligne peut mettre environ 3 secondes de plus à se clore, le temps que sa traduction arrive. Dans ce cas, la traduction peut rester ouverte (`final: false`) et continuer à s'allonger après que l'original est devenu définitif.
- La traduction est gratuite. Elle ne consomme aucune minute.

Les langues cibles et les paires de langues disponibles dépendent du fournisseur de reconnaissance vocale en direct. Lisez les [capacités](https://transcript1.com/fr/docs/live#capabilities) au lieu de coder des règles en dur. Une cible non prise en charge renvoie `422 unsupported_translation`. Par exemple, un fournisseur peut exiger un `language` explicite plutôt que `auto`, ou n'autoriser que les paires incluant l'anglais.

## Protocole WebSocket

### Connexion

```javascript
new WebSocket(websocket_url, ['t1-live', token]);
```

Le token voyage comme sous-protocole WebSocket. Il n'apparaît jamais dans une URL ni dans un journal d'accès. N'envoyez pas d'en-tête `Authorization` : le token tient lieu d'identifiant. Le serveur envoie un ping toutes les 30 secondes ; les navigateurs et le paquet `ws` répondent automatiquement. Un socket qui n'envoie aucun pong pendant 60 secondes est coupé.

Une connexion valide à une session qui a déjà un socket remplace l'ancien. Voir [Reprise et remplacement](https://transcript1.com/fr/docs/live#resume-and-takeover).

### Messages du client

| Message | Signification |
|---|---|
| Trame binaire | Audio : PCM s16le mono à la `sample_rate` de la session. Chaque trame a une longueur paire et fait au plus 256 Ko. Environ 100 ms par trame, c'est une bonne taille. |
| `{"type":"finalize"}` | Clôt tout de suite la ligne en cours, par exemple quand la vidéo est mise en pause. |
| `{"type":"end"}` | Termine la session et l'enregistre. Le serveur répond `session.ended` et ferme le socket. |
| Tout le reste | Un événement `error` non fatal avec le code `invalid_message`. |

Une trame de longueur impaire ou trop volumineuse est une violation du protocole et ferme le socket avec `4008`.

Commencez à envoyer de l'audio après `session.started`. Un client qui envoie l'audio plus vite que le temps réel paie la durée de l'audio plutôt que le temps de connexion (voir [Facturation](https://transcript1.com/fr/docs/live#billing)) : envoyez donc au rythme où l'audio est produit.

### Événements du serveur

Chaque événement est une trame texte JSON avec un `type`.

| Événement | Envoyé quand |
|---|---|
| `session.started` | La connexion a été acceptée. Envoyez de l'audio à partir de maintenant. |
| `line` | Une ligne de sous-titres a été créée ou modifiée. |
| `usage` | Après chaque débit. |
| `warning` | Quelque chose demande de l'attention mais la session continue. |
| `error` | Un problème non fatal, ou la raison d'une fermeture immédiate. |
| `reconnect` | Le serveur est sur le point de redémarrer. Reprenez la session. |
| `session.ended` | La session est terminée et enregistrée. C'est le dernier événement avant une fermeture normale. |

`session.started` indique s'il s'agit d'une reprise et quelle quantité d'audio la session a déjà traitée :

```json
{
  "type": "session.started",
  "session_id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "resumed": false,
  "offset_ms": 0
}
```

`line` porte l'état complet et actuel d'une ligne : remplacez donc la ligne que vous avez avec le même `id`. Voir [L'objet ligne](https://transcript1.com/fr/docs/live#the-line-object).

```json
{
  "type": "line",
  "line": {
    "id": 12,
    "start_ms": 53120,
    "end_ms": 56040,
    "speaker": null,
    "language": "en",
    "text": "Welcome back to the stream",
    "final": true,
    "translation": { "language": "es", "text": "Bienvenidos de nuevo", "final": false }
  }
}
```

`usage` est envoyé après chaque débit, avec le temps de connexion, la durée de l'audio, les minutes de solde débitées jusqu'ici et les minutes qu'il vous reste :

```json
{
  "type": "usage",
  "connected_ms": 61240,
  "audio_ms": 60100,
  "minutes_charged": 2,
  "minutes_remaining": 898
}
```

`warning` contient un `code` :

| `code` | Signification |
|---|---|
| `balance_low` | Le prochain débit échouerait. La session se termine avec `balance_exhausted` quand il arrivera. Non envoyé tant que la [recharge automatique](https://transcript1.com/fr/docs/billing-and-limits#auto-top-up) est active. |
| `audio_dropped` | De l'audio est arrivé avant que la session soit prête, ou trop d'audio est arrivé pendant la connexion au fournisseur vocal. L'audio le plus ancien a été abandonné. |
| `idle_timeout_soon` | Aucun audio depuis 4 minutes. La session se termine après 5 minutes sans audio. Envoyez de l'audio pour la conserver. |

```json
{ "type": "warning", "code": "idle_timeout_soon" }
```

`error` contient un `code` et un `message`. Ces codes peuvent arriver sur un socket en direct :

| `code` | Fatal | Signification |
|---|---|---|
| `invalid_message` | Non | Une trame texte qui n'est ni `finalize` ni `end`. |
| `invalid_token` | Oui | Le token est invalide, expiré ou déjà utilisé. Le socket se ferme avec `4001`. |
| `not_found` | Oui | La session n'existe pas. Le socket se ferme avec `4004`. |
| `session_ended` | Oui | La session est déjà terminée. Le socket se ferme avec `4004`. |

```json
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }
```

`reconnect` arrive avant que le serveur ferme le socket avec `1012`, par exemple lors d'un déploiement. Récupérez un nouveau token et reprenez.

```json
{ "type": "reconnect", "reason": "server_restart" }
```

`session.ended` est le dernier événement de chaque session. Les chiffres sont les totaux définitifs :

```json
{
  "type": "session.ended",
  "reason": "client_ended",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "minutes_charged": 3,
  "connected_ms": 142500,
  "audio_ms": 141900
}
```

### L'objet ligne

| Champ | Signification |
|---|---|
| `id` | Un numéro qui commence à 0 et augmente de un à chaque ligne. C'est l'indice du segment dans la transcription enregistrée. |
| `start_ms`, `end_ms` | Début et fin dans le temps audio de la session. Ce temps se poursuit à travers les reconnexions. |
| `speaker` | L'étiquette du locuteur quand `diarize` est activé, sinon `null`. Les étiquettes sont les mêmes que dans les transcriptions ordinaires. |
| `language` | La langue détectée ou indiquée de la ligne, ou `null` si elle n'est pas encore connue. |
| `text` | Le texte original jusqu'ici. |
| `final` | `true` une fois que le texte original ne changera plus. |
| `translation` | `null` sans `translate_to`, sinon `language`, `text` et `final`. |

Affichez les lignes avec `final: false` dans un style plus clair : leur texte peut encore changer. Le texte d'une ligne avec `final: true` ne change plus jamais, et une ligne définitive n'est jamais supprimée.

Le SDK utilise `camelCase` pour les champs de ligne : `startMs`, `endMs`.

### Motifs de fin

`session.ended` et `GET /v1/live/sessions/:id` donnent un `end_reason` :

| Motif | Signification |
|---|---|
| `client_ended` | Vous avez envoyé `end`. |
| `balance_exhausted` | Le débit suivant a échoué. La session se termine à un changement de minute et elle est enregistrée. |
| `idle_timeout` | Aucun audio pendant 5 minutes. |
| `max_duration` | La session est restée connectée 300 minutes. Démarrez-en une nouvelle. |
| `disconnected` | Le client est resté absent plus de 2 minutes sans reprendre. |
| `upstream_error` | Le fournisseur de reconnaissance vocale est tombé en panne. |
| `internal_error` | Une erreur s'est produite sur nos serveurs. |
| `not_connected` | La session ne s'est jamais connectée dans les 5 minutes. Elle n'est jamais facturée et n'est visible que par `GET`. |

### Codes de fermeture

| Code | Signification |
|---|---|
| `1000` | Fermeture normale. Elle suit toujours `session.ended`. |
| `1011` | Erreur inattendue du serveur. Le serveur récupère la session : reprenez avec un nouveau token. |
| `1012` | Redémarrage du serveur. Il suit `reconnect` : reprenez avec un nouveau token. |
| `4001` | Le token est invalide, expiré ou déjà utilisé. |
| `4004` | La session est introuvable ou déjà terminée. |
| `4008` | Violation du protocole : une trame binaire de longueur impaire ou trop volumineuse. |
| `4010` | Remplacée par une connexion plus récente à la même session. |

Pendant le redémarrage de l'API, une nouvelle requête WebSocket peut recevoir un HTTP `503`. Réessayez dans un instant.

## Reprise et remplacement

Une session survit à une coupure réseau, à un déploiement de l'API et à un plantage. Pendant 2 minutes après une coupure, elle vous attend ; ensuite elle se termine en `disconnected` et elle est enregistrée.

1. Appelez `POST /v1/live/sessions/:id/token`. La réponse contient `token`, `token_expires_at` et `websocket_url`, les mêmes champs qu'à la création de la session. L'appel échoue avec `409 session_ended` une fois la session terminée.
2. Ouvrez un nouveau WebSocket avec ce token.
3. `session.started` arrive avec `resumed: true`. `offset_ms` est la quantité d'audio déjà traitée par la session : continuez avec l'audio à partir de ce point. Les identifiants de ligne continuent.

Les lignes déjà définitives sont conservées. La partie en cours au moment de la coupure est perdue : renvoyez donc quelques secondes d'audio à partir de `offset_ms` si vous les gardez en mémoire tampon. Les interruptions pendant la reconnexion ne sont pas facturées.

Le remplacement passe par le même appel. Si le serveur croit encore qu'un socket est ouvert, une nouvelle connexion valide le remplace, et l'ancien socket est fermé avec `4010` une fois ses lignes enregistrées. La limite de sessions simultanées ne bloque jamais une reprise.

Le SDK fait tout cela pour vous : lors d'une coupure ou d'un événement `reconnect`, il récupère un token et se reconnecte avec un délai croissant de 0.5 à 8 secondes pendant 2 minutes au maximum, et il renvoie les 10 dernières secondes d'audio envoyées pendant la coupure.

## Facturation

La transcription en direct puise dans le même solde de minutes que les transcriptions classiques.

- **Le temps de connexion est facturé**, depuis l'acceptation du socket jusqu'à sa fermeture. Le silence et une vidéo en pause comptent. Le temps entre une coupure et la reprise ne compte pas.
- **Par minute entamée, minimum 1.** La première minute est débitée à l'acceptation de la première connexion, et chaque minute suivante dès qu'elle commence.
- **Plus vite que le temps réel, c'est la durée de l'audio qui est facturée.** Si l'audio que vous envoyez dure plus longtemps que la connexion, la durée de l'audio est facturée à la place.
- **Le tarif se lit dans l'API.** Une minute entamée coûte `rate` minutes de solde, arrondies à l'unité supérieure. Le tarif vaut souvent 1 et peut avoir des décimales. Lisez le tarif en direct dans `rates.live` de `GET /v1/account`, dans `rate` de `GET /v1/live/capabilities` et dans `rate` de la session que vous créez. Le tarif d'une session reste fixe une fois la session créée.
- **La traduction est gratuite.**
- **Jamais connectée, jamais facturée.** Une session qui ne se connecte pas dans les 5 minutes se termine en `not_connected`.
- **Les échecs sont remboursés.** Si une session se termine par `upstream_error` ou `internal_error` avant d'avoir produit une seule ligne, tous les débits sont remboursés. Une session vide à cause du silence est facturée et enregistrée vide.
- **Quand le solde est épuisé** et que la [recharge automatique](https://transcript1.com/fr/docs/billing-and-limits#auto-top-up) est active, transcript1 débite votre carte enregistrée pendant que la session continue de diffuser, et la session se poursuit. Si la recharge automatique est désactivée ou en pause, ou si le débit échoue, la session se finalise, s'enregistre et se termine par `balance_exhausted` à un changement de minute. L'avertissement `balance_low` vient d'abord, sauf si la recharge automatique est active.

La création d'une session renvoie `402 insufficient_minutes` quand votre solde utilisable est inférieur à une minute entamée au tarif actuel. Si la recharge automatique est active, transcript1 tente d'abord une recharge. Voir [Facturation et limites](https://transcript1.com/fr/docs/billing-and-limits#live-transcription).

## Limites

| | Free | Monthly | Annual |
|---|---|---|---|
| Sessions en direct simultanées | 1 | 2 | 3 |

- Une session compte tant qu'elle est `waiting`, `connected` ou `disconnected` (dans sa fenêtre de 2 minutes). Au-delà de la limite, `POST /v1/live/sessions` renvoie `429 too_many_live_sessions` avec `Retry-After: 30`.
- Les sessions en direct ne comptent pas dans la limite de transcriptions actives.
- Une session dure au maximum 300 minutes de temps de connexion. Elle se termine ensuite avec `max_duration`.
- Une session sans audio pendant 5 minutes se termine avec `idle_timeout` ; vous recevez `idle_timeout_soon` après 4 minutes. Un flux en pause continue de facturer du temps de connexion et compte comme inactif : terminez donc la session si la pause est longue.
- transcript1 limite l'ensemble des sessions en direct. Quand elles sont toutes utilisées, `POST /v1/live/sessions` renvoie `503 live_capacity`, qui peut être réessayé. Attendez `Retry-After` (30 secondes) et réessayez. Cette vérification n'a lieu qu'à la création d'une session, jamais en cours de route.
- La création de sessions et la récupération de tokens comptent dans vos requêtes par minute.
- L'audio est uniquement du PCM brut : `pcm_s16le`, mono, de 8000 à 48000 Hz.

## Capacités

`GET /v1/live/capabilities` décrit ce que la transcription en direct prend en charge à l'instant, pour que les clients n'aient jamais à coder en dur les règles du fournisseur. Le scope requis est `transcriptions:read`.

```json
{
  "available": true,
  "auto_language": true,
  "languages": ["en", "es"],
  "translation": {
    "targets": ["es", "de"],
    "pairs": "any",
    "requires_language": false
  },
  "diarize": true,
  "audio_formats": ["pcm_s16le"],
  "sample_rate": { "min": 8000, "max": 48000 },
  "max_session_minutes": 300,
  "rate": 1
}
```

Les listes sont abrégées ici. Lisez-les depuis l'API.

- `translation.pairs` vaut `"any"` (toute langue détectée vers toute cible), ou une liste d'objets avec `from` et `to`.
- `translation.requires_language` vaut `true` quand la traduction exige un `language` explicite plutôt que `auto`.
- Quand la transcription en direct n'est pas configurée, la réponse est `available: false` avec seulement `rate`, et la création d'une session renvoie `503 live_unavailable`.

Avec le SDK : `await t1.live.capabilities()`.

## Consulter une session

`GET /v1/live/sessions/:id` renvoie l'état d'une session. Le scope requis est `transcriptions:read`.

```bash
curl https://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
```

```json
{
  "id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "status": "ended",
  "end_reason": "client_ended",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "options": {
    "language": "en",
    "translate_to": "es",
    "diarize": false,
    "audio_format": "pcm_s16le",
    "sample_rate": 16000,
    "save": true
  },
  "connected_ms": 142500,
  "audio_ms": 141900,
  "minutes_charged": 3,
  "rate": 1,
  "created_at": "2026-11-02T11:58:12Z",
  "ended_at": "2026-11-02T12:00:35Z"
}
```

`status` vaut `waiting`, `connected`, `disconnected` ou `ended`. `end_reason` vaut `null` tant que la session n'est pas terminée. Avec le SDK : `await t1.live.get(id)`.

## Erreurs

Les erreurs de requête utilisent l'[enveloppe d'erreur](https://transcript1.com/fr/docs/errors) habituelle. Voici celles que renvoient `POST /v1/live/sessions` et ses endpoints voisins :

| Statut HTTP | `code` | Réessayer ? | Quand |
|---|---|---|---|
| 422 | `invalid_request` | Non | Un champ manque ou est invalide, par exemple une `sample_rate` hors de la plage 8000 à 48000. |
| 422 | `unsupported_language` | Non | `language` n'est pas pris en charge pour la transcription en direct. |
| 422 | `unsupported_translation` | Non | La langue cible, ou la paire de langues, n'est pas prise en charge. |
| 402 | `insufficient_minutes` | Non | Votre solde utilisable est inférieur à une minute entamée au tarif actuel. |
| 403 | `email_not_verified` | Non | Vérifiez d'abord votre adresse e-mail. |
| 429 | `too_many_live_sessions` | Oui | Les sessions en direct simultanées de votre offre sont toutes utilisées. Attendez `Retry-After`. |
| 503 | `live_capacity` | Oui | Toute la capacité en direct est utilisée. Attendez `Retry-After` (30 secondes). |
| 503 | `live_unavailable` | Non | La transcription en direct n'est pas disponible pour le moment. |
| 409 | `session_ended` | Non | `POST /v1/live/sessions/:id/token` pour une session déjà terminée. |

Les problèmes sur le socket arrivent sous forme d'événements `error` et de [codes de fermeture](https://transcript1.com/fr/docs/live#close-codes).

## Transcriptions enregistrées

Avec `save: true` (la valeur par défaut), la session est enregistrée comme une transcription ordinaire à sa fin. L'audio n'est jamais stocké.

- `transcription_id` est renvoyé à la création de la session. Son statut est `live` pendant la session et `completed` après. Une session qui ne s'est jamais connectée laisse une transcription `canceled`.
- La transcription a `source: { "platform": "live", "session_id": "lv_…" }`. Il n'y a ni lien ni fichier. Le tableau de bord la liste comme une session en direct.
- Chaque ligne devient un segment, avec le même indice que l'`id` de la ligne. Lisez la transcription en JSON, texte, SRT ou VTT comme n'importe quelle autre : voir la [référence de l'API](https://transcript1.com/fr/docs/api-reference).
- La traduction en direct est enregistrée avec elle. Lisez-la avec le paramètre `translation`, comme expliqué dans [Traduction](https://transcript1.com/fr/docs/translation#read-the-translation).
- Ajoutez d'autres langues de traduction plus tard avec `POST /v1/transcriptions/:id/translations`, une fois la transcription `completed`. Voir [Traduction](https://transcript1.com/fr/docs/translation#translate-an-existing-transcript).

Avec `save: false`, rien n'est conservé à la fin de la session : il n'y a pas de transcription et `transcription_id` vaut `null`. Vous recevez quand même chaque ligne sur le socket, et la facturation habituelle s'applique.

## Sécurité

- Le token que vous obtenez à la création d'une session est à usage unique et expire après 60 secondes. Il est envoyé comme sous-protocole WebSocket, pas dans l'URL, donc il reste hors des journaux d'accès. Un token qui fuite est inutile une fois utilisé ou expiré.
- N'utilisez une clé API que depuis un serveur que vous contrôlez. Ne mettez jamais de clé API dans une page web ou une extension.
- Dans un navigateur ou une extension, connectez les utilisateurs avec OAuth et passez le token d'accès OAuth à `getAccessToken` de `createLiveClient`. Le token a besoin du scope `transcriptions:write` pour démarrer des sessions. Les tokens d'accès OAuth expirent après 1 heure, et chaque reprise redemande un token neuf à `getAccessToken`.
- Votre serveur peut aussi créer la session avec sa clé API et ne transmettre au navigateur que `websocket_url` et `token`. Le client se connecte alors directement. Il ne peut pas reprendre la session sans un nouveau token de votre part.
