# Reporting API Documentation

All endpoints require **JWT Bearer Authentication** unless stated otherwise.

```
Authorization: Bearer <your_jwt_token>
```

**Base URL**: `/api/reporting`

---

## Table of Contents

1. [Inbound Calls](#1-inbound-calls)
2. [Outbound Calls](#2-outbound-calls)
3. [Missed Calls](#3-missed-calls)
4. [Attendance](#4-attendance)
5. [Performance](#5-performance)
6. [Blacklist](#6-blacklist)
7. [Call Recordings](#7-call-recordings)
8. [Agent Call Details](#8-agent-call-details-rejected--no-answer)
10. [Route Summary](#route-summary)

---

## 1. Inbound Calls

### 1.1 List Inbound Calls

```
GET /api/reporting/inbound
```

Returns paginated answered inbound queue calls from the `inbound_reports` table.
Role-based access is applied automatically (company_manager sees all; group managers see their groups; agents see only their own calls).

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| did_ext   | string | No       | Filter by DID number. |
| user_ext  | string | No       | Filter by agent extension/user ID. |
| group_ext | string | No       | Filter by queue/group ID. |
| search    | string | No       | Free-text search (queue name, agent, caller number). |
| page      | int    | No       | Page number (default: 1). |
| per_page  | int    | No       | Results per page (default: 10, max: 200). |

**Example — Today's calls (default)**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/inbound
```

**Example — Filter by date range and group**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/inbound?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&group_ext=101"
```

**Example — Filter by DID and agent**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/inbound?did_ext=0501234567&user_ext=205"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": {
      "data": [
        {
          "call_time": "2025-01-15 09:22:10 AM",
          "queue_description": "Sales Queue",
          "enter_queuename": "101",
          "agent": "Ahmed Ali",
          "hangup_by": "Caller",
          "ring_time": "00:00:18",
          "talk_time": "00:04:32",
          "caller_number": "0501234567",
          "did": "800100",
          "transfers_count": 0,
          "survey": null,
          "linkedid": "1736929330.1452",
          "listen_url": "https://example.com/api/reporting/listen/1736929330.1452",
          "listen_status": "pending"
        }
      ],
      "total": 342,
      "per_page": 10,
      "current_page": 1,
      "last_page": 34
    },
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "did_ext": null,
      "user_ext": null,
      "group_ext": null,
      "search": null
    }
  },
  "errors": [],
  "message": "Inbound calls retrieved successfully"
}
```

> **Field notes**:
> - `ring_time` / `talk_time` — formatted as `HH:MM:SS`.
> - `hangup_by` — `"Caller"` (COMPLETECALLER) or `"Agent"` (COMPLETEAGENT).
> - `listen_status` — `"pending"` (not reviewed), `"done"` (reviewed), `"not_available"` (no permission or no recording).

---

### 1.2 Get Call Transfers

```
GET /api/reporting/inbound/{linkedid}/transfers
```

Returns all transfer legs for a specific call.

**Path Parameters**

| Parameter | Type   | Description |
|-----------|--------|-------------|
| linkedid  | string | The `linkedid` of the call. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/inbound/1736929330.1452/transfers
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "transfers": [
      {
        "calldate": "2025-01-15 09:22:28",
        "duration": 18,
        "user_extension": "205",
        "user_name": "Ahmed Ali"
      }
    ],
    "count": 1
  },
  "errors": [],
  "message": "Transfer data retrieved"
}
```

---

### 1.3 Mark Inbound Call as Done

```
POST /api/reporting/inbound/done/{id}
```

Marks an inbound call as processed (`data5 = 1`) in both `queuelog` and `inbound_reports`.

**Path Parameters**

| Parameter | Type | Description |
|-----------|------|-------------|
| id        | int  | The call `id` (queuelog row ID). |

**Example**
```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/inbound/done/98765
```

**Example Response (200)**
```json
{
  "success": true,
  "data": { "id": "98765" },
  "errors": [],
  "message": "Inbound call marked as done"
}
```

---

### 1.4 Inbound Distribution

```
GET /api/reporting/inDist
```

Returns answered vs missed call distribution grouped by hour-of-day, day-of-week, and month.

> **Data source**: `inbound_reports` (one row per call; rows are classified by
> `call_status` — `answered` or `missed`) — the same table used by the Inbound and
> Inbound & Missed reports; no dependency on CDR / queuelog or other PBX tables.
> Response structure is unchanged; `filters` additionally echoes the applied
> `group_ext` (additive, `null` when not provided).

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| group_ext | int    | No       | Group (queue) ID. Narrows results to that one queue within the queues visible to the caller. Same semantics as the Inbound endpoint. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/inDist?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "hours_table": [
      { "label": "08:00", "answord": 45, "missed": 5, "offered": 50, "rate": "90.0 %" }
    ],
    "days_table": [
      { "label": "Sunday", "answord": 120, "missed": 18, "offered": 138, "rate": "87.0 %" }
    ],
    "months_table": [
      { "label": "2025-01", "answord": 890, "missed": 110, "offered": 1000, "rate": "89.0 %" }
    ]
  },
  "errors": [],
  "message": "Inbound distribution retrieved"
}
```

---

## 2. Outbound Calls

### 2.1 List Outbound Calls

```
GET /api/reporting/outbound
```

Returns outbound calls (`dcontext = from-internal`) with duration formatted as `HH:mm:ss`.
Only company users are included; role-based group restrictions apply.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| did_ext   | string | No       | Filter by source extension (cnum). |
| user_ext  | string | No       | Filter by user ID. |
| search    | string | No       | Free-text filter on destination number (numeric). |
| page      | int    | No       | Page number. |
| per_page  | int    | No       | Results per page (default: 10). |

**Example — All today's outbound**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/outbound
```

**Example — Filter by agent and date**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/outbound?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&user_ext=205"
```

**Example — Search by destination number**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/outbound?search=0501"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": {
      "data": [
        {
          "call_time": "2025-01-15 10:05:33 AM",
          "linkedid": "1736932000.2001",
          "total_duration": "00:03:15",
          "talk_time": "00:03:10",
          "ring_time": "00:00:05",
          "disposition": "ANSWERED",
          "dialed_number": "0501112222",
          "agent_ext": "205",
          "recording": "out-205-1736932000.2001.wav",
          "agent_name": "Mohammed Hassan",
          "listen_url": "https://example.com/api/reporting/listen/1736932000.2001",
          "listen_status": "done"
        }
      ],
      "total": 128,
      "per_page": 10,
      "current_page": 1,
      "last_page": 13
    },
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "did_ext": null,
      "user_ext": "205",
      "search": null
    }
  },
  "errors": [],
  "message": "Outbound calls retrieved successfully"
}
```

> **Field notes**:
> - `total_duration` — full call duration (HH:MM:SS).
> - `talk_time` — answered talk time (HH:MM:SS).
> - `ring_time` — ringing time before answer (HH:MM:SS).
> - `dialed_number` — destination number dialed by agent.
> - `agent_ext` — agent's internal extension (cnum).
> - `listen_status` — `"pending"`, `"done"`, or `"not_available"`.

---

### 2.2 Mark Outbound Call as Done

```
POST /api/reporting/outbound/done/{id}
```

Sets `userfield = 1` on the CDR record identified by `linkedid`, marking the call as reviewed/done. Mirrors `OutboundController::outDone()`.

**Path Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| id        | string | Yes      | The `linkedid` of the outbound call. |

**Example**
```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/outbound/done/1736932000.2001
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "id": "1736932000.2001"
  },
  "errors": [],
  "message": "Outbound call marked as done"
}
```

---

### 2.3 Outbound Distribution

```
GET /api/reporting/outDist
```

Returns outbound call counts grouped by hour, day, and month.

> **Data source**: `outbound_reports` (one row per call) — the same table and query
> logic as the Outbound Calls report; no dependency on CDR / queuelog or other PBX
> tables. Response structure, field names, labels, and formats are unchanged.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/outDist?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "hours_table": [
      { "label": "09:00", "calls": 22, "answered": 18 }
    ],
    "days_table": [
      { "label": "Monday", "calls": 55, "answered": 48 }
    ],
    "months_table": [
      { "label": "2025-01", "calls": 590, "answered": 510 }
    ]
  },
  "errors": [],
  "message": "Outbound distribution retrieved"
}
```

---

## 3. Missed Calls

### 3.1 List Missed Calls

```
GET /api/reporting/missed
```

Returns calls that were abandoned or timed out (ABANDON / EXITWITHTIMEOUT) without being answered,
aggregated per caller with attempt count.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| queuename | string | No       | Filter by queue ID. |
| search    | string | No       | Free-text search on caller number. |
| page      | int    | No       | Page number. |
| per_page  | int    | No       | Results per page (default: 10). |

**Example — Today's missed calls**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/missed
```

**Example — Filter by queue and date range**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/missed?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&queuename=101"
```

**Example — Search by caller number**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/missed?search=0509"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": {
      "data": [
        {
          "call_time": "2025-01-15 11:30:22 AM",
          "callid": "1736929800.1601",
          "caller_name": "",
          "caller_number": "0509876543",
          "queuename": "101",
          "queue_name": "Support Queue",
          "hangup_type": "ABANDON",
          "hangup_reason": "NOANSWER",
          "ring_duration": "00:00:42",
          "done_by": null,
          "ring_details": [
            {
              "agent": "Ahmed Ali",
              "event": "RINGNOANSWER",
              "ring_duration": "00:00:20",
              "time": "2025-01-15 11:30:25"
            }
          ]
        }
      ],
      "total": 87,
      "per_page": 10,
      "current_page": 1,
      "last_page": 9
    },
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "queuename": null,
      "search": null
    },
    "options": {
      "queues": { "101": "Support Queue" }
    }
  },
  "errors": [],
  "message": "Missed calls retrieved successfully"
}
```

> **Field notes**:
> - `caller_name` — CallerID name (data1 from ENTERQUEUE; often empty).
> - `caller_number` — CallerID number.
> - `queue_name` — Human-readable queue name.
> - `hangup_reason` — SIP reason code from last ABANDON/EXITWITHTIMEOUT.
> - `ring_duration` — Total ring time for this call (HH:MM:SS).
> - `done_by` — Name of user who marked call done, or `null`.
> - `ring_details` — Embedded ring attempts per agent (only > 1 second, ms converted to HH:MM:SS).

---

### 3.2 Get Ring Details for a Missed Call

```
GET /api/reporting/ring-details/{callid}
```

Returns the full ring attempt history for a missed call, plus concurrent inbound calls,
outbound calls, and other missed calls that were active during the same wait window.
Mirrors `MissedController::getRingDetails()`.

**Path Parameters**

| Parameter | Type   | Description |
|-----------|--------|-------------|
| callid    | string | The callid of the missed call. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/ring-details/1736929800.1601
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "ring_details": [
      {
        "agent": "Ahmed Abdo",
        "event": "RINGNOANSWER",
        "ring_duration": "00:00:20",
        "time": "2025-01-15 11:30:25"
      },
      {
        "agent": "Ahmed Abdo",
        "event": "RINGCANCELED",
        "ring_duration": "00:00:06",
        "time": "2025-01-15 11:30:46"
      }
    ],
    "inbound_calls": [
      {
        "callid": "1736929700.1590",
        "enter_time": "2025-01-15 11:29:50",
        "event": "ENTERQUEUE",
        "did": "800100",
        "dstchannel": "SIP/205-00000012",
        "src": "0501234567",
        "number": "0501234567",
        "queue_name": "Sales Queue",
        "total_duration": "00:02:25",
        "agent": "SIP/205"
      }
    ],
    "outbound_calls": [
      {
        "uniqueid": "1736929750.1595",
        "calldate": "2025-01-15 11:30:10",
        "src": "205",
        "dst": "0509876543",
        "duration": "00:01:35",
        "billsec": "00:01:20",
        "ringtime": "00:00:15",
        "disposition": "ANSWERED",
        "agent_name": "Ahmed Ali"
      }
    ],
    "active_missed_calls": [
      {
        "callid": "1736929810.1605",
        "agent": "SIP/210",
        "event": "RINGNOANSWER",
        "ring_duration": "00:00:20",
        "time": "2025-01-15 11:30:30"
      }
    ],
    "ring_details_count": 2,
    "inbound_calls_count": 1,
    "outbound_calls_count": 1,
    "active_missed_calls_count": 1
  },
  "errors": [],
  "message": "Ring details retrieved"
}
```

> **Notes**:
> - `ring_details` / `active_missed_calls` — `ring_duration` is converted from milliseconds to `HH:MM:SS`.
> - `inbound_calls` — `total_duration` formatted as `HH:MM:SS`.
> - `outbound_calls` — `duration`, `billsec`, `ringtime` formatted as `HH:MM:SS`.
> - All arrays are empty (`[]`) when ENTERQUEUE or final ABANDON/EXITWITHTIMEOUT is not found.

---

### 3.3 Mark Missed Call as Handled

```
POST /api/reporting/missed-done/{num}
```

Marks all missed calls from a caller number as handled (`data5 = 1`).

**Path Parameters**

| Parameter | Type   | Description |
|-----------|--------|-------------|
| num       | string | Caller phone number. |

**Example**
```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/missed-done/0509876543
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "caller_number": "0509876543",
    "done_by": 12
  },
  "errors": [],
  "message": "Missed call marked as done"
}
```

**Example Response — Not found (404)**
```json
{
  "success": false,
  "data": {},
  "errors": { "error": "Number not found or already marked" },
  "message": "Not Found"
}
```

---

### 3.4 Missed Call Statistics (Chart Data)

```
GET /api/reporting/missed/stats
```

Returns chart-ready counts for missed call types (ABANDON vs EXITWITHTIMEOUT) and the
breakdown of hangup reason codes. Useful for pie/bar charts on dashboards.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |

**Example — Today's stats**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/missed/stats
```

**Example — Custom range**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/missed/stats?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "status_data": {
      "labels": ["ABANDON", "EXITWITHTIMEOUT"],
      "counts": [65, 22]
    },
    "reason_data": {
      "labels": ["Unknown", "Busy", "Timeout"],
      "counts": [45, 28, 14]
    },
    "total_missed": 87,
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59"
    }
  },
  "errors": [],
  "message": "Missed call stats retrieved successfully"
}
```

---

## 4. Attendance

### 4.1 Attendance Log

```
GET /api/reporting/attendance
```

Returns paginated agent activity log (LOGGED_IN, pause, unpause, Reachable events).

> **Data source**: `attendance` — the canonical AMI event store, the same table
> used by the web Attendance report. Attendance events are no longer written to
> `activity_log`. Response structure, field names, labels, and formats are
> unchanged.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| user_ext  | int    | No       | Filter by user ID. |
| group_ext | int    | No       | Filter by group ID (returns users in that group). |
| search    | string | No       | Free-text search on user name. |
| page      | int    | No       | Page number. |
| per_page  | int    | No       | Results per page (default: 10). |

**Example — Today's attendance log**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance
```

**Example — Filter by user and date range**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/attendance?daterange=2025-01-15+00:00:00+-+2025-01-15+23:59:59&user_ext=12"
```

**Example — Filter by group**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/attendance?group_ext=101&daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": {
      "data": [
        {
          "event_time": "2025-01-15 08:01:22",
          "agent_id": 12,
          "status": "LOGGED_IN",
          "pause_reason": null,
          "agent_name": "Ahmed Ali",
          "agent_email": "ahmed@example.com",
          "duration": "00d 02h 29m 00s"
        },
        {
          "event_time": "2025-01-15 10:30:00",
          "agent_id": 12,
          "status": "pause",
          "pause_reason": "Lunch Break",
          "agent_name": "Ahmed Ali",
          "agent_email": "ahmed@example.com",
          "duration": "00d 00h 30m 00s"
        }
      ],
      "total": 210,
      "per_page": 10,
      "current_page": 1,
      "last_page": 21
    },
    "filters": {
      "daterange": "2025-01-15 00:00:00 - 2025-01-15 23:59:59",
      "user_ext": 12,
      "group_ext": null
    }
  },
  "errors": [],
  "message": "Attendance data retrieved successfully"
}
```

> **Field notes**:
> - `event_time` — When this status event occurred.
> - `status` — Event type: `LOGGED_IN`, `pause`, `unpause`, `Reachable`, `LOGGED_OUT`.
> - `pause_reason` — Pause reason label (only set when `status = "pause"`).
> - `duration` — Time until the next event for this agent (format: `DDd HHh MMm SSs`), or `"Ongoing"`.

---

### 4.2 Attendance Summary (Per-Agent Totals)

```
GET /api/reporting/attendance/summary
```

Returns per-agent totals: total login time, pause time, active time, and pause reason breakdown.
Computes durations by scanning sequential events (login → pause → unpause → logout).

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| user_ext  | int    | No       | Filter by user ID. |
| group_ext | int    | No       | Filter by group ID. |

**Example — Today's summary for all agents**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance/summary
```

**Example — Summary for a group over a month**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/attendance/summary?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&group_ext=101"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "summary": [
      {
        "name": "Ahmed Ali",
        "email": "ahmed@example.com",
        "code": "1001",
        "pause_reasons": [
          { "reason": "Lunch Break", "duration": 1800, "formatted_duration": "00:30:00" },
          { "reason": "Meeting",     "duration": 900,  "formatted_duration": "00:15:00" }
        ],
        "logged_in_time": "00:45:00",
        "pause_time":     "00:45:00",
        "reachable_time": "06:47:00",
        "total_time":     "08:17:00",
        "LOGGED_IN_formatted": "00:45:00",
        "pause_formatted":     "00:45:00",
        "Reachable_formatted": "06:47:00",
        "total_formatted":     "08:17:00"
      }
    ],
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "user_ext": null,
      "group_ext": 101
    },
    "options": {
      "agents": { "12": "Ahmed Ali" },
      "groups": { "101": "Sales Queue" }
    }
  },
  "errors": [],
  "message": "Attendance summary retrieved successfully"
}
```

> **Field notes**:
> - `logged_in_time` — Time spent in LOGGED_IN / Unreachable state (HH:MM:SS).
> - `pause_time` — Total pause duration (HH:MM:SS).
> - `reachable_time` — Total Reachable / unpause duration (HH:MM:SS).
> - `total_time` — Full period from first event to end of range (HH:MM:SS).
> - `code` — Agent user code (from `users.code`); empty string when unset.
> - `LOGGED_IN_formatted` / `pause_formatted` / `Reachable_formatted` / `total_formatted` —
>   the same values as the `*_time` fields above, exposed under the web report's
>   field names (additive; the `*_time` fields are unchanged).

---

### 4.3 List Company Users

```
GET /api/reporting/attendance/users
```

Returns all company users for populating filter dropdowns.

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance/users
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "users": [
      { "id": 12, "name": "Ahmed Ali" },
      { "id": 15, "name": "Sara Mohammed" }
    ]
  },
  "errors": [],
  "message": "Users retrieved"
}
```

---

### 4.4 List Company Groups

```
GET /api/reporting/attendance/groups
```

Returns all company groups/queues for populating filter dropdowns.

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance/groups
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "groups": [
      { "id": 101, "name": "Sales Queue" },
      { "id": 102, "name": "Support Queue" }
    ]
  },
  "errors": [],
  "message": "Groups retrieved"
}
```

---

### 4.5 Available Pause Reasons

```
GET /api/reporting/attendance/pause-reasons
```

Returns all configured pause reasons from `user_status`, plus which reasons have actually
been used in the activity log. Useful for building pause reason filter dropdowns.

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance/pause-reasons
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "available_reasons": [
      {
        "name": "Lunch Break",
        "command": "pause",
        "color": "#FFA500",
        "icon": "fa-utensils"
      },
      {
        "name": "Meeting",
        "command": "pause",
        "color": "#0000FF",
        "icon": "fa-users"
      }
    ],
    "used_reasons": [
      { "reason": "Lunch Break" },
      { "reason": "Meeting" },
      { "reason": "Prayer" }
    ]
  },
  "errors": [],
  "message": "Pause reasons retrieved"
}
```

---

### 4.6 Pause Reason Statistics

```
GET /api/reporting/attendance/pause-stats
```

Returns statistics per pause reason: occurrence count + total accumulated duration.
Duration is calculated as the time from each pause event to the next unpause/Reachable event.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |

**Example — Today's pause stats**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/attendance/pause-stats
```

**Example — Monthly pause stats**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/attendance/pause-stats?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "statistics": [
      {
        "reason": "Lunch Break",
        "occurrence_count": 42,
        "total_duration": 75600,
        "formatted_duration": "21:00:00"
      },
      {
        "reason": "Meeting",
        "occurrence_count": 18,
        "total_duration": 16200,
        "formatted_duration": "04:30:00"
      }
    ],
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59"
    }
  },
  "errors": [],
  "message": "Pause reason statistics retrieved"
}
```

---

## 5. Performance

### 5.1 Inbound Queue Performance

```
GET /api/reporting/performance
```

Returns per-queue inbound performance metrics from `inbound_reports`: total calls, answered,
dropped, SLA distribution buckets, average ring/talk time, per-agent breakdown.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| user_ext  | string | No       | Filter by agent extension. |
| group_ext | int    | No       | Filter by queue/group ID. |

**Example — Today's inbound performance**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/performance
```

**Example — Filter by queue over a month**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/performance?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&group_ext=101"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "summary": {
      "overall_total_calls": 500,
      "overall_answered_calls": 440,
      "overall_dropped_calls": 40,
      "overall_failover_calls": 20,
      "overall_talk_time": "01:20:35",
      "average_ring_time": "00:00:22",
      "average_call_time": "00:02:50",
      "service_level": {
        "0_to_5": 120,
        "6_to_10": 95,
        "11_to_15": 80,
        "16_to_20": 60,
        "21_to_25": 40,
        "26_to_30": 25,
        "31_to_35": 12,
        "36_to_40": 5,
        "41_to_up": 3
      },
      "overall_ring_time": "00:03:02",
      "overall_total_transfers": 5,
      "service_level_over": 82.4
    },
    "agents": [
      {
        "agent_id": 5,
        "agent_name": "Mohammed Hassan",
        "agent_calls": 120,
        "inbound_rejected": 3,
        "inbound_noanswer": 8,
        "avg_ring_time": "00:00:18",
        "total_talk_time": "01:20:35",
        "avg_talk_time": "00:00:40",
        "agent_hangup": 90,
        "customer_hangup": 30,
        "avg_handle_time": "00:01:20",
        "agent_transfers": 4,
        "transferred_out": 0,
        "transferred_in": 0,
        "agent_code": "205"
      }
    ],
    "queues": [
      {
        "overall_total_callsForQueue": 300,
        "overall_answered_callsForQueue": 270,
        "overall_dropped_callsForQueue": 20,
        "overall_failover_callsForQueue": 10,
        "overall_ring_timeForQueue": 5400,
        "overall_talk_timeForQueue": "01:20:35",
        "average_ring_timeForQueue": "00:00:20",
        "average_call_timeForQueue": "00:02:50",
        "queueExt": "101",
        "queueName": "Sales Queue",
        "overall_transfers_countForQueue": 3
      }
    ],
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "group_ext": 101
    }
  },
  "errors": [],
  "message": "Performance report retrieved successfully"
}
```

> **Field notes**:
> - Data, business logic, and access restrictions match the web `reporting/performance` page exactly
>   (accessible queues, `report_excluded_groups` setting, company-manager visibility, agent self-scope).
> - `overall_ring_time`, `overall_total_transfers`, `service_level_over`, `avg_handle_time`,
>   `agent_transfers`, `transferred_out`, `transferred_in`, `overall_transfers_countForQueue`,
>   and `agent_code` are additive fields mirroring the web implementation; all pre-existing
>   field names are unchanged.
> - `agent_code` — the agent's `users.code`, using the same source and permission scope as
>   Outbound Performance's `agent_code` (visible company users with accessible-queue
>   restriction); `null` when the agent cannot be resolved.
> - `service_level_over` — percentage of answered calls answered within the configured
>   `service_level_ringe_time` company setting (default 1000 seconds).
> - `group_ext` is an API-only convenience filter; it narrows results within queues the user can
>   already access.

---

### 5.2 Outbound Performance

```
GET /api/reporting/outPerformance
```

Returns outbound performance in three sections: overall summary, per-agent totals, and per-agent-per-day breakdown.
All duration fields are formatted as `HH:MM:SS` using `SEC_TO_TIME()`.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| user_ext  | int    | No       | Filter by user ID (agent extension). |

**Example — Today's outbound performance**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/outPerformance
```

**Example — Filter by agent over a month**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/outPerformance?daterange=2025-01-01+00:00:00+-+2025-01-31+23:59:59&user_ext=205"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "summary": {
      "agents_count": 5,
      "total_calls": 128,
      "answered_calls": 98,
      "no_answer_calls": 22,
      "busy_calls": 4,
      "failed_calls": 4,
      "total_duration": "05:12:00",
      "avg_duration": "00:02:26",
      "total_talk_time": "04:04:00",
      "avg_talk_time": "00:02:29"
    },
    "by_agent": [
      {
        "agent_ext": "205",
        "total_calls": 28,
        "answered_calls": 22,
        "no_answer_calls": 4,
        "busy_calls": 1,
        "failed_calls": 1,
        "total_duration": "01:24:00",
        "avg_duration": "00:03:00",
        "total_talk_time": "01:20:00",
        "avg_talk_time": "00:03:38",
        "agent_name": "Mohammed Hassan",
        "agent_code": "205"
      }
    ],
    "by_date": [
      {
        "call_date": "2025-01-15",
        "agent_ext": "205",
        "total_calls": 12,
        "answered_calls": 10,
        "no_answer_calls": 1,
        "busy_calls": 1,
        "failed_calls": 0,
        "total_duration": "00:36:00",
        "avg_duration": "00:03:00",
        "total_talk_time": "00:33:20",
        "avg_talk_time": "00:03:20",
        "agent_name": "Mohammed Hassan"
      }
    ],
    "filters": {
      "daterange": "2025-01-01 00:00:00 - 2025-01-31 23:59:59",
      "user_ext": "205"
    }
  },
  "errors": [],
  "message": "Outbound performance report retrieved successfully"
}
```

> **Field notes**:
> - `summary` — Company-wide totals.
> - `by_agent` — One row per agent (period totals), including the additive `agent_code` field
>   (the agent's `users.code`, mirroring the web implementation; all pre-existing field names are unchanged).
> - `by_date` — One row per agent per day.
> - `total_duration` — All calls duration. `total_talk_time` — Answered calls only.
> - `avg_talk_time` — Average talk time for **answered** calls only (`NULL`-safe).
> - Agent scope, access restrictions, disposition filters, and the `uniqueid = linkedid` filter
>   match the web `reporting/outperformance` page exactly (visible company users, accessible
>   queues for users without `access_to_all_entity`, company managers excluded for non-managers).

---

## 6. Blacklist

### 7.1 List Blacklisted Numbers

```
GET /api/reporting/blacklist
```

Returns the current Asterisk blacklist.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| search    | string | No       | Filter by phone number (partial match). |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  https://example.com/api/reporting/blacklist
```

**Example — Search for a specific prefix**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/blacklist?search=0501234"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "blacklist": [
      { "number": "0501234567", "context": "from-pstn" },
      { "number": "0509999000", "context": "from-pstn" }
    ],
    "total": 2
  },
  "errors": [],
  "message": "Blacklist retrieved"
}
```

---

### 7.2 Add Number to Blacklist

```
POST /api/reporting/blacklistAdd
```

Adds a number to the Asterisk blacklist via `asterisk-blacklist-add`.
Requires `reporting.create` permission.

**Request Body (JSON or form-data)**

| Field  | Type   | Required | Description |
|--------|--------|----------|-------------|
| number | string | Yes      | Phone number to blacklist. |

**Example**
```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"number":"0509999999"}' \
  https://example.com/api/reporting/blacklistAdd
```

**Example Response (200)**
```json
{
  "success": true,
  "data": { "number": "0509999999" },
  "errors": [],
  "message": "Number added to blacklist"
}
```

**Example Response — Missing number (422)**
```json
{
  "success": false,
  "data": {},
  "errors": { "error": "Number is required" },
  "message": "Validation Error"
}
```

---

## 7. Call Recordings

### 8.1 List Calls with Recordings

```
GET /api/reporting/calls
```

Returns answered calls that have a recording file in the CDR.

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| daterange | string | No       | `"YYYY-MM-DD HH:mm:ss - YYYY-MM-DD HH:mm:ss"`. Defaults to today. |
| search    | string | No       | Free-text search. |
| page      | int    | No       | Page number. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/calls?daterange=2025-01-15+00:00:00+-+2025-01-15+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "calls": [
      {
        "linkedid": "1736929330.1452",
        "calldate": "2025-01-15 09:22:10",
        "src": "0501234567",
        "dst": "800100",
        "disposition": "ANSWERED",
        "duration": "00:04:32",
        "recordingfile": "q-101-1736929330.1452.wav",
        "listen_url": "/api/reporting/listen/1736929330.1452"
      }
    ],
    "pagination": {
      "total": 55,
      "per_page": 10,
      "current_page": 1,
      "last_page": 3
    }
  },
  "errors": [],
  "message": "Calls retrieved successfully"
}
```

---

### 8.2 Stream / Download a Recording

```
GET /api/reporting/listen/{id}
```

Streams the WAV audio file for a call. Serves from local disk at
`/var/spool/asterisk/monitor/YYYY/MM/DD/` or from S3 if configured.

**Path Parameters**

| Parameter | Type   | Description |
|-----------|--------|-------------|
| id        | string | The `linkedid` of the call. |

**Response**: Audio stream (`audio/wav`) with `Content-Disposition: inline`.

**Example — Stream in browser / download**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  -o call.wav \
  https://example.com/api/reporting/listen/1736929330.1452
```

**Error Response — Not found (404)**
```json
{
  "success": false,
  "data": {},
  "errors": { "error": "Recording not found" },
  "message": "Not Found"
}
```

---

## 8. Agent Call Details (Rejected & No-Answer)

### 9.1 Rejected Calls (BUSY) for an Agent

```
GET /api/reporting/rejected-calls
```

Returns inbound calls where an agent's extension returned BUSY (agent rejected the call).
Filters out very short spurious records (`duration > 2`).

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| user_id   | string | **Yes**  | The agent's extension number (DST field in CDR). |
| from      | string | **Yes**  | Start datetime `"YYYY-MM-DD HH:mm:ss"`. |
| to        | string | **Yes**  | End datetime `"YYYY-MM-DD HH:mm:ss"`. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/rejected-calls?user_id=205&from=2025-01-01+00:00:00&to=2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": [
      {
        "call_time": "2025-01-15 10:05:00",
        "caller_number": "0501234567",
        "disposition": "BUSY",
        "duration": "00:00:04"
      }
    ],
    "count": 1,
    "filters": {
      "userId": "205",
      "from": "2025-01-01 00:00:00",
      "to": "2025-01-31 23:59:59"
    }
  },
  "errors": [],
  "message": "Rejected calls retrieved successfully"
}
```

**Example Response — Missing parameters (422)**
```json
{
  "success": false,
  "data": {},
  "errors": { "error": "user_id, from and to are required" },
  "message": "Bad Request"
}
```

---

### 9.2 No-Answer / Failed Calls for an Agent

```
GET /api/reporting/no-answer-calls
```

Returns inbound calls where the agent did not answer (NO ANSWER or FAILED disposition).
Filters out very short spurious records (`duration > 2`).

**Query Parameters**

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| user_id   | string | **Yes**  | The agent's extension number (DST field in CDR). |
| from      | string | **Yes**  | Start datetime `"YYYY-MM-DD HH:mm:ss"`. |
| to        | string | **Yes**  | End datetime `"YYYY-MM-DD HH:mm:ss"`. |

**Example**
```bash
curl -H "Authorization: Bearer $TOKEN" \
  "https://example.com/api/reporting/no-answer-calls?user_id=205&from=2025-01-01+00:00:00&to=2025-01-31+23:59:59"
```

**Example Response (200)**
```json
{
  "success": true,
  "data": {
    "data": [
      {
        "call_time": "2025-01-15 09:10:00",
        "caller_number": "0509876543",
        "disposition": "NO ANSWER",
        "duration": "00:00:05"
      },
      {
        "call_time": "2025-01-14 14:35:00",
        "caller_number": "0501112233",
        "disposition": "FAILED",
        "duration": "00:00:03"
      }
    ],
    "count": 2,
    "filters": {
      "userId": "205",
      "from": "2025-01-01 00:00:00",
      "to": "2025-01-31 23:59:59"
    }
  },
  "errors": [],
  "message": "No-answer calls retrieved successfully"
}
```

---

## Standard Response Format

All endpoints return the same JSON wrapper:

```json
{
  "success": true | false,
  "data": { ... },
  "errors": { ... } | [],
  "message": "Human-readable status message"
}
```

## Authentication Errors

**401 Unauthorized** (missing or invalid token):
```json
{ "message": "Unauthenticated." }
```

**403 Forbidden** (insufficient permissions):
```json
{ "message": "This action is unauthorized." }
```

---

## Route Summary

| Method | Endpoint                                    | Controller                                      | Description                        |
|--------|---------------------------------------------|-------------------------------------------------|------------------------------------|
| GET    | /api/reporting/inbound                      | InboundApiController@inbound                    | List answered inbound calls        |
| GET    | /api/reporting/inbound/{linkedid}/transfers | InboundApiController@getTransfers               | Get transfer legs of a call        |
| POST   | /api/reporting/inbound/done/{id}            | InboundApiController@inDone                     | Mark inbound call as done          |
| GET    | /api/reporting/inDist                       | InboundApiController@inDist                     | Inbound call distribution          |
| GET    | /api/reporting/outbound                     | OutboundApiController@outbound                  | List outbound calls                |
| POST   | /api/reporting/outbound/done/{id}           | OutboundApiController@outDone                   | Mark outbound call as done         |
| GET    | /api/reporting/outDist                      | OutboundApiController@outDist                   | Outbound call distribution         |
| GET    | /api/reporting/missed                       | MissedCallsApiController@missed                 | List missed calls                  |
| GET    | /api/reporting/ring-details/{callid}        | MissedCallsApiController@getRingDetails         | Ring attempt history               |
| POST   | /api/reporting/missed-done/{num}            | MissedCallsApiController@missedDone             | Mark missed caller as handled      |
| GET    | /api/reporting/missed/stats                 | MissedCallsApiController@callStats              | Missed call type/reason chart data |
| GET    | /api/reporting/attendance                   | AttendenceApiController@attendance              | Attendance event log               |
| GET    | /api/reporting/attendance/summary           | AttendenceApiController@summary                 | Per-agent attendance totals        |
| GET    | /api/reporting/attendance/users             | AttendenceApiController@users                   | List company users                 |
| GET    | /api/reporting/attendance/groups            | AttendenceApiController@groups                  | List company groups/queues         |
| GET    | /api/reporting/attendance/pause-reasons     | AttendenceApiController@getAvailablePauseReasons| Configured pause reasons           |
| GET    | /api/reporting/attendance/pause-stats       | AttendenceApiController@getPauseReasonsStatistics| Pause reason usage stats          |
| GET    | /api/reporting/performance                  | ReportingApiController@performance              | Inbound queue performance report   |
| GET    | /api/reporting/outPerformance               | ReportingApiController@outPerformance           | Outbound agent performance report  |
| GET    | /api/reporting/rejected-calls               | ReportingApiController@getRejectedCalls         | Agent rejected (BUSY) calls        |
| GET    | /api/reporting/no-answer-calls              | ReportingApiController@getNoAnswerCalls         | Agent no-answer/failed calls       |
| GET    | /api/reporting/blacklist                    | ReportingApiController@blacklist                | List blacklisted numbers           |
| POST   | /api/reporting/blacklistAdd                 | ReportingApiController@blacklistAdd             | Add number to blacklist            |
| GET    | /api/reporting/calls                        | CallsApiController@calls                        | List calls with recordings         |
| GET    | /api/reporting/listen/{id}                  | CallsApiController@listenFile                   | Stream recording audio file        |
