# Ζωντανή ανάλυση κλήσης από συνεργάτη

Μια υπηρεσία συνεργάτη, για παράδειγμα ανίχνευσης απάτης, μπορεί να παρακολουθεί τις κλήσεις σε μία από τις γραμμές σας (SIP trunks) καθώς γίνονται και να λέει στο AI της κλήσης τι να κάνει.

- **Συμβάντα κλήσης.** Όταν μια κλήση στη γραμμή απαντηθεί και όταν λήξει, στέλνουμε με POST ένα σύντομο μήνυμα JSON στον συνεργάτη.
- **Ήχος.** Για κάθε κλήση ανοίγουμε ένα WebSocket προς τον συνεργάτη και μεταδίδουμε και τις δύο πλευρές της κλήσης.
- **Οδηγίες πίσω στο AI.** Ο συνεργάτης στέλνει ένα μήνυμα για μια ζωντανή κλήση, για παράδειγμα *«Προειδοποίησε τον καλούντα: το να ζητά κάποιος OTP προσποιούμενος την τράπεζα είναι γνωστή απάτη.»* Το βλέπει μόνο το AI. Το AI ενεργεί αμέσως, με δικά του λόγια και στη γλώσσα της κλήσης. Το μήνυμα δεν εμφανίζεται ποτέ στην απομαγνητοφώνηση.

## Ενεργοποίηση για μια γραμμή

1. Ανοίξτε την **Τηλεφωνία** και μετά τη γραμμή (SIP trunk) της οποίας τις κλήσεις θέλετε να αναλύονται.
2. Στη **Ζωντανή ανάλυση κλήσης**, συμπληρώστε τις διευθύνσεις και το token του συνεργάτη (δείτε τον πίνακα παρακάτω).
3. Πατήστε **Αποθήκευση ζωντανής ανάλυσης κλήσης**. Από την επόμενη κλήση και μετά, κάθε κλήση σε εκείνη τη γραμμή πηγαίνει στον συνεργάτη.

Για να σταματήσετε, αδειάστε και τις δύο διευθύνσεις και αποθηκεύστε. Μπορείτε επίσης να το ορίσετε από τον βοηθό AI σας με το εργαλείο `set_trunk_call_stream`. Δεν είναι διαθέσιμο στους αριθμούς της Voice Logica: χρησιμοποιήστε δική σας γραμμή.

| Πεδίο | Παράδειγμα |
| --- | --- |
| **Διεύθυνση ήχου** (WebSocket) | `wss://partner.example.com:8443/voicelogica/stream` |
| **Διεύθυνση συμβάντων** (HTTP) | `https://partner.example.com:8443/voicelogica/events` |
| **Token** | Μια μεγάλη τυχαία συμβολοσειρά. Τη στέλνουμε ως `Authorization: Bearer <token>` και στις δύο. |

Κάθε διεύθυνση είναι ένα πλήρες URL με τη θύρα και τη διαδρομή, για παράδειγμα `wss://150.140.10.20:8443/stream`. Χρησιμοποιήστε `wss://` και `https://`: τότε ο ήχος και οι αριθμοί τηλεφώνου ταξιδεύουν κρυπτογραφημένα. Τα απλά `ws://` και `http://` είναι μόνο για δοκιμές.

Ο συνεργάτης μπορεί επίσης να δέχεται συνδέσεις μόνο από τις διευθύνσεις IP των διακομιστών μας. Ζητήστε μας τη λίστα.

## Συμβάντα κλήσης

Για κάθε κλήση στη γραμμή, είτε την απαντά το AI είτε συνδέεται με πρόσωπο, στέλνουμε ένα σύντομο μήνυμα JSON στη διεύθυνση συμβάντων καθώς εξελίσσονται τα πράγματα: η κλήση απαντήθηκε, μια μεταφορά ξεκινά, πετυχαίνει ή αποτυγχάνει, η κλήση λήγει.

### Ονόματα συμβάντων

Το πεδίο `event` κάθε συμβάντος γράφεται **`subject.action`**: για τι αφορά, και μετά τι συνέβη. Κάθε συμβάν σήμερα αφορά την κλήση, οπότε όλα ξεκινούν με `call.`. Αργότερα ίσως προσθέσουμε άλλα θέματα στην ίδια διεύθυνση (για παράδειγμα μια ηχογράφηση ή μια ανάλυση), οπότε δρομολογείτε με βάση το πλήρες όνομα και αγνοείτε όσα δεν γνωρίζετε.

| Συμβάν | Πότε στέλνεται |
| --- | --- |
| `call.started` | Η κλήση απαντήθηκε. |
| `call.transfer_started` | Μια μεταφορά ξεκινά: το AI ή ένα πρόσωπο περνά την κλήση παρακάτω. |
| `call.transferred` | Η μεταφορά πέτυχε: ο καλών είναι τώρα με τον νέο προορισμό. |
| `call.transfer_failed` | Η μεταφορά δεν έγινε: ο παραλήπτης αρνήθηκε, ήταν απασχολημένος, δεν απάντησε, ή ο αριθμός απέτυχε. |
| `call.transfer_cancelled` | Η μεταφορά ακυρώθηκε και ο καλών μένει εκεί που ήταν. |
| `call.ended` | Η κλήση τελείωσε. |

Μια κλήση μπορεί να έχει πολλές μεταφορές, τη μία μετά την άλλη. Κάθε προσπάθεια στέλνει τα δικά της συμβάντα μεταφοράς.

### Πεδία σε κάθε συμβάν

| Πεδίο | |
| --- | --- |
| `event` | Το όνομα του συμβάντος, από τον πίνακα παραπάνω. |
| `callId` | Το δικό μας id για την κλήση. Χρησιμοποιήστε το για να στείλετε οδηγίες στο AI αυτής της κλήσης, και για να αντιστοιχίσετε συμβάντα και ροή ήχου της ίδιας κλήσης. |
| `sipCallId` | Το SIP Call-ID της κλήσης στη γραμμή σας, όταν υπάρχει. |
| `sipTrunkId` | Η γραμμή (SIP trunk) στην οποία είναι η κλήση. |
| `direction` | `inbound` (κάποιος κάλεσε τη γραμμή) ή `outbound` (η γραμμή κάλεσε κάποιον). |
| `from`, `to` | Ο αριθμός του καλούντος και ο καλούμενος αριθμός. |
| `startedAt` | Πότε ξεκίνησε η κλήση (ISO 8601, UTC). |

### Επιπλέον πεδία ανά συμβάν

| Συμβάν | Επιπλέον πεδία |
| --- | --- |
| `call.transfer_started`, `call.transferred`, `call.transfer_cancelled` | `transfer.destination` (ο αριθμός ή η διεύθυνση), `transfer.destinationType` (π.χ. `external_number`, `user`, `user_group`), `transfer.mode` (`blind`, `attended` ή `warm`), `transfer.reason` (γιατί μετέφερε το AI, όταν το ανέφερε). |
| `call.transfer_failed` | Τα ίδια, συν `transfer.failedReason`: τι συνέβη, όπως καταγράφηκε στην κλήση, π.χ. «Destination declined», «busy», «No answer after 30 seconds of ringing». |
| `call.ended` | `endedAt`, `durationSeconds`, `status` (π.χ. `answered`, `transferred`, `missed`), `reason` (γιατί έληξε, π.χ. `caller_hungup`). |

Ένα πεδίο για το οποίο δεν έχουμε τιμή παραλείπεται αντί να σταλεί κενό.

### Πώς γίνεται η παράδοση

- **Αίτημα:** `POST` στη διεύθυνση συμβάντων, σώμα `application/json`, κεφαλίδα `Authorization: Bearer <token>`. Ελέγξτε το token σε κάθε αίτημα και απορρίψτε οτιδήποτε άλλο.
- **Η απάντησή σας:** οποιοδήποτε `2xx` μέσα σε 5 δευτερόλεπτα. Το σώμα αγνοείται.
- **Επαναλήψεις:** αν δεν υπάρχει `2xx` (ή δεν υπάρχει απάντηση σε 5 δευτερόλεπτα) προσπαθούμε άλλη μία φορά αμέσως. Αν αποτύχει κι αυτό, το συμβάν απορρίπτεται και η αποτυχία καταγράφεται στην κλήση. Δεν κρατάμε ποτέ την κλήση όσο περιμένουμε.
- **Μία φορά μόνο:** κάθε συμβάν στέλνεται μία φορά ανά κλήση (μία φορά ανά προσπάθεια για τα συμβάντα μεταφοράς), ακόμη κι όταν την ίδια κλήση τη χειρίζονται πολλοί διακομιστές μας.
- **Σειρά:** τα συμβάντα φεύγουν καθώς αλλάζει η κλήση, από όποιον διακομιστή μας χειρίστηκε την αλλαγή, οπότε δύο συμβάντα κοντά χρονικά (για παράδειγμα `call.transferred` και `call.ended`) μπορεί να φτάσουν με οποιαδήποτε σειρά. Βασιστείτε στο όνομα του συμβάντος, όχι στη σειρά άφιξης.
- **Νέα συμβάντα και πεδία:** μπορεί να προσθέσουμε ονόματα συμβάντων και πεδία χωρίς προειδοποίηση. Αγνοήστε όσα δεν γνωρίζετε. Η αφαίρεση ή μετονομασία κάποιου θα ανακοινωνόταν πρώτα.

### Παραδείγματα

```json
{
  "event": "call.started",
  "callId": "6adf55e278e0d0de8ce090b2",
  "sipCallId": "a84c3c7f-77c8@10.0.0.5",
  "sipTrunkId": "69dcacb29e637bf355a3b834",
  "direction": "inbound",
  "from": "+306900000000",
  "to": "+302100000000",
  "startedAt": "2026-10-26T13:30:11.970Z"
}
```

```json
{
  "event": "call.ended",
  "callId": "6adf55e278e0d0de8ce090b2",
  "sipTrunkId": "69dcacb29e637bf355a3b834",
  "direction": "inbound",
  "from": "+306900000000",
  "to": "+302100000000",
  "startedAt": "2026-10-26T13:30:11.970Z",
  "endedAt": "2026-10-26T13:31:31.242Z",
  "durationSeconds": 79,
  "status": "answered",
  "reason": "caller_hungup"
}
```

Τα συμβάντα μεταφοράς φέρουν ένα αντικείμενο `transfer`:

```json
{
  "event": "call.transfer_failed",
  "callId": "6adf55e278e0d0de8ce090b2",
  "sipTrunkId": "69dcacb29e637bf355a3b834",
  "direction": "inbound",
  "from": "+306900000000",
  "to": "+302100000000",
  "startedAt": "2026-10-26T13:30:11.970Z",
  "transfer": {
    "destination": "+302109999999",
    "destinationType": "external_number",
    "mode": "warm",
    "reason": "Caller asked for the accounts department",
    "failedReason": "Destination declined"
  }
}
```

Το `callId` είναι το id που χρησιμοποιείτε όταν στέλνετε οδηγίες για αυτή την κλήση.

## Ροή ήχου

Ένα WebSocket ανά κλήση, που ανοίγει μόλις η κλήση φτάσει σε εμάς, με `Authorization: Bearer <token>`. Κάθε κλήση στη γραμμή μεταδίδεται, είτε την απαντά το AI είτε συνδέεται με πρόσωπο.

**1. Πρώτο μήνυμα (κείμενο, JSON):** η κλήση και η μορφή του ήχου.

```json
{
  "event": "start",
  "callId": "6adf55e278e0d0de8ce090b2",
  "sipTrunkId": "69dcacb29e637bf355a3b834",
  "direction": "inbound",
  "from": "+306900000000",
  "to": "+302100000000",
  "startedAt": "2026-10-26T13:30:11.970Z",
  "version": 1,
  "bidirectional": false,
  "audio": {
    "encoding": "pcm_s16le",
    "sampleRate": 8000,
    "channels": 2,
    "frameMs": 20,
    "layout": ["caller", "callee"],
    "framing": "channel_prefixed"
  }
}
```

Το `version` είναι η έκδοση του πρωτοκόλλου, `1` σήμερα. Το `bidirectional` είναι `false`: στέλνουμε μόνο ήχο, και οτιδήποτε στέλνει πίσω ο συνεργάτης στο socket αγνοείται. Γραμμές όπου ο συνεργάτης τρέχει όλη την κλήση και ο ήχος του παίζεται στον καλούντα θα χρησιμοποιούν το ίδιο πρωτόκολλο με `bidirectional: true`, οπότε ελέγξτε και τα δύο πεδία.

**2. Ήχος (δυαδικά):** κάθε μήνυμα είναι μία πλευρά της κλήσης, διάρκειας 20 ms.

| Byte | Περιεχόμενο |
| --- | --- |
| 0 | Πλευρά: `0` = ο καλών (το πρόσωπο στη γραμμή σας), `1` = η άλλη πλευρά (το AI, ή το πρόσωπο στο οποίο συνδέεται η κλήση) |
| 1 και μετά | 20 ms PCM 16-bit little-endian, mono. Στα 8 kHz αυτό είναι 320 bytes, οπότε κάθε μήνυμα είναι 321 bytes. |

Οι δύο πλευρές έρχονται ως ξεχωριστά μηνύματα, ώστε λαμβάνετε την καθεμία ολόκληρη, χωρίς μίξη και χωρίς σιωπή για να ευθυγραμμιστούν.

**3. Τελευταίο μήνυμα (κείμενο, JSON)** όταν λήγει η κλήση. Μετά κλείνουμε τη σύνδεση. Ο λόγος που έληξε η κλήση έρχεται στο συμβάν `call.ended`.

```json
{ "event": "stop", "callId": "6adf55e278e0d0de8ce090b2" }
```

Αν ο συνεργάτης διαβάζει πολύ αργά και περιμένουν να σταλούν περισσότερα από περίπου 1 MB, απορρίπτουμε καρέ ήχου μέχρι να προλάβει, ώστε ένας αργός συνεργάτης να μην καθυστερεί ποτέ την κλήση.

## Στείλτε μια οδηγία στο AI μιας κλήσης

```bash
curl -X POST https://api.voicelogica.ai/api/v1/phones/calls/6adf55e278e0d0de8ce090b2/instructions \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Warn the caller: asking for an OTP while posing as the bank is a known scam.", "type": "possible_scam", "severity": "high", "confidence": 0.87}'
```

| Πεδίο | |
| --- | --- |
| `message` | Υποχρεωτικό, έως 2.000 χαρακτήρες. Τι πρέπει να κάνει ή να πει το AI τώρα. |
| `type` | Προαιρετικό, π.χ. `possible_scam`. |
| `severity` | Προαιρετικό, π.χ. `warning`, `high`, `critical`. |
| `confidence` | Προαιρετικό, 0 έως 1. Κρατιέται μόνο στο αρχείο καταγραφής της κλήσης. |

Το κλειδί API χρειάζεται το δικαίωμα **call.send_call_instruction**. Δημιουργήστε το στο **Ομάδα και πρόσβαση - Κλειδιά API** στην εταιρεία που κατέχει τη γραμμή ([Κλειδιά API](https://docs.voicelogica.ai/el/getting-started/api-keys/)). Από έναν βοηθό AI συνδεδεμένο στη Voice Logica (MCP), το ίδιο κάνει το εργαλείο `send_call_instruction`.

**Απάντηση:** `200` με `{ "callId": "...", "delivered": true }` μόλις το AI λάβει την οδηγία. `400` όταν η κλήση έχει λήξει, δεν έχει απαντηθεί ακόμη, ή δεν έχει AI. `404` όταν η κλήση δεν ανήκει στις κλήσεις αυτής της εταιρείας.

Κάθε οδηγία κρατιέται στο αρχείο καταγραφής της κλήσης με τον τύπο, τη σοβαρότητα και το confidence της.

**Ακούει ο καλών την οδηγία;**
Όχι. Μόνο το AI τη βλέπει. Ο καλών ακούει αυτό που λέει στη συνέχεια το AI, με δικά του λόγια.

**Ποιες κλήσεις στέλνονται στον συνεργάτη;**
Κάθε κλήση σε εκείνη τη γραμμή, ήχος και συμβάντα, είτε την απαντά το AI είτε συνδέεται με πρόσωπο. Για να στέλνετε μόνο ορισμένες κλήσεις, βάλτε τις σε ξεχωριστή γραμμή.

**Μπορεί ο συνεργάτης να στείλει πολλές ειδοποιήσεις σε μία κλήση;**
Ναι. Κάθε οδηγία εκτελείται μόλις φτάσει.
