post('/bird/voice/calls', function () { global $response; // Permission: create/place a voice call via Bird self::requirePermission('modules_bird_voice_calls_create'); $client = new bird(); // Require workspace/channel per Bird API docs $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $payload = $this->getParametersAsArray(); unset($payload['workspaceId'], $payload['channelId']); $res = $client->createVoiceCall($ws, $ch, $payload); $response->success($res ?? ['status' => 'ok']); }, [ 'modules_bird_voice_calls_create' => 'Create/place a voice call via Bird', ]); // List voice calls (passthrough of query filters) $this->get('/bird/voice/calls', function () { global $response; // Permission: list voice calls via Bird self::requirePermission('modules_bird_voice_calls_list'); $client = new bird(); $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $query = $this->getParametersAsArray(); unset($query['workspaceId'], $query['channelId']); $res = $client->listVoiceCalls($ws, $ch, $query); $response->success($res ?? []); }, [ 'modules_bird_voice_calls_list' => 'List voice calls via Bird', ]); // Get a specific call by ID $this->get('/bird/voice/calls/{id}', function () { global $response; // Permission: get a specific voice call via Bird self::requirePermission('modules_bird_voice_calls_get'); $client = new bird(); $id = (string)$this->fromRoute('id'); if ($id === null || $id === '') { $response->error('Missing id', 400); } $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $res = $client->getVoiceCall($ws, $ch, $id); $response->success($res ?? []); }, [ 'modules_bird_voice_calls_get' => 'Get a voice call by ID via Bird', ]); // Hang up an active call by ID $this->post('/bird/voice/calls/{id}/hangup', function () { global $response; // Permission: hang up a specific voice call via Bird self::requirePermission('modules_bird_voice_calls_hangup'); $client = new bird(); $id = (string)$this->fromRoute('id'); if ($id === null || $id === '') { $response->error('Missing id', 400); } $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $payload = []; $cause = $this->normalizeOptionalString($this->fromRequest('cause') ?? $this->fromQuery('cause')); if ($cause !== '') { $payload['cause'] = $cause; } $res = $client->hangupVoiceCall($ws, $ch, $id, $payload); $response->success($res ?? ['status' => 'ok']); }, [ 'modules_bird_voice_calls_hangup' => 'Hang up a voice call by ID via Bird', ]); // Say a message on an active call and hang up afterwards $this->post('/bird/voice/calls/{id}/say', function () { global $response; // Permission: say a message on a voice call via Bird self::requirePermission('modules_bird_voice_calls_say'); $client = new bird(); $id = (string)$this->fromRoute('id'); if ($id === null || $id === '') { $response->error('Missing id', 400); } $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $payload = $this->getParametersAsArray(); unset($payload['workspaceId'], $payload['channelId']); // Bird's /say REST API endpoint plays the message. // To fulfill the requirement of hanging up afterwards, we should ensure the call is terminated. // Many Bird actions support a 'hangup' field in the payload to terminate the call after the action is complete. if (!isset($payload['hangup'])) { $payload['hangup'] = true; } $res = $client->sayMessage($ws, $ch, $id, $payload); $response->success($res ?? ['status' => 'ok']); }, [ 'modules_bird_voice_calls_say' => 'Say a message on a voice call by ID via Bird', ]); // Gather input on an active call $this->post('/bird/voice/calls/{id}/gather', function () { global $response; // Permission: gather input on a voice call via Bird self::requirePermission('modules_bird_voice_calls_gather'); $client = new bird(); $id = (string)$this->fromRoute('id'); if ($id === null || $id === '') { $response->error('Missing id', 400); } $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $payload = $this->getParametersAsArray(); unset($payload['workspaceId'], $payload['channelId']); $res = $client->gatherMessage($ws, $ch, $id, $payload); $response->success($res ?? ['status' => 'ok']); }, [ 'modules_bird_voice_calls_gather' => 'Gather input on a voice call by ID via Bird', ]); // Place test outbound call and hang up when accepted $this->post('/bird/voice/calls/test-outbound', function () { global $response; self::requirePermission('modules_bird_voice_calls_test_outbound'); $client = new bird(); $ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId')); $ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId')); if ($ws === '') { $ws = $this->getConfiguredWorkspaceId($client); } if ($ch === '') { $ch = $this->getConfiguredChannelId($client); } if ($ws === '' || $ch === '') { $response->error('Missing required parameters: workspaceId, channelId', 400); } $payload = $this->getParametersAsArray(); unset($payload['workspaceId'], $payload['channelId']); try { $result = $client->createOutboundTestCallAndHangupWhenAccepted($ws, $ch, $payload); $response->success($result); } catch (\Throwable $e) { $response->error($e->getMessage(), 500); } }, [ 'modules_bird_voice_calls_test_outbound' => 'Place a test outbound call and hang up when accepted', ]); } }