createCall method

Future<CreateRealtimeCallResponse> createCall({
  1. required String sdp,
  2. RealtimeSessionType type = RealtimeSessionType.realtime,
  3. RealtimeModel? model,
  4. String? instructions,
  5. dynamic maxOutputTokens,
  6. List<Modality>? outputModalities,
  7. RealtimeSessionAudio? audio,
  8. List<RealtimeFunctionTool>? tools,
  9. ToolChoice? toolChoice,
  10. Tracing? tracing,
  11. List<String>? include,
  12. Map<String, dynamic>? prompt,
  13. RealtimeTruncation? truncation,
  14. double? temperature,
})

Create a new Realtime API call over WebRTC and receive the SDP answer.

Mirrors: POST /v1/realtime/calls

Required:

  • sdp : the WebRTC SDP offer generated by the caller.

Session configuration:

  • Provide either a full sessionJson that matches the API docs or use the convenience params (same semantics as createRealtimeClientSecret).

Notes:

  • session.type must be "realtime" for Realtime sessions (default here).
  • outputModalities cannot request both "audio" and "text" at once.
  • The response body is plain text (application/sdp) with the SDP answer.
  • The Location header includes the call ID for follow‑ups (monitoring WebSocket, /accept, /hangup, etc.).

Implementation

Future<CreateRealtimeCallResponse> createCall({
  // SDP offer
  required String sdp,
  // Convenience fields (used only if `sessionJson` is null or empty)
  RealtimeSessionType type = RealtimeSessionType.realtime,
  RealtimeModel? model, // e.g., RealtimeModel.gptRealtime
  String? instructions,
  dynamic maxOutputTokens, // int | "inf"
  List<Modality>? outputModalities, // ["audio"] | ["text"]
  RealtimeSessionAudio? audio, // input/output audio config
  List<RealtimeFunctionTool>? tools,
  ToolChoice? toolChoice,
  Tracing? tracing, // "auto" or object
  List<String>? include, // e.g. ["item.input_audio_transcription.logprobs"]
  Map<String, dynamic>? prompt, // {id, variables?, version?}
  RealtimeTruncation? truncation, // "auto" or {type:"retention_ratio", retention_ratio:0.5}
  double? temperature,
}) async {
  final session = <String, dynamic>{
    'type': type.toJson(),
    if (model != null) 'model': model.toJson(),
    if (instructions != null) 'instructions': instructions,
    if (maxOutputTokens != null) 'max_output_tokens': maxOutputTokens,
    if (outputModalities != null) 'output_modalities': outputModalities.map((m) => m.toJson()).toList(),
    if (tools != null) 'tools': tools.map((t) => t.toJson()).toList(),
    if (toolChoice != null) 'tool_choice': toolChoice.toJson(),
    if (tracing != null) 'tracing': tracing.toJson(),
    if (include != null) 'include': include,
    if (prompt != null) 'prompt': prompt,
    if (truncation != null) 'truncation': truncation.toJson(),
    if (audio != null) 'audio': audio.toJson(),
    if (temperature != null) 'temperature': temperature,
  };
  final request = http.MultipartRequest("POST", baseUrl.resolve("realtime/calls"))
    ..files.add(http.MultipartFile.fromBytes(
      "sdp",
      utf8.encode(sdp),
      contentType: MediaType("application", "sdp"),
    ))
    ..files.add(http.MultipartFile.fromBytes(
      "session",
      utf8.encode(jsonEncode(
        session,
      )),
      contentType: MediaType("application", "json"),
    ));

  request.headers.addAll(getHeaders({}) ?? {});

  final streamedResponse = await request.send();
  final res = await http.Response.fromStream(streamedResponse);

  // The spec returns 201 Created with the SDP **answer** in the body.
  if (res.statusCode != 201 && res.statusCode != 200) {
    throw OpenAIRequestException.fromHttpResponse(res);
  }

  // Extract the Location header (case-insensitive) to get the call ID.
  String? locationHeader;
  res.headers.forEach((k, v) {
    if (k.toLowerCase() == 'location') locationHeader = v;
  });

  Uri? locationUri;
  String? callId;

  if (locationHeader != null && locationHeader!.isNotEmpty) {
    locationUri = Uri.tryParse(locationHeader!);

    // Try to parse `/v1/realtime/calls/<id>` regardless of absolute/relative form.
    final String path = locationUri?.path ?? locationHeader!;
    final match = RegExp(r'/realtime/calls/([^/?#]+)').firstMatch(path);
    if (match != null) callId = match.group(1);
  }

  return CreateRealtimeCallResponse(
    sdpAnswer: res.body, // plain text SDP answer
    callId: callId,
    location: locationUri,
  );
}