# Bird Module

This module provides Bird API integration for voice calls and number management.

## Endpoints

### Voice calls
- `POST /bird/voice/calls`
  - Permission: `modules_bird_voice_calls_create`
  - Required: `workspaceId`, `channelId`
  - Body: passthrough to Bird call create API (except `workspaceId` and `channelId`).

- `GET /bird/voice/calls`
  - Permission: `modules_bird_voice_calls_list`
  - Required query: `workspaceId`, `channelId`
  - Query params are passed through to Bird.

- `GET /bird/voice/calls/{id}`
  - Permission: `modules_bird_voice_calls_get`
  - Required query: `workspaceId`, `channelId`

- `POST /bird/voice/calls/{id}/hangup`
  - Permission: `modules_bird_voice_calls_hangup`
  - Required: `workspaceId`, `channelId`

- `POST /bird/voice/calls/test-outbound`
  - Permission: `modules_bird_voice_calls_test_outbound`
  - Required: `workspaceId`, `channelId`
  - Places an outbound call to the fixed test number `+45 42 33 11 28` (`+4542331128`), polls call state, and sends hangup when state is `accepted` or `ongoing`.
  - Optional tuning params:
    - `pollIntervalSeconds` (default `2`)
    - `maxPollSeconds` (default `30`)
    - `hangupCause`

- `POST /bird/voice/calls/webhook/inbound`
  - Permission: `modules_bird_voice_call_webhooks_trigger`
  - Stateful inbound IVR webhook for phone-controlled department gates.
  - On the initial request, the webhook accepts the live inbound call through Bird before returning the first gather command.
  - Supports both request formats:
    - Native-flow mode: top-level `{ callId, channelId, workspaceId }` for the initial fetch, then `{ callId, channelId, workspaceId, keys }` for selections.
    - Compatibility mode: `{ payload, request, waitConditions }`, which returns a raw Bird `callCommand` gather envelope.
  - Uses `department_gates` rows with `config.type=PHONE_CALL` as the source of truth for which departments and entrance/exit gates are offered.
  - Department menu order follows `departments.order_priority`.
  - Supports multi-digit department selections such as `10#`.
  - Always prompts for both department selection and gate selection, even when only one valid option exists.
  - Uses compact gate numbering:
    - both gates available: `1=entrance`, `2=exit`
    - entrance only: `1=entrance`
    - exit only: `1=exit`

### Bird Flow Builder setup
- Preferred native-flow setup:
  - `Voice` trigger
  - `Answer call`
  - `Async HTTP request` (initial IVR state fetch)
  - `Gather digits from a call`
  - `Async HTTP request` (submit selected keys)
  - If the second HTTP response returns `completed=false`, run another `Gather digits from a call` with the returned prompt and gather settings.
  - If the second HTTP response returns `completed=true`, end the flow or optionally say the returned `message`.
- The caller must be connected in Bird before the HTTP webhook step runs. If the flow omits `Answer call`, the inbound call can keep ringing until Bird times it out.
- Initial HTTP step:
  - URL: `https://api.truckwash.io:4433/bird/voice/calls/webhook/inbound`
  - Content type: `application/json`
  - Body:

```json
{
  "callId": "{{callId}}",
  "channelId": "{{channelId}}",
  "workspaceId": "{{workspaceId}}"
}
```

- Example native-flow response:

```json
{
  "requestId": "request-123",
  "callId": "4015cf84-8028-46a1-a0d9-9213e5bf4f09",
  "status": "gather",
  "completed": false,
  "stage": "department_select",
  "prompt": "Choose department. Press 1 for Roskilde. Press 2 for Demo.",
  "gather": {
    "input": "dtmf",
    "maxNumKeys": 1,
    "endKey": "#",
    "timeout": 30,
    "retries": 3,
    "say": {
      "locale": "en-US",
      "voice": "female",
      "text": "Choose department. Press 1 for Roskilde. Press 2 for Demo."
    }
  }
}
```

- Use the initial HTTP step output to configure `Gather digits from a call`:
  - Prompt/text from `prompt` or `gather.say.text`
  - `maxNumKeys`, `endKey`, `timeout`, and `retries` from `gather`
  - TTS `locale` and `voice` from `gather.say`
- Selection HTTP step:
  - POST the same `callId`, `channelId`, and `workspaceId` plus the gathered digits as `keys`.
  - Example body:

```json
{
  "callId": "{{callId}}",
  "channelId": "{{channelId}}",
  "workspaceId": "{{workspaceId}}",
  "keys": "{{gatheredKeys}}"
}
```

- Bird's current Voice API examples use BCP-47 locales such as `en-US` and TTS voices such as `female`/`male`. Avoid undocumented values like `alice`.
- Compatibility mode:
  - The webhook still supports the older `{ payload, request, waitConditions }` contract and returns a raw `202 Accepted` Bird `callCommand` gather envelope.
  - That mode is not suitable for Bird Flow Builder `Async HTTP request`, because Bird treats the response as data rather than executing the returned call command.

### Numbers
- `GET /bird/numbers`
  - Permission: `modules_bird_numbers_list`
  - Optional query: `workspaceId` (if not configured)
  - Query params are passed through to Bird.

- `GET /bird/numbers/{id}`
  - Permission: `modules_bird_numbers_get`
  - Optional query: `workspaceId` (if not configured)

- `DELETE /bird/numbers/{id}`
  - Permission: `modules_bird_numbers_delete`
  - Optional query/body: `workspaceId` (if not configured)
  - Releases/deletes a number if supported by Bird account policy.

## Logging

Bird requests are logged through `logs_o` with module `bird`, including:
- Request start (`BIRD_HTTP_REQUEST`)
- Response status (`BIRD_HTTP_RESPONSE`)
- API or transport errors (`BIRD_HTTP_ERROR`, `BIRD_HTTP_CURL_ERROR`)
- Test outbound flow events (`BIRD_TEST_OUTBOUND_CALL_*`)

## Notes

- Bird credentials and base URL are configured in Bird module config.
- Route handlers enforce workspace/channel requirements before outbound API calls.
