---
title: Smart Routings
excerpt: Concepts communs de l'API Smart Routings, et endpoints des règles entrantes (files d'attente), groupes, plages horaires, exceptions, import/export, lookup des règles entrantes et tokens CFD.
order: 9
---

Les Smart Routings décident où aboutit un appel entrant, selon des règles entrantes (files d'attente), des horaires d'ouverture, des exceptions, la géographie, des clés CTI ou des contacts. Pour une présentation fonctionnelle, voir la [documentation Smart Routings](/fr/docs/smart-routings/introduction).

Cette page présente les concepts communs à tous les endpoints Smart Routings, puis les endpoints des règles entrantes. Les autres ressources ont leur propre page :

| Page | Ressources |
|---|---|
| [CTI](/fr/docs/cx-api/smart-routings-ctis) | CTI et leurs destinations, lookups CTI |
| [Contacts](/fr/docs/cx-api/smart-routings-contacts) | Contacts de routage et champs de contact |
| [Routage géographique](/fr/docs/cx-api/smart-routings-geo) | Modèles, listes et destinations géographiques, lookup géographique |
| [Enquêtes](/fr/docs/cx-api/smart-routings-surveys) | Enquêtes et réponses aux enquêtes |

**En-têtes :** `Authorization: Bearer {token-workspace}`

---

## Choix du compte client

Les données Smart Routings appartiennent à un compte client. Chaque endpoint Smart Routings (ainsi que les autres endpoints scopés au client, comme les [clés d'intégration](/fr/docs/cx-api/integration-keys) ou l'[historique des appels](/fr/docs/cx-api/crm-calls)) doit savoir sur quel client il agit. Le client **n'est pas** déduit du token : il doit être indiqué dans la requête.

| Paramètre | Description |
|---|---|
| `customer_account` | Code de compte client (recommandé) |
| `customer_id` | ID du client, utilisé en l'absence de `customer_account` |
| `account_id` | Alias de `customer_id` |

Le paramètre est généralement passé dans la query string (`?customer_account=ACME01`) ; il est aussi lu dans le corps de la requête.

La requête est rejetée avec un code `403` lorsque :

- aucun client ne correspond aux paramètres (`No valid account specified.`) ;
- l'utilisateur authentifié n'appartient pas à ce client (`You do not have access to this account.`) ;
- le client OAuth est restreint à un autre client (`This credential is not authorized for this account.`).

Utilisez [`GET /api/customers/resolve`](/fr/docs/cx-api/customers#get-apicustomersresolve) pour vérifier vers quel client pointent un identifiant d'accès et un `customer_account`.

---

## Scopes et authentification

| Scope | Donne accès à |
|---|---|
| `client` | Tous les endpoints Smart Routings |
| `cfd` | Endpoints de lookup (règles entrantes, géographique, CTI) et enregistrement des réponses aux enquêtes uniquement |

Les call flows s'authentifient généralement avec un [token CFD](#tokens-cfd), échangé contre un token d'accès court avec le scope `cfd`. Tout token OAuth disposant du scope `client` fonctionne aussi sur les lookups (voir [Authentification](/fr/docs/cx-api/authentication)).

---

## Permissions

Les permissions sont vérifiées sur le rôle de l'utilisateur authentifié. Le propriétaire du workspace et les utilisateurs ayant le rôle `superadmin` disposent de toutes les permissions.

| Ressource | Permissions |
|---|---|
| Règles entrantes | `call-queue.view`, `call-queue.create`, `call-queue.edit`, `call-queue.delete`, `call-queue.lookup` |
| Groupes | `call-queue-group.view`, `call-queue-group.create`, `call-queue-group.edit`, `call-queue-group.delete` |
| Plages horaires et exceptions | Mêmes permissions que leur parent : `view` pour lire, `edit` pour créer, modifier ou supprimer |
| Tokens CFD | `cfd-token.view`, `cfd-token.create` |

---

## Conventions

- Les endpoints d'écriture attendent un **corps JSON** (`Content-Type: application/json`), sauf l'endpoint d'import (multipart).
- Les listes sont paginées (voir [Pagination](/fr/docs/cx-api/introduction#pagination)) et acceptent les paramètres `filter[...]`, `sort` et `include` indiqués pour chaque endpoint. Les filtres texte acceptent des valeurs partielles.
- Les erreurs de validation renvoient un code `400` avec un tableau `hint` :

```json
{
  "response": "bad request: please check body parameters.",
  "hint": ["The name field is required."]
}
```

- Les erreurs de règle métier renvoient un `message` :

```json
{ "message": "The provided time span conflicts with another span for this entity." }
```

- **Types de destination**, utilisés par les règles entrantes, les plages horaires et les exceptions : `extension`, `voicemail`, `call_queue`, `end_call`, `external_number`, `other`.
- **Heure locale.** Les plages horaires et les exceptions sont évaluées dans le fuseau horaire du client (paramètre `timezone`, voir [`PUT /api/customers/settings`](/fr/docs/cx-api/customers#put-apicustomerssettings)).

---

## Règles entrantes (files d'attente)

Une règle entrante décrit une file d'attente 3CX ou un numéro SDA (DID), sa destination par défaut et, éventuellement, le groupe dont elle hérite les plages horaires et les exceptions.

### L'objet règle entrante

```json
{
  "id": 5,
  "customer_id": 12,
  "call_queue_group_id": 2,
  "active": true,
  "name": "Support",
  "host_name": "acme.3cx.eu",
  "type": "queue",
  "code": "SUP",
  "number": "800",
  "did_number": null,
  "default_destination_type": "voicemail",
  "default_destination": "800",
  "created_at": "2025-01-15T09:30:00.000000Z",
  "updated_at": "2025-01-20T16:02:11.000000Z",
  "group": { "id": 2, "name": "Business hours" }
}
```

| Champ | Description |
|---|---|
| `type` | `queue` (identifiée par `number`) ou `did` (identifiée par `did_number`) |
| `host_name` | Nom d'hôte 3CX sur lequel se trouve la file |
| `code` | Code libre, 50 caractères max. |
| `call_queue_group_id` | Groupe auquel appartient la règle, ou `null` |

---

### GET /api/smart-routings/call-queues/queues

Retourne les règles entrantes du client, de la plus récemment modifiée à la plus ancienne.

**Paramètres de requête**

| Paramètre | Description |
|---|---|
| `customer_account` | **Requis.** Code de compte client |
| `filter[call_queue_group_id]` | ID de groupe exact |
| `filter[active]` | Valeur exacte, `1` ou `0` |
| `filter[type]` | `queue` ou `did` |
| `filter[name]`, `filter[code]`, `filter[number]`, `filter[did_number]`, `filter[host_name]` | Correspondance partielle |
| `filter[search]` | Correspondance partielle sur le nom, le code, le numéro ou le numéro SDA |
| `include` | `customer`, `exceptions`, `timeSpans` |
| `sort` | `id`, `created_at`, `updated_at`, `name`, `code`, `type`, `call_queue_group_id`, `resource` (numéro ou numéro SDA). Préfixer par `-` pour un ordre décroissant. Par défaut : `-updated_at` |
| `per_page` | Résultats par page (défaut : 20) |

**Réponse `200`** : liste paginée d'objets règle entrante

---

### GET /api/smart-routings/call-queues/queues/{id}

Retourne une règle entrante avec son `group`, ses `exceptions` et ses `time_spans`.

**Réponse `200`** : objet règle entrante

---

### POST /api/smart-routings/call-queues/queues

Crée une règle entrante pour le client. Si `call_queue_group_id` est renseigné, les plages horaires et exceptions du groupe sont copiées sur la nouvelle règle.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `name` | string | oui | Nom, 50 caractères max. |
| `type` | string | non | `queue` ou `did` |
| `number` | string | non | Numéro de la file, 50 caractères max. |
| `did_number` | string | non | Numéro SDA, 255 caractères max. |
| `host_name` | string | non | Nom d'hôte 3CX |
| `code` | string | non | Code libre, 50 caractères max. |
| `active` | boolean | non | Règle active ou non |
| `call_queue_group_id` | integer | non | ID d'un groupe existant |
| `default_destination_type` | string | non | Un des types de destination |
| `default_destination` | string | non | Destination par défaut, 255 caractères max. |

**Réponse `201`** : l'objet règle entrante créé

---

### PUT /api/smart-routings/call-queues/queues/{id}

Modifie une règle entrante. Accepte les mêmes champs que la création, tous facultatifs.

Déplacer la règle dans un autre groupe remplace ses plages horaires et exceptions par des copies de celles du nouveau groupe. Passer `call_queue_group_id` à `null` la détache du groupe : les copies deviennent ses propres lignes, modifiables.

**Réponse `201`** : l'objet règle entrante modifié

---

### DELETE /api/smart-routings/call-queues/queues/{id}

Supprime une règle entrante ainsi que ses plages horaires et exceptions.

**Réponse `200`**

```json
{ "response": "element deleted" }
```

---

### PATCH /api/smart-routings/call-queues/queues/bulk

Modifie plusieurs règles entrantes du client en une fois. Seuls les champs présents dans le corps sont modifiés. Requiert `call-queue.edit`.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | oui | IDs des règles à modifier |
| `active` | boolean | non | Active ou désactive |
| `default_destination_type` | string | non | Un des types de destination |
| `default_destination` | string | non | Destination par défaut |

```json
{ "ids": [5, 6, 9], "default_destination_type": "voicemail", "default_destination": "800" }
```

**Réponse `200`** : tableau des objets règle entrante modifiés

---

### DELETE /api/smart-routings/call-queues/queues/bulk

Supprime plusieurs règles entrantes du client, ainsi que leurs plages horaires et leurs exceptions. Requiert `call-queue.delete`. Les règles d'un autre client sont ignorées.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | oui | IDs des règles à supprimer |

**Réponse `200`**

```json
{ "response": "elements deleted" }
```

---

## Groupes

Un groupe partage ses plages horaires et ses exceptions avec toutes les règles entrantes qu'il contient. Les règles en reçoivent des **copies verrouillées** : seuls `destination` et `destination_type` peuvent être modifiés sur la règle elle-même.

### L'objet groupe

```json
{
  "id": 2,
  "customer_id": 12,
  "name": "Business hours",
  "created_at": "2025-01-10T08:00:00.000000Z",
  "updated_at": "2025-01-10T08:00:00.000000Z",
  "queues_count": 4
}
```

`queues_count` n'est présent que dans les listes.

---

### GET /api/smart-routings/call-queues/groups

Retourne les groupes du client, du plus récemment modifié au plus ancien.

**Paramètres de requête**

| Paramètre | Description |
|---|---|
| `customer_account` | **Requis.** Code de compte client |
| `filter[name]` | Correspondance partielle |
| `include` | `queues`, `customer`, `exceptions`, `timeSpans` |
| `sort` | `id`, `created_at`, `updated_at`, `name`. Par défaut : `-updated_at` |
| `per_page` | Résultats par page (défaut : 20) |

**Réponse `200`** : liste paginée d'objets groupe

---

### GET /api/smart-routings/call-queues/groups/{id}

Retourne un groupe avec ses `queues`, `exceptions` et `time_spans`.

**Réponse `200`** : objet groupe

---

### POST /api/smart-routings/call-queues/groups

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `name` | string | oui | Nom, 50 caractères max. |

**Réponse `201`** : l'objet groupe créé

---

### PUT /api/smart-routings/call-queues/groups/{id}

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `name` | string | non | Nom, 50 caractères max. |

**Réponse `201`** : l'objet groupe modifié

---

### DELETE /api/smart-routings/call-queues/groups/{id}

Supprime un groupe. Ses règles entrantes sont conservées et détachées du groupe ; les copies héritées deviennent leurs propres lignes.

**Réponse `200`**

```json
{ "response": "element deleted" }
```

---

## Plages horaires

Une plage horaire route les appels vers une destination pendant une période d'ouverture, chaque semaine (`day_of_week`) ou à une date précise (`date_full`). Une plage horaire appartient à une règle entrante ou à un groupe : les endpoints qui listent ou créent des plages identifient ce parent avec deux paramètres de requête.

| Paramètre | Description |
|---|---|
| `time_spanable_type` | `call_queue` (règle entrante) ou `call_queue_group` |
| `time_spanable_id` | ID de la règle entrante ou du groupe |

Une règle entrante qui appartient à un groupe ne peut pas avoir ses propres plages horaires : créez-les sur le groupe (`422`).

### L'objet plage horaire

```json
{
  "id": 31,
  "source_time_span_id": null,
  "imported_at": null,
  "time_spanable_id": 5,
  "time_spanable_type": "call_queue",
  "reference": "morning",
  "destination": "800",
  "destination_type": "call_queue",
  "day_of_week": 1,
  "date_full": null,
  "start_time": "08:30:00",
  "end_time": "12:30:00",
  "created_at": "2025-01-15T09:35:00.000000Z",
  "updated_at": "2025-01-15T09:35:00.000000Z"
}
```

| Champ | Description |
|---|---|
| `day_of_week` | `1` (lundi) à `7` (dimanche) |
| `date_full` | Date précise (`AAAA-MM-JJ`). Prioritaire sur les plages hebdomadaires ce jour-là |
| `source_time_span_id` | Renseigné sur les copies héritées d'un groupe (lignes verrouillées) |
| `imported_at` | Renseigné sur les plages créées par un [import](#import-et-export) |

---

### GET /api/smart-routings/call-queues/time-spans

**Paramètres de requête**

| Paramètre | Description |
|---|---|
| `customer_account` | **Requis.** Code de compte client |
| `time_spanable_type`, `time_spanable_id` | **Requis.** Parent des plages |
| `filter[reference]`, `filter[destination]`, `filter[day_of_week]`, `filter[date_full]`, `filter[start_time]`, `filter[end_time]` | Filtres |
| `sort` | `id`, `created_at`, `updated_at`. Par défaut : `-updated_at` |
| `per_page` | Résultats par page (défaut : 20) |

**Réponse `200`** : liste paginée d'objets plage horaire

---

### GET /api/smart-routings/call-queues/time-spans/{id}

Retourne une plage horaire avec son parent (`time_spanable`).

**Réponse `200`** : objet plage horaire

---

### POST /api/smart-routings/call-queues/time-spans

Crée une plage horaire sur le parent indiqué dans la query string. Les plages créées sur un groupe sont copiées sur toutes les règles entrantes du groupe.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `start_time` | string | oui | Heure de début, `HH:MM:SS` |
| `end_time` | string | oui | Heure de fin, `HH:MM:SS`, postérieure à `start_time` |
| `day_of_week` | integer | oui* | `1` (lundi) à `7` (dimanche) |
| `date_full` | string | oui* | Date précise, `AAAA-MM-JJ` |
| `destination_type` | string | non | Un des types de destination |
| `destination` | string | non | Destination, 50 caractères max. |
| `reference` | string | non | Référence libre, 50 caractères max. |

*Fournir `day_of_week` ou `date_full`. Si les deux sont envoyés, `day_of_week` est ignoré.

**Réponse `201`** : l'objet plage horaire créé

**Réponse `422`** : la plage chevauche une autre plage du même parent le même jour

---

### PUT /api/smart-routings/call-queues/time-spans/{id}

Modifie une plage horaire. Accepte les mêmes champs, tous facultatifs. Sur une plage verrouillée (héritée d'un groupe), seuls `destination` et `destination_type` peuvent être envoyés (`422` sinon).

**Réponse `201`** : l'objet plage horaire modifié

---

### DELETE /api/smart-routings/call-queues/time-spans/{id}

Supprime une plage horaire. Les plages verrouillées ne peuvent pas être supprimées (`403`) : supprimez-les sur le groupe.

**Réponse `200`**

```json
{ "response": "element deleted" }
```

---

### POST /api/smart-routings/call-queues/time-spans/bulk

Crée la même période d'ouverture sur plusieurs jours en une fois : une plage par valeur de `day_of_week` et de `date_full`. Requiert les paramètres de requête `time_spanable_type` et `time_spanable_id`.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `day_of_week` | integer[] | oui* | Jours de la semaine, `1` à `7` |
| `date_full` | string[] | oui* | Dates, `AAAA-MM-JJ` |
| `start_time` | string | oui | Heure de début, `HH:MM:SS` |
| `end_time` | string | oui | Heure de fin, `HH:MM:SS` |
| `destination_type` | string | non | Un des types de destination |
| `destination` | string | non | Destination |
| `reference` | string | non | Référence libre |

*Fournir `day_of_week`, `date_full`, ou les deux.

```json
{
  "day_of_week": [1, 2, 3, 4, 5],
  "start_time": "08:30:00",
  "end_time": "12:30:00",
  "destination_type": "call_queue",
  "destination": "800"
}
```

**Réponse `201`** : tableau de **toutes** les plages horaires du parent

**Réponse `422`** : une des plages entre en conflit avec une plage existante

---

### PATCH /api/smart-routings/call-queues/time-spans/bulk

Modifie la destination de plusieurs plages horaires du parent. Fonctionne aussi sur les plages verrouillées.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | oui | IDs des plages horaires |
| `destination_type` | string | non | Un des types de destination |
| `destination` | string | non | Destination |

**Réponse `200`** : tableau des objets plage horaire modifiés

---

### DELETE /api/smart-routings/call-queues/time-spans/bulk

Supprime plusieurs plages horaires du parent.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | non | IDs des plages horaires à supprimer |

> [!WARNING]
> Sans `ids`, **toutes** les plages horaires du parent sont supprimées.

**Réponse `200`**

```json
{ "response": "elements deleted" }
```

**Réponse `403`** : une des plages ciblées est verrouillée (héritée d'un groupe)

---

## Exceptions

Une exception remplace les plages horaires un jour donné (jour férié, fermeture…). Les exceptions sont prioritaires sur les plages horaires. Comme les plages horaires, elles appartiennent à une règle entrante ou à un groupe, identifié par deux paramètres de requête sur les endpoints de liste, de création et de traitement par lot.

| Paramètre | Description |
|---|---|
| `exceptionable_type` | `call_queue` (règle entrante) ou `call_queue_group` |
| `exceptionable_id` | ID de la règle entrante ou du groupe |

Une règle entrante qui appartient à un groupe ne peut pas avoir ses propres exceptions : créez-les sur le groupe (`422`).

### L'objet exception

```json
{
  "id": 77,
  "source_exception_id": null,
  "exceptionable_id": 2,
  "exceptionable_type": "call_queue_group",
  "reference": "France:christmasDay",
  "label": "Noël",
  "destination": "800",
  "destination_type": "voicemail",
  "day": "2026-12-25",
  "created_at": "2026-01-05T10:00:00.000000Z",
  "updated_at": "2026-01-05T10:00:00.000000Z"
}
```

`source_exception_id` est renseigné sur les copies verrouillées héritées d'un groupe.

---

### GET /api/smart-routings/call-queues/exceptions

**Paramètres de requête**

| Paramètre | Description |
|---|---|
| `customer_account` | **Requis.** Code de compte client |
| `exceptionable_type`, `exceptionable_id` | **Requis.** Parent des exceptions |
| `filter[reference]`, `filter[destination]`, `filter[day]` | Filtres |
| `sort` | `id`, `created_at`, `updated_at`, `day`. Par défaut : `day` |
| `per_page` | Résultats par page (défaut : 20) |

**Réponse `200`** : liste paginée d'objets exception

---

### GET /api/smart-routings/call-queues/exceptions/{id}

Retourne une exception avec son parent (`exceptionable`).

**Réponse `200`** : objet exception

---

### POST /api/smart-routings/call-queues/exceptions

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `day` | string | oui | Date, `AAAA-MM-JJ` |
| `label` | string | non | Libellé, 50 caractères max. |
| `reference` | string | non | Référence, 50 caractères max., unique par parent |
| `destination_type` | string | non | Un des types de destination |
| `destination` | string | non | Destination, 50 caractères max. |

**Réponse `201`** : l'objet exception créé

**Réponse `400`** : la référence existe déjà, ou une autre exception existe ce jour-là

---

### PUT /api/smart-routings/call-queues/exceptions/{id}

Modifie une exception. Accepte les mêmes champs, tous facultatifs. Sur une exception verrouillée, seuls `destination` et `destination_type` peuvent être envoyés (`422` sinon).

**Réponse `201`** : l'objet exception modifié

---

### DELETE /api/smart-routings/call-queues/exceptions/{id}

Supprime une exception. Les exceptions verrouillées ne peuvent pas être supprimées (`403`).

**Réponse `200`**

```json
{ "response": "element deleted" }
```

---

### PATCH /api/smart-routings/call-queues/exceptions/bulk

Modifie la destination de plusieurs exceptions du parent. Fonctionne aussi sur les exceptions verrouillées.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | oui | IDs des exceptions |
| `destination_type` | string | non | Un des types de destination |
| `destination` | string | non | Destination |

**Réponse `200`** : tableau des objets exception modifiés

---

### DELETE /api/smart-routings/call-queues/exceptions/bulk

Supprime plusieurs exceptions du parent.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `ids` | integer[] | oui | IDs des exceptions à supprimer |

**Réponse `200`**

```json
{ "response": "elements deleted" }
```

**Réponse `403`** : une des exceptions ciblées est verrouillée

---

### GET /api/smart-routings/call-queues/exceptions/holidays

Liste les pays disponibles pour l'import des jours fériés.

**Réponse `200`**

```json
{ "countries": ["Andorra", "Argentina", "Australia", "Austria", "Belgium", "...", "France", "..."] }
```

---

### POST /api/smart-routings/call-queues/exceptions/holidays

Crée une exception par jour férié d'un pays sur le parent indiqué par `exceptionable_type` et `exceptionable_id`. Sans `year`, ce sont les jours fériés des 12 prochains mois (à partir d'aujourd'hui, sur deux années civiles si besoin) qui sont créés ; avec `year`, ceux de cette année civile. Chaque exception reçoit le nom du jour férié comme `label` et `{pays}:{jour férié}` comme `reference` (ex. `France:christmasDay`).

Un jour férié est ignoré lorsque le parent a déjà une exception ce jour-là : la requête peut donc être relancée sans créer de doublons.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `country` | string | oui | Nom du pays tel que renvoyé par `GET …/exceptions/holidays` (ex. `France`) |
| `year` | integer | non | Année (4 chiffres). Par défaut : les 12 prochains mois |
| `destination_type` | string | non | Type de destination appliqué à chaque jour férié |
| `destination` | string | non | Destination appliquée à chaque jour férié |

**Réponse `201`** : tableau des objets exception créés (vide si chaque jour férié avait déjà une exception)

**Réponse `422`** : pays inconnu ou année invalide

---

## Import et export

Les règles entrantes d'un client peuvent être exportées vers un tableur et importées depuis celui-ci. Le fichier contient une ligne par plage horaire ; les règles sans plage horaire occupent une seule ligne.

| Ligne | Contenu |
|---|---|
| 1 | Intitulés de section : `Call queue`, `Time span` |
| 2 | Noms des colonnes |
| 3+ | Données |

| Colonne | Description |
|---|---|
| `name` | Nom de la règle |
| `number` | Numéro de la file, ou numéro SDA pour les règles `did` |
| `type` | `queue` ou `did` |
| `host_name` | Nom d'hôte 3CX, doit faire partie des hôtes du client |
| `group_name` | Nom du groupe ; créé s'il n'existe pas |
| `active` | `1` ou `0` |
| `code` | Code libre |
| `default_destination_type`, `default_destination` | Destination par défaut |
| `day_of_week` | `1` (lundi) à `7` (dimanche) |
| `full_date` | Date précise, `AAAA-MM-JJ` |
| `start`, `end` | Heures, `HH:MM:SS` |
| `destination_type`, `destination` | Destination de la plage horaire |
| `reference` | Référence de la plage horaire |

### GET /api/smart-routings/call-queues/export

Télécharge les règles entrantes du client au format `inbound_rules.xlsx`. Requiert `call-queue.view`.

**Réponse `200`** : fichier Excel

---

### POST /api/smart-routings/call-queues/import

Importe des règles entrantes depuis un fichier `.xlsx` ou `.csv` (CSV : séparateur `;`, délimiteur de texte `"`). Requiert `call-queue.create`. Envoyez la requête en `multipart/form-data`.

Les règles sont rapprochées sur `type` et `number` (ou numéro SDA) : les règles existantes sont mises à jour, les autres sont créées. Le fichier entier est d'abord validé puis importé en une seule transaction : si une ligne est invalide, rien n'est importé.

**Champs du formulaire**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `document` | file | oui | Fichier `.xlsx` ou `.csv` |
| `replace_all` | boolean | non | Supprime aussi les règles du client absentes du fichier, et les groupes restés vides |

**Réponse `200`**

```json
{ "response": "Import successful." }
```

**Réponse `422`** : lignes invalides

```json
{
  "response": "The import failed validation.",
  "failures": [
    {
      "row": 4,
      "attribute": "host_name",
      "errors": ["The selected host_name is invalid."],
      "values": { "name": "Support", "number": "800", "type": "queue", "host_name": "unknown.3cx.eu" }
    }
  ]
}
```

---

## Lookup des règles entrantes

### GET /api/smart-routings/call-queues/lookup

Retourne la destination d'une règle entrante à un instant donné. C'est l'endpoint appelé par un call flow à l'arrivée d'un appel. Accepte le scope `client` ou `cfd` et requiert `call-queue.lookup`.

La destination est choisie dans cet ordre :

1. une exception ce jour-là ;
2. une plage horaire à cette date précise (`date_full`) couvrant l'heure ;
3. une plage horaire hebdomadaire (`day_of_week`) couvrant l'heure.

Seules les règles actives sont prises en compte. Le jour et l'heure sont évalués dans le fuseau horaire du client.

**Paramètres de requête**

| Paramètre | Type | Requis | Description |
|---|---|---|---|
| `customer_account` | string | oui* | Code de compte client |
| `customer_id` | integer | oui* | ID du client |
| `queue_id` | integer | oui** | ID de la règle entrante |
| `queue_number` | string | oui** | Numéro de la file |
| `queue_name` | string | oui** | Nom de la règle |
| `day` | string | non | Jour à évaluer, `AAAA-MM-JJ`. Par défaut : aujourd'hui |
| `time` | string | non | Heure à évaluer, `HH:MM:SS`. Par défaut : maintenant |

*Fournir `customer_account` ou `customer_id`.
**Fournir un des paramètres `queue_id`, `queue_number` ou `queue_name` (vérifiés dans cet ordre).

**Réponse `200`**

```json
{
  "reference": "morning",
  "destination": "800",
  "label": null,
  "inherited": true,
  "matched": "time_span,day_of_week"
}
```

| Champ | Description |
|---|---|
| `reference` | Référence de l'exception ou de la plage horaire trouvée |
| `destination` | Destination vers laquelle router l'appel |
| `label` | Libellé de l'exception (`null` pour les plages horaires) |
| `inherited` | `true` lorsque la correspondance provient du groupe de la règle |
| `matched` | `exception`, `time_span,date_full` ou `time_span,day_of_week` |

**Réponse `404`**

```json
{ "message": "No destination found matching given parameters." }
```

Autres messages `404` : `No customer found matching given parameters.`, `No queue found matching given parameters.`. Si rien ne correspond, appliquez la destination par défaut de la règle dans votre call flow.

**Réponse `400`** : paramètres invalides, ou `day`/`time` invalide

---

## Tokens CFD

Un token CFD est un secret de longue durée conçu pour les call flows (3CX Call Flow Designer). Il est rattaché à un utilisateur et s'échange contre un token d'accès court avec le scope `cfd`, qui ne donne accès qu'aux endpoints de lookup et à l'enregistrement des réponses aux enquêtes.

Les tokens CFD sont définis au niveau du workspace : ils ne sont pas filtrés par client, mais les endpoints requièrent tout de même `customer_account`.

### L'objet token CFD

```json
{
  "id": 3,
  "user_id": 8,
  "name": "Main call flow",
  "token": "q8V3...80 characters...Xz",
  "hosts": ["203.0.113.10"],
  "expires_at": "2027-01-01T00:00:00.000000Z",
  "created_at": "2026-01-05T10:00:00.000000Z",
  "updated_at": "2026-01-05T10:00:00.000000Z",
  "expired": false,
  "user": { "id": 8, "name": "Call flow bot", "group": "admin", "email": "cfd@acme.com" }
}
```

> La valeur `token` est renvoyée par tous les endpoints de tokens CFD. Traitez-la comme un mot de passe.

| Champ | Description |
|---|---|
| `hosts` | Adresses IP autorisées à échanger le token. `null` ou `["*"]` autorise toutes les adresses |
| `expires_at` | Date d'expiration, ou `null` sans expiration |
| `expired` | `true` dès que `expires_at` est passée. Toujours `false` quand `expires_at` vaut `null` |

L'endpoint de liste ne renvoie pas `hosts`.

---

### GET /api/smart-routings/cfd-tokens

Requiert `cfd-token.view`.

**Paramètres de requête**

| Paramètre | Description |
|---|---|
| `customer_account` | **Requis.** Code de compte client |
| `filter[name]` | Correspondance partielle |
| `include` | `user` (toujours chargé) |
| `sort` | `id`, `name`, `expires_at`, `created_at`, `updated_at`. Par défaut : `-updated_at` |
| `per_page` | Résultats par page (défaut : 20) |

**Réponse `200`** : liste paginée d'objets token CFD

---

### GET /api/smart-routings/cfd-tokens/{id}

Requiert `cfd-token.view`. Les utilisateurs non admin ne peuvent lire que leurs propres tokens.

**Réponse `200`** : objet token CFD

---

### POST /api/smart-routings/cfd-tokens

Crée un token CFD. La valeur `token` est générée (80 caractères). Requiert `cfd-token.create`.

**Corps de la requête**

| Champ | Type | Requis | Description |
|---|---|---|---|
| `user_id` | integer | oui | Utilisateur au nom duquel le token s'authentifie |
| `name` | string | non | Nom, 50 caractères max. |
| `hosts` | string[] | non | Adresses IP autorisées |
| `expires_at` | string | non | Date d'expiration, `AAAA-MM-JJ`. Le token expire à 00:00 UTC à cette date. Omettez-la pour un token qui n'expire jamais |

**Réponse `201`** : l'objet token CFD créé

---

### PUT /api/smart-routings/cfd-tokens/{id}

Modifie un token CFD (`user_id`, `name`, `hosts`, `expires_at`). La valeur `token` ne peut pas être modifiée. Autorisé aux utilisateurs admin et au propriétaire du token.

**Réponse `201`** : l'objet token CFD modifié

---

### DELETE /api/smart-routings/cfd-tokens/{id}

Autorisé aux utilisateurs admin et au propriétaire du token.

**Réponse `200`**

```json
{ "response": "element deleted" }
```

---

### GET /api/smart-routings/cfd-auth/t/{token}

Échange un token CFD contre un token d'accès avec le scope `cfd`. **Aucun en-tête `Authorization`** n'est nécessaire.

**Réponse `200`**

```json
{
  "message": "success",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOi...",
  "expires_at": "2026-10-09T11:42:00+00:00",
  "user": { "id": 8, "name": "Call flow bot", "email": "cfd@acme.com", ... }
}
```

Utilisez le `token` renvoyé comme Bearer token des appels de lookup. Il est valable **1 heure** (`expires_at`) : échangez à nouveau le token CFD à chaque appel plutôt que de conserver le token d'accès.

**Réponse `403`**

```json
{ "message": "Host not authorised." }
```

**Réponse `404`** : token inconnu, token expiré, ou son utilisateur n'existe plus

```json
{ "message": "This token does not exists or has expired." }
```

L'expiration est vérifiée à chaque échange : une fois `expires_at` passée, le token CFD ne peut plus être échangé contre un token d'accès. Un token sans `expires_at` n'expire jamais. La vérification dans le contrôleur :

```php
if (! $cfdToken || $cfdToken->expired) {
    return response()->json([
        'message' => 'This token does not exists or has expired.',
    ], 404);
}
```

> [!WARNING]
Faire expirer ou supprimer un token CFD révoque aussi les tokens d'accès qu'il a émis : les lookups faits avec eux répondent `401` immédiatement.
