createRealtimeClientSecret method

Future<CreateRealtimeClientSecretResponse> createRealtimeClientSecret({
  1. int? expiresAfterSeconds,
  2. String expiresAfterAnchor = 'created_at',
  3. Map<String, dynamic>? sessionJson,
  4. String sessionType = 'realtime',
  5. String? model,
  6. List<Modality>? outputModalities,
  7. AudioFormat? inputAudioFormat,
  8. AudioFormat? outputAudioFormat,
  9. SpeechVoice? voice,
  10. num? speed,
  11. String? instructions,
  12. dynamic maxOutputTokens,
  13. List<RealtimeFunctionTool>? tools,
  14. ToolChoice? toolChoice,
  15. Tracing? tracing,
  16. TurnDetection? turnDetection,
  17. NoiseReduction? inputAudioNoiseReduction,
  18. InputAudioTranscription? inputAudioTranscription,
  19. List<String>? include,
})

Create an ephemeral client secret (ek_...) with an associated session config.

POST /v1/realtime/client_secrets

You can either:

  • pass a fully-formed sessionJson that matches the docs; or
  • use the convenience arguments to build a typical minimal config.

Notes:

  • expiresAfterSeconds must be 10–7200 (defaults to 600s server-side).
  • session must contain a type: "realtime" or "transcription".

Implementation

Future<CreateRealtimeClientSecretResponse> createRealtimeClientSecret({
  // Expiration policy
  int? expiresAfterSeconds, // 10..7200 (2h). Server default: 600 (10min)
  String expiresAfterAnchor = 'created_at',

  // Provide a fully-formed session payload if you need full control.
  Map<String, dynamic>? sessionJson,

  // --- Convenience fields to build a minimal session quickly ---
  // (Used only when `sessionJson` is null)
  String sessionType = 'realtime', // "realtime" | "transcription"
  String? model, // e.g. RealtimeModel.gptRealtime.toJson()
  List<Modality>? outputModalities, // e.g. [Modality.audio] or [Modality.text]
  // Audio convenience
  AudioFormat? inputAudioFormat,
  AudioFormat? outputAudioFormat,
  SpeechVoice? voice,
  num? speed,
  // Guidance & tools
  String? instructions,
  dynamic maxOutputTokens, // int | "inf"
  List<RealtimeFunctionTool>? tools,
  ToolChoice? toolChoice,
  Tracing? tracing,
  // Detection/transcription knobs
  TurnDetection? turnDetection,
  NoiseReduction? inputAudioNoiseReduction,
  InputAudioTranscription? inputAudioTranscription, // for transcription sessions
  List<String>? include, // e.g. ["item.input_audio_transcription.logprobs"]
}) async {
  if (expiresAfterSeconds != null && (expiresAfterSeconds < 10 || expiresAfterSeconds > 7200)) {
    throw ArgumentError('expiresAfterSeconds must be between 10 and 7200 seconds.');
  }

  // Build payload
  final payload = <String, dynamic>{};

  if (expiresAfterSeconds != null || expiresAfterAnchor != 'created_at') {
    payload['expires_after'] = {
      'anchor': expiresAfterAnchor,
      if (expiresAfterSeconds != null) 'seconds': expiresAfterSeconds,
    };
  }

  // Assemble `session` block
  Map<String, dynamic> session = sessionJson ?? {};
  if (session.isEmpty) {
    // Minimal session using convenience params
    session = {'type': sessionType};
    if (model != null) session['model'] = model;
    if (instructions != null) session['instructions'] = instructions;
    if (maxOutputTokens != null) session['max_output_tokens'] = maxOutputTokens;
    if (tools != null) session['tools'] = tools.map((t) => t.toJson()).toList();
    if (toolChoice != null) session['tool_choice'] = toolChoice.toJson();
    if (tracing != null) session['tracing'] = tracing.toJson();
    if (turnDetection != null) session['turn_detection'] = turnDetection.toJson();

    // Modalities: For realtime sessions, server defaults to ["audio"].
    // You may set ["text"] to disable audio output (cannot request both).
    if (outputModalities != null && sessionType == 'realtime') {
      session['output_modalities'] = outputModalities.map((m) => m.toJson()).toList();
    }

    // Audio block
    final audio = <String, dynamic>{};

    // Input sub-block (both session types)
    final input = <String, dynamic>{};
    if (inputAudioFormat != null) input['format'] = inputAudioFormat.toJson();
    if (inputAudioNoiseReduction != null) {
      input['noise_reduction'] = inputAudioNoiseReduction.toJson();
    }
    if (input.isNotEmpty) audio['input'] = input;

    if (sessionType == 'realtime') {
      // Output sub-block only exists on realtime sessions
      final output = <String, dynamic>{};
      if (outputAudioFormat != null) output['format'] = outputAudioFormat.toJson();
      if (voice != null) output['voice'] = voice.toJson();
      if (speed != null) output['speed'] = speed;
      if (output.isNotEmpty) audio['output'] = output;
    } else {
      // Transcription-only session extras
      if (include != null) session['include'] = include;
      if (inputAudioTranscription != null) {
        audio['transcription'] = inputAudioTranscription.toJson();
      }
    }

    if (audio.isNotEmpty) session['audio'] = audio;
  }

  payload['session'] = session;

  final res = await postJson('/realtime/client_secrets', payload);

  // The reference returns 201 Created (but accept 200 just in case).
  if (res.statusCode == 201 || res.statusCode == 200) {
    return CreateRealtimeClientSecretResponse.fromJson(jsonDecode(res.body));
  }
  throw OpenAIRequestException.fromHttpResponse(res);
}