[TUTO] comment obtenir des fichiers ressources wazo api pour l'ia

CHAPITRE 6 : Provisioning Physique (wazo-provd)

6.1 Introduction à wazo-provd

wazo-provd est le service de provisioning des terminaux physiques. Il génère les fichiers de configuration pour les téléphones SIP, ATA et gateways en se basant sur des plugins.

6.1.1 Architecture de Provisioning

┌─────────────────────────────────────────────────────────────────────────────┐
│                    ARCHITECTURE PROVISIONING WAZO                            │
└─────────────────────────────────────────────────────────────────────────────┘

    ┌─────────────┐         ┌─────────────┐         ┌─────────────┐
    │   PHONE     │         │  wazo-provd │         │  wazo-confd │
    │  (Boot)    │         │  (Serveur)  │         │ (Config)    │
    └──────┬──────┘         └──────┬──────┘         └──────┬──────┘
           │                        │                        │
           │ 1. DHCP Request       │                        │
           │──────────────────────►│                        │
           │                        │                        │
           │ 2. DHCP Response      │                        │
           │   + HTTP URL          │                        │
           │◄──────────────────────│                        │
           │                        │                        │
           │ 3. HTTP Provisioning   │                        │
           │   Request             │                        │
           │──────────────────────►│                        │
           │                        │ 4. Get config         │
           │                        │──────────────────────►│
           │                        │◄───────────────────────│
           │                        │                       │
           │ 5. Generate PJSIP     │                       │
           │    + Base config       │                       │
           │◄───────────────────────│                       │
           │                        │                       │
           │ 6. Apply config       │                       │
           │   (Auto-register)     │                       │

6.1.2 Composants Clés

Composant Description
Plugin Module spécifique à un modèle de téléphone (Snom, Yealink, Polycom)
Template Fichiers de configuration Jinja2 générés par plugin
Device Enregistrement du terminal (MAC, vendor, model)
Configuration Ensemble des paramètres应用到 un device

6.2 Gestion des Devices

6.2.1 CRUD des Devices

Endpoint

GET/POST    /api/provd/0.1/devices
GET/PUT/DELETE /api/provd/0.1/devices/{device_id}

Création d’un Device

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "mac": "001122334455",
    "ip": "192.168.1.100",
    "vendor": "Snom",
    "model": "D345",
    "plugin": "snom",
    "description": "Telephone Alice",
    "template": "standard"
  }' \
  "https://wazo.example.com:8667/api/provd/0.1/devices"

Payload Détaillé

Champ Type Description
mac string Adresse MAC du terminal (format: 001122334455)
ip string Adresse IP actuelle (optionnel, mis à jour automatiquement)
vendor string Fabricant (Snom, Yealink, Polycom, etc.)
model string Modèle spécifique
plugin string Plugin à utiliser (doit correspondre au vendor)
template string Template de configuration
description string Description libre
status string Statut: autoprov, configured, waiting

Réponse

{
  "id": "device-uuid-001",
  "mac": "001122334455",
  "ip": "192.168.1.100",
  "vendor": "Snom",
  "model": "D345",
  "plugin": "snom",
  "status": "autoprov",
  "template": null,
  "config_version": null,
  "created_at": "2026-03-07T15:30:00.000000Z",
  "updated_at": "2026-03-07T15:30:00.000000Z"
}

6.2.2 États d’un Device

État Description
autoprov Device détecté, en attente de configuration
waiting En attente de synchronisation
configured Configuration appliquée
failed Échec de configuration

6.2.3 Liste des Plugins Disponibles

curl -k -X GET \
  -H "X-Auth-Token: {token}" \
  "https://wazo.example.com:8667/api/provd/0.1/plugins"
{
  "items": [
    {"name": "snom", "version": "3.3.1"},
    {"name": "yealink", "version": "85.0.1.20"},
    {"name": "polycom", "version": "5.9.2"},
    {"name": "aastra", "version": "3.3.1-SP4"}
  ]
}

6.3 Mécanisme de Synchronisation

6.3.1 Processus de Synchronisation Complet

La synchronisation est le processus par lequel un terminal téléphone récupère et applique sa configuration.

┌─────────────────────────────────────────────────────────────────────────────┐
│                    PROCESSUS DE SYNCHRONISATION                                │
└─────────────────────────────────────────────────────────────────────────────┘

  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
  │   DEVICE    │     │  wazo-provd  │     │  wazo-confd  │
  │  (Telephone) │     │              │     │              │
  └──────┬───────┘     └──────┬───────┘     └──────────────┘
         │                      │                      │
         │  1. Boot & DHCP      │                      │
         │─────────────────────►│                      │
         │                      │                      │
         │  2. HTTP GET         │                      │
         │  /provd/.../mac      │                      │
         │─────────────────────►│                      │
         │                      │                      │
         │                      │  3. Lookup device    │
         │                      │─────────────────────►│
         │                      │◄─────────────────────│
         │                      │                      │
         │                      │  4. Generate config   │
         │                      │  (Jinja2 templates)  │
         │                      │                      │
         │  5. Return config    │                      │
         │  (SIP credentials,   │                      │
         │   proxy, codecs)    │                      │
         │◄─────────────────────│                      │
         │                      │                      │
         │  6. Apply & Register│                      │
         │  to SIP proxy       │                      │
         │                      │                      │
         │                      │  7. REGISTER event   │
         │                      │◄─────────────────────│

6.3.2 Étapes de Synchronisation API

Étape 1 : Associer une Ligne au Device (wazo-confd)

# Créer d'abord la ligne dans confd (voir Chapitre 3)
# ...
# Puis lier la ligne au device dans provd
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"line_id": 42}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001"

Étape 2 : Appliquer un Template

curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"template": "standard"}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001/config"

Étape 3 : Déclencher la Synchronisation

curl -k -X PUT \
  -H "X-Auth-Token: {token}" \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001/synchronize"

:warning: Attention : La synchronisation déclenche un redémarrage du téléphone. Planifiez cette opération pendant les heures creuses.

Vérification du Statut

curl -k -X GET \
  -H "X-Auth-Token: {token}" \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001"
{
  "id": "device-uuid-001",
  "mac": "001122334455",
  "ip": "192.168.1.100",
  "status": "configured",
  "remote_address": "192.168.1.100:5060",
  "plugin": "snom",
  "template": "standard",
  "config_version": 17,
  "lines": [
    {
      "id": 42,
      "name": "alice-line-001",
      "exten": "1001"
    }
  ],
  "updated_at": "2026-03-07T16:00:00.000000Z"
}

6.3.3 Détection Automatique (Auto-provisioning)

Wazo détecte automatiquement les nouveaux appareils via :

  1. DHCP : Le serveur DHCP informe provd des nouvelles demandes
  2. HTTP : Le téléphone boot et contacte le serveur provisioning

Pour désactiver l’auto-provisioning :

# Via configuration système
# /etc/wazo-provd/conf.d/custom.yml
enabled_autoprov: false

6.4 Templates de Configuration

6.4.1 Concept des Templates

Les templates définissent la configuration appliquée à un device. Ils utilisent le moteur Jinja2.

Hiérarchie des Templates

┌─────────────────────────────────────────────────────────────────────────────┐
│                  HIÉRARCHIE TEMPLATES WAZO                                   │
└─────────────────────────────────────────────────────────────────────────────┘

                    ┌─────────────────┐
                    │   GLOBAL        │
                    │  (système)      │
                    └────────┬────────┘
                             │
                    ┌────────┴────────┐
                    │                 │
              ┌─────▼─────┐     ┌───▼────┐
              │ PLUGIN    │     │ CUSTOM  │
              │ Templates │     │Templates│
              └─────┬─────┘     └───┬────┘
                    │                 │
                    └────────┬────────┘
                             │
                      ┌──────▼──────┐
                      │  DEVICE     │
                      │  (final)    │
                      └─────────────┘

6.4.2 Création d’un Template Personnalisé

Les templates personnalisés s’ajoutent au niveau du plugin :

# Emplacement des templates
/var/lib/wazo-provd/plugins/wazo-sn

om-3.3.1/templates/

# Créer un template personnalisé
mkdir -p /var/lib/wazo-provd/plugins/wazo-sn

om-3.3.1/templates/custom

Exemple de Template Personnalisé

{# /var/lib/wazo-provd/plugins/wazo-sn

om-3.3.1/templates/custom/base.tpl #}
{% extends "base.tpl" %}

{% block sip_settings %}
{{ parent() }}
phone_setting.display_method: 0
phone_setting.backlight_level: 3
{% endblock %}

6.4.3 Variables de Template

Variable Description Exemple
{{ line.endpoint.username }} Identifiant SIP alice_auth
{{ line.endpoint.password }} Mot de passe SIP P4ssw0rd!
{{ line.extension }} Numéro interne 1001
{{ wazo_server_ip }} IP serveur Wazo 192.168.1.1
{{ wazo_proxy_ip }} IP du proxy SIP 192.168.1.1

6.5 Association Ligne-Terminal

6.5.1 Linking Device ↔ Line

L’association device↔line peut se faire de deux manières :

Méthode 1 : Via provd (Provisioning)

# Dans provd - Associer une ligne existante
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"line_id": 42}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001"

Méthode 2 : Via confd (Configuration)

# Dans confd - Associer un device à une ligne
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"device_id": "device-uuid-001"}' \
  "https://wazo.example.com:9486/api/confd/1.1/lines/42/device"

Note : Les deux méthodes synchronisent automatiquement l’autre côté.


6.6 Scénario Complet : Provisioning d’un Téléphone

# =============================================================================
# ÉTAPE 1 : Créer l'endpoint SIP dans confd
# =============================================================================
curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "name": "snom-d345-001",
    "auth_section_options": [
      ["username", "alice_sip"],
      ["password", "S3cur3P4ssw0rd!"]
    ],
    "endpoint_section_options": [
      ["disallow", "all"],
      ["allow", "ulaw,alaw,g722"],
      ["context", "default"]
    ]
  }' \
  "https://wazo.example.com:9486/api/confd/1.1/endpoints/sip"

# Réponse : {"uuid": "endpoint-sip-uuid", ...}


# =============================================================================
# ÉTAPE 2 : Créer la ligne SIP
# =============================================================================
curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "context": "default",
    "name": "alice-line-001",
    "protocol": "sip"
  }' \
  "https://wazo.example.com:9486/api/confd/1.1/lines"

# Réponse : {"id": 42, ...}


# =============================================================================
# ÉTAPE 3 : Lier endpoint à la ligne
# =============================================================================
curl -k -X PUT \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com:9486/api/confd/1.1/lines/42/endpoints/sip/endpoint-sip-uuid"


# =============================================================================
# ÉTAPE 4 : Créer l'extension
# =============================================================================
curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "exten": "1001",
    "context": "default"
  }' \
  "https://wazo.example.com:9486/api/confd/1.1/extensions"

# Réponse : {"id": 88, ...}


# =============================================================================
# ÉTAPE 5 : Lier extension à la ligne
# =============================================================================
curl -k -X PUT \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com:9486/api/confd/1.1/lines/42/extensions/88"


# =============================================================================
# ÉTAPE 6 : Créer le device dans provd
# =============================================================================
curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "mac": "001122334455",
    "vendor": "Snom",
    "model": "D345",
    "plugin": "snom",
    "description": "Telephone Alice - Bureau Paris"
  }' \
  "https://wazo.example.com:8667/api/provd/0.1/devices"

# Réponse : {"id": "device-uuid-001", "status": "autoprov", ...}


# =============================================================================
# ÉTAPE 7 : Associer la ligne au device
# =============================================================================
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"line_id": 42}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001"


# =============================================================================
# ÉTAPE 8 : Appliquer le template et synchroniser
# =============================================================================
# Appliquer template
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: {token}" \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{"template": "standard"}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001/config"

# Synchroniser (déclenche reboot du téléphone)
curl -k -X PUT \
  -H "X-Auth-Token: {token}" \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001/synchronize"


# =============================================================================
# ÉTAPE 9 : Vérifier le status final
# =============================================================================
curl -k -X GET \
  -H "X-Auth-Token: {token}" \
  "https://wazo.example.com:8667/api/provd/0.1/devices/device-uuid-001"

# Réponse attendue :
# {
#   "id": "device-uuid-001",
#   "status": "configured",
#   "ip": "192.168.1.100",
#   "remote_address": "192.168.1.100:5060",
#   "lines": [{"id": 42, "exten": "1001"}]
# }

6.7 Troubleshooting Provisioning

6.7.1 Commandes de Diagnostic

# Voir les logs de provisioning
journalctl -u wazo-provd -f

# Liste des devices avec status
curl -k -H 'X-Auth-Token: {token}' \
  "https://wazo.example.com:8667/api/provd/0.1/devices" | jq '.items[].status'

# Vérifier plugin installé
curl -k -H 'X-Auth-Token: {token}' \
  "https://wazo.example.com:8667/api/provd/0.1/plugins" | jq '.items[].name'

6.7.2 Problèmes Courants

Problème Cause Solution
Device toujours autoprov Plugin manquant Installer le plugin correspondant
Échec synchronize Timeout réseau Vérifier connectivité téléphone
Pas de registration Mauvais credentials Vérifier endpoint SIP dans confd
Config non appliquée Template invalide Vérifier syntaxe Jinja2

Résumé du Chapitre 6

Ressource Endpoints Clés Point Critique
Device POST /devices, /devices/{id}/synchronize MAC obligatoire
Plugin /plugins Doit correspondre au vendor
Template /devices/{id}/config Jinja2, hérité plugin
Association PUT /devices/{id} avec line_id Sync auto confd↔provd
Synchronisation /devices/{id}/synchronize Déclenche reboot

Résumé des Chapitres 5 & 6

Services Avancés (Chapitre 5)

Objet Relations Clé
Queue → Agents, Skills, Schedule strategy, timeout
Ring Group → Users, Extensions extension, strategy
IVR → Destinations imbriquées choices[]
Conference → Extension pin
Schedule → Timeperiods, Timerules timezone

Provisioning (Chapitre 6)

Objet Relations Clé
Device → Plugin, Template, Line mac, status
Plugin → Templates Vendor/Model
Template → Jinja2 vars Héritage
Synchronisation → reboot automatique /synchronize

Fin des Chapitres 5 et 6 — Suite : Chapitre 7 (CTI, WebSockets, Temps Réel)


CHAPITRE 7 : CTI, WebSockets et Temps Réel

7.1 Architecture des Services Temps Réel

Wazo propose plusieurs services pour la gestion temps réel des communications :

Service Port Rôle
wazo-calld 8668 Contrôle des appels, transferts, applications
wazo-websocketd 9502 Events temps réel via WebSocket
wazo-chatd 9504 Présence et messagerie (deprecated)
wazo-agentd 9503 Gestion des agents ACD

:warning: Important : Tous les services temps réel nécessitent un token d’authentification valide avec les ACL appropriées.


7.2 wazo-calld — Contrôle des Appels

Le service wazo-calld est le micro-service de contrôle d’appels pour les cas d’usage de communication unifiée. Il gère :

  • Création d’appels sortants
  • Antworting/hangup d’appels entrants
  • Transferts (aveugle et assisté)
  • Enregistrement d’appels
  • Voicemails
  • Applications (conférences, bridges)

7.2.1 Liste des Appels Utilisateur

Récupère la liste des appels actifs pour l’utilisateur authentifié.

Endpoint

GET /api/calld/1.0/users/me/calls

Headers

X-Auth-Token: ***
Wazo-Tenant: {tenant_uuid}    # Optionnel si multi-tenant

Réponse

{
  "items": [
    {
      "answer_time": "2019-08-24T14:15:22Z",
      "bridges": ["bridge_id_123"],
      "call_id": "1455123422.8",
      "caller_id_name": "John Doe",
      "caller_id_number": "1001",
      "conversation_id": "conv_abc123",
      "creation_time": "2019-08-24T14:15:00Z",
      "dialed_extension": "2001",
      "direction": "internal",
      "hangup_time": null,
      "is_caller": true,
      "is_video": false,
      "line_id": 12,
      "muted": false,
      "on_hold": false,
      "parked": false,
      "peer_caller_id_name": "Jane Doe",
      "peer_caller_id_number": "2001",
      "record_state": "inactive",
      "sip_call_id": "abc123def456@192.168.1.100",
      "status": "Up",
      "talking_to": {
        "1455123423.9": "Jane Doe"
      },
      "user_uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094"
    }
  ]
}

Codes HTTP

Code Description
200 Succès
401 Token invalide ou expiré
503 Service indisponible

7.2.2 Créer un Appel Sortant (depuis utilisateur)

Initie un nouvel appel depuis l’utilisateur authentifié.

Endpoint

POST /api/calld/1.0/users/me/calls

Headers

X-Auth-Token: ***
Content-Type: application/json
Wazo-Tenant: {tenant_uuid}    # Optionnel

Payload

{
  "extension": "2001",
  "line_id": 12,
  "auto_answer_caller": false,
  "from_mobile": false,
  "all_lines": false,
  "variables": {}
}
Champ Type Requis Description
extension string Oui Extension à appeler
line_id integer Non ID de la ligne à utiliser (défaut: ligne principale)
auto_answer_caller boolean Non Force le téléphone à répondre automatiquement
from_mobile Non Non Appeler depuis le mobile
all_lines boolean Non Utiliser toutes les lignes de l’utilisateur
variables object Non Variables de canal Asterisk

Exemple cURL

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "extension": "2001",
    "line_id": 12
  }' \
  https://wazo.example.com/api/calld/1.0/users/me/calls

Réponse (201 Created)

{
  "answer_time": null,
  "bridges": [],
  "call_id": "1455123422.8",
  "caller_id_name": "John Doe",
  "caller_id_number": "1001",
  "conversation_id": null,
  "creation_time": "2019-08-24T14:15:00Z",
  "dialed_extension": "2001",
  "direction": "internal",
  "hangup_time": null,
  "is_caller": true,
  "is_video": false,
  "line_id": 12,
  "muted": false,
  "on_hold": false,
  "parked": false,
  "peer_caller_id_name": "",
  "peer_caller_id_number": "",
  "record_state": "inactive",
  "sip_call_id": null,
  "status": "Ring",
  "talking_to": {},
  "user_uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094"
}

Codes HTTP

Code Description
201 Appel créé avec succès
400 Requête invalide
503 Service unavailable

7.2.3 Créer un Appel (Admin - Destination arbitraire)

Crée un appel depuis une source spécifiée vers une destination arbitraire. Requiert l’ACL calld.calls.create.

Endpoint

POST /api/calld/1.0/calls

Headers

X-Auth-Token: ***
Content-Type: application/json
Wazo-Tenant: {tenant_uuid}

Payload

{
  "source": {
    "user": "user_uuid_ou_numero",
    "line_id": 12,
    "auto_answer": false,
    "from_mobile": false,
    "all_lines": false
  },
  "destination": {
    "context": "default",
    "extension": "2001",
    "priority": 1
  },
  "variables": {}
}
Champ Type Requis Description
source.user string Oui UUID utilisateur ou numéro
source.line_id integer Non ID de ligne spécifique
source.auto_answer boolean Non Réponse automatique
destination.context string Oui Contexte de destination
destination.extension string Oui Extension de destination
destination.priority integer Non Priorité Asterisk (défaut: 1)

Exemple cURL

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "source": {
      "user": "a1223fe6-bff8-4fb6-a982-f9157dea5094",
      "line_id": 12
    },
    "destination": {
      "context": "default",
      "extension": "2001"
    }
  }' \
  https://wazo.example.com/api/calld/1.0/calls

7.2.4 Répondre à un Appel Entrant

Répond à un appel entrant pour l’utilisateur authentifié.

Endpoint

PUT /api/calld/1.0/users/me/calls/{call_id}/answer
Paramètre Type Description
call_id string ID de l’appel à répondre

Headers

X-Auth-Token: ***
Wazo-Tenant: {tenant_uuid}

Exemple cURL

curl -k -X PUT \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  https://wazo.example.com/api/calld/1.0/users/me/calls/1455123422.8/answer

Réponse

  • 204 No Content : Appel répondu avec succès
  • 404 : Appel non trouvé
  • 403 : Pas autorisé à répondre à cet appel

7.2.5 Raccrocher un Appel

Raccroche un appel actif.

Endpoint

DELETE /api/calld/1.0/users/me/calls/{call_id}

Headers

X-Auth-Token: ***
Wazo-Tenant: {tenant_uuid}

Exemple cURL

curl -k -X DELETE \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  https://wazo.example.com/api/calld/1.0/users/me/calls/1455123422.8

Codes HTTP

Code Description
204 Appel raccroché
403 Pas autorisé
404 Appel non trouvé
503 Service unavailable

7.2.6 Initier un Transfert

Transfère un appel vers une autre extension. Supporte les transferts aveugle et assisté.

Endpoint

POST /api/calld/1.0/transfers

Headers

X-Auth-Token: ***
Content-Type: application/json
Wazo-Tenant: {tenant_uuid}

Payload

{
  "initiator_call": "1455123422.8",
  "transferred_call": "1455123423.9",
  "exten": "2001",
  "context": "default",
  "flow": "attended",
  "timeout": 30,
  "variables": {}
}
Champ Type Requis Description
initiator_call string Oui ID de l’appel initiateur (celui qui déclenche le transfert)
transferred_call string Oui ID de l’appel transféré
exten string Oui Extension du destinataire
context string Oui Contexte du destinataire
flow string Non attended (assisté) ou blind (aveugle). Défaut: attended
timeout integer Non Timeout en secondes (défaut: illimité)
variables object Non Variables de canal

Exemple cURL (Transfert assisté)

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "initiator_call": "1455123422.8",
    "transferred_call": "1455123423.9",
    "exten": "2001",
    "context": "default",
    "flow": "attended",
    "timeout": 30
  }' \
  https://wazo.example.com/api/calld/1.0/transfers

Réponse (201 Created)

{
  "flow": "attended",
  "id": "transfer_abc123",
  "initiator_call": "1455123422.8",
  "initiator_tenant_uuid": "tenant_xyz",
  "initiator_uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094",
  "recipient_call": "1455123424.0",
  "status": "starting",
  "transferred_call": "1455123423.9"
}

Statuts de Transfert

Statut Description
starting Transfert en cours d’initialisation
attended Transfert assisté en cours (initiateur en attente)
completed Transfert terminé avec succès
canceled Transfert annulé
failed Échec du transfert

Annuler un Transfert

DELETE /api/calld/1.0/transfers/{transfer_id}

7.2.7 Enregistrement d’Appel

Démarre/arrête/marquepause l’enregistrement d’un appel.

Démarrer l’Enregistrement

PUT /api/calld/1.0/users/me/calls/{call_id}/record/start

Arrêter l’Enregistrement

PUT /api/calld/1.0/users/me/calls/{call_id}/record/stop

Pause/Resume

PUT /api/calld/1.0/users/me/calls/{call_id}/record/pause
PUT /api/calld/1.0/users/me/calls/{call_id}/record/resume

Exemple cURL

# Démarrer l'enregistrement
curl -k -X PUT \
  -H "X-Auth-Token: *** \
  https://wazo.example.com/api/calld/1.0/users/me/calls/1455123422.8/record/start

# Arrêter l'enregistrement
curl -k -X PUT \
  -H "X-Auth-Token: *** \
  https://wazo.example.com/api/calld/1.0/users/me/calls/1455123422.8/record/stop

:warning: Important : L’enregistrement nécessite que la fonctionnalité soit activée sur l’utilisateur, la queue ou le groupe.


7.3 Services Utilisateur (DND, forwards, incallfilter)

Les services utilisateur permettent de gérer les fonctionnalités de confort via l’API REST. Ces endpoints sont gérés par wazo-confd.

7.3.1 Do Not Disturb (DND)

Active ou désactive le mode Ne Pas Déranger pour un utilisateur.

Activer DND

PUT /api/confd/1.1/users/{user_uuid}/services/dnd/enable

Désactiver DND

PUT /api/confd/1.1/users/{user_uuid}/services/dnd/disable

Headers

X-Auth-Token: ***
Wazo-Tenant: {tenant_uuid}

Exemple cURL

# Activer DND
curl -k -X PUT \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  https://wazo.example.com/api/confd/1.1/users/a1223fe6-bff8-4fb6-a982-f9157dea5094/services/dnd/enable

# Désactiver DND
curl -k -X PUT \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  https://wazo.example.com/api/confd/1.1/users/a1223fe6-bff8-fb6-a982-f9157dea5094/services/dnd/disable

Réponse

{
  "enabled": true
}

Note : Depuis XiVO 16.13, le DND utilisateur est effectif indépendamment de l’extension DND (*25).


7.3.2 Filtre d’Appel Entrant (In-Call Filter)

Active ou désactive le filtrage des appels entrants.

Activer le Filtre

PUT /api/confd/1.1/users/{user_uuid}/services/incallfilter/enable

Désactiver le Filtre

PUT /api/confd/1.1/users/{user_uuid}/services/incallfilter/disable

Exemple cURL

curl -k -X PUT \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  https://wazo.example.com/api/confd/1.1/users/a1223fe6-bff8-4fb6-a982-f9157dea5094/services/incallfilter/enable

7.3.3 Renvois d’Appels (Forwards)

Gère les renvois d’appels : inconditionnel, sur occupation, sur non-réponse.

Liste des Types de Renvoi

Type Description
unconditional Renvoi inconditionnel
busy Renvoi si occupé
noanswer Renvoi si pas de réponse

####Configurer un Renvoi

PUT /api/confd/1.1/users/{user_uuid}/forwards/{forward_type}

Payload

{
  "enabled": true,
  "destination": "2001"
}
Champ Type Description
enabled boolean Active/désactive le renvoi
destination string Extension de destination

Exemple cURL (Renvoi sur occupation)

curl -k -X PUT \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "enabled": true,
    "destination": "2001"
  }' \
  https://wazo.example.com/api/confd/1.1/users/a1223fe6-bff8-4fb6-a982-f9157dea5094/forwards/busy

Récupérer les Renvois

GET /api/confd/1.1/users/{user_uuid}/forwards/{forward_type}

Réponse

{
  "enabled": true,
  "destination": "2001"
}

7.4 WebSocket — Events Temps Réel

Le service wazo-websocketd permet de recevoir les événements Wazo en temps réel via une connexion WebSocket. C’est le canal privilégié pour les applications CTI et les tableaux de bord temps réel.

7.4.1 Connexion WebSocket

Endpoint

wss://{wazo_host}:9502/?version=2&token={auth_token}
Paramètre Type Description
version integer Version de l’API (2)
token string Token d’authentification Wazo (doit avoir l’ACL websocketd)

Exemple JavaScript

var socket = new WebSocket("wss://wazo.example.com:9502/?version=2&token=" + token);

socket.onmessage = function(event) {
    var msg = JSON.parse(event.data);
    switch (msg.op) {
        case "init":
            // Connexion établie, s'abonner aux événements
            subscribe("call_created");
            subscribe("call_updated");
            start();
            break;
        case "start":
            console.log("En attente d'événements...");
            break;
        case "event":
            console.log("Événement reçu:", msg.event, msg.data);
            break;
    }
};

function subscribe(eventName) {
    socket.send(JSON.stringify({
        op: "subscribe",
        data: {
            event_name: eventName
        }
    }));
}

function start() {
    socket.send(JSON.stringify({
        op: "start"
    }));
}

Message du Serveur — Init

{
  "op": "init",
  "code": 0,
  "data": {
    "version": 2
  }
}

Codes d’Erreur WebSocket

Code Description
0 Succès
4003 Token expiré

7.4.2 Opérations WebSocket

Subscribe — S’abonner aux Événements

{
  "op": "subscribe",
  "data": {
    "event_name": "call_created"
  }
}

Pour s’abonner à tous les événements :

{
  "op": "subscribe",
  "data": {
    "event_name": "*"
  }
}

Start — Commencer la Réception

{
  "op": "start"
}

Réponse du Serveur

{
  "op": "subscribe",
  "code": 0
}

7.4.3 Événements Disponibles

Événements d’Appels

Événement Description
call_created Nouvel appel créé
call_updated Statut d’appel modifié
call_ended Appel terminé
call_held Appel en attente
call_resumed Appel repris

Exemple — call_created

{
  "name": "call_created",
  "origin_uuid": "08c56466-8f29-45c7-9856-92bf1ba89b82",
  "data": {
    "bridges": [],
    "call_id": "1455123422.8",
    "caller_id_name": "Some One",
    "caller_id_number": "1001",
    "creation_time": "2016-02-10T11:57:02.592-0500",
    "status": "Ring",
    "talking_to": {},
    "user_uuid": "2e752722-0864-4665-887d-a78a024cf7c7"
  }
}

Événements de Services Utilisateur

Événement Description
users_services_dnd_updated DND modifié
users_services_incallfilter_updated Filtre d’appel modifié
users_forwards_unconditional_updated Renvoi inconditionnel modifié
users_forwards_busy_updated Renvoi sur occupation modifié
users_forwards_noanswer_updated Renvoi sur non-réponse modifié

Exemple — users_services_dnd_updated

{
  "name": "users_services_dnd_updated",
  "required_acl": "events.config.users.a1223fe6-bff8-4fb6-a982-f9157dea5094.services.dnd.updated",
  "origin_uuid": "ca7f87e9-c2c8-5fad-baba1b-c3140ebb9be3",
  "data": {
    "user_uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094",
    "enabled": true
  }
}

Événements d’Agents

Événement Description
agent_status_update Statut agent modifié (login/logout/pause)

Exemple — agent_status_update

{
  "name": "agent_status_update",
  "required_acl": "events.statuses.agents",
  "origin_uuid": "ca7f87e9-c2c8-5fad-ba1b-c3140ebb9be3",
  "data": {
    "agent_id": 42,
    "xivo_id": "ca7f87e9-c2c8-5fad-ba1b-c3140ebb9be3",
    "status": "logged_in"
  }
}

7.5 Switchboard — standard automatique

Le switchboard permet aux opérateurs de gérer les appels entrants avec des fonctionnalités de mise en attente, transfert et interception.

7.5.1 Récupérer les Appels en Attente

GET /api/calld/1.0/switchboard/waits

Headers

X-Auth-Token: ***
Wazo-Tenant: {tenant_uuid}

Réponse

{
  "items": [
    {
      "call_id": "1455123422.8",
      "caller_id_name": "Caller Name",
      "caller_id_number": "1001",
      "conversation_id": null,
      "creation_time": "2019-08-24T14:15:00Z",
      "dialed_extension": "4000",
      "line_id": 15,
      "status": "Ring",
      "user_uuid": "operator_uuid"
    }
  ]
}

7.5.2 Mettre en Attente

PUT /api/calld/1.0/switchboard/{call_id}/hold

7.5.3 Reprendre depuis Attente

PUT /api/calld/1.0/switchboard/{call_id}/retrieve

7.5.4 Rediriger vers File d’Attente

PUT /api/calld/1.0/switchboard/{call_id}/redirect-queue/{queue_id}

7.6 Applications — Conférencing et Bridges

Les applications permettent de créer des conférences et des bridges audio complexes.

7.6.1 Créer un Appel dans une Application

POST /api/calld/1.0/applications/{application_uuid}/calls

Payload

{
  "context": "default",
  "exten": "1001",
  "autoanswer": false,
  "displayed_caller_id_name": "Conference",
  "displayed_caller_id_number": "3000"
}

Exemple cURL

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -d '{
    "context": "default",
    "exten": "1001",
    "autoanswer": true
  }' \
  https://wazo.example.com/api/calld/1.0/applications/app_uuid_123/calls

7.7 Récapitulatif des ACLs Requises

Action ACL Requise
Liste appels utilisateur calld.users.me.calls.read
Créer appel utilisateur calld.users.me.calls.create
Répondre à un appel calld.users.me.calls.{call_id}.answer
Raccrocher un appel calld.users.me.calls.{call_id}.delete
Démarrer enregistrement calld.users.me.calls.{call_id}.record.start
Créer transfert calld.transfers.create
Liste services utilisateur confd.users.{user_uuid}.services.read
Modifier DND confd.users.{user_uuid}.services.dnd.update
Modifier forwards confd.users.{user_uuid}.forwards.{type}.update
WebSocket websocketd

7.8 Patterns d’Intégration CTI

7.8.1 Pattern : Supervision d’Appels

  1. Se connecter au WebSocket avec un token ayant websocketd
  2. S’abonner aux événements call_created, call_updated, call_ended
  3. Maintenir un état local des appels actifs
  4. Utiliser l’API REST pour les actions (answer, hold, transfer)

7.8.2 Pattern : Clic-to-Call

  1. UI web/un client génère un clic sur un numéro
  2. Appeler POST /api/calld/1.0/users/me/calls avec l’extension
  3. Sur réception de call_answered, notifier l’utilisateur
  4. Afficher le statut de l’appel en temps réel

7.8.3 Pattern : Supervision Présence

  1. S’abonner aux événements users_services_dnd_updated, users_forwards_*_updated
  2. Mettre à jour l’interface utilisateur en temps réel
  3. Afficher le statut DND/Forward pour chaque utilisateur

Fin du Chapitre 7


CHAPITRE 8 : CDR, Statistiques et Webhooks

8.1 Call Detail Records (CDR) — wazo-call-logd

Le service wazo-call-logd gère les enregistrements de détails d’appels (CDR). Il fournit l’historique complet de tous les appels passés par le système Wazo.

8.1.1 Architecture

Les CDR sont générés à partir des entrées CEL (Channel Event Log) d’Asterisk. Le service wazo-call-logd traite ces événements pour créer des enregistrements enrichis.

┌─────────────┐    CEL Events    ┌──────────────────┐
│  Asterisk   │ ───────────────► │  wazo-call-logd  │
└─────────────┘                  └────────┬─────────┘
                                           │
                                    ┌──────▼────────┐
                                    │   CDR Table   │
                                    │  (PostgreSQL) │
                                    └───────────────┘

8.1.2 Endpoints CDR

Liste des CDR (Admin)

GET /api/call-logd/1.0/cdr

CDR de l’utilisateur authentifié

GET /api/calld/1.0/users/me/cdr

CDR d’un utilisateur spécifique

GET /api/calld/1.0/users/{user_uuid}/cdr

CDR unique par ID

GET /api/call-logd/1.0/cdr/{cdr_id}

8.1.3 Filtres CDR — Guide Complet

Paramètres de Filtrage

Paramètre Type Description
from string Date de début (ISO 8601: 2024-01-01T00:00:00)
until string Date de fin (ISO 8601)
limit integer Nombre max de résultats (défaut: 50)
offset integer Décalage pour la pagination
order string Champ de tri (date, duration, caller_id_number)
direction string Ordre de tri (asc ou desc)
call_direction string internal, inbound ou outbound
caller_id_name string Nom de l’appelant
caller_id_number string Numéro de l’appelant
destination_extension string Extension de destination
user_uuid string UUID de l’utilisateur
tags string Tags personnalisés (séparés par virgule)
from_id string CDR ID de départ (pour pagination)

8.1.4 Exemples de Filtres CDR

Filtrer par période (les 30 derniers jours)

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?from=2024-01-01T00:00:00&until=2024-01-31T23:59:59"

Filtrer par utilisateur

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?user_uuid=a1223fe6-bff8-4fb6-a982-f9157dea5094"

Filtrer appels entrants

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?call_direction=inbound"

Filtrer par numéro appelant

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?caller_id_number=1001"

Filtrer par tags (département)

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?tags=sales"

Combinaison de filtres

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?from=2024-01-01&until=2024-01-31&call_direction=outbound&limit=100&order=date&direction=desc"

8.1.5 Format de Réponse CDR

{
  "items": [
    {
      "id": "cdr_uuid_123",
      "date": "2024-01-15T14:30:00+00:00",
      "date_answer": "2024-01-15T14:30:05+00:00",
      "date_end": "2024-01-15T14:35:00+00:00",
      "direction": "internal",
      "caller_id_name": "John Doe",
      "caller_id_number": "1001",
      "destination_extension": "2001",
      "destination_name": "Jane Doe",
      "destination_number": "2001",
      "user_uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094",
      "ended_by": null,
      "duration": 295,
      "billable": true,
      "tags": ["sales", "france"]
    }
  ],
  "total": 150,
  "filtered": 25
}

Description des Champs

Champ Type Description
id string UUID unique du CDR
date datetime Date de création de l’appel
date_answer datetime Date de réponse (null si non répondu)
date_end datetime Date de fin d’appel
direction string internal, inbound, outbound
caller_id_name string Nom affiché de l’appelant
caller_id_number string Numéro de l’appelant
destination_extension string Extension de destination
destination_name string Nom du destinataire
destination_number string Numéro du destinataire
user_uuid string UUID de l’utilisateur Wazo
duration integer Durée totale en secondes
billable boolean Indique si l’appel est facturable
tags array Tags personnalisés

8.1.6 Exporter en CSV

Pour obtenir les CDR au format CSV, ajouter le header Accept: text/csv:

curl -k -X GET \
  -H "X-Auth-Token: *** \
  -H "Accept: text/csv; charset=utf-8" \
  "https://wazo.example.com/api/call-logd/1.0/cdr?from=2024-01-01&until=2024-01-31" -o cdr_export.csv

8.1.7 Régénération des CDR

Si des CDR sont manquants, ils peuvent être regénérés:

# Supprimer les CDR des 30 derniers jours
xivo-call-logs delete -d 30

# Régénérer les CDR
xivo-call-logs generate -d 30

# Avec un nombre spécifique de CEL
xivo-call-logs -c 100000

8.2 Statistiques des Files d’Attente

Les statistiques des queues sont stockées dans la base de données Asterisk et peuvent être interrogées via l’API.

8.2.1 Tables de Statistiques

Table Description
queue_log Log brut des événements de queue
stat_call_on_queue Statistiques par appel
stat_queue_periodic Agrégations périodiques (par heure)

8.2.2 Métriques Disponibles

Statistiques par Appel (stat_call_on_queue)

Champ Description
callid ID de l’appel (lien avec CDR)
time Horodatage de l’appel
ringtime Durée de sonnerie (secondes)
talktime Durée de conversation (secondes)
waittime Temps d’attente (secondes)
status Statut de l’appel
queue_id ID de la queue
agent_id ID de l’agent qui a répondu

Statut des Appels

Statut Description
answered Appel répondu par un agent
abandoned Appel abandonné par l’appelant
full Appel rejeté car queue pleine
closed Appel rejeté car queue fermée
joinempty Appel rejeté car aucun agent disponible
leaveempty Appel laissé car plus d’agents disponibles
divert_ca_ratio Appels divertis selon ratio agents/appels
divert_waittime Appels divertis selon temps d’attente
timeout Appels ayant expiré le timeout

Agrégations (stat_queue_periodic)

Champ Description
time Période (granularité: heure)
answered Nombre d’appels répondus
abandoned Nombre d’appels abandonnés
total Total des appels reçus
full Appels rejetés queue pleine
closed Appels rejetés queue fermée
joinempty Appels rejeés aucun agent
leaveempty Appels diverted agents indisponibles
divert_ca_ratio Divertis ratio agents
divert_waittime Divertis temps d’attente
timeout Appels expirés

8.3 Webhooks — wazo-webhookd

Le service wazo-webhookd permet de créer des abonnements qui déclenchent des HTTP callbacks lorsque des événements Wazo se produisent.

8.3.1 Architecture

┌─────────────┐    Events     ┌──────────────────┐    HTTP POST    ┌──────────────┐
│  Wazo Bus   │ ────────────► │  wazo-webhookd   │ ──────────────► │  External    │
│  (RabbitMQ) │               │  (Subscription)  │                 │  Server      │
└─────────────┘               └──────────────────┘                 └──────────────┘

8.3.2 Créer une Subscription

Endpoint

POST /api/webhookd/1.0/subscriptions

Headers

X-Auth-Token: ***
Content-Type: application/json
Wazo-Tenant: {tenant_uuid}

Payload

{
  "name": "My Webhook",
  "events": ["call_created", "user_created"],
  "service": "http",
  "config": {
    "url": "https://my-server.com/webhook",
    "method": "POST",
    "timeout": 30
  }
}
Champ Type Requis Description
name string Oui Nom de la subscription
events array Oui Liste des événements
service string Oui Service handler (http, example)
config object Oui Configuration du service
config.url string Oui URL de callback
config.method string Non Méthode HTTP (GET, POST, PUT)
config.timeout integer Non
user_uuid string Non Filtrer par utilisateur

Exemple cURL

curl -k -X POST \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: *** \
  -H "Wazo-Tenant: {tenant_uuid}" \
  -d '{
    "name": "CRM Integration",
    "events": ["call_created", "call_ended"],
    "service": "http",
    "config": {
      "url": "https://crm.example.com/wazo-events",
      "method": "POST"
    }
  }' \
  https://wazo.example.com/api/webhookd/1.0/subscriptions

Réponse (201 Created)

{
  "uuid": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
  "name": "CRM Integration",
  "events": ["call_created", "call_ended"],
  "service": "http",
  "config": {
    "url": "https://crm.example.com/wazo-events",
    "method": "POST"
  },
  "enabled": true,
  "owner_uuid": "admin_uuid",
  "tenant_uuid": "tenant_uuid"
}

8.3.3 Liste des Subscriptions

GET /api/webhookd/1.0/subscriptions

Réponse

{
  "items": [
    {
      "uuid": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
      "name": "CRM Integration",
      "events": ["call_created"],
      "service": "http",
      "config": {
        "url": "https://crm.example.com/wazo-events",
        "method": "POST"
      },
      "enabled": true
    }
  ]
}

8.3.4 Mettre à Jour une Subscription

PUT /api/webhookd/1.0/subscriptions/{subscription_id}

Payload

{
  "name": "Updated Name",
  "enabled": false,
  "events": ["call_created", "user_created"]
}

8.3.5 Supprimer une Subscription

DELETE /api/webhookd/1.0/subscriptions/{subscription_id}

8.3.6 Événements Disponibles pour Webhooks

Événements d’Appels

Événement Description
call_created Appel créé
call_updated Appel mis à jour
call_ended Appel terminé
call_answered Appel répondu
call_rotute_noanswer Appel non répondu

Événements Utilisateurs

Événement Description
user_created Utilisateur créé
user_updated Utilisateur modifié
user_deleted Utilisateur supprimé
user_status_update Statut utilisateur changé
user_voicemail_message_created Nouveau message voicemail
user_voicemail_message_updated Message voicemail modifié
user_voicemail_message_deleted Message voicemail supprimé

Événements de Services

Événement Description
users_services_dnd_updated DND activé/désactivé
users_services_incallfilter_updated Filtre d’appel modifié
users_forwards_unconditional_updated Renvoi inconditionnel
users_forwards_busy_updated Renvoi sur occupation
users_forwards_noanswer_updated Renvoi sur non-réponse

Événements d’Agents

Événement Description
agent_status_update Agent login/logout
agent_paused Agent en pause
agent_unpaused Agent reprend

Événements de Configuration

Événement Description
endpoint_status_update Statut endpoint changé
favorite_added Favori ajouté
favorite_deleted Favori supprimé
relocate_initiated Relocalisation initiée
relocate_answered Relocalisation répondue
relocate_completed Relocalisation terminée
relocate_ended Relocalisation terminée

8.3.7 Format des Payloads Webhook

call_created

{
  "name": "call_created",
  "origin_uuid": "wazo_server_uuid",
  "data": {
    "call_id": "1455123422.8",
    "caller_id_name": "John Doe",
    "caller_id_number": "1001",
    "destination_extension": "2001",
    "user_uuid": "user_uuid_123",
    "direction": "internal",
    "status": "Ring"
  },
  "timestamp": "2024-01-15T14:30:00Z"
}

call_ended

{
  "name": "call_ended",
  "origin_uuid": "wazo_server_uuid",
  "data": {
    "call_id": "1455123422.8",
    "caller_id_name": "John Doe",
    "caller_id_number": "1001",
    "destination_extension": "2001",
    "user_uuid": "user_uuid_123",
    "duration": 180,
    "hangup_cause": "NORMAL_CLEARING"
  },
  "timestamp": "2024-01-15T14:33:00Z"
}

user_created

{
  "name": "user_created",
  "origin_uuid": "wazo_server_uuid",
  "data": {
    "uuid": "user_uuid_456",
    "firstname": "Jane",
    "lastname": "Doe",
    "email": "jane.doe@example.com",
    "username": "jdoe"
  },
  "timestamp": "2024-01-15T14:30:00Z"
}

user_status_update

{
  "name": "user_status_update",
  "origin_uuid": "wazo_server_uuid",
  "data": {
    "user_uuid": "user_uuid_123",
    "status": "available",
    "presence": "online"
  },
  "timestamp": "2024-01-15T14:30:00Z"
}

agent_status_update

{
  "name": "agent_status_update",
  "origin_uuid": "wazo_server_uuid",
  "data": {
    "agent_id": 42,
    "xivo_id": "wazo_server_uuid",
    "status": "logged_in"
  },
  "timestamp": "2024-01-15T14:30:00Z"
}

8.3.8 Filtrage par Utilisateur

Il est possible de filtrer les webhooks pour un utilisateur spécifique:

{
  "name": "User-Specific Webhook",
  "events": ["call_created", "user_status_update"],
  "service": "http",
  "config": {
    "url": "https://my-server.com/webhook"
  },
  "user_uuid": "specific_user_uuid"
}

Événements supportés pour le filtrage par utilisateur

  • users_services_incallfilter_updated
  • users_services_dnd_updated
  • users_forwards_unconditional_updated
  • users_forwards_noanswer_updated
  • users_forwards_busy_updated
  • user_voicemail_message_updated
  • user_voicemail_message_deleted
  • user_voicemail_message_created
  • user_status_update
  • relocate_ended
  • relocate_completed
  • relocate_answered
  • relocate_initiated
  • favorite_deleted
  • favorite_added
  • endpoint_status_update
  • call_updated
  • call_log_user_created
  • call_ended
  • call_created
  • agent_unpaused
  • agent_status_update
  • agent_paused

8.4 Patterns d’Intégration

8.4.1 Pattern : Export CDR vers CRM

import requests

# Récupérer les CDR d'hier
from datetime import datetime, timedelta

yesterday = (datetime.now() - timedelta(days=1)).isoformat()
today = datetime.now().isoformat()

response = requests.get(
    "https://wazo.example.com/api/call-logd/1.0/cdr",
    headers={
        "X-Auth-Token": f"{token}",
        "Wazo-Tenant": tenant_uuid
    },
    params={
        "from": yesterday,
        "until": today,
        "limit": 1000
    }
)

for cdr in response.json()["items"]:
    # Envoyer au CRM
    requests.post(
        "https://crm.example.com/api/calls",
        json=cdr
    )

8.4.2 Pattern : Webhook de Notification

from flask import Flask, request
import hmac
import hashlib

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # Vérifier la signature
    signature = request.headers.get('X-Wazo-Signature')
    if not verify_signature(request.data, signature):
        return "Invalid signature", 401
    
    event = request.json
    
    if event['name'] == 'call_created':
        print(f"Nouvel appel de {event['data']['caller_id_number']}")
    
    elif event['name'] == 'user_created':
        print(f"Nouvel utilisateur: {event['data']['email']}")
    
    return "OK", 200

def verify_signature(payload, signature):
    # Implémenter la vérification HMAC
    expected = hmac.new(
        b'secret_key',
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

8.4.3 Pattern : Statistiques en Temps Réel

// Connexion WebSocket pour les stats temps réel
const ws = new WebSocket('wss://wazo.example.com:9502/?version=2&token=' + token);

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    
    if (msg.op === 'event') {
        // Mettre à jour les compteurs
        switch (msg.event) {
            case 'call_created':
                stats.callsCreated++;
                break;
            case 'call_ended':
                stats.callsEnded++;
                updateDurationStats(msg.data.duration);
                break;
        }
        
        updateDashboard();
    }
};

8.5 Récapitulatif des Services

IMPORTANT : Toutes les API passent par nginx sur le port 443. Les ports ci-dessous sont les ports directs des microservices (pour debugging uniquement).

Service Port Direct Nginx Route API Base Purpose
wazo-call-logd 9500 /api/call-logd/1.0/* /api/call-logd/1.0 Call Detail Records
wazo-webhookd 9300 /api/webhookd/1.0/* /api/webhookd/1.0 Webhook subscriptions
wazo-websocketd 9502 /api/websocketd/* WebSocket Real-time events
wazo-calld 9500 /api/calld/1.0/* /api/calld/1.0 Call control

Fin du Chapitre 8 — Fin de l’Ouvrage

Chapitre 9 — ARI (Asterisk REST Interface) sur Wazo 26.06

Statut : testé et validé en live sur wazohermesx.xxxxx.ts.net (Asterisk 22.8.2, Debian 12.14, juillet 2026).
Tous les pièges documentés ici ont été rencontrés et résolus en production.


9.1 Vue d’ensemble

ARI (Asterisk REST Interface) est l’API HTTP/WebSocket d’Asterisk qui permet de
contrôler les appels téléphoniques programmatiquement. Sur Wazo 26.06, c’est
la méthode recommandée pour :

  • Bots vocaux (type AVA, GPT-realtime)
  • IVR dynamiques avec logique externe
  • Call recording programmatique
  • Bridges complexes multi-parties
  • Originate call (appels sortants)

Différence avec AMI (Asterisk Manager Interface)

Feature ARI (HTTP/WS) AMI (TCP/Events)
Transport HTTP + WebSocket TCP plain
Auth HTTP Basic Plain text
Stasis apps :white_check_mark: Natif :cross_mark: Limité
WebSocket events :white_check_mark: Standard :cross_mark: Pas natif
Channels control RESTful Action/Response
Originate call POST /channels Action: Originate
Default port Wazo 5039 5038

Pour un bot vocal/IA, ARI est fortement préféré.


9.2 Architecture

┌─────────────────────────────────┐
│         Wazo Platform           │
│  ┌──────────────────────────┐   │
│  │   Asterisk 22 + ARI      │◄──── Port 5039 (HTTP/WS)
│  │   res_ari (14 modules)   │   │
│  │   app_stasis             │   │
│  │   dialplan: [ava-agent]  │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘
                ▲
                │ ARI (HTTP REST + WebSocket events)
                │
┌─────────────────────────────────┐
│        Bot / Client Python      │
│  ┌──────────────────────────┐   │
│  │  aiohttp + WebSocket     │   │
│  │  Stasis app listener     │───┼──► api.groq.com (LLM)
│  │  Channel control         │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘
                ▲
                │ SIP (PJSIP)
                │
┌─────────────────────────────────┐
│        Téléphone / Softphone    │
└─────────────────────────────────┘

9.3 Configuration minimale côté Wazo

9.3.1 User ARI (créer sans toucher ari.conf)

RÈGLE D’OR : ne jamais modifier /etc/asterisk/ari.conf directement.
C’est un #include ari.d/*.conf géré par Wazo.

Créer /etc/asterisk/ari.d/02-ava.conf (Wazo fournit déjà 01-wazo.conf) :

; AVA AI Voice Agent - user ARI
; NE PAS inclure de section [general] ici (déjà dans 01-wazo.conf)
; Sinon : duplicate object 'general' (pitfall #22)

[ava_user]
type = user
read_only = no
password = <MOT_DE_PASSE_GENERE>
password_format = plain
chown asterisk:www-data /etc/asterisk/ari.d/02-ava.conf
chmod 0660 /etc/asterisk/ari.d/02-ava.conf
asterisk -rx "module reload res_ari"

:warning: PIÈGE #22 (CRITIQUE) : ne JAMAIS avoir deux [general] dans ari.d/*.conf.
Asterisk refuse de parser ari.conf avec l’erreur :

Config file 'ari.conf' could not be loaded; configuration contains
       a duplicate object: 'general' of type 'general'

Conséquence : res_ari.so se charge (Use Count 13) mais ARI inactif.

9.3.2 Dialplan Stasis

Créer /etc/asterisk/extensions_extra.d/ava-agent.conf (non managé par wazo-confgend) :

[ava-agent]
; Pattern wildcard : accepte tout numéro composé
exten => _X.,1,NoOp(=== AVA - ${EXTEN} from ${CALLERID(num)} ===)
 same => n,Stasis(ava-stasis,${EXTEN},${CALLERID(num)})
 same => n,Hangup()

; Extension start pour appels SIP entrants réels
exten => s,1,NoOp(=== AVA entrant ${CALLERID(num)} ===)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Stasis(ava-stasis,s,${CALLERID(num)})
 same => n,Hangup()

exten => h,1,NoOp(AVA hangup)
 same => n,Return()
chown asterisk:www-data /etc/asterisk/extensions_extra.d/ava-agent.conf
chmod 0660 /etc/asterisk/extensions_extra.d/ava-agent.conf
asterisk -rx "dialplan reload"

9.3.3 Vérification

# 1. User ARI créé
asterisk -rx "ari show users"
# Sortie attendue :
# r/o?  Username
# ----  --------
# No    ava_user
# No    xivo

# 2. ARI configuré
asterisk -rx "ari show status"
# Sortie attendue :
# ARI Status:
# Enabled: Yes
# Output format: compact
# Auth realm: Asterisk REST Interface
# Allowed Origins: *

# 3. /ari/ listé dans HTTP
asterisk -rx "http show status" | grep "/ari"
# /ari/... => Asterisk RESTful API

# 4. Test login
curl -u ava_user:<MDP> http://127.0.0.1:5039/ari/asterisk/info
# → JSON avec build, system, config, status

9.4 Endpoints principaux

9.4.1 Informations Asterisk

# GET /ari/asterisk/info
curl -u user:pwd http://host:5039/ari/asterisk/info

# GET /ari/asterisk/variables (variables globales)
curl -u user:pwd http://host:5039/ari/asterisk/variables

# GET /ari/asterisk/log  (Wazo ne loggue pas par défaut)

9.4.2 Channels (canaux actifs)

# Lister les canaux
curl -u user:pwd http://host:5039/ari/channels

# Détails d'un canal
curl -u user:pwd http://host:5039/ari/channels/<channel-id>

# Créer un canal (test ARI direct)
curl -u user:pwd -X POST http://host:5039/ari/channels \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "Local/1001@ava-agent",
    "app": "ava-stasis",
    "callerId": "TestCall"
  }'
# → JSON avec id, name, state, dialplan context, etc.

# Contrôler un canal
curl -u user:pwd -X POST http://host:5039/ari/channels/<id>/answer       # décrocher
curl -u user:pwd -X POST http://host:5039/ari/channels/<id>/ring         # faire sonner
curl -u user:pwd -X POST http://host:5039/ari/channels/<id>/play \
  -H "Content-Type: application/json" \
  -d '{"media": "sound:custom/bienvenue"}'                                 # jouer un son
curl -u user:pwd -X DELETE http://host:5039/ari/channels/<id>             # raccrocher

9.4.3 Bridges (conférences)

# Créer un bridge
curl -u user:pwd -X POST http://host:5039/ari/bridges \
  -H "Content-Type: application/json" \
  -d '{"type": "mixing,dtmf_events"}'

# Ajouter un channel au bridge
curl -u user:pwd -X POST http://host:5039/ari/bridges/<bridge-id>/addChannel \
  -H "Content-Type: application/json" \
  -d '{"channel": "<channel-id>"}'

# Lister les canaux du bridge
curl -u user:pwd http://host:5039/ari/bridges/<bridge-id>

9.4.4 Endpoints SIP

# Lister tous les endpoints
curl -u user:pwd http://host:5039/ari/endpoints

# Détails d'un endpoint
curl -u user:pwd http://host:5039/ari/endpoints/PJSIP/<endpoint-id>

9.4.5 Recordings (enregistrements)

# Démarrer enregistrement d'un canal
curl -u user:pwd -X POST http://host:5039/ari/channels/<id>/record \
  -H "Content-Type: application/json" \
  -d '{"name": "call-001", "format": "wav"}'

# Arrêter
curl -u user:pwd -X DELETE http://host:5039/ari/channels/<id>/record

# Lister recordings
curl -u user:pwd http://host:5039/ari/recordings/stored

9.4.6 Applications Stasis

# Lister les apps Stasis actives
curl -u user:pwd http://host:5039/ari/applications
# → [{"name": "callcontrol", ...}, {"name": "adhoc_conference", ...}, ...]

# Détails
curl -u user:pwd http://host:5039/ari/applications/ava-stasis

9.4.7 Playbacks (sons en cours)

curl -u user:pwd http://host:5039/ari/playbacks/<playback-id>
# Contrôler : POST /playbacks/{id}/control (pause, unpause, restart, stop, forward, reverse)

9.5 WebSocket ARI (events Stasis)

9.5.1 Connexion

import aiohttp
import json

async def listen_ari():
    auth = aiohttp.BasicAuth("ava_user", "password")  # aiohttp < 4.0
    async with aiohttp.ClientSession(auth=auth) as session:
        ws_url = "http://host:5039/ari/events?app=ava-stasis&subscribeAll=events"
        async with session.ws_connect(ws_url) as ws:
            async for msg in ws:
                if msg.type != aiohttp.WSMsgType.TEXT:
                    continue
                evt = json.loads(msg.data)
                yield evt

:warning: Piège aiohttp 3.9+ : aiohttp.BasicAuth est deprecated. Utiliser :

headers = {"Authorization": aiohttp.encode_basic_auth("ava_user", "password")}
async with aiohttp.ClientSession(headers=headers) as session:

9.5.2 Events principaux

Event Quand Champs clés
StasisStart Nouveau call dans Stasis channel.id, channel.caller.number, args[]
StasisEnd Channel quitte Stasis channel.id
ChannelCreated Nouveau channel créé channel.id, channel.dialplan.context
ChannelDestroyed Channel terminé channel.id, cause, cause_txt
ChannelCallerId Caller ID assigné channel.id, caller.number
ChannelConnectedLine Connected line update channel.id, connected.number
ChannelDialplan Dialplan en cours channel.id, dialplan.app_name, dialplan.app_data
ChannelVarset Variable de canal changée channel.id, variable, value
ChannelDtmfReceived DTMF reçu channel.id, digit, duration_ms
Dial Dial en cours dial.status, caller, peer
PlaybackFinished Son terminé playback.id, playback.target_uri
RecordingStarted / RecordingFinished Enregistrement recording.name, recording.format

9.5.3 Exemple Python complet (client Stasis minimal)

import asyncio
import aiohttp
import json

AR_URL = "http://127.0.0.1:5039"
USER = "ava_user"
PASS = "<MOT_DE_PASSE>"
APP = "ava-stasis"

async def main():
    auth = aiohttp.BasicAuth(USER, PASS)
    async with aiohttp.ClientSession(auth=auth) as session:
        # S'abonner aux events
        ws_url = f"{AR_URL}/ari/events?app={APP}&subscribeAll=events"
        async with session.ws_connect(ws_url) as ws:
            print(f"WS connected, app={APP}")
            async for msg in ws:
                if msg.type != aiohttp.WSMsgType.TEXT:
                    continue
                evt = json.loads(msg.data)
                t = evt.get("type", "?")
                if t == "StasisStart":
                    ch_id = evt["channel"]["id"]
                    caller = evt["channel"]["caller"]["number"]
                    args = evt.get("args", [])
                    print(f"StasisStart {ch_id} caller={caller} args={args}")

                    # Répondre
                    async with session.post(f"{AR_URL}/ari/channels/{ch_id}/answer") as r:
                        print(f"answer -> {r.status}")

                    # Jouer un son
                    async with session.post(
                        f"{AR_URL}/ari/channels/{ch_id}/play",
                        json={"media": "sound:custom/bienvenue"}
                    ) as r:
                        print(f"play -> {r.status}")

asyncio.run(main())

Test bout-en-bout validé sur wazohermesx (juillet 2026) :

[8.51s] >> StasisStart channel=1783953092.9
  ** STASIS_START args=['4004', ''] caller=
HTTP 200  /ari/asterisk/info
{"build":{"os":"Linux","kernel":"5.15.0-164-generic",...}}

9.6 Pièges vérifiés en production (22 pièges)

Pièges CRITIQUES (cassent l’install)

#1. Ne PAS modifier /etc/asterisk/ari.conf
C’est un #include ari.d/*.conf géré par Wazo. Créer ari.d/XX-name.conf.

#2. Port ARI = 5039 sur Wazo 26.06 (pas 8088)
Vérifier /etc/asterisk/http.d/01-wazo.conf pour le bindport réel.

#3. pip3 install sur un serveur Wazo CASSE wazo-ui
Utiliser apt install ou un venv isolé.

#4. Login admin Wazo via API = HTTP Basic Auth (PAS JSON body)

curl -u root:pass -X POST https://wazo/api/auth/0.1/token

#5. Hash password root Wazo peut être cassé
Créer un nouveau user via PasswordEncrypter + UPDATE SQL direct.

#6. Policy master n’a PAS les ACL confd.contexts.*
Créer une policy custom avec confd.# wildcard.

#14. /ari/ absent même avec res_ari chargé (Use Count 13)
Voir section 9.10 “Diagnostic avancé”.

#20. res_ari.so Use Count > 0 mais HTML 404 sur /ari
Voir section 9.10.

#21. Paquet asterisk upstream sans ARI
Sur Wazo, il faut wazo-asterisk (fork Wazo).

#22. :warning: [general] dupliqué dans ari.d/*.conf casse ARI
Cause du bug qui a coûté 3 jours : mon script configure-wazo.sh créait
un [general] vide en plus de celui de 01-wazo.conf. Asterisk refusait
de parser ari.conf → ARI inactif.
Fix : un seul [general] dans tout ari.d/, fichiers custom = [user] uniquement.

Pièges MOYENS (font perdre du temps)

#7. Answer() + Wait() cassent les canaux Local/...
Les canaux ARI auto-générés (Local/1001@ava-agent) n’ont pas de média.
Pour les tests, utiliser Stasis() direct sans Answer.

#8. Dialplan doit être dans extensions_extra.d/
Pas dans extensions.conf (régénéré par wazo-confgend).

#9. Table auth_policy_access (PAS auth_policy_acl)
Schéma Wazo récent utilise (policy_uuid, access_id) comme FK vers auth_access(id, access).

#10. Policy auth_policy requiert slug
NOT NULL constraint.

Pièges INFORMATIVES

#11. Contexte Wazo généré dynamiquement (vide)
Le contexte ctx-master-internal-... créé via API a juste i/t en dialplan.

#12. Nom technique du contexte auto-renommé
name: "ava-agent"name: "ctx-master-internal-3a664d19-...".

#13. extensions_extra.d/ permissions restrictives
chown asterisk:www-data && chmod 0775.

#15. Restart ne suffit pas si conf cassée
Voir #22.

#16. http.d/*.conf ignoré si http.conf n’a pas #include
Sur Wazo, c’est déjà inclus par défaut.

#17. wazo-confgend.timer régénère 01-wazo.conf toutes les 5 min
Stopper le timer avant les modifs manuelles.

#18. “Not enabled” après restart = conflit http.d/*.conf
Voir #19.

#19. Conflit [general] entre 2 fichiers http.d/
Un seul fichier doit avoir [general].

Voir ava-deploy/docs/WAZO-26.06-PITFALLS.md pour le détail complet.


9.7 Scripts opérationnels (ava-deploy)

Le dépôt mobilejudi/ava-deploy contient 8 scripts dédiés ARI :

Script Usage
scripts/install-local.sh Install AVA + ARI user + dialplan sur même serveur Wazo
scripts/install-remote.sh Install AVA seul (Wazo distant) — fix bug AVA_DIALPLAN_CONTEXT
scripts/configure-wazo.sh Configure juste ARI + dialplan côté Wazo (corrigé : pas de [general] dupliqué)
scripts/validate.sh Tests post-install ARI/Stasis/Groq/service (diagnostic enrichi 401/404/000)
scripts/diagnose-ari.sh Diagnostic complet ARI avec option --fix
scripts/recover-ari.sh Recovery auto du cas pathologique #20 (Use Count > 0 mais HTML 404)
scripts/uninstall.sh Rollback complet
examples/dialplan/ava-agent.conf Snippet dialplan à copier
examples/ari-conf/02-ava.conf Snippet user ARI (sans [general])

9.8 Cas pratique : connecter AVA à Wazo

Variables d’environnement AVA ai_engine

# /opt/ava/.env
ASTERISK_HOST=192.168.1.17         # ou 127.0.0.1 si même machine
ASTERISK_ARI_PORT=5039             # PAS 8088
ASTERISK_ARI_USERNAME=ava_user
ASTERISK_ARI_PASSWORD=mCsd2cIoLQI7Og3zrMP5nKHiAtXtixf9
ASTERISK_ARI_APP=ava-stasis        # DOIT matcher Stasis() du dialplan

# LLM
GROQ_API_KEY=gsk_xxx
LLM_BASE_URL=https://api.groq.com/openai/v1
LLM_MODEL=llama-3.3-70b-versatile

Test bout-en-bout en 30 secondes

# 1. Test ARI login
curl -u ava_user:$PWD http://$HOST:5039/ari/asterisk/info | python3 -m json.tool | head

# 2. Test création canal via ARI (sans téléphone physique)
curl -u ava_user:$PWD -X POST http://$HOST:5039/ari/channels \
  -H "Content-Type: application/json" \
  -d '{"endpoint":"Local/1001@ava-agent","app":"ava-stasis","callerId":"Test"}'
# → doit retourner id, name "Local/1001@ava-agent-00000000;1", state "Down"

# 3. Vérifier que StasisStart arrive (avec un listener WS actif)
# (le listener AVA devrait recevoir l'event StasisStart args=['1001'])

Dialplan complet recommandé

[ava-agent]
; Tests ARI directs (canaux Local/...)
exten => _X.,1,NoOp(=== AVA ARI test ${EXTEN} ===)
 same => n,Stasis(ava-stasis,${EXTEN},${CALLERID(num)})
 same => n,Hangup()

; Appels SIP entrants réels (canaux PJSIP avec média)
exten => s,1,NoOp(=== AVA entrant ${CALLERID(num)} ===)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Stasis(ava-stasis,s,${CALLERID(num)})
 same => n,Hangup()

; Transferts aveugles vers extensions internes
[ava-transfers]
exten => _X.,1,NoOp(AVA Blind Transfer to ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN},30,tT)
 same => n,Hangup()

exten => h,1,NoOp(AVA hangup)
 same => n,Return()

9.9 Snippets copy-paste

Python : POST /ari/channels avec gestion d’erreur

import aiohttp

async def originate(session, host: str, port: int, user: str, pwd: str):
    url = f"http://{host}:{port}/ari/channels"
    body = {
        "endpoint": "Local/1001@ava-agent",
        "app": "ava-stasis",
        "callerId": "Outbound",
        "variables": {"CHANNEL(linkedid)": "outbound-001"}
    }
    try:
        async with session.post(url, json=body,
                                auth=aiohttp.BasicAuth(user, pwd)) as r:
            if r.status in (200, 201):
                return await r.json()
            else:
                text = await r.text()
                raise RuntimeError(f"ARI POST failed: HTTP {r.status}: {text}")
    except aiohttp.ClientError as e:
        raise RuntimeError(f"ARI connection failed: {e}")

Bash : vérificateur ARI rapide

#!/usr/bin/env bash
# check-ari.sh — vérifie qu'ARI est opérationnel
set -u
HOST="${1:-127.0.0.1}"
PORT="${2:-5039}"
USER="${3:-ava_user}"
PASS="${4:-$(cat /etc/ava/credentials.env 2>/dev/null | grep ASTERISK_ARI_PASSWORD | cut -d= -f2)}"

echo "Test ARI sur $HOST:$PORT"

# 1. ARI listé
if ! curl -sf -u "$USER:$PASS" "http://$HOST:$PORT/ari/asterisk/info" -o /tmp/ari.json; then
    echo "FAIL : /ari/asterisk/info inaccessible"
    exit 1
fi

# 2. Version
VER=$(python3 -c "import json; print(json.load(open('/tmp/ari.json'))['system']['version'])")
echo "  Asterisk version: $VER"

# 3. Modules res_ari
MODS=$(asterisk -rx "module show like res_ari" | grep -c Running)
echo "  res_ari modules: $MODS"

# 4. /ari dans HTTP
if asterisk -rx "http show status" | grep -q "/ari/..."; then
    echo "  /ari/ registered: OK"
else
    echo "  /ari/ registered: MISSING"
fi

echo "OK"

9.10 Diagnostic avancé (quand /ari ne marche pas)

Procédure systématique

# 1. Les .so existent ?
ls /usr/lib/asterisk/modules/res_ari*.so
# Si vide → installer wazo-asterisk (paquet correct)

# 2. Les modules sont chargés ?
asterisk -rx "module show like res_ari" | head -3
# Doit afficher : res_ari.so ... 13 ... Running

# 3. /ari listé dans HTTP ?
asterisk -rx "http show status" | grep "/ari"
# Doit afficher : /ari/... => Asterisk RESTful API

# 4. ari show status
asterisk -rx "ari show status"
# Si "Error getting ARI configuration" → pb de conf ARI

# 5. Verbose au boot
systemctl stop asterisk
/usr/sbin/asterisk -cvvv 2>&1 | grep -iE "ari|stasis|error|warn" | head -20
# Chercher : "duplicate object 'general'" → pitfall #22

Erreurs spécifiques et leurs fixes

Erreur Cause Fix
Error getting ARI configuration ari.conf invalide Vérifier [general] dupliqué (#22)
Unable to load module res_ari.so .so manquante Installer wazo-asterisk
Connection refused :5039 Asterisk pas démarré ou bindaddr=127.0.0.1 Vérifier http.d/01-wazo.conf
HTTP 404 sur /ari/asterisk/info Module pas enregistré sur HTTP Restart complet + vérifier verbose
HTTP 401 Mauvais user/password Vérifier ari show users
Use Count 0 sur res_ari.so Module chargé mais pas utilisé Vérifier qu’aucune Stasis ne l’utilise

Script de recovery auto

# Depuis ava-deploy
curl -fsSL https://raw.githubusercontent.com/mobilejudi/ava-deploy/main/scripts/recover-ari.sh | sudo bash

Ou :

sudo /opt/ava-deploy/scripts/recover-ari.sh

Le script :

  1. Stoppe wazo-confgend.timer (cause #17)
  2. Restart complet Asterisk (avec kill -9 si bloqué)
  3. Vérifie /ari/ apparaît
  4. Si non : unload+load forcé des modules HTTP
  5. Sinon : guide de réinstallation package

9.11 Performances observées

Opération Latence Notes
GET /ari/asterisk/info < 50ms HTTP local
POST /ari/channels (Local/…) < 100ms Création + Dial
WebSocket event delivery < 50ms Subscribed apps
Groq LLM TTFB (llama-3.3-70b) 88ms Depuis wazohermesx
Groq streaming first token < 100ms Via OpenAI client

9.12 Sécurité

Réseau

  • Bindaddr : 127.0.0.1 recommandé (localhost uniquement).
  • Bindaddr 0.0.0.0 : OK si firewall restreint (Tailscale, VPN).
  • Pas d’exposition publique : ARI n’a aucune auth avancée (Basic Auth only).

Credentials

  • /etc/ava/credentials.env : chmod 0600, owned ava:ava
  • /opt/ava/.env : chmod 0600, owned ava:ava
  • ARI user password : généré à l’install, 24 chars base64url

Audit

# Vérifier les accès ARI récents
journalctl -u asterisk --since "1 day ago" | grep -i "ari\|authentication"

# Lister les apps Stasis actives
asterisk -rx "ari show applications"

9.13 Références croisées

  • ava-deploy : https://github.com/mobilejudi/ava-deploy — scripts déploiement
  • ava-deploy issues : https://gitlab.com/mobilejudi2/ava-deploy
  • WAZO_PLATFORM_DOCUMENTATION_EXHAUSTIVE.md : chapitre Asterisk
  • WAZO_API_BIBLE_CH3_CH4.md : API confd (contextes, extensions, incalls)
  • WAZO_API_BIBLE_CH7.md : API calld (Call Control via calld, pas ARI direct)
  • WAZO_AUTH_PITFALLS.md : bugs auth wazo-auth (référence skills)

Statut doc : juillet 2026, validé sur wazohermesx.
Prochaine mise à jour : si Asterisk 23+ change l’API ARI (peu probable, ARI est stable depuis v13).


PARTIE 1 : Provisioning Core & Utilisateurs

Cette partie couvre les workflows fondamentaux de gestion des utilisateurs et du provisioning de base. Ces scénarios sont les plus fréquents et constituent le socle de toute intégration Wazo.


1.1 Création d’un Utilisateur Complet (8 Étapes)

Objectif

Créer un utilisateur téléphonique complet avec tous les composants nécessaires : compte d’accès, ligne SIP, extension, boîte vocale, et les liaisons entre tous ces éléments. C’est le scénario le plus courant pour le provisioning de nouveaux employés.

Services impliqués

  • wazo-confd : Gestion des utilisateurs, lignes, extensions, endpoints SIP, voicemails
  • wazo-auth : Gestion des comptes d’authentification

Le Workflow détaillé

Étape 1 : Créer le compte utilisateur dans confd

POST /api/confd/1.1/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "firstname": "Jean",
  "lastname": "Dupont",
  "email": "jean.dupont@acme.fr",
  "username": "jdupont",
  "caller_id_name": "Jean Dupont",
  "caller_id_number": "1001"
}

Réponse :

{
  "uuid": "a1223fe6-bff8-4fb6-a982-f9157dea5094",
  "firstname": "Jean",
  "lastname": "Dupont",
  "email": "jean.dupont@acme.fr",
  "username": "jdupont",
  ...
}

:link: Chaînage : Récupérez le champ uuid — il sera utilisé dans toutes les étapes suivantes pour lier les ressources. Stockez-le dans USER_UUID.

Étape 2 : Créer la boîte vocale (optionnel mais recommandé)

POST /api/confd/1.1/voicemails
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "jdupont",
  "number": "1001",
  "email": "jean.dupont@acme.fr",
  "timezone": "Europe/Paris",
  "password": "1234",
  "max_messages": 50
}

Réponse :

{
  "id": 12,
  "name": "jdupont",
  "number": "1001",
  ...
}

:link: Chaînage : Récupérez le champ id — il sera utilisé pour lier la boîte vocale à l’utilisateur. Stockez-le dans VM_ID.

Étape 3 : Créer l’endpoint SIP technique

POST /api/confd/1.1/endpoints/sip
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "jdupont-sip",
  "name": "jdupont",
  "auth_section_options": [
    ["username", "jdupont"],
    ["password", "secure_password_sip"]
  ],
  "endpoint_section_options": [
    ["disallow", "all"],
    ["allow", "ulaw,alaw,g722"],
    ["direct_media", "no"],
    ["rtp_symmetric", "yes"]
  ]
}

Réponse :

{
  "uuid": "b2345gh7-abc9-4def-ghij-klmnopqr6789",
  "label": "jdupont-sip",
  "name": "jdupont",
  ...
}

:link: Chaînage : Récupérez le champ uuid — il sera utilisé pour lier l’endpoint SIP à la ligne. Stockez-le dans SIP_UUID.

Étape 4 : Créer la ligne téléphonique

POST /api/confd/1.1/lines
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "jdupont-line",
  "context": "default",
  "caller_id_name": "Jean Dupont",
  "caller_id_number": "1001"
}

Réponse :

{
  "id": 25,
  "name": "jdupont-line",
  "context": "default",
  ...
}

:link: Chaînage : Récupérez le champ id — il sera utilisé pour lier l’extension, l’endpoint SIP et l’utilisateur. Stockez-le dans LINE_ID.

Étape 5 : Créer l’extension

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "1001",
  "context": "default"
}

Réponse :

{
  "id": 156,
  "exten": "1001",
  "context": "default"
}

:link: Chaînage : Récupérez le champ id — il sera utilisé pour lier l’extension à la ligne. Stockez-le dans EXT_ID.

Étape 6 : Lier l’extension à la ligne

PUT /api/confd/1.1/lines/{LINE_ID}/extensions/{EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 7 : Lier l’endpoint SIP à la ligne

PUT /api/confd/1.1/lines/{LINE_ID}/endpoints/sip/{SIP_UUID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 8 : Lier la ligne à l’utilisateur

PUT /api/confd/1.1/users/{USER_UUID}/lines/{LINE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 9 (optionnel) : Lier la boîte vocale à l’utilisateur

PUT /api/confd/1.1/users/{USER_UUID}/voicemails/{VM_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 10 : Créer le compte d’authentification

POST /api/auth/0.1/users
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "username": "jdupont",
  "password": "initial_password",
  "firstname": "Jean",
  "lastname": "Dupont",
  "email": "jean.dupont@acme.fr"
}

Réponse :

{
  "uuid": "c3456ij8-def0-4abc-lmno-pqrstu901234",
  "username": "jdupont",
  ...
}

:link: Chaînage : Récupérez le champ uuid — il correspond au compte d’authentification. Stockez-le dans AUTH_USER_UUID.

Étape 11 : Lier le compte auth à l’utilisateur confd

PUT /api/confd/1.1/users/{USER_UUID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "auth_user_uuid": "c3456ij8-def0-4abc-lmno-pqrstu901234"
}

Réponse : 200 OK

Point d’attention / Warning

:warning: Important :

  • L’ordre des étapes est STRICT — l’extension doit être créée AVANT d’être liée à la ligne
  • Les passwords SIP doivent être sécurisés (minimum 12 caractères, complexité)
  • Le context doit exister dans Wazo (créez-le via /api/confd/1.1/contexts si nécessaire)
  • La boîte vocale est optionnelle mais recommandée pour un utilisateur complet

1.2 Suppression Propre d’un Utilisateur (8 Étapes)

Objectif

Supprimer un utilisateur et toutes ses ressources associées de manière propre et ordonnée, sans laisser d’orphelins dans la base de données. L’ordre de suppression est critique pour éviter les erreurs de contrainte.

Services impliqués

  • wazo-confd : Tous les composants de configuration
  • wazo-provd : Gestion des devices

Le Workflow détaillé

Étape 1 : Récupérer les lignes de l’utilisateur

GET /api/confd/1.1/users/{USER_UUID}/lines
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "id": 25,
      "name": "jdupont-line",
      ...
    }
  ]
}

:link: Chaînage : Stockez le LINE_ID = 25

Étape 2 : Récupérer les devices associés à la ligne

GET /api/confd/1.1/lines/{LINE_ID}/devices
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "id": "001122334455",
      "mac": "001122334455",
      "model": "Yealink T46S",
      ...
    }
  ]
}

:link: Chaînage : Stockez le DEVICE_ID = 001122334455

Étape 3 : Dissocier le device de la ligne

DELETE /api/confd/1.1/lines/{LINE_ID}/devices/{DEVICE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 4 : Réinitialiser le device en mode autoprov

POST /api/provd/0.1/devices/{DEVICE_ID}/autoprov
X-Auth-Token: {admin_token}

Réponse : 200 OK

Cette étape permet au téléphone de se réapprovisionner automatiquement lors du prochain redémarrage.

Étape 5 : Récupérer les extensions de la ligne

GET /api/confd/1.1/lines/{LINE_ID}/extensions
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "id": 156,
      "exten": "1001",
      "context": "default"
    }
  ]
}

:link: Chaphinage : Stockez EXT_ID = 156

Étape 6 : Dissocier l’extension de la ligne

DELETE /api/confd/1.1/lines/{LINE_ID}/extensions/{EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 7 : Dissocier la ligne de l’utilisateur

DELETE /api/confd/1.1/users/{USER_UUID}/lines/{LINE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 8 : Supprimer l’extension

DELETE /api/confd/1.1/extensions/{EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 9 : Supprimer la ligne

DELETE /api/confd/1.1/lines/{LINE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 10 : Supprimer l’endpoint SIP

DELETE /api/confd/1.1/endpoints/sip/{SIP_UUID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 11 : Supprimer l’utilisateur

DELETE /api/confd/1.1/users/{USER_UUID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Point d’attention / Warning

:warning: Important :

  • L’ORDRE EST CRITIQUE : supprimez toujours dans l’ordre inverse de la création
  • Ne supprimez jamais un endpoint SIP utilisé par d’autres lignes
  • Le device doit être dissocié AVANT de supprimer la ligne
  • Vérifiez qu’aucun trunk ou queue n’utilise ces ressources avant suppression

1.3 Importation CSV en Masse d’Utilisateurs (4 Étapes)

Objectif

Importer rapidement des dizaines ou centaines d’utilisateurs simultanément via un fichier CSV, en utilisant l’import automatique de Wazo qui crée tous les éléments en une seule opération.

Services impliqués

  • wazo-confd : Import des utilisateurs via endpoint spécialisé

Le Workflow détaillé

Étape 1 : Récupérer le template CSV

GET /api/confd/1.1/users/export
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}
Accept: text/csv

Réponse (CSV) :

firstname,lastname,email,username,extension,context,line_name,voicemail_number
Jean,Dupont,jd@acme.fr,jdupont,1001,default,jd-line,1001
Marie,Martin,mm@acme.fr,mmartin,1002,default,mm-line,1002

:link: Chaînage : Ce template vous montre les colonnes attendues. Préparez votre fichier CSV en suivant ce format.

Étape 2 : Importer le fichier CSV

POST /api/confd/1.1/users/import
Content-Type: text/csv
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Body (CSV) :

firstname,lastname,email,username,extension,context,line_name,voicemail_number
Jean,Dupont,jd@acme.fr,jdupont,1001,default,jd-line,1001
Marie,Martin,mm@acme.fr,mmartin,1002,default,mm-line,1002
Pierre,Durand,pd@acme.fr,pdurand,1003,default,pd-line,1003

Réponse :

{
  "created_users": [
    {"uuid": "user-uuid-1", "username": "jdupont"},
    {"uuid": "user-uuid-2", "username": "mmartin"},
    {"uuid": "user-uuid-3", "username": "pdurand"}
  ],
  "errors": []
}

:link: Chaînage : La réponse contient les UUIDs créés. Stockez-les pour les utiliser dans les étapes suivantes si besoin.

Étape 3 : Vérifier les créations

GET /api/confd/1.1/users?search=Dupont
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "uuid": "user-uuid-1",
      "firstname": "Jean",
      "lastname": "Dupont",
      ...
    }
  ]
}

Étape 4 : Associer les lignes aux utilisateurs (si nécessaire)

PUT /api/confd/1.1/users/{USER_UUID}/lines/{LINE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • L’import CSV crée automatiquement les lignes et extensions correspondantes
  • Les voicemails ne sont PAS créés automatiquement — faites-le séparément si besoin
  • En cas d’erreur sur une ligne, les autres lignes du fichier sont quand même créées
  • Vérifiez toujours le champ errors dans la réponse

1.4 Renvois d’Appel Utilisateur (4 Étapes)

Objectif

Configurer les renvois d’appel (forwards) pour un utilisateur : inconditionnel, sur occupation, et sur non-réponse. Ces services permettent la continuité des communications en cas d’absence.

Services impliqués

  • wazo-confd : Configuration des services de renvoi

Le Workflow détaillé

Étape 1 : Lister les renvois actuels

GET /api/confd/1.1/users/{USER_UUID}/forwards
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "type": "unconditional",
      "enabled": false,
      "destination": null
    },
    {
      "type": "busy",
      "enabled": false,
      "destination": null
    },
    {
      "type": "noanswer",
      "enabled": false,
      "destination": null
    }
  ]
}

Étape 2 : Configurer le renvoi inconditionnel

PUT /api/confd/1.1/users/{USER_UUID}/forwards/unconditional
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "enabled": true,
  "destination": "1005"
}

Réponse :

{
  "enabled": true,
  "destination": "1005"
}

:link: Chaînage : Le numéro de destination peut être une extension interne ou un numéro externe

Étape 3 : Configurer le renvoi sur occupation

PUT /api/confd/1.1/users/{USER_UUID}/forwards/busy
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "enabled": true,
  "destination": "2001"
}

Étape 4 : Configurer le renvoi sur non-réponse

PUT /api/confd/1.1/users/{USER_UUID}/forwards/noanswer
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "enabled": true,
  "destination": "3000",
  "timeout": 18
}
Paramètre Description
timeout Durée avant renvoi (en secondes, défaut: 18)

Point d’attention / Warning

:warning: Important :

  • Le renvoi inconditionnel est prioritaire sur tous les autres
  • Le timeout du renvoi sur non-réponse doit être inférieur au timeout de la ligne
  • Les destinations externes nécessitent les droits d’appels sortants appropriés

1.5 Services Utilisateur : DND et Filtre d’Appel (4 Étapes)

Objectif

Activer le mode “Ne Pas Déranger” (DND) et le filtre d’appel entrant pour un utilisateur. Le DND bloque tous les appels entrants ; le filtre permet de筛选 les appels selon certaines règles.

Services impliqués

  • wazo-confd : Services DND et incallfilter

Le Workflow détaillé

Étape 1 : Activer le DND

PUT /api/confd/1.1/users/{USER_UUID}/services/dnd/enable
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "enabled": true
}

Étape 2 : Désactiver le DND

PUT /api/confd/1.1/users/{USER_UUID}/services/dnd/disable
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 3 : Activer le filtre d’appel entrant

PUT /api/confd/1.1/users/{USER_UUID}/services/incallfilter/enable
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "enabled": true
}

Étape 4 : Désactiver le filtre d’appel entrant

PUT /api/confd/1.1/users/{USER_UUID}/services/incallfilter/disable
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • Depuis XiVO 16.13, le DND est effectif indépendamment de l’extension *25
  • Le filtre d’appel nécessite une configuration supplémentaire des règles de filtrage

1.6 Création d’un Nouveau Tenant (Multi-Tenant) (7 Étapes)

Objectif

Créer un nouveau tenant isolé pour un client ou un département, avec ses propres contextes, utilisateurs et politiques d’accès. Le multi-tenant permet une isolation complète des données.

Services impliqués

  • wazo-auth : Gestion des tenants et utilisateurs d’authentification
  • wazo-confd : Gestion des contextes

Le Workflow détaillé

Étape 1 : Créer le tenant

POST /api/auth/0.1/tenants
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "name": "ACME Corp",
  "slug": "acme"
}

Réponse :

{
  "uuid": "tenant-uuid-acme123",
  "name": "ACME Corp",
  "slug": "acme",
  "parent_uuid": "master-tenant-uuid"
}

:link: Chaînage : Récupérez le champ uuid — il sera utilisé pour toutes les opérations sur ce tenant. Stockez-le dans TENANT_UUID.

Étape 2 : Créer l’utilisateur administrateur du tenant

POST /api/auth/0.1/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "username": "admin_acme",
  "password": "secure_password",
  "firstname": "Admin",
  "lastname": "ACME"
}

Réponse :

{
  "uuid": "admin-auth-uuid-456",
  "username": "admin_acme",
  ...
}

:link: Chaînage : Stockez ADMIN_AUTH_UUID

Étape 3 : Créer une policy d’administration

POST /api/auth/0.1/policies
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "admin-policy",
  "description": "Full admin access for ACME tenant",
  "acl": [
    "confd.#",
    "calld.#",
    "provd.#"
  ]
}

Réponse :

{
  "uuid": "policy-uuid-789",
  "name": "admin-policy",
  ...
}

:link: Chaînage : Stockez POLICY_UUID

Étape 4 : Assigner la policy à l’administrateur

POST /api/auth/0.1/users/{ADMIN_AUTH_UUID}/policies
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "policy_uuid": "policy-uuid-789"
}

Étape 5 : Créer le contexte interne pour le tenant

POST /api/confd/1.1/contexts
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "interne-acme",
  "name": "Interne ACME",
  "type": "internal",
  "user_ranges": [
    {"start": "1000", "end": "1999"}
  ]
}

Réponse :

{
  "id": 45,
  "label": "interne-acme",
  "type": "internal",
  ...
}

:link: Chaînage : Stockez CTX_INTERNAL_ID = 45

Étape 6 : Créer le contexte entrant pour le tenant

POST /api/confd/1.1/contexts
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "entrant-acme",
  "name": "Entrant ACME",
  "type": "incall",
  "incall_ranges": [
    {"start": "003338000100", "end": "003338000200"}
  ]
}

Réponse :

{
  "id": 46,
  "label": "entrant-acme",
  "type": "incall",
  ...
}

:link: Chaînage : Stockez CTX_INCALL_ID = 46

Étape 7 : Vérifier le tenant

GET /api/auth/0.1/tenants/{TENANT_UUID}
X-Auth-Token: {admin_token}

Point d’attention / Warning

:warning: Important :

  • Le header Wazo-Tenant est OBLIGATOIRE pour toutes les opérations après la création du tenant
  • L’ACL confd.# donne accès à toutes les ressources confd du tenant
  • Les contextes créés n’ont pas de relation automatique — créez des liens explicites si nécessaire
  • La suppression d’un tenant est IRRÉVERSIBLE

1.7 Fallbacks et Options Utilisateur (4 Étapes)

Objectif

Configurer les fallbacks (renvois en cas d’indisponibilité) et les options avancées d’un utilisateur : timeout, destination si pas de réponse, boîte vocale, etc.

Services impliqués

  • wazo-confd : Configuration des fallbacks utilisateur

Le Workflow détaillé

Étape 1 : Récupérer les fallbacks actuels

GET /api/confd/1.1/users/{USER_UUID}/fallbacks
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "noanswer_destination": null,
  "busy_destination": null,
  "congestion_destination": null,
  "fail_destination": null,
  "noanswer_timeout": 18
}

Étape 2 : Configurer le fallback sur non-réponse

PUT /api/confd/1.1/users/{USER_UUID}/fallbacks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "noanswer_destination": {
    "type": "voicemail",
    "voicemail_id": 12
  },
  "noanswer_timeout": 25
}

Étape 3 : Configurer le fallback sur occupation

PUT /api/confd/1.1/users/{USER_UUID}/fallbacks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "busy_destination": {
    "type": "voicemail",
    "voicemail_id": 12
  }
}

Étape 4 : Configurer le fallback sur indisponibilité (fail)

PUT /api/confd/1.1/users/{USER_UUID}/fallbacks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "fail_destination": {
    "type": "extension",
    "extension": "1000",
    "context": "default"
  }
}
Type de destination Paramètres requis
voicemail voicemail_id
extension extension, context
user user_id
custom content (dialplan)

Point d’attention / Warning

:warning: Important :

  • Le noanswer_timeout doit être cohérent avec le timeout de sonnerie du téléphone
  • Les fallbacks sont évalués dans l’ordre : busy → noanswer → congestion → fail
  • Configurez toujours une destination de dernier recours (fallback final)

1.8 Gestion des Funckeys (Touches de Fonction) (5 Étapes)

Objectif

Configurer les touches de fonction (BLF, speed dial, pickup) sur les телефонов prenant en charge les touches programmable. Ces touches permettent un accès rapide aux fonctions fréquentes.

Services impliqués

  • wazo-confd : Configuration des funckeys

Le Workflow détaillé

Étape 1 : Lister les destinations disponibles

GET /api/confd/1.1/funckeys/destinations
X-Auth-Token: {admin_token}

Réponse :

{
  "items": [
    {"type": "user", "description": "Appeler un utilisateur"},
    {"type": "queue", "description": "Appeler une file d'attente"},
    {"type": "custom", "description": "Extension personnalisée"},
    {"type": "transfer", "description": "Transférer l'appel"},
    {"type": "bsfilter", "description": "Filtre boss-secrétaire"}
  ]
}

Étape 2 : Créer un template de funckeys

POST /api/confd/1.1/funckeys/templates
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "standard-template",
  "keys": {
    "1": {
      "destination_type": "user",
      "user_id": "user-uuid-1"
    },
    "2": {
      "destination_type": "queue",
      "queue_id": 10
    },
    "3": {
      "destination_type": "custom",
      "extension": "*25",
      "label": "DND"
    }
  }
}

Réponse :

{
  "id": 5,
  "name": "standard-template",
  "keys": {...}
}

:link: Chaînage : Stockez TEMPLATE_ID = 5

Étape 3 : Appliquer le template à un utilisateur

PUT /api/confd/1.1/users/{USER_UUID}/funckeys/templates/{TEMPLATE_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 4 : Ajouter une funckey individuelle (override)

PUT /api/confd/1.1/users/{USER_UUID}/funckeys/5
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination_type": "custom",
  "extension": "*26",
  "label": "Renvoi ON",
  "blf": true
}

Étape 5 : Récupérer les funckeys fusionnées

GET /api/confd/1.1/users/{USER_UUID}/funckeys?view=merged
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • Le paramètre blf: true permet la surveillance d’état (Busy Lamp Field)
  • Les funckeys individuelles surchargent le template
  • La suppression d’une funckey la retire complètement

1.9 Récapitulatif des Endpoints Utilisateur

Ressource CRUD Endpoint
Utilisateur C POST /users
Utilisateur R GET /users/{uuid}
Utilisateur U PUT /users/{uuid}
Utilisateur D DELETE /users/{uuid}
Ligne C POST /lines
Extension C POST /extensions
Endpoint SIP C POST /endpoints/sip
Voicemail C POST /voicemails
Forward U PUT /users/{uuid}/forwards/{type}
DND U PUT /users/{uuid}/services/dnd/enable
Fallback U PUT /users/{uuid}/fallbacks
Funckey C PUT /users/{uuid}/funckeys/{position}

Fin de la PARTIE 1


PARTIE 2 : Terminaux, Trunks et Routage

Cette partie couvre les workflows de provisioning des terminaux physiques, la configuration des trunks SIP/IAX pour la connectivité externe, et le routage des appels entrants et sortants. Ces scénarios sont essentiels pour connecter Wazo au monde extérieur.


2.1 Provisioning d’un Téléphone Yealink (7 Étapes)

Objectif

Provisionner un téléphone Yealink automatiquement via le réseau : enregistrer le device, le configurer avec un template, et déclencher la synchronisation pour que le téléphone récupère sa configuration.

Services impliqués

  • wazo-provd : Gestion du provisioning des terminaux
  • wazo-confd : Configuration des lignes et devices
  • wazo-calld : Vérification du statut des lignes

Le Workflow détaillé

Étape 1 : Installer le plugin Yealink (première fois seulement)

POST /api/provd/0.1/pgmgr/install/install
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "id": "xivo-yealink-v86"
}

Réponse :

{
  "id": "op_install_001",
  "plugin_id": "xivo-yealink-v86",
  "status": "pending"
}

:link: Chaînage : Stockez OPER_ID = “op_install_001”

Étape 2 : Attendre l’installation du plugin (polling)

GET /api/provd/0.1/pgmgr/install/install/{OPER_ID}
X-Auth-Token: {admin_token}

Réponse :

{
  "id": "op_install_001",
  "plugin_id": "xivo-yealink-v86",
  "status": "done",
  "result": "Plugin installed successfully"
}

Étape 3 : Supprimer le monitor d’installation

DELETE /api/provd/0.1/pgmgr/install/install/{OPER_ID}
X-Auth-Token: {admin_token}

Étape 4 : Créer le device dans provd

POST /api/provd/0.1/devmgr/devices
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "mac": "001122334455",
  "model": "Yealink",
  "vendor": "Yealink",
  "description": "Bureau Jean Dupont"
}

Réponse :

{
  "id": "001122334455",
  "mac": "001122334455",
  "model": "Yealink T46S",
  "vendor": "Yealink",
  "status": "not_configured"
}

:link: Chaînage : Stockez DEVICE_ID = “001122334455”

Étape 5 : Récupérer la config autoprov

GET /api/provd/0.1/devmgr/devices/{DEVICE_ID}/autoprov
X-Auth-Token: {admin_token}

Réponse :

{
  "config": "autoprov",
  "lines": []
}

Étape 6 : Associer la ligne au device

PUT /api/confd/1.1/lines/{LINE_ID}/devices/{DEVICE_ID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 7 : Synchroniser le device

POST /api/provd/0.1/devmgr/synchronize
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "id": "001122334455"
}

Réponse :

{
  "id": "op_sync_001",
  "device_id": "001122334455",
  "status": "pending"
}

:link: Chaînage : Stockez SYNC_OPER_ID = “op_sync_001” — attendre la fin puis supprimer

Point d’attention / Warning

:warning: Important :

  • Always wait for plugin installation to complete BEFORE creating devices
  • Delete the sync operation after polling to clean up monitoring
  • Le téléphone doit être allumé et connecté au réseau pour recevoir la configuration
  • La synchronisation peut prendre 30-60 secondes

2.2 Configurer un Trunk SIP Opérateur (6 Étapes)

Objectif

Configurer un trunk SIP pour connecter Wazo à un opérateur téléphonique (OVH, SFR, Free, etc.) permettant les appels entrants et sortants.

Services impliqués

  • wazo-confd : Gestion des transports, endpoints SIP, trunks, outcalls

Le Workflow détaillé

Étape 1 : Créer le transport SIP

POST /api/confd/1.1/sip/transports
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "transport-opérateur-udp",
  "protocol": "udp",
  "bind": "0.0.0.0:5060",
  "external_media_address": "203.0.113.1"
}

Réponse :

{
  "uuid": "transport-uuid-001",
  "name": "transport-opérateur-udp",
  "protocol": "udp",
  ...
}

:link: Chaînage : Stockez TRANSPORT_UUID

Étape 2 : Créer l’endpoint SIP du trunk

POST /api/confd/1.1/endpoints/sip
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "trunk-operateur",
  "name": "trunk_operateur",
  "transport_uuid": "transport-uuid-001",
  "aor_section_options": [
    ["contact", "sip:operator.fr:5060"]
  ],
  "auth_section_options": [
    ["username", "mon_compte"],
    ["password", "mot_de_passe_sip"]
  ],
  "endpoint_section_options": [
    ["disallow", "all"],
    ["allow", "ulaw,alaw"],
    ["direct_media", "no"]
  ],
  "registration_section_options": [
    ["server_uri", "sip:operator.fr:5060"],
    ["client_uri", "sip:mon_compte@operator.fr"]
  ]
}

Réponse :

{
  "uuid": "trunk_sip_uuid_001",
  "label": "trunk-operateur",
  ...
}

:link: Chaînage : Stockez TRUNK_SIP_UUID

Étape 3 : Créer le trunk

POST /api/confd/1.1/trunks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "Trunk Opérateur",
  "context": "from-extern"
}

Réponse :

{
  "id": 10,
  "name": "Trunk Opérateur",
  "context": "from-extern"
}

:link: Chaphinage : Stockez TRUNK_ID = 10

Étape 4 : Lier l’endpoint SIP au trunk

PUT /api/confd/1.1/trunks/{TRUNK_ID}/endpoints/sip/{TRUNK_SIP_UUID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse : 204 No Content

Étape 5 : Créer l’outcall (appels sortants)

POST /api/confd/1.1/outcalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "appels-sortants-national"
}

Réponse :

{
  "id": 5,
  "name": "appels-sortants-national"
}

:link: Chaînage : Stockez OUTCALL_ID = 5

Étape 6 : Associer le trunk à l’outcall

PUT /api/confd/1.1/outcalls/{OUTCALL_ID}/trunks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "trunks": [{"id": 10}]
}

Point d’attention / Warning

:warning: Important :

  • Le context du trunk doit être “from-extern” pour recevoir les appels entrants
  • Les credentials SIP doivent correspondre exactement à ceux fournis par l’opérateur
  • Vérifiez la registration dans wazo-calld après configuration

2.3 Configurer un Trunk IAX2 Inter-Site (5 Étapes)

Objectif

Configurer un trunk IAX2 pour interconnecter deux serveurs Wazo ou PBX Asterisk sur des sites distants, permettant des appels internes entre sites sans passer par le réseau public.

Services impliqués

  • wazo-confd : Gestion des endpoints IAX, trunks, outcalls

Le Workflow détaillé

Étape 1 : Créer l’endpoint IAX

POST /api/confd/1.1/endpoints/iax
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "trunk-iax-site2",
  "type": "friend",
  "host": "dynamic",
  "context": "from-extern",
  "secret": "iax_secret_password",
  "transfer": "yes",
  "qualify": "yes"
}

Réponse :

{
  "id": 8,
  "name": "trunk-iax-site2",
  ...
}

:link: Chaînage : Stockez IAX_ID = 8

Étape 2 : Créer le trunk

POST /api/confd/1.1/trunks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "Trunk IAX Site 2",
  "context": "from-extern"
}

Réponse :

{
  "id": 12,
  "name": "Trunk IAX Site 2"
}

:link: Chaînage : Stockez IAX_TRUNK_ID = 12

Étape 3 : Lier l’endpoint IAX au trunk

PUT /api/confd/1.1/trunks/{IAX_TRUNK_ID}/endpoints/iax/{IAX_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 4 : Créer l’outcall

POST /api/confd/1.1/outcalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "inter-site"
}

:link: Chaînage : Stockez OUTCALL_ID

Étape 5 : Vérifier le trunk

GET /api/calld/1.0/trunks
X-Auth-Token: {admin_token}

Point d’attention / Warning

:warning: Important :

  • IAX utilise le port 4569 par défaut
  • Le secret doit être identique sur les deux serveurs
  • host: dynamic permet à l’autre site de s’enregistrer

2.4 DID Entrant vers Utilisateur (5 Étapes)

Objectif

Configurer un numéro DID (Direct Inward Dialing) entrants pour rediriger les appels vers un utilisateur spécifique, avec possibilité de planning horaire.

Services impliqués

  • wazo-confd : Gestion des schedules, incalls, extensions

Le Workflow détaillé

Étape 1 : Créer le schedule (optionnel)

POST /api/confd/1.1/schedules
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "horaires-bureau",
  "timezone": "Europe/Paris",
  "open_periods": [
    {
      "start": "09:00",
      "end": "18:00",
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
    }
  ]
}

Réponse :

{
  "id": 15,
  "name": "horaires-bureau",
  ...
}

:link: Chaînage : Stockez SCHEDULE_ID = 15

Étape 2 : Créer l’extension entrante

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "003338000100",
  "context": "from-extern"
}

:link: Chaînage : Stockez EXT_ID

Étape 3 : Créer l’incall

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "user",
    "user_id": "user-uuid-123"
  },
  "extensions": [{"id": EXT_ID}],
  "schedule_id": 15
}

Réponse :

{
  "id": 20,
  "destination": {
    "type": "user",
    "user_id": "user-uuid-123"
  },
  ...
}

:link: Chaînage : Stockez INCALL_ID = 20

Étape 4 : Vérifier l’incall

GET /api/confd/1.1/incalls/{INCALL_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 5 : Vérifier la ligne utilisateur

GET /api/confd/1.1/users/{USER_UUID}/lines
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • Le context de l’extension doit être “from-extern”
  • Sans schedule, l’appel est toujours routé
  • La destination peut être : user, queue, ivr, conference, extension

2.5 DISA — Accès Direct au Système (4 Étapes)

Objectif

Configurer le DISA (Direct Inward System Access) permettant à un appelant externe d’accéder au système Wazo et de composer des numéros internes après authentification par PIN.

Services impliqués

  • wazo-confd : Gestion des schedules, incalls

Le Workflow détaillé

Étape 1 : Créer un schedule 24/7 (optionnel)

POST /api/confd/1.1/schedules
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "24x7",
  "timezone": "Europe/Paris",
  "open_periods": [
    {
      "start": "00:00",
      "end": "23:59",
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
    }
  ]
}

:link: Chaînage : Stockez SCHEDULE_ID

Étape 2 : Créer l’extension DISA

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "9999",
  "context": "from-extern"
}

:link: Chaînage : Stockez EXT_ID

Étape 3 : Créer l’incall avec destination DISA

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "application",
    "application": "disa",
    "pin": "5678",
    "context": "default"
  },
  "extensions": [{"id": EXT_ID}],
  "schedule_id": SCHEDULE_ID
}

Réponse :

{
  "id": 25,
  "destination": {
    "type": "application",
    "application": "disa",
    "pin": "5678",
    "context": "default"
  }
}

:link: Chaînage : Stockez INCALL_ID

Étape 4 : Vérifier la configuration

GET /api/confd/1.1/incalls/{INCALL_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • Le PIN doit être suffisamment complexe (4+ chiffres)
  • Le context DISA détermine quelles extensions sont accessibles
  • Limitez l’utilisation du DISA pour des raisons de sécurité

2.6 Template SIP Mutualisé pour Endpoints (5 Étapes)

Objectif

Créer un template SIP réutilisable pour configurer rapidement plusieurs endpoints avec des paramètres communs, simplifiant la gestion de parc de téléphone.

Services impliqués

  • wazo-confd : Gestion des templates SIP, endpoints SIP

Le Workflow détaillé

Étape 1 : Créer le template SIP

POST /api/confd/1.1/endpoints/sip/templates
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "tpl-yealink-corp",
  "endpoint_section_options": [
    ["disallow", "all"],
    ["allow", "ulaw,alaw,g722"],
    ["direct_media", "no"],
    ["rtp_symmetric", "yes"]
  ],
  "aor_section_options": [
    ["max_contacts", "1"]
  ]
}

Réponse :

{
  "uuid": "template-uuid-001",
  "label": "tpl-yealink-corp",
  ...
}

:link: Chaînage : Stockez TPL_UUID

Étape 2 : Créer un endpoint avec le template (Alice)

POST /api/confd/1.1/endpoints/sip
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "alice-sip",
  "name": "alice",
  "templates": [{"uuid": "template-uuid-001"}],
  "auth_section_options": [
    ["username", "alice"],
    ["password", "alice_password"]
  ]
}

Réponse :

{
  "uuid": "sip-uuid-alice",
  "label": "alice-sip",
  ...
}

:link: Chaînage : Stockez SIP_UUID_ALICE

Étape 3 : Créer un endpoint avec le template (Bob)

POST /api/confd/1.1/endpoints/sip
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "label": "bob-sip",
  "name": "bob",
  "templates": [{"uuid": "template-uuid-001"}],
  "auth_section_options": [
    ["username", "bob"],
    ["password", "bob_password"]
  ]
}

:link: Chaînage : Stockez SIP_UUID_BOB

Étape 4 : Visualiser les options héritées

GET /api/confd/1.1/endpoints/sip/{SIP_UUID_ALICE}?view=merged
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "uuid": "sip-uuid-alice",
  "label": "alice-sip",
  "endpoint_section_options": [
    ["disallow", "all"],
    ["allow", "ulaw,alaw,g722"],
    ...
  ]
}

Étape 5 : Mettre à jour le template (mise à jour globale)

PUT /api/confd/1.1/endpoints/sip/templates/{TPL_UUID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "endpoint_section_options": [
    ["disallow", "all"],
    ["allow", "ulaw,alaw,g722,h264"],
    ["direct_media", "no"]
  ]
}

Point d’attention / Warning

:warning: Important :

  • Les endpoints enfants peuvent surcharger les options du template
  • La mise à jour du template affecte tous les endpoints enfants
  • Utilisez view=merged pour voir la configuration finale

2.7 Transport TLS et WSS pour WebRTC (6 Étapes)

Objectif

Configurer des transports SIP sécurisés TLS et WSS (WebSocket Secure) pour permettre l’enregistrement de téléphones distants et clients WebRTC.

Services impliqués

  • wazo-confd : Gestion des transports SIP

Le Workflow détaillé

Étape 1 : Créer le transport TLS

POST /api/confd/1.1/sip/transports
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "transport-tls",
  "protocol": "tls",
  "bind": "0.0.0.0:5061",
  "certfile": "/etc/asterisk/keys/wazo.crt",
  "privkey": "/etc/asterisk/keys/wazo.key",
  "method": "tlsv1_2"
}

Réponse :

{
  "uuid": "tls-transport-uuid",
  "name": "transport-tls",
  "protocol": "tls"
}

:link: Chaînage : Stockez T_TLS_UUID

Étape 2 : Créer le transport WSS (WebRTC)

POST /api/confd/1.1/sip/transports
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "transport-wss",
  "protocol": "wss",
  "bind": "0.0.0.0:8089"
}

Réponse :

{
  "uuid": "wss-transport-uuid",
  "name": "transport-wss",
  "protocol": "wss"
}

:link: Chaînage : Stockez T_WSS_UUID

Étape 3 : Configurer un endpoint pour TLS

PUT /api/confd/1.1/endpoints/sip/{SIP_UUID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "transport_uuid": "tls-transport-uuid",
  "endpoint_section_options": [
    ["media_encryption", "srtp"]
  ]
}

Étape 4 : Configurer un trunk pour WebRTC

PUT /api/confd/1.1/endpoints/sip/{TRUNK_SIP_UUID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "transport_uuid": "wss-transport-uuid",
  "endpoint_section_options": [
    ["dtls_enable", "yes"],
    ["dtls_verify", "fingerprint"],
    ["dtls_auto_generate_cert", "yes"],
    ["ice_support", "yes"],
    ["rtcpmux", "yes"]
  ]
}

Point d’attention / Warning

:warning: Important :

  • Les certificats TLS doivent être signés par une CA reconnue ou ajoutés au truststore
  • WSS est obligatoire pour les clients WebRTC (navigateurs)
  • SRTP (media_encryption) requiert TLS

2.8 Configuration RTP et PJSIP Global (5 Étapes)

Objectif

Configurer les paramètres globaux Asterisk pour les codecs audio, les ports RTP, et les options PJSIP avancées.

Services impliqués

  • wazo-confd : Configuration Asterisk

Le Workflow détaillé

Étape 1 : Lire la configuration RTP actuelle

GET /api/confd/1.1/asterisk/rtp/general
X-Auth-Token: {admin_token}

Réponse :

{
  "options": {
    "rtpstart": 10000,
    "rtpend": 20000
  }
}

Étape 2 : Configurer les ports RTP

PUT /api/confd/1.1/asterisk/rtp/general
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "options": {
    "rtpstart": 10000,
    "rtpend": 20000,
    "strictrtp": "yes",
    "ICESupport": "no"
  }
}

Étape 3 : Configurer PJSIP Global

PUT /api/confd/1.1/asterisk/pjsip/global
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "options": {
    "keep_alive_interval": 30,
    "max_forwards": 70
  }
}

Étape 4 : Configurer PJSIP System

PUT /api/confd/1.1/asterisk/pjsip/system
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "options": {
    "timer_t1": 500,
    "timer_b": 3000
  }
}

Point d’attention / Warning

:warning: Important :

  • Les changements nécessitent un redémarrage d’Asterisk
  • Les ports RTP (10000-20000) doivent être autorisés dans le firewall
  • strictrtp aide à prévenir les problèmes NAT

2.9 Music on Hold avec Fichier Audio (4 Étapes)

Objectif

Configurer une musique d’attente personnalisée avec des fichiers audio uploadés, puis l’appliquer à une queue ou un utilisateur.

Services impliqués

  • wazo-confd : Gestion MOH

Le Workflow détaillé

Étape 1 : Créer la catégorie MOH

POST /api/confd/1.1/moh
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "musique-corporate",
  "mode": "files",
  "random": true
}

Réponse :

{
  "uuid": "moh-uuid-001",
  "name": "musique-corporate",
  "mode": "files"
}

:link: Chaînage : Stockez MOH_UUID

Étape 2 : Uploader le fichier audio

PUT /api/confd/1.1/moh/{MOH_UUID}/files/accueil.wav
Content-Type: audio/wav
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

[binary audio data]

Étape 3 : Vérifier la MOH

GET /api/confd/1.1/moh/{MOH_UUID}
X-Auth-Token: {admin_token}

Étape 4 : Appliquer à une queue

PUT /api/confd/1.1/queues/{QUEUE_ID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "music_on_hold": "moh-uuid-001"
}

Point d’attention / Warning

:warning: Important :

  • Les formats supportés : WAV, MP3, OGG
  • La fréquence d’échantillonnage recommandée : 8kHz ou 16kHz
  • La MOH est diffusée quand l’appelant est en attente

2.10 Récapitulatif des Endpoints de Provisioning

Ressource CRUD Endpoint
Device C POST /devmgr/devices
Device R GET /devmgr/devices/{id}
Device D DELETE /devmgr/devices/{id}
Sync C POST /devmgr/synchronize
Plugin C POST /pgmgr/install/install
Transport SIP C POST /sip/transports
Endpoint SIP C POST /endpoints/sip
Endpoint IAX C POST /endpoints/iax
Trunk C POST /trunks
Outcall C POST /outcalls
Incall C POST /incalls
Schedule C POST /schedules
MOH C POST /moh
Template SIP C POST /endpoints/sip/templates

Fin de la PARTIE 2


PARTIE 3 : Services Avancés & Call Center

Cette partie couvre les configurations avancées du centre d’appels (ACD), les services de routage intelligent, et les fonctionnalités avancées comme les IVR, les conférences et le filtrage d’appels.


3.1 File d’Attente ACD Complète (9 Étapes)

Objectif

Créer une file d’attente complète pour un centre d’appels avec agents, skills, stratégies de distribution et plannings horaires.

Services impliqués

  • wazo-confd : Gestion des queues, agents, skills, schedules, incalls

Le Workflow détaillé

Étape 1 : Créer la queue

POST /api/confd/1.1/queues
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "support-technique",
  "display_name": "Support Technique",
  "strategy": "rrmemory",
  "timeout": 30,
  "retry": 5,
  "maxlen": 10,
  "announce_frequency": 30,
  "music_on_hold": "moh-uuid-001"
}

Réponse :

{
  "id": 15,
  "name": "support-technique",
  "strategy": "rrmemory",
  ...
}

:link: Chaînage : Stockez QUEUE_ID = 15

Étape 2 : Créer l’extension de la queue

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "3000",
  "context": "default"
}

:link: Chaînage : Stockez QUEUE_EXT_ID

Étape 3 : Lier l’extension à la queue

PUT /api/confd/1.1/queues/{QUEUE_ID}/extensions/{QUEUE_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 4 : Créer un agent

POST /api/confd/1.1/agents
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "number": "5001",
  "firstname": "Alice",
  "lastname": "Agent",
  "password": "1234"
}

Réponse :

{
  "id": 8,
  "number": "5001",
  ...
}

:link: Chaînage : Stockez AGENT_ID = 8

Étape 5 : Créer un skill

POST /api/confd/1.1/agents/skills
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "technique",
  "description": "Compétences techniques"
}

Réponse :

{
  "id": 3,
  "name": "technique",
  ...
}

:link: Chaînage : Stockez SKILL_ID = 3

Étape 6 : Associer le skill à l’agent

PUT /api/confd/1.1/agents/{AGENT_ID}/skills/{SKILL_ID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "skill_weight": 10
}

Étape 7 : Ajouter l’agent à la queue

PUT /api/confd/1.1/queues/{QUEUE_ID}/members/agents/{AGENT_ID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "penalty": 0,
  "priority": 1
}

Étape 8 : Créer le schedule (optionnel)

POST /api/confd/1.1/schedules
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "horaires-support",
  "timezone": "Europe/Paris",
  "open_periods": [
    {
      "start": "09:00",
      "end": "18:00",
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
    }
  ]
}

:link: Chaînage : Stockez SCHEDULE_ID

Étape 9 : Créer l’incall pour la queue

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "queue",
    "queue_id": 15
  },
  "extensions": [{"id": QUEUE_EXT_ID}],
  "schedule_id": SCHEDULE_ID
}

Point d’attention / Warning

:warning: Important :

  • La stratégie linear ne peut pas être activée via API si elle n’était pas configurée initialement
  • Les skills permettent un routage intelligent basé sur les compétences
  • Le penalty de l’agent détermine sa priorité dans la queue

3.2 Skill Rules — Routage ACD par Compétence (6 Étapes)

Objectif

Créer des règles de routage basées sur les skills des agents pour направлять intelligemment les appels vers les agents appropriés.

Services impliqués

  • wazo-confd : Gestion des skill rules

Le Workflow détaillé

Étape 1 : Créer un skill

POST /api/confd/1.1/agents/skills
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "francais",
  "description": "French language skill"
}

:link: Chaînage : Stockez SKILL_FR_ID

Étape 2 : Associer le skill à l’agent avec weight

PUT /api/confd/1.1/agents/{AGENT_ID}/skills/{SKILL_FR_ID}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "skill_weight": 100
}

Étape 3 : Créer une skill rule

POST /api/confd/1.1/queues/skillrules
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "regle-francais",
  "rules_definition": "FR > 0"
}

Réponse :

{
  "id": 7,
  "name": "regle-francais",
  "rules_definition": "FR > 0"
}

:link: Chaînage : Stockez SKILL_RULE_ID = 7

Étape 4 : Créer l’incall avec skill rule

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "queue",
    "queue_id": QUEUE_ID,
    "skill_rule_id": 7
  },
  "extensions": [{"id": EXT_ID}]
}

:link: Chaînage : Lesskill_rule_variables peuvent être ajoutées pour passer des variables

Point d’attention / Warning

:warning: Important :

  • La syntaxe des règles utilise les opérateurs : >, <, =, & (AND), | (OR)
  • Les noms de skills sont en MAJUSCULES dans les règles
  • Example : FR > 0 & TECH > 50

3.3 Groupe d’Appel — Ring Group (6 Étapes)

Objectif

Créer un groupe d’appel qui sonne sur plusieurs postes simultanément, idéal pour les équipes qui doivent répondre aux appels entrants.

Services impliqués

  • wazo-confd : Gestion des groups, extensions

Le Workflow détaillé

Étape 1 : Créer le ring group

POST /api/confd/1.1/groups
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "equipe-commerciale",
  "label": "Équipe Commerciale",
  "strategy": "all"
}

Réponse :

{
  "id": 12,
  "name": "equipe-commerciale",
  "strategy": "all"
}

:link: Chaînage : Stockez GROUP_ID = 12

Étape 2 : Créer l’extension

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "2000",
  "context": "default"
}

:link: Chaînage : Stockez GRP_EXT_ID

Étape 3 : Lier l’extension au groupe

PUT /api/confd/1.1/groups/{GROUP_ID}/extensions/{GRP_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 4 : Ajouter les membres

PUT /api/confd/1.1/groups/{GROUP_ID}/members/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [
    {"uuid": "user-uuid-1", "priority": 1},
    {"uuid": "user-uuid-2", "priority": 2}
  ]
}

Étape 5 : Configurer les fallbacks

PUT /api/confd/1.1/groups/{GROUP_ID}/fallbacks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "noanswer_destination": {
    "type": "voicemail",
    "voicemail_id": 5
  }
}

Point d’attention / Warning

:warning: Important :

  • Stratégies : all (tous), ring (cyclique), random
  • Les utilisateurs doivent avoir une ligne configurée pour recevoir les appels

3.4 Call Filter — Boss/Secrétaire (6 Étapes)

Objectif

Configurer le filtre boss-secrétaire permettant aux secrétaires de gérer les appels du patron, avec interception et renvoi automatique.

Services impliqués

  • wazo-confd : Gestion des callfilters

Le Workflow détaillé

Étape 1 : Créer le call filter

POST /api/confd/1.1/callfilters
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "filter-boss-secretary",
  "strategy": "all-recipients-then-all-surrogates"
}

Réponse :

{
  "id": 5,
  "name": "filter-boss-secretary",
  "strategy": "all-recipients-then-all-surrogates"
}

:link: Chaînage : Stockez CALL_FILTER_ID = 5

Étape 2 : Ajouter le boss (recipient)

PUT /api/confd/1.1/callfilters/{CALL_FILTER_ID}/recipients/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "boss-uuid"}]
}

Étape 3 : Ajouter le secrétaire (surrogate)

PUT /api/confd/1.1/callfilters/{CALL_FILTER_ID}/surrogates/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "secretary-uuid"}]
}

Étape 4 : Configurer les fallbacks

PUT /api/confd/1.1/callfilters/{CALL_FILTER_ID}/fallbacks
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "noanswer_destination": {
    "type": "voicemail",
    "voicemail_id": 10
  }
}

Étape 5 : Activer le filtre sur le boss

PUT /api/confd/1.1/users/{BOSS_UUID}/services/incallfilter/enable
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • surrogates_timeout est distinct du timeout par recipient
  • Le filtre doit être activé sur l’utilisateur boss

3.5 Call Pickup — Interception d’Appel (5 Étapes)

Objectif

Configurer le pickup de groupe permettant à un utilisateur d’intercepter un appel qui sonne sur un collègue.

Services impliqués

  • wazo-confd : Gestion des callpickups

Le Workflow détaillé

Étape 1 : Créer le call pickup

POST /api/confd/1.1/callpickups
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "interception-groupe",
  "enabled": true
}

Réponse :

{
  "id": 3,
  "name": "interception-groupe",
  "enabled": true
}

:link: Chaînage : Stockez PICKUP_ID = 3

Étape 2 : Ajouter les cibles (ceux qu’on peut intercepter)

PUT /api/confd/1.1/callpickups/{PICKUP_ID}/targets/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "user-uuid-1"}, {"uuid": "user-uuid-2"}]
}

Étape 3 : Ajouter les intercepteurs

PUT /api/confd/1.1/callpickups/{PICKUP_ID}/interceptors/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "user-uuid-3"}, {"uuid": "user-uuid-4"}]
}

Point d’attention / Warning

:warning: Important :

  • L’extension par défaut pour le pickup est *8
  • Le pickup peut aussi être configuré par groupes

3.6 Parking Lot — Parquage d’Appel (5 Étapes)

Objectif

Configurer un parking lot pour parquer un appel et le récupérer depuis un autre poste.

Services impliqués

  • wazo-confd : Gestion des parkinglots

Le Workflow détaillé

Étape 1 : Créer le parking lot

POST /api/confd/1.1/parkinglots
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "parking-principal",
  "slots_start": 701,
  "slots_end": 720,
  "timeout": 120
}

Réponse :

{
  "id": 2,
  "name": "parking-principal",
  "slots_start": 701,
  "slots_end": 720
}

:link: Chaînage : Stockez PARKING_ID

Étape 2 : Créer l’extension de parking

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "700",
  "context": "default"
}

:link: Chaînage : Stockez PARKING_EXT_ID

Étape 3 : Lier l’extension au parking

PUT /api/confd/1.1/parkinglots/{PARKING_ID}/extensions/{PARKING_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • L’appel parqué expire après le timeout
  • Les slots définissent les extensions où les appels sont parqués

3.7 IVR Complet — Menu Vocal Interactif (7 Étapes)

Objectif

Créer un SVI (Serveur Vocal Interactif) complet avec messages d’accueil, choix de menu, et destinations variables.

Services impliqués

  • wazo-confd : Gestion des sounds, ivr

Le Workflow détaillé

Étape 1 : Uploader le son d’accueil

POST /api/confd/1.1/sounds
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "ivr-accueil"
}

:link: Chaînage : Stockez SOUND_NAME = “ivr-accueil”

Étape 2 : Uploader le fichier audio

PUT /api/confd/1.1/sounds/{SOUND_NAME}/files/accueil.wav
Content-Type: audio/wav
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

[binary audio data]

Étape 3 : Créer l’IVR

POST /api/confd/1.1/ivr
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "menu-principal",
  "greeting_sound": "ivr-accueil/accueil.wav",
  "menu_sound": "ivr-accueil/menu.wav",
  "choices": {
    "1": {
      "destination": {
        "type": "queue",
        "queue_id": 15
      }
    },
    "2": {
      "destination": {
        "type": "extension",
        "extension": "1000",
        "context": "default"
      }
    },
    "3": {
      "destination": {
        "type": "voicemail",
        "voicemail_id": 5
      }
    }
  },
  "timeout": 5,
  "max_attempts": 3
}

Réponse :

{
  "id": 8,
  "name": "menu-principal",
  ...
}

:link: Chaînage : Stockez IVR_ID = 8

Étape 4 : Créer l’extension IVR

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "9000",
  "context": "default"
}

:link: Chaînage : Stockez IVR_EXT_ID

Étape 5 : Créer l’incall

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "ivr",
    "ivr_id": 8
  },
  "extensions": [{"id": IVR_EXT_ID}]
}

Point d’attention / Warning

:warning: Important :

  • Les chemins de sons sont relatifs au répertoire /var/lib/wazo/sounds/playback/
  • Uploadez les sons AVANT de créer l’IVR

3.8 Salle de Conférence avec DID (7 Étapes)

Objectif

Créer une salle de conférence permanente accessible par DID externe avec code PIN.

Services impliqués

  • wazo-confd : Gestion des conferences, extensions, incalls
  • wazo-calld : Contrôle de la conférence

Le Workflow détaillé

Étape 1 : Créer la conférence

POST /api/confd/1.1/conferences
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "conference-direct",
  "pin": "1234",
  "admin_pin": "9999",
  "max_users": 50,
  "record": true
}

Réponse :

{
  "id": 6,
  "name": "conference-direct",
  "pin": "1234",
  ...
}

:link: Chaînage : Stockez CONF_ID = 6

Étape 2 : Créer l’extension interne

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "4001",
  "context": "default"
}

:link: Chaînage : Stockez CONF_EXT_ID

Étape 3 : Lier l’extension à la conférence

PUT /api/confd/1.1/conferences/{CONF_ID}/extensions/{CONF_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 4 : Créer l’incall pour le DID

POST /api/confd/1.1/incalls
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": {
    "type": "conference",
    "conference_id": 6
  }
}

:link: Chaînage : Stockez INCALL_CONF_ID

Étape 5 : Ajouter l’extension DID entrante

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "0033380002000",
  "context": "from-extern"
}

:link: Chaînage : Stockez EXT_DID_CONF

Étape 6 : Lier le DID à l’incall

PUT /api/confd/1.1/incalls/{INCALL_CONF_ID}/extensions/{EXT_DID_CONF}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • Le pin est pour les participants, admin_pin pour le contrôle
  • L’enregistrement nécessite au moins 1 participant actif

3.9 Call Permissions — Contrôle des Appels Sortants (5 Étapes)

Objectif

Créer des règles de permissions d’appels pour contrôler quels numéros peuvent être composés (interne, local, national, international).

Services impliqués

  • wazo-confd : Gestion des callpermissions

Le Workflow détaillé

Étape 1 : Créer une permission de deny

POST /api/confd/1.1/callpermissions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "interdit-international",
  "mode": "deny",
  "extensions": ["0033.", "0044."]
}

Réponse :

{
  "id": 10,
  "name": "interdit-international",
  "mode": "deny"
}

:link: Chaînage : Stockez PERM_DENY_ID

Étape 2 : Créer une permission avec mot de passe

POST /api/confd/1.1/callpermissions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "autorise-international",
  "mode": "allow",
  "extensions": ["0033."],
  "password": "1234"
}

:link: Chaînage : Stockez PERM_ALLOW_ID

Étape 3 : Appliquer à un outcall

PUT /api/confd/1.1/outcalls/{OUTCALL_ID}/callpermissions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "call_permissions_id": 10
}

Point d’attention / Warning

:warning: Important :

  • mode: deny avec extensions = bloquer ces numéros
  • mode: allow avec extensions = autoriser ces numéros

3.10 Paging / Intercom (4 Étapes)

Objectif

Configurer le paging (intercom) pour permettre l’envoi de messages广播 à un groupe de postes.

Services impliqués

  • wazo-confd : Gestion des pagings

Le Workflow détaillé

Étape 1 : Créer le paging

POST /api/confd/1.1/pagings
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "bureau-ouvert",
  "duplex": false,
  "announce_caller": true
}

Réponse :

{
  "id": 4,
  "name": "bureau-ouvert",
  "duplex": false
}

:link: Chaînage : Stockez PAGING_ID

Étape 2 : Ajouter les appelants

PUT /api/confd/1.1/pagings/{PAGING_ID}/callers/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "manager-uuid"}]
}

Étape 3 : Ajouter les membres

PUT /api/confd/1.1/pagings/{PAGING_ID}/members/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "user-uuid-1"}, {"uuid": "user-uuid-2"}]
}

Étape 4 : Créer l’extension de paging

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "8000",
  "context": "default"
}

:link: Chaînage : Stockez PAGING_EXT_ID

Étape 5 : Lier l’extension

PUT /api/confd/1.1/pagings/{PAGING_ID}/extensions/{PAGING_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Point d’attention / Warning

:warning: Important :

  • duplex: false = simplex (broadcast unidirectionnel)
  • duplex: true = bidirectionnel (intercom)

3.11 Switchboard — Standard Téléphonique (6 Étapes)

Objectif

Configurer un standard automatique pour permettre à un opératrice de gérer les appels entrants avec answer, hold, transfer.

Services impliqués

  • wazo-confd : Gestion des switchboards
  • wazo-calld : Contrôle des appels

Le Workflow détaillé

Étape 1 : Créer le switchboard

POST /api/confd/1.1/switchboards
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "standard-principal",
  "timeout": 30
}

:link: Chaînage : Stockez SWITCHBOARD_ID

Étape 2 : Ajouter les membres

PUT /api/confd/1.1/switchboards/{SWITCHBOARD_ID}/members/users
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "users": [{"uuid": "op-uuid"}]
}

Étape 3 : Créer l’extension

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

:link: Chaînage : Stockez SW_EXT_ID

Étape 4 : Lier l’extension

PUT /api/confd/1.1/switchboards/{SWITCHBOARD_ID}/extensions/{SW_EXT_ID}
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Étape 5 : Récupérer les appels en attente

GET /api/calld/1.0/switchboards/{SWITCHBOARD_ID}/calls/queued
X-Auth-Token: {admin_token}

Étope 6 : Answer un appel

PUT /api/calld/1.0/switchboards/{SWITCHBOARD_ID}/calls/queued/{CALL_ID}/answer
X-Auth-Token: {admin_token}

Point d’attention / Warning

:warning: Important :

  • Le switchboard nécessite une licence ou configuration spécifique
  • Les actions : answer, hold, retrieve, redirect-queue

3.12 Récapitulatif des Endpoints Services Avancés

Ressource CRUD Endpoint
Queue C POST /queues
Agent C POST /agents
Skill C POST /agents/skills
Skill Rule C POST /queues/skillrules
Group C POST /groups
Call Filter C POST /callfilters
Call Pickup C POST /callpickups
Parking C POST /parkinglots
IVR C POST /ivr
Conference C POST /conferences
Call Permission C POST /callpermissions
Paging C POST /pagings
Switchboard C POST /switchboards

Fin de la PARTIE 3


PARTIE 4 : Temps Réel, WebRTC & Conférence

Cette partie couvre les workflows de communication en temps réel, les conférences audio/vidéo, les appels WebRTC, et les fonctionnalités de présence et de messagerie instantanée. Ces scénarios permettent d’exploiter les capacités avancées de l’écosystème Wazo pour la collaboration moderne.


4.1 Transfert d’Appel (Attended Transfer)

Objectif

Effectuer un transfert attended (avec annonce) où l’initiateur met l’appel original en attente, appelle le destinataire du transfert, et connecte les deux parties. Ce scénario est essentiel pour les standardistes et les assistantes qui doivent presenter un appel avant de le transferer.

Services impliqués

  • wazo-calld : Gestion des appels actifs et des transfert
  • wazo-confd : Lecture des utilisateurs et lignes
  • wazo-websocketd : Notifications temps réel

Le Workflow détaillé

Étape 1 : Initier le premier appel (Appelant → Intermédiaire)

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {intermediaire_token}

Payload :

{
  "extension": "1001",
  "line_id": 5,
  "context": "default"
}

Reponse :

{
  "call_id": "call-abc123-def456",
  "status": "ringing",
  "peer_caller_id_number": "1002"
}

:link: Chaînage : Stockez le call_id de l’appel en cours — il sera utilisé pour le transfert. Stockez dans CALL_ID_ORIGINAL.

Étape 2 : Mettre l’appel en attente

PUT /api/calld/1.0/calls/{call_id_original}/hold
X-Auth-Token: {intermediaire_token}

Reponse :

{
  "call_id": "call-abc123-def456",
  "status": "hold"
}

Étape 3 : Appeler le destinataire du transfert

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {intermediaire_token}

Payload :

{
  "extension": "1003",
  "line_id": 5,
  "context": "default"
}

Reponse :

{
  "call_id": "call-xyz789-uvw012",
  "status": "ringing",
  "peer_caller_id_number": "1003"
}

:link: Chaînage : Stockez ce nouveau call_id — il devient CALL_ID_TRANSFERT.

Étape 4 : Effectuer le transfert attended

PUT /api/calld/1.0/calls/{call_id_original}/transfer
Content-Type: application/json
X-Auth-Token: {intermediaire_token}

Payload :

{
  "transferee_call_id": "call-xyz789-uvw012",
  "extension": "1003",
  "context": "default"
}

Reponse :

{
  "call_id": "call-abc123-def456",
  "status": "bridged",
  "transferee_call_id": "call-xyz789-uvw012"
}

Avertissements

  • Ordre critique : Le transfert attended necessite que l’appel original soit mis en attente AVANT d’appeler le destinataire
  • Timeouts : Si le destinataire ne répond pas dans 30 secondes, l’appel est renvoyé vers l’intermédiaire
  • Droit de transfert : L’utilisateur doit avoir le droit transfer dans sa configuration de ligne

4.2 Transfert d’Appel (Blind Transfer)

Objectif

Effectuer un transfert direct sans annonce, où l’appel est immediatement envoye vers le destinataire. Ce scenario est plus rapide que le transfert attended mais ne permet pas de verifier si le destinataire est disponible.

Services impliqués

  • wazo-calld : Gestion des appels actifs et des transferts

Le Workflow détaillé

Étape 1 : Initier l’appel original

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "extension": "1001",
  "line_id": 5,
  "context": "default"
}

Reponse :

{
  "call_id": "call-blind001",
  "status": "ringing"
}

:link: Chaînage : Stockez CALL_ID.

Étape 2 : Effectuer le transfert direct (blind)

PUT /api/calld/1.0/calls/{call_id}/transfer
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "extension": "1003",
  "context": "default"
}

Reponse :

{
  "call_id": "call-blind001",
  "status": "transferring"
}

Note : Pas de transferee_call_id — c’est un transfert blind

Avertissements

  • Irréversible : Une fois le transfert initiates, il ne peut pas être annule
  • Statut de l’appel : L’appel original disparait et un nouvel appel est cree vers le destinataire

4.3 Conference Ad-hoc (Appel Conference Instantane)

Objectif

Creer une conference ad-hoc instantanee en appelant plusieurs participants depuis un point d’entree unique. Ce scenario permet d’organiser rapidement une reunion telephonique sans configuration prealable de salle de conference.

Services impliqués

  • wazo-calld : Creation des conferences ad-hoc et gestion des appels
  • wazo-confd : Lecture des utilisateurs et extensions

Le Workflow détaillé

Étape 1 : Appeler le premier participant

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "extension": "1001",
  "line_id": 5,
  "context": "default"
}

Reponse :

{
  "call_id": "call-conf-001",
  "status": "ringing"
}

:link: Chaînage : Stockez CALL_ID_1.

Étape 2 : Ajouter le deuxieme participant

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "extension": "1002",
  "line_id": 5,
  "context": "default"
}

Reponse :

{
  "call_id": "call-conf-002",
  "status": "ringing"
}

:link: Chaînage : Stockez CALL_ID_2.

Étape 3 : Creer la conference et y ajouter les appelants

POST /api/calld/1.0/conferences
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "name": "conference_adhoc_001",
  "extension": "3000",
  "context": "default"
}

Reponse :

{
  "id": 15,
  "name": "conference_adhoc_001",
  "extension": "3000",
  "context": "default"
}

:link: Chaphinage : Stockez l’ID de conference CONF_ID.

Étape 4 : Transferer les appels vers la conference

PUT /api/calld/1.0/calls/{call_id_1}/transfer
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "extension": "3000",
  "context": "default"
}

Repetez pour CALL_ID_2.

Avertissements

  • Limite de participants : La configuration de la conference dans confd definit le nombre maximum de participants
  • Audio only : Les conferences ad-hoc sont en audio uniquement (pas de video)
  • Mute des participants : L’initiateur peut mettre en mute les participants via calld

4.4 Configuration Salle de Conference Standard

Objectif

Creer et configurer une salle de conference permanente avec des options avancees : PIN de protection, musique d’attente, annonce des participants, enregistrement. Ce scenario est utilise pour les reunions regulieres avec des acces securises.

Services impliqués

  • wazo-confd : Creation et configuration des salles de conference

Le Workflow détaillé

Étape 1 : Creer la conference

POST /api/confd/1.1/conferences
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "Conseil d'Administration",
  "extension": "3100",
  "context": "default",
  "pin": "8426",
  "admin_pin": "1234",
  "max_members": 20,
  "music_on_hold_when_empty": true,
  "announce_join_leave": true,
  "announce_only_user": false,
  "require_moderator": false,
  "record": true
}

Reponse :

{
  "id": 42,
  "uuid": "conf-550e8400-e29b-41d4-a716-446655440000",
  "name": "Conseil d'Administration",
  "extension": "3100",
  "context": "default",
  "pin": "8426",
  "max_members": 20,
  ...
}

:link: Chaînage : Stockez CONF_UUID et CONF_EXTENSION.

Étape 2 : Associer un schedule (optionnel)

PUT /api/confd/1.1/conferences/{conf_uuid}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "schedule_id": 7
}

Step 3: Configure Recording Storage

POST /api/confd/1.1/conferences/{conf_uuid}/recordings
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "destination": "s3",
  "bucket": "wazo-recordings",
  "path": "conferences/{year}/{month}/{day}/{name}"
}

Avertissements

  • PIN security : Le PIN doit contenir au moins 4 chiffres ; le PIN admin permet de moderer
  • Enregistrement : L’enregistrement necessite de l’espace de stockage configure
  • Concurrent conferences : Verifiez les limites de licences pour les conferences simultanees

4.5 Réunion WebRTC avec Autorisation

Objectif

Creer une reunion video via WebRTC avec controle d’acces par identifiant de reunion et mot de passe. Ce scenario est utilise pour les reunions virtuelles avec participants externes ou internes, offrant une experience navigateurs sans installation de client.

Services impliqués

  • wazo-calld : Gestion des appels et reunions WebRTC
  • wazo-confd : Lecture des salles de conference
  • wazo-websocketd : Notifications temps reel

Le Workflow détaillé

Étape 1 : Verifier l’acces a la reunion

GET /api/calld/1.0/conferences/{conference_id}/join
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "meeting_id": "reunion-2024-001",
  "password": "secret123"
}

Reponse :

{
  "allowed": true,
  "conference_id": 42,
  "bridge_id": "bridge-webrtc-001"
}

Étape 2 : Creer le token de connexion WebRTC

POST /api/calld/1.0/webrtc/token
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "conference_id": 42,
  "display_name": "Jean Dupont",
  "email": "jean.dupont@acme.fr"
}

Reponse :

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "websocket_url": "wss://wazo.example.com:443/ws",
  "conference_bridge": "bridge-webrtc-001"
}

Étape 3 : Connecter le client WebRTC

Utilisez le token pour initialiser une connexion WebRTC depuis le navigateur :

// Exemple avec JavaScript
const ws = new WebSocket('wss://wazo.example.com/ws?token=' + token);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  // Gerer les evenements : participant_joined, participant_left, etc.
};

Étape 4 : Mettre a jour les permissions en cours de reunion

PUT /api/calld/1.0/conferences/{conference_id}/participants/{participant_id}
Content-Type: application/json
X-Auth-Token: {moderator_token}

Payload :

{
  "muted": false,
  "video": true,
  "floor": true
}

Avertissements

  • Token expiration : Les tokens WebRTC expirent apres 4 heures par defaut
  • Bandwidth : Les reunions video consomment beaucoup de bande passante ; predeployer les parametres RTP
  • Browser compatibility : Verifier la compatibilite des navigateurs avec les codecs utilises

4.6 Paging (Diffusion Audio Unidirectionnelle)

Objectif

Effectuer une diffusion audio unidirectionnelle vers plusieurs terminaux sans que les destinataires ne puissent repondre. Ce scenario est utilise pour les annonces generales dans les entrepoints, bureaux ou zones comunes.

Services impliqués

  • wazo-confd : Configuration des groupes de paging
  • wazo-calld : Activation du paging

Le Workflow détaillé

Étape 1 : Creer le groupe de paging

POST /api/confd/1.1/paging
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "Entrepot Zone A",
  "extension": "9001",
  "context": "default",
  "timeout": 30,
  "record": false,
  "announce": true,
  "announce_sound": "beep"
}

Reponse :

{
  "id": 8,
  "uuid": "paging-550e8400-e29b-41d4-a716-446655440000",
  "name": "Entrepot Zone A",
  "extension": "9001",
  ...
}

:link: Chaînage : Stockez PAGING_UUID.

Étape 2 : Ajouter des membres au groupe de paging

PUT /api/confd/1.1/paging/{paging_uuid}/members
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "user_uuids": [
    "user-uuid-1",
    "user-uuid-2",
    "user-uuid-3"
  ]
}

Étape 3 : Initier le paging

POST /api/calld/1.0/paging/{paging_extension}
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "context": "default"
}

Reponse :

{
  "call_id": "call-paging-001",
  "status": "paging",
  "paging_extension": "9001"
}

Avertissements

  • Sens unique : Les participants ne peuvent pas repondre au paging
  • Interruption : L’initiateur peut arreter le paging a tout moment
  • Licences : Verifier les limites de canaux simultanes pour le paging

4.7 Intercom (Appel Main libre)

Objectif

Activer le mode main libre sur un terminal pour permettre des appels immediats sans decrocher. Ce scenario est utilise pour les secretariats, zones de reception ou situations ou les mains doivent rester libres.

Services implique

  • wazo-confd : Configuration des funckeys et extensions
  • wazo-calld : Gestion des appels

Le Workflow detaille

Etape 1 : Configurer la fonction intercom sur le terminal

POST /api/confd/1.1/funckeys
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "user_uuid": "user-uuid-cible",
  "keynum": 1,
  "label": "Intercom Bureau",
  "function": "intercom",
  "extension": "1005",
  "context": "default"
}

Etape 2 : Activer le terminal en mode intercom

PUT /api/confd/1.1/lines/{line_id}/extensions/{ext_id}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "intercom_enabled": true
}

Etape 3 : Declencher l’appel intercom

Appuyez sur la touche de fonction configuree ou appelez :

POST /api/calld/1.0/calls/intercom
Content-Type: application/json
X-Auth-Token: {initiator_token}

Payload :

{
  "extension": "1005",
  "context": "default"
}

Reponse :

{
  "call_id": "call-intercom-001",
  "status": "Talking",
  "auto_answer": true
}

Avertissements

  • Auto-reponse : Le terminal cible repond automatiquement (compatibilite SIP required)
  • Contexte : L’extension intercom doit etre dans le meme contexte que l’appelant
  • Securite : Limiter l’acces a cette fonction pour eviter les abus

4.8 Messagerie Instantanée (Chat)

Objectif

Envoyer et recevoir des messages instantanes entre utilisateurs Wazo via le service de chat integre. Ce scenario permet la communication textuelle synchrone et asynchrone directement depuis les clients Wazo.

Services implique

  • wazo-chatd : Gestion des messages et conversations
  • wazo-presenced : Gestion des presences et statuts

Le Workflow detaille

Etape 1 : Recuperer la liste des conversations

GET /api/chatd/1.0/users/{user_uuid}/conversations
X-Auth-Token: {user_token}

Reponse :

{
  "items": [
    {
      "uuid": "conv-001",
      "name": "Discussion avec Marie",
      "participants": ["user-uuid-1", "user-uuid-2"],
      "last_message": "Bonjour !",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ],
  "total": 1
}

:link: Chaînage : Stockez CONV_UUID.

Etape 2 : Envoyer un message

POST /api/chatd/1.0/conversations/{conv_uuid}/messages
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "content": "Bonjour Marie, avez-vous recu le rapport ?",
  "content_type": "text/plain"
}

Reponse :

{
  "uuid": "msg-001",
  "conversation_uuid": "conv-001",
  "sender_uuid": "user-uuid-1",
  "content": "Bonjour Marie, avez-vous recu le rapport ?",
  "created_at": "2024-01-15T11:00:00Z"
}

Etape 3 : Recevoir les messages (WebSocket)

wss://wazo.example.com/api/chatd/1.0/ws?token={user_token}

Message recu :

{
  "event": "message_created",
  "data": {
    "uuid": "msg-002",
    "conversation_uuid": "conv-001",
    "sender_uuid": "user-uuid-2",
    "content": "Oui, je l'ai recu. Merci !",
    "created_at": "2024-01-15T11:05:00Z"
  }
}

Etape 4 : Marquer comme lu

PUT /api/chatd/1.0/conversations/{conv_uuid}/read
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "message_uuid": "msg-001"
}

Avertissements

  • Chiffrement : Les messages ne sont pas chiffres par defaut en stockage
  • Retention : Configurer la politique de rétention des messages
  • Tailles des fichiers : Les pieces jointes ont des limites de taille

4.9 Gestion des Presences et Statuts

Objectif

Gerer et consultes les statuts de presence des utilisateurs en temps reel. Ce scenario est utilise pour les dashboards d’equipe, les statistiques de disponibilite et l’integration avec les systemes de supervision.

Services implique

  • wazo-presenced : Gestion des presences
  • wazo-chatd : Consultation des statuts

Le Workflow detaille

Etape 1 : Consulter le statut d’un utilisateur

GET /api/presenced/1.0/users/{user_uuid}/presence
X-Auth-Token: {admin_token}

Reponse :

{
  "user_uuid": "user-uuid-cible",
  "presence": "available",
  "status": "En reunion",
  "last_update": "2024-01-15T10:00:00Z",
  "endpoint": "SIP/1001"
}

Etape 2 : Mettre a jour son propre statut

PUT /api/presenced/1.0/users/{user_uuid}/presence
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "presence": "busy",
  "status": "En appel avec client"
}

Reponse :

{
  "user_uuid": "user-uuid-1",
  "presence": "busy",
  "status": "En appel avec client",
  "last_update": "2024-01-15T11:30:00Z"
}

Etape 3 : Consulter les presences de tous les utilisateurs

GET /api/presenced/1.0/presences
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Reponse :

{
  "items": [
    {
      "user_uuid": "user-uuid-1",
      "presence": "available",
      "status": "Disponible"
    },
    {
      "user_uuid": "user-uuid-2",
      "presence": "busy",
      "status": "En reunion"
    },
    {
      "user_uuid": "user-uuid-3",
      "presence": "away",
      "status": "Absent"
    }
  ]
}

Etape 4 : Recevoir les notifications de presence (WebSocket)

wss://wazo.example.com/api/presenced/1.0/ws?token={user_token}

Notification recue :

{
  "event": "user_presentity_changed",
  "data": {
    "user_uuid": "user-uuid-2",
    "presence": "available",
    "status": "De retour",
    "last_update": "2024-01-15T12:00:00Z"
  }
}

Avertissements

  • Mise a jour automatique : La presence est automatiquement mise a jour lors des appels
  • Duree de validite : Un statut “away” est applique apres un delai d’inactivite configure
  • Statuts personnalises : Les utilisateurs peuvent definir leurs propres messages de statut

4.10 Statut “Ne Pas Deranger” (DND) API

Objectif

Activer ou desactiver a distance le statut “Ne Pas Deranger” (Do Not Disturb) pour un utilisateur. Ce scenario permet aux administrateurs ou aux systemes automatises de gerer la disponibilite des utilisateurs.

Services implique

  • wazo-confd : Configuration du DND
  • wazo-calld : Application en temps reel

Le Workflow detaille

Etape 1 : Activer le DND pour un utilisateur

PUT /api/confd/1.1/users/{user_uuid}/services/dnd
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "enabled": true
}

Reponse :

{
  "enabled": true,
  "user_uuid": "user-uuid-cible"
}

Etape 2 : Verifier le statut DND

GET /api/confd/1.1/users/{user_uuid}/services/dnd
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Reponse :

{
  "enabled": true,
  "user_uuid": "user-uuid-cible"
}

Etape 3 : Desactiver le DND

PUT /api/confd/1.1/users/{user_uuid}/services/dnd
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "enabled": false
}

Avertissements

  • Appels d’urgence : Le DND ne bloque pas les appels d’urgence (112, etc.)
  • Appels internes : Le comportement avec les appels internes depend de la configuration
  • Notification : Les appelants peuvent recevoir un message vocal informant du DND

4.11 Journal d’Appels (CDR) en Temps Reel

Objectif

Consulter les appels en cours et les historiques en temps reel pour le monitoring операционной деятельности. Ce scenario est utilise pour les tableaux de bord operateurs et la supervision des communications.

Services implique

  • wazo-calld : Appels actifs
  • wazo-cdr : Historique des appels

Le Workflow detaille

Etape 1 : Lister les appels actifs

GET /api/calld/1.0/calls
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Reponse :

{
  "items": [
    {
      "call_id": "call-abc123",
      "status": "talking",
      "caller_id_num": "1001",
      "caller_id_name": "Jean Dupont",
      "peer_caller_id_num": "1002",
      "peer_caller_id_name": "Marie Martin",
      "direction": "internal",
      "creation_time": "2024-01-15T14:30:00Z",
      "duration": 120
    }
  ]
}

Etape 2 : Obtenir les details d’un appel specifique

GET /api/calld/1.0/calls/{call_id}
X-Auth-Token: {admin_token}

Reponse :

{
  "call_id": "call-abc123",
  "status": "talking",
  "caller_id_num": "1001",
  "caller_id_name": "Jean Dupont",
  "peer_caller_id_num": "1002",
  "peer_caller_id_name": "Marie Martin",
  "conversation": "bridge-uuid-123",
  "channels": [
    "channel-uuid-1",
    "channel-uuid-2"
  ]
}

Etape 3 : Consulter le CDR historique

GET /api/cdr/1.0/cdr
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Parametres de requete :

?start=2024-01-15T00:00:00Z&end=2024-01-15T23:59:59Z&limit=100

Reponse :

{
  "items": [
    {
      "id": 15234,
      "call_id": "call-cdr-001",
      "caller": "1001",
      "caller_name": "Jean Dupont",
      "callee": "1002",
      "callee_name": "Marie Martin",
      "direction": "internal",
      "duration": 180,
      "answered": true,
      "start_time": "2024-01-15T14:30:00Z",
      "answer_time": "2024-01-15T14:30:05Z",
      "end_time": "2024-01-15T14:33:05Z"
    }
  ]
}

Etape 4 : Recevoir les notifications d’appels (WebSocket)

wss://wazo.example.com/api/calld/1.0/ws?token={admin_token}

Nouvel appel :

{
  "event": "call_started",
  "data": {
    "call_id": "call-notif-001",
    "caller_id_num": "1001",
    "peer_caller_id_num": "1002"
  }
}

Appel termine :

{
  "event": "call_ended",
  "data": {
    "call_id": "call-notif-001",
    "duration": 120,
    "reason": "normal_clearing"
  }
}

Avertissements

  • Ressources systeme : Les requetes frequentes sur les CDR peuvent impacter les performances
  • Retention : Les CDR sont conserves selon la politique de rétention configuree
  • Droits d’acces : Seul le superadmin ou les utilisateurs avec les bons ACL peuvent acceder a tous les CDR

4.12 Recording (Enregistrement des Appels)

Objectif

Activer et gerer l’enregistrement des appels pour la formation, la qualite ou la conformite. Ce scenario couvre l’activation, la pause et la recuperation des enregistrements.

Services implique

  • wazo-confd : Configuration de l’enregistrement
  • wazo-calld : Gestion temps reel de l’enregistrement

Le Workflow detaille

Etape 1 : Activer l’enregistrement sur une ligne

PUT /api/confd/1.1/lines/{line_id}
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "record_incoming": true,
  "record_outgoing": true
}

Etape 2 : Demarrer l’enregistrement en cours d’appel

POST /api/calld/1.0/calls/{call_id}/record
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "format": "wav"
}

Reponse :

{
  "call_id": "call-abc123",
  "recording": {
    "id": "rec-001",
    "status": "recording",
    "format": "wav"
  }
}

Etape 3 : Pause / Reprise de l’enregistrement

PUT /api/calld/1.0/calls/{call_id}/record/pause
X-Auth-Token: {admin_token}

Reponse :

{
  "call_id": "call-abc123",
  "recording": {
    "id": "rec-001",
    "status": "paused"
  }
}

Pour reprendre :

PUT /api/calld/1.0/calls/{call_id}/record/resume
X-Auth-Token: {admin_token}

Etape 4 : Recuperer la liste des enregistrements

GET /api/calld/1.0/recordings
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Reponse :

{
  "items": [
    {
      "id": "rec-001",
      "call_id": "call-abc123",
      "duration": 180,
      "format": "wav",
      "created_at": "2024-01-15T14:30:00Z",
      "file_path": "/var/spool/asterisk/monitor/2024/01/15/call-abc123.wav"
    }
  ]
}

Etape 5 : Telecharger un enregistrement

GET /api/calld/1.0/recordings/{recording_id}/file
X-Auth-Token: {admin_token}

Response : Fichier audio (WAV ou OGG selon configuration)

Avertissements

  • Consentement : Informer les participants de l’enregistrement (conformite legale)
  • Stockage : Prevoir suffisamment d’espace de stockage pour les enregistrements
  • Cryptage : Les fichiers peuvent être chiffre’s au repos selon la configuration

4.13 Park Call (Stationnement d’Appel)

Objectel

Stationner un appel dans une zone de parking pour le reprendre depuis un autre poste ou le transferer. Ce scenario est utilise dans les environnements de bureau ouvert ou les centres d’appel.

Services implique

  • wazo-confd : Configuration des zones de parking
  • wazo-calld : Gestion du stationnement

Le Workflow detaille

Etape 1 : Configurer une zone de parking (si non existante)

POST /api/confd/1.1/extensions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "exten": "7000",
  "context": "parking",
  "commented": false,
  "label": "Zone Parking Principal"
}

:link: Chaînage : Extension de parking = 7000.

Etape 2 : Stationner l’appel en cours

Depuis le poste de l’agent (via code de fonction) :

POST /api/calld/1.0/calls/{call_id}/park
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "parking_extension": "7000"
}

Reponse :

{
  "call_id": "call-abc123",
  "status": "parked",
  "parking_extension": "7000",
  "timeout": 60
}

Etape 3 : Recuperer l’appel stationne

POST /api/calld/1.0/calls
Content-Type: application/json
X-Auth-Token: {user_token}

Payload :

{
  "extension": "7000",
  "context": "parking"
}

Avertissements

  • Timeout : Par defaut, l’appel revient vers le stationneur apres 60 secondes
  • Zone de parking : Verifier que la zone est configuree dans le dialplan
  • Limite : Nombre de places limité selon la configuration

4.14 Monitoring en Temps Reel (Spy/Barge)

Objectif

Ecouter en silence un appel en cours (spy) ou y participer (barge). Ce scenario est utilise pour la formation des nouveaux agents ou la supervision qualitative.

Services implique

  • wazo-calld : Gestion de l’interception

Le Workflow detaille

Etape 1 : Lister les appels actifs pour identifier la cible

GET /api/calld/1.0/calls?extension=1001
X-Auth-Token: {supervisor_token}

Reponse :

{
  "items": [
    {
      "call_id": "call-target-001",
      "status": "talking",
      "peer_caller_id_num": "1001"
    }
  ]
}

:link: Chaînage : Stockez CALL_ID.

Etape 2 : Effectuer une ecoute discrete (Spy)

POST /api/calld/1.0/calls/{call_id}/spy
Content-Type: application/json
X-Auth-Token: {supervisor_token}

Payload :

{
  "whisper": false
}

Reponse :

{
  "spy_call_id": "call-spy-001",
  "status": "spy",
  "target_call_id": "call-target-001"
}

Etape 3 : Participer a l’appel (Barge)

POST /api/calld/1.0/calls/{call_id}/spy
Content-Type: application/json
X-Auth-Token: {supervisor_token}

Payload :

{
  "whisper": true
}

Reponse :

{
  "spy_call_id": "call-barge-001",
  "status": "barge",
  "target_call_id": "call-target-001"
}

Avertissements

  • Droits : Seuls les utilisateurs avec le droit spy peuvent ecouter
  • Notification : Dans certains pays, la notification de surveillance est requise
  • Audio quality : La qualite depend de la bande passante disponible

4.15 API Stasis (Asterisk ARI) pour Integration Avancee

Objectif

Utiliser l’API Stasis (Asterisk REST Interface) pour une integration avancee avec des applications tierces. Ce scenario permet de controler les canaux Asterisk directement pour des cas d’usage complexes.

Services implique

  • wazo-calld : Pont vers Stasis
  • Asterisk ARI : API native Asterisk

Le Workflow detaille

Etape 1 : S’authentifier auprès dARI

Authorization: Basic {base64(ari_user:ari_password)}

Etape 2 : Creer un point d’entree Stasis

POST /ari/applications
Content-Type: application/json

Payload :

{
  "name": "my_custom_app",
  "display_name": "Application Personnalisee"
}

Reponse :

{
  "name": "my_custom_app",
  "events": {
    "channel UsserEvent",
    "channel_destroyed",
    "stasis_start"
  }
}

Etape 3 : Declarer un canal vers Stasis

POST /ari/channels
Content-Type: application/json

Payload :

{
  "endpoint": "SIP/1001@default",
  "app": "my_custom_app",
  "appArgs": "dialplan"
}

Reponse :

{
  "id": "channel-uuid-123",
  "name": "SIP/1001-00000001",
  "state": "Ring",
  "dialplan": {
    "context": "default",
    "exten": "1001",
    "priority": 1
  }
}

Etape 4 : Recevoir les evenements Stasis

Connexion WebSocket :

wss://wazo.example.com:8089/ari/events?app=my_custom_app

Evenement recu :

{
  "type": "StasisStart",
  "channel": {
    "id": "channel-uuid-123",
    "name": "SIP/1001-00000001"
  },
  "args": ["dialplan"]
}

Etape 5 : Controler le canal

POST /ari/channels/{channel_id}/play
Content-Type: application/json

Payload :

{
  "media": "sound:welcome"
}

Avertissements

  • Complexite : ARI necessite une bonne connaissance d’Asterisk
  • Performance : Attention aux operations bloqueantes qui peuvent saturer Asterisk
  • Securite : Limiter l’acces a ARI et utiliser l’authentification forte
  • Stability : Les applications mal configurees peuvent destabiliser le PBX

Resume des Services pour PARTIE 4

Scenario Service Principal Operations cles
Transfert Attended calld PUT /calls/{id}/transfer
Transfert Blind calld PUT /calls/{id}/transfer
Conference Ad-hoc calld POST /conferences, transfer
Salle Conference confd POST /conferences
WebRTC calld POST /webrtc/token
Paging confd/calld POST /paging/{ext}
Intercom confd/calld POST /calls/intercom
Chat chatd POST /conversations/{id}/messages
Presence presenced GET/PUT /users/{uuid}/presence
DND confd PUT /users/{uuid}/services/dnd
CDR Temps Reel calld/cdr GET /calls, GET /cdr
Recording calld POST /calls/{id}/record
Park Call calld POST /calls/{id}/park
Spy/Barge calld POST /calls/{id}/spy
Stasis ARI calld/asterisk ARI REST + WebSocket

Cette partie couvre les scenarios de communication en temps reel. Pour les aspects de securite et integrations avancees, consultez la PARTIE 5.

PARTIE 5 : Sécurité, Authentification et Intégrations

Cette partie couvre les workflows d’intégration liés à la sécurité, l’authentification avancée et les intégrations tierces avec Wazo. Ces scénarios sont essentiels pour déployer Wazo en environnement de production avec des exigences de sécurité strictes.


5.1 Rotation Sécurisée des Tokens API

Objectif

Automatiser le renouvellement des tokens d’authentification pour maintenir des sessions longues sans interruption, tout en respectant les bonnes pratiques de sécurité (tokens à durée de vie limitée).

Services impliqués

  • wazo-auth : Gestion centrale de l’authentification et des tokens
  • wazo-confd : API de configuration nécessitant une authentification

Le Workflow détaillé

Étape 1 : Authentification initiale et obtention du token

POST /api/auth/0.1/token
Content-Type: application/json

Payload :

{
  "backend": "wazo_user",
  "expiration": 3600,
  "username": "admin",
  "password": "secure_password"
}

Réponse :

{
  "token": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
  "expires_at": "2024-01-15T15:00:00Z",
  "auth_id": "admin_uuid"
}

:link: Chaînage : Récupérez le champ token — il sera utilisé comme X-Auth-Token dans tous les appels API suivants. Notez également expires_at pour planifier le renouvellement.

Étape 2 : Surveillance de l’expiration et renouvellement proactif

POST /api/auth/0.1/token
Content-Type: application/json

Payload :

{
  "backend": "wazo_user",
  "expiration": 3600,
  "username": "admin",
  "password": "secure_password"
}

:link: Chaînage : Générez un nouveau token avant l’expiration du précédent (idealement 5 minutes avant). Le nouveau token remplace complètement l’ancien.

Step 3 : Invalidation du token (logout)

DELETE /api/auth/0.1/token/{token}

Headers :

X-Auth-Token: {current_token}

Point d’attention / Warning

:warning: Important :

  • Never hardcode credentials in source code. Use environment variables or a secrets manager.
  • Tokens with expiration: 3600 (1 hour) are recommended for long-running scripts; shorter durations (300-600s) for higher security.
  • Always implement token caching to avoid authenticating on every API call.
  • The token refresh should be handled automatically by your client library or implemented with a background thread.

5.2 Intégration LDAP / Active Directory

Objectif

Synchroniser automatiquement les utilisateurs depuis un annuaire LDAP (OpenLDAP ou Active Directory) vers Wazo, permettant une gestion centralisée des identités et une authentification unique.

Services impliqués

  • wazo-auth : Backend d’authentification LDAP
  • wazo-confd : API de gestion des utilisateurs
  • LDAP/AD : Annuaire externe source

Le Workflow détaillé

Étape 1 : Configuration du backend LDAP dans wazo-auth

PUT /api/auth/0.1/backends/ldap/config
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "host": "ldap://ldap.example.com",
  "port": 389,
  "bind_dn": "cn=admin,dc=example,dc=com",
  "bind_password": "ldap_admin_password",
  "user_base_dn": "ou=users,dc=example,dc=com",
  "user_filter": "(objectClass=person)",
  "user_attributes": {
    "uuid": "entryUUID",
    "email": "mail",
    "firstname": "givenName",
    "lastname": "sn",
    "username": "sAMAccountName"
  },
  "group_base_dn": "ou=groups,dc=example,dc=com",
  "group_filter": "(objectClass=groupOfNames)",
  "group_member_attribute": "member"
}

:link: Chaînage : Cette configuration établit la connexion LDAP. Aucun UUID à chaîner ici — c’est une configuration globale du service.

Étape 2 : Activer le backend LDAP

POST /api/auth/0.1/backends/ldap
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "enabled": true
}

Étape 3 : Tester la connexion LDAP

GET /api/auth/0.1/backends/ldap/status
X-Auth-Token: {admin_token}

Réponse :

{
  "status": "ok",
  "ldap_status": "connected",
  "users_found": 150,
  "groups_found": 12
}

Étape 4 : Mapper les groupes LDAP vers les ACLs Wazo

PUT /api/auth/0.1/backends/ldap/groups
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "mappings": [
    {
      "ldap_group": "cn=admins,ou=groups,dc=example,dc=com",
      "wazo_acl": ["confd.users.*", "confd.lines.*", "confd.extensions.*"]
    },
    {
      "ldap_group": "cn=agents,ou=groups,dc=example,dc=com",
      "wazo_acl": ["confd.users.me.read", "calld.calls.read"]
    }
  ]
}

:link: Chaînage : Les ACLs configurées ici déterminent les permissions des utilisateurs LDAP une fois connectés. Chaque utilisateur LDAP hérite des ACLs de son groupe.

Étape 5 : Première authentification LDAP (provisionnement automatique)

POST /api/auth/0.1/token
Content-Type: application/json

Payload :

{
  "backend": "ldap",
  "expiration": 3600,
  "username": "john.doe",
  "password": "user_ldap_password"
}

Réponse :

{
  "token": "new_token_xyz",
  "expires_at": "2024-01-15T16:00:00Z",
  "auth_id": "ldap_user_uuid"
}

:link: Chaînage : Le premier login d’un utilisateur LDAP crée automatiquement un enregistrement utilisateur dans Wazo (provisionnement à la demande). L’UUID retourné (auth_id) correspond à l’utilisateur Wazo créé.

Point d’attention / Warning

:warning: Important :

  • Ensure the LDAP bind account has read-only access to the LDAP directory.
  • User synchronization is on-demand (first login), not automatic/scheduled.
  • Password changes in LDAP are automatically reflected in Wazo on next login.
  • For Active Directory, use port 389 (LDAP) or 636 (LDAPS) with SSL/TLS.

5.3 Authentification SSO SAML 2.0 (Azure AD / Okta)

Objectif

Implémenter une authentification unique (SSO) via SAML 2.0 permettant aux utilisateurs de se connecter à Wazo avec leurs identités Azure AD ou Okta, sans gestion de mots de passe locaux.

Services impliqués

  • wazo-auth : Service SAML IdP
  • wazo-confd : API de configuration
  • Azure AD / Okta : Identity Provider (IdP) externe

Le Workflow détaillé

Étape 1 : Configuration du service SAML dans wazo-auth

PUT /api/auth/0.1/backends/saml/config
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "entity_id": "https://wazo.example.com",
  "sso_url": "https://login.microsoftonline.com/{tenant_id}/saml2",
  "certificate": "-----BEGIN CERTIFICATE-----\n{MIIC...}\n-----END CERTIFICATE-----",
  "attribute_mapping": {
    "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
    "firstname": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
    "lastname": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
    "username": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
  },
  "autoprovisioning": true,
  "enabled": true
}

:link: Chaînage : Cette configuration établit Wazo en tant que Service Provider (SP). L’entity_id doit correspondre à la configuration dans Azure AD/Okta.

Étape 2 : Récupérer les métadonnées SP pour configuration IdP

GET /api/auth/0.1/backends/saml/metadata
X-Auth-Token: {admin_token}

Réponse :

<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://wazo.example.com">
  <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://wazo.example.com/api/auth/0.1/backends/saml/callback" index="0"/>
  </SPSSODescriptor>
</EntityDescriptor>

:link: Chaînage : Copy this metadata to configure Azure AD/Okta as the Identity Provider. The Location URL is where SAML responses will be sent.

Étape 3 : Configurer Azure AD (dans le portail Azure)

  1. Enterprise Application → New Application → “Create your own application”
  2. Single sign-on → Select “SAML”
  3. Basic SAML Configuration :
    • Identifier (Entity ID): https://wazo.example.com
    • Reply URL: https://wazo.example.com/api/auth/0.1/backends/saml/callback
    • Sign on URL: https://wazo.example.com
  4. SAML Certificates : Download the SAML signing certificate

Étape 4 : Configurer les attributs utilisateur SAML (Azure AD)

Dans Azure AD, ensure these attributes are sent in the SAML token:

Attribute Value
user.email user.mail
user.firstname user.givenName
user.lastname user.surname

Étape 5 : Activer le backend SAML

POST /api/auth/0.1/backends/saml
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "enabled": true
}

Étape 6 : Authentification SAML (initiation par SP)

GET /api/auth/0.1/backends/saml/login

Réponse (302 Redirect) :

Location: https://login.microsoftonline.com/{tenant_id}/saml2?SAMLRequest=...

:link: Chaînage : Redirigez l’utilisateur vers cette URL. Après authentification sur Azure AD, il sera redirigé vers le callback Wazo avec le token SAML.

Étape 7 : Callback SAML et obtention du token Wazo

POST /api/auth/0.1/backends/saml/callback
Content-Type: application/x-www-form-urlencoded

SAMLResponse=...

Réponse :

{
  "token": "saml_generated_token_abc123",
  "expires_at": "2024-01-15T17:00:00Z",
  "auth_id": "saml_user_uuid"
}

:link: Chaînage : Le premier login SAML crée automatiquement l’utilisateur dans Wazo si autoprovisioning: true. L’auth_id retourné correspond à l’utilisateur Wazo créé.

Point d’attention / Warning

:warning: Important :

  • Always use HTTPS in production for both Wazo and the IdP.
  • Keep the SAML certificate from Azure AD/Okta up to date (renew before expiration).
  • Test with a non-admin user first — admin accounts may have different attribute mappings.
  • The autoprovisioning option automatically creates users on first login; disable if you want manual approval.

5.4 Refresh Tokens et Sessions Longues

Objectif

Permettre des sessions utilisateur persistantes avec renouvellement automatique des tokens, idéal pour des applications client qui nécessitent un accès prolongé sans reconnecter l’utilisateur.

Services impliqués

  • wazo-auth : Gestion des tokens et refresh tokens

Le Workflow détaillé

Étape 1 : Demander un token avec refresh token

POST /api/auth/0.1/token
Content-Type: application/json

Payload :

{
  "backend": "wazo_user",
  "expiration": 3600,
  "refresh_expiration": 86400,
  "username": "admin",
  "password": "secure_password"
}

Réponse :

{
  "token": "main_token_abc",
  "refresh_token": "refresh_token_xyz",
  "expires_at": "2024-01-15T16:00:00Z",
  "refresh_expires_at": "2024-01-16T16:00:00Z",
  "auth_id": "user_uuid_123"
}

:link: Chaînage : Récupérez les deux tokens :

  • token : Pour les appels API normaux (expiration courte)
  • refresh_token : Pour renouvellement (expiration longue)

Étape 2 : Utiliser le token principal pour les API

GET /api/confd/1.1/users
X-Auth-Token: main_token_abc

Étape 3 : Renouveler le token avec le refresh token

POST /api/auth/0.1/token
Content-Type: application/json

Payload :

{
  "backend": "wazo_user",
  "refresh_token": "refresh_token_xyz",
  "expiration": 3600
}

Réponse :

{
  "token": "new_main_token_def",
  "refresh_token": "new_refresh_token_ghi",
  "expires_at": "2024-01-15T18:00:00Z",
  "refresh_expires_at": "2024-01-16T18:00:00Z"
}

:link: Chaînage : Un nouveau couple token/refresh_token est généré. Les anciens tokens sont automatiquement invalidés. Continuez à utiliser le nouveau refresh_token.

Étape 4 : Révoquer le refresh token (logout)

DELETE /api/auth/0.1/token/{refresh_token}
Content-Type: application/json
X-Auth-Token: main_token_abc

Point d’attention / Warning

:warning: Important :

  • Refresh tokens have a longer lifespan (default: 24 hours) than access tokens (default: 1 hour).
  • Store refresh tokens securely — they allow long-term access.
  • If a refresh token is compromised, revoke it immediately with DELETE.
  • Each refresh generates a new refresh token (rotation) for security.

5.5 External Auth (Google, Microsoft, Firebase FCM)

Objectif

Permettre l’authentification via des providers externes (Google, Microsoft) ou la réception de notifications push via Firebase Cloud Messaging (FCM), étendant les méthodes de connexion au-delà des credentials locaux.

Services impliqués

  • wazo-auth : Gestion des backends d’authentification externe
  • wazo-confd : API de configuration utilisateur
  • Google / Microsoft : Providers OAuth2
  • Firebase : Service de notifications push

Le Workflow détaillé

Étape 1 : Configuration Google OAuth2

PUT /api/auth/0.1/backends/google/config
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "client_id": "google_client_id.apps.googleusercontent.com",
  "client_secret": "google_client_secret",
  "redirect_uri": "https://wazo.example.com/api/auth/0.1/backends/google/callback"
}

Étape 2 : Activation du backend Google

POST /api/auth/0.1/backends/google
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "enabled": true
}

Étape 3 : Initier l’authentification Google

GET /api/auth/0.1/backends/google/login

Réponse :

Location: https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&response_type=code&scope=email%20profile

Étape 4 : Callback Google OAuth et obtention du token Wazo

POST /api/auth/0.1/backends/google/callback
Content-Type: application/x-www-form-urlencoded

code=google_authorization_code

Réponse :

{
  "token": "google_wazo_token",
  "expires_at": "2024-01-15T17:00:00Z",
  "auth_id": "google_user_uuid"
}

:link: Chaînage : Le premier login crée l’utilisateur Wazo. Lier le compte Google à un utilisateur existant (voir étape 5).

Étape 5 : Lier un compte externe à un utilisateur existant

PUT /api/auth/0.1/users/{user_uuid}/external/{backend}
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "external_id": "google_user_id",
  "email": "user@gmail.com"
}

Configuration Firebase FCM (Notifications Push)

PUT /api/auth/0.1/backends/fcm/config
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "server_key": "firebase_server_key"
}

Point d’attention / Warning

:warning: Important :

  • External auth requires setting up OAuth2 credentials in the provider’s developer console.
  • The redirect_uri must exactly match what’s configured in Google/Microsoft.
  • External auth can be linked to existing users or create new ones (autoprovisioning).
  • Firebase FCM is primarily used for mobile push notifications in Wazo UC client.

5.6 ACLs et Permissions (Créer un Rôle Restrictif)

Objectif

Créer un rôle personnalisé avec des permissions granulaires pour limiter l’accès API d’utilisateurs ou services, conformément au principe du moindre privilège.

Services impliqués

  • wazo-auth : Gestion des ACLs et policies

Le Workflow détaillé

Étape 1 : Lister les ACLs disponibles

GET /api/auth/0.1/acl
X-Auth-Token: {admin_token}

Réponse :

{
  "items": [
    "confd.users.*",
    "confd.users.me.read",
    "confd.lines.*",
    "confd.lines.{line_id}.read",
    "calld.calls.*",
    "calld.users.me.calls.*",
    "websocketd",
    "provd.devices.*"
  ]
}

:link: Chaînage : Cette liste définit toutes les permissions disponibles. Notez les patterns avec {variable} — ils permettent un accès conditionnel.

Étape 2 : Créer une policy avec ACLs restrictives

POST /api/auth/0.1/policies
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "name": "agent_readonly",
  "description": "Policy for call center agents - read only access",
  "acl": [
    "confd.users.me.read",
    "confd.users.me.lines.read",
    "confd.users.me.voicemails.read",
    "calld.users.me.calls.read",
    "calld.users.me.calls.create",
    "websocketd"
  ]
}

Réponse :

{
  "uuid": "policy_uuid_abc123",
  "name": "agent_readonly",
  "description": "Policy for call center agents",
  "acl": ["confd.users.me.read", ...],
  "tenant_uuid": "tenant_xyz"
}

:link: Chaînage : Récupérez le uuid de la policy — il sera utilisé pour l’assigner à un utilisateur.

Étape 3 : Assigner la policy à un utilisateur

POST /api/auth/0.1/users/{user_uuid}/policies
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload :

{
  "policy_uuid": "policy_uuid_abc123"
}

:link: Chaînage : L’utilisateur hérite immédiatement des permissions de la policy. Les ACLs sont combinées avec celles existantes.

Étape 4 : Vérifier les ACLs effectives d’un utilisateur

GET /api/auth/0.1/users/{user_uuid}/acl
X-Auth-Token: {admin_token}

Réponse :

{
  "acl": [
    "confd.users.me.read",
    "confd.users.me.lines.read",
    "calld.users.me.calls.read",
    "calld.users.me.calls.create",
    "websocketd"
  ],
  "read_only": false
}

Point d’attention / Warning

:warning: Important :

  • ACLs are additive — an user gets the union of all their policy ACLs.
  • Use {uuid} patterns to restrict access to specific resources (e.g., confd.lines.15.read).
  • The websocketd ACL is required for real-time events over WebSocket.
  • Test policies with a non-admin account before deploying in production.

5.7 Création et Débogage d’un Webhook

Objectif

Configurer un webhook pour recevoir des notifications HTTP automatiques lors d’événements Wazo (appels, utilisateurs, agents), permettant des intégrations temps réel avec des systèmes tiers (CRM, helpdesk, analytics).

Services impliqués

  • wazo-webhookd : Service de gestion des webhooks
  • wazo-bus : Bus de messages événements

Le Workflow détaillé

Étape 1 : Créer une subscription webhook

POST /api/webhookd/1.0/subscriptions
Content-Type: application/json
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Payload :

{
  "name": "CRM Call Events",
  "events": ["call_created", "call_ended"],
  "service": "http",
  "config": {
    "url": "https://crm.example.com/api/wazo/calls",
    "method": "POST",
    "timeout": 30,
    "headers": {
      "X-API-Key": "crm_secret_key",
      "Content-Type": "application/json"
    }
  }
}

Réponse :

{
  "id": "webhook_uuid_abc123",
  "name": "CRM Call Events",
  "events": ["call_created", "call_ended"],
  "service": "http",
  "config": {
    "url": "https://crm.example.com/api/wazo/calls",
    "method": "POST",
    "timeout": 30
  },
  "enabled": true,
  "owner_uuid": "admin_uuid",
  "tenant_uuid": "tenant_xyz"
}

:link: Chaînage : Récupérez le id — il permet de modifier ou supprimer le webhook ultérieurement.

Étape 2 : Tester le webhook manuellement

POST /api/webhookd/1.0/subscriptions/{webhook_uuid_abc123}/test
Content-Type: application/json
X-Auth-Token: {admin_token}

Payload (optionnel — événement de test) :

{
  "event": "call_created",
  "payload": {
    "call_id": "test_call_id",
    "caller_id_number": "1001"
  }
}

Réponse :

{
  "status": "sent",
  "http_status": 200,
  "response_time_ms": 150
}

:link: Chaînage : Un status 200 indique que le serveur distant a accepté la requête.

Étape 3 : Lister les webhooks existants

GET /api/webhookd/1.0/subscriptions
X-Auth-Token: {admin_token}
Wazo-Tenant: {tenant_uuid}

Réponse :

{
  "items": [
    {
      "id": "webhook_uuid_abc123",
      "name": "CRM Call Events",
      "events": ["call_created", "call_ended"],
      "service": "http",
      "enabled": true
    }
  ]
}

Étape 4 : Vérifier les logs de delivery webhook

GET /api/webhookd/1.0/subscriptions/{webhook_uuid_abc123}/logs
X-Auth-Token: {admin_token}

Réponse :

{
  "items": [
    {
      "id": "log_uuid",
      "subscription_id": "webhook_uuid_abc123",
      "event": "call_created",
      "status": "delivered",
      "attempts": 1,
      "created_at": "2024-01-15T14:30:00Z",
      "http_code": 200,
      "response_body": "OK"
    },
    {
      "id": "log_uuid_2",
      "subscription_id": "webhook_uuid_abc123",
      "event": "call_created",
      "status": "failed",
      "attempts": 3,
      "created_at": "2024-01-15T14:35:00Z",
      "http_code": 500,
      "error": "Internal Server Error"
    }
  ]
}

:link: Chaînage : Les logs permettent de diagnostiquer les échecs. Notez le champ attempts — les webhooks sont automatiquement retentés jusqu’à 3 fois en cas d’échec.

Étape 5 : Désactiver ou supprimer un webhook

# Désactiver temporairement
PUT /api/webhookd/1.0/subscriptions/{webhook_uuid_abc123}
Content-Type: application/json
X-Auth-Token: {admin_token}

{
  "enabled": false
}

# Supprimer définitivement
DELETE /api/webhookd/1.0/subscriptions/{webhook_uuid_abc123}
X-Auth-Token: {admin_token}

Point d’attention / Warning

:warning: Important :

  • Webhooks are triggered asynchronously — there’s no immediate delivery guarantee.
  • The receiving endpoint must return HTTP 2xx within 30 seconds (configurable timeout).
  • Failed deliveries are retried up to 3 times with exponential backoff.
  • Use the user_uuid field to filter webhooks for specific users (reduces noise).
  • Always implement idempotency in your webhook handler — the same event may be delivered multiple times.

5.8 Application Stasis / ARI et Écoute Discrète (Snoop)

Objectif

Créer une application Asterisk Stasis via l’interface ARI (Asterisk REST Interface) pour intercepter et analyser les appels en temps réel, ou implémenter l’écoute discrète (snoop) pour la supervision d’appels. Tous les exemples sont alignés sur le Chapitre 9 — ARI (Bible 9), testé et validé sur Wazo 26.06.

Services impliqués

  • Asterisk ARI : Interface RESTful pour contrôle d’appels (port 5039)
  • wazo-calld : Abstraction haut niveau pour ARI (alternative à ARI direct)
  • wazo-confd : Configuration des endpoints
  • wazo-confgend : Régénère ari.d/*.conf (à stopper avant modification manuelle)

Le Workflow détaillé

Étape 1 : Créer l’utilisateur ARI (NE PAS toucher à ari.conf)

:warning: Piège critique (Bible 9, §9.3 + pitfall #22) : ne jamais modifier
/etc/asterisk/ari.conf directement. Ce fichier est un #include ari.d/*.conf
géré par Wazo. Tout [general] ajouté dans un fichier custom dupliquera celui
déjà présent dans 01-wazo.conf et Asterisk refusera de parser la conf :
duplicate object 'general'. Résultat : res_ari.so chargé (Use Count > 0)
mais /ari/ jamais listé dans HTTP.

Créer un fichier dédié dans ari.d/ (Wazo fournit déjà 01-wazo.conf) :

; /etc/asterisk/ari.d/02-cookbook.conf
; PAS de section [general] ici — elle est dans 01-wazo.conf
; Sinon : duplicate object 'general' (pitfall #22)

[cookbook_user]
type = user
read_only = no
password = <MOT_DE_PASSE_GENERE>
password_format = plain
chown asterisk:www-data /etc/asterisk/ari.d/02-cookbook.conf
chmod 0660 /etc/asterisk/ari.d/02-cookbook.conf

# Stopper le timer qui régénère la conf (sinon écrasement)
systemctl stop wazo-confgend.timer
asterisk -rx "module reload res_ari"

Vérifier la prise en compte :

asterisk -rx "ari show users"      # doit afficher cookbook_user
asterisk -rx "ari show status"     # Enabled: Yes
asterisk -rx "http show status" | grep "/ari"   # /ari/... listé

:link: Chaînage : ces credentials (cookbook_user:<password>) serviront à
l’authentification ARI via HTTP Basic Auth (curl -u, aiohttp.BasicAuth ou
header Authorization: Basic ...). ARI n’utilise pas X-Auth-Token.

Étape 2 : Déclarer le dialplan Stasis dans extensions_extra.d/

:warning: Piège #8 : pas dans extensions.conf (régénéré par wazo-confgend).

; /etc/asterisk/extensions_extra.d/cookbook-agent.conf

[cookbook-agent]
exten => _X.,1,NoOp(=== Cookbook Stasis ${EXTEN} from ${CALLERID(num)} ===)
 same => n,Stasis(cookbook-stasis,${EXTEN},${CALLERID(num)})
 same => n,Hangup()

exten => s,1,NoOp(=== Cookbook Stasis entrant ${CALLERID(num)} ===)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Stasis(cookbook-stasis,s,${CALLERID(num)})
 same => n,Hangup()

exten => h,1,NoOp(Cookbook hangup)
 same => n,Return()
chown asterisk:www-data /etc/asterisk/extensions_extra.d/cookbook-agent.conf
chmod 0660 /etc/asterisk/extensions_extra.d/cookbook-agent.conf
asterisk -rx "dialplan reload"

Étape 3 : Enregistrer un channel dans l’application Stasis

:warning: Piège #3 (Bible 9) : l’app Stasis doit être créée via ARI (POST
/ari/applications) après que le dialplan la référence via
Stasis(<name>, ...). Sinon : app_stasis.c:129 Stasis(<name>, ...) failed.

# 1. Vérifier que l'app apparaît comme "non registered"
curl -u cookbook_user:<MDP> http://<host>:5039/ari/applications
# → [{"name":"callcontrol",...},{"name":"adhoc_conference",...}]
#    cookbook-stasis n'est PAS dans la liste tant qu'aucun canal n'est entré

# 2. Créer un canal de test (canal Local/ sans média pour smoke test)
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/channels \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "Local/1001@cookbook-agent",
    "app": "cookbook-stasis",
    "callerId": "TestCall"
  }'
# → {"id":"...","name":"Local/1001@cookbook-agent-00000000;1","state":"Down",...}

Champs réellement acceptés par Asterisk 22 :

Champ Type Notes
endpoint string Local/...@context ou PJSIP/... (PAS SIP/...)
app string Nom de l’app Stasis
callerId string Défini automatiquement quand un canal PJSIP sonne
variables object Variables de canal initiales (CHANNEL(...) autorisé)
timeout int Secondes avant raccroché auto

Étape 4 : Écoute discrète (Snoop) avec ARI

:warning: Correction : il n’existe pas de préfixe PJSIP/snoop:. L’écoute
discrète en ARI passe par l’application Spy (chan_spy.so) ou par un
bridge dédié. En ARI pur, l’idiome recommandé est de créer un second canal
branché sur le même contexte Stasis et de le joindre à un bridge mixing avec
le canal cible.

# 1. Créer un canal cible
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/channels \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "Local/1002@cookbook-agent",
    "app": "cookbook-stasis",
    "variables": {"SPY_TARGET": "true"}
  }'

# 2. Créer un bridge "mixing" (whisper both directions)
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/bridges \
  -H "Content-Type: application/json" \
  -d '{"type":"mixing,dtmf_events"}'

# 3. Ajouter les deux canaux au bridge
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/bridges/<bridge-id>/addChannel \
  -H "Content-Type: application/json" \
  -d '{"channel":"<channel-id-target>"}'
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/bridges/<bridge-id>/addChannel \
  -H "Content-Type: application/json" \
  -d '{"channel":"<channel-id-supervisor>"}'

:link: Chaînage : les bridges mixing sont l’idiome ARI natif pour la
supervision. L’app Spy côté dialplan reste valide pour l’écoute audio
unidirectionnelle (Voir Bible 9 §9.4.3 et asterisk -rx "app show Spy").

Étape 5 : Contrôler un canal (Mute, Raccrocher)

# Raccrocher (DELETE — pas POST)
curl -u cookbook_user:<MDP> -X DELETE http://<host>:5039/ari/channels/<channel-id>

# Mute (ARIA ne fournit PAS de hold ; pour mettre en attente, basculer sur
# wazo-calld POST /api/calld/1.0/users/{uuid}/calls/{call_id}/hold/start)
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/channels/<channel-id>/mute \
  -H "Content-Type: application/json" \
  -d '{"direction":"both"}'

# Jouer un son
curl -u cookbook_user:<MDP> -X POST http://<host>:5039/ari/channels/<channel-id>/play \
  -H "Content-Type: application/json" \
  -d '{"media":"sound:custom/bienvenue"}'

:warning: Correction : /ari/channels/{id}/hold n’existe pas en ARI natif.
L’hold passe par Mute (silence ponctuel) ou par wazo-calld. Le cookbook
historique mentionnait POST /hold et DELETE /hold qui retournent 404.

Étape 6 : Recevoir les événements Stasis (WebSocket)

import asyncio
import aiohttp
import json

AR_URL = "http://<host>:5039"
USER = "cookbook_user"
PASS = "<MOT_DE_PASSE>"
APP = "cookbook-stasis"

async def listen():
    # aiohttp < 4.0 : BasicAuth OK ; >= 4.0 : encode_basic_auth + headers
    headers = {"Authorization": aiohttp.encode_basic_auth(USER, PASS)}
    async with aiohttp.ClientSession(headers=headers) as session:
        ws_url = f"{AR_URL}/ari/events?app={APP}&subscribeAll=events"
        async with session.ws_connect(ws_url) as ws:
            async for msg in ws:
                if msg.type != aiohttp.WSMsgType.TEXT:
                    continue
                evt = json.loads(msg.data)
                if evt.get("type") == "StasisStart":
                    print("StasisStart", evt["channel"]["id"])
                elif evt.get("type") == "ChannelDestroyed":
                    print("Hangup", evt["channel"]["id"], evt["channel"].get("cause_txt"))

asyncio.run(listen())

:warning: Correction : le port est 5039 (pas 8089) et la query string est
?app=<name>&subscribeAll=events (pas api_key=). Le token Basic Auth est
passé dans le header Authorization, pas dans l’URL.

Exemple d’événement StasisStart (tel qu’envoyé par Asterisk 22) :

{
  "type": "StasisStart",
  "timestamp": "2024-01-15T14:30:00.000+0000",
  "channel": {
    "id": "1783953092.9",
    "name": "Local/1001@cookbook-agent-00000000;1",
    "state": "Ring",
    "caller": {"name": "", "number": ""},
    "connected": {"name": "", "number": ""},
    "dialplan": {"context": "cookbook-agent", "exten": "1001", "priority": 2}
  },
  "args": ["1001", ""],
  "application": "cookbook-stasis"
}

Exemple d’événement ChannelDestroyed :

{
  "type": "ChannelDestroyed",
  "timestamp": "2024-01-15T14:35:00.000+0000",
  "channel": {
    "id": "1783953092.9",
    "name": "Local/1001@cookbook-agent-00000000;1",
    "state": "Up",
    "cause": 16,
    "cause_txt": "Normal Clearing"
  },
  "application": "cookbook-stasis"
}

Point d’attention / Warning

:warning: Important (rapportés à la Bible 9 §9.6) :

  • ARI utilise HTTP Basic Auth sur le port 5039, jamais X-Auth-Token.
  • Ne jamais créer de section [general] dans ari.d/*.conf (pitfall #22).
  • Créer l’app Stasis après avoir chargé le dialplan qui la référence.
  • /ari/channels/{id}/hold n’existe pas : utiliser Mute ou wazo-calld.
  • Le Snoop ARI passe par un bridge mixing, pas par un endpoint magique.
  • aiohttp.BasicAuth est deprecated ≥ 4.0 : utiliser
    aiohttp.encode_basic_auth + header Authorization.
  • Pour les opérations courantes (hold, transfer, hangup) préférer
    wazo-calld qui encapsule ARI et gère les ACL.
  • Voir WAZO_API_BIBLE_CH9_ARI.md (Bible 9) pour la procédure complète
    incluant les 22 pièges vérifiés en production sur wazohermesx.

5.9 Récapitulatif des Services de Sécurité

IMPORTANT : Toutes les API passent par nginx sur le port 443. Les ports ci-dessous sont les ports directs des microservices (pour debugging uniquement).

Service Port Direct Nginx Route API Base Purpose
wazo-auth 9497 /api/auth/0.1/* /api/auth/0.1 Authentication, tokens, LDAP, SAML
wazo-webhookd 9300 /api/webhookd/1.0/* /api/webhookd/1.0 Webhook subscriptions
Asterisk ARI 5039 /ari/* (nginx port 443) ou 5039 direct /ari Call control, Stasis apps
wazo-websocketd 9502 /api/websocketd/* WebSocket Real-time events

5.10 Patterns de Sécurité Recommandés

Pattern 1 : Rotation Automatisée des Credentials

import os
import requests
from datetime import datetime, timedelta

class WazoSecureClient:
    def __init__(self, host, admin_user, admin_password):
        self.host = host
        self.admin_user = admin_user
        self.admin_password = admin_password
        self.token = None
        self.token_expires = None
        
    def _is_token_valid(self):
        if not self.token or not self.token_expires:
            return False
        # Refresh 5 minutes before expiration
        return datetime.utcnow() < (self.token_expires - timedelta(minutes=5))
    
    def _authenticate(self):
        response = requests.post(
            f"https://{self.host}/api/auth/0.1/token",
            json={
                "backend": "wazo_user",
                "expiration": 3600,
                "username": self.admin_user,
                "password": self.admin_password
            }
        )
        data = response.json()
        self.token = data["token"]
        self.token_expires = datetime.fromisoformat(data["expires_at"].replace("Z", "+00:00"))
        
    def get_token(self):
        if not self._is_token_valid():
            self._authenticate()
        return self.token
    
    def api_call(self, method, endpoint, **kwargs):
        token = self.get_token()
        headers = kwargs.get("headers", {})
        headers["X-Auth-Token"] = token
        kwargs["headers"] = headers
        return requests.request(method, f"https://{self.host}{endpoint}", **kwargs)

Pattern 2 : Webhook avec Signature HMAC

import hmac
import hashlib
import json
from flask import Flask, request, jsonify

app = Flask(__name__)
SECRET = os.environ.get("WEBHOOK_SECRET", "")

@app.route("/webhook", methods=["POST"])
def handle_webhook():
    # Verify HMAC signature
    signature = request.headers.get("X-Wazo-Signature", "")
    expected = hmac.new(
        SECRET.encode(),
        request.data,
        hashlib.sha256
    ).hexdigest()
    
    if not hmac.compare_digest(signature, expected):
        return jsonify({"error": "Invalid signature"}), 401
    
    event = request.json
    event_name = event.get("name")
    event_data = event.get("data", {})
    
    # Process event
    if event_name == "call_created":
        process_new_call(event_data)
    elif event_name == "user_created":
        process_new_user(event_data)
    
    return jsonify({"status": "ok"}), 200

Fin de la PARTIE 5 — Fin de l’Ouvrage

Rien n’est parfait, il y a surement des erreurs ce n’est pas l’évangile mais je pense que ça peut aider !!! lol :smiley:

Merci pour le partage, ça a l’air pas mal !

Voilà un petit retour : normalement, tu peux accéder à l’ARI via Nginx sans modifier la configuration, avec une URL du type : https://wazo_stack/api/asterisk/ari.

L’idéal serait de régler le problème avec le dépôt Git afin qu’il soit possible de proposer des contributions plus facilement.

Merci pour ton retour. J’ai mis à jour la derniere partie wazo cookbook 5.

Effectivement ça pourrait etre intéressant d’avoir un git permettant de proposer des contributions , surtout que j’ai bosser avec l’ia sur d’autres sujets plus ou moins avancé et fini qui pourrait intéressé du monde, comme un debut de softphone avec pas mal de fonctionnalités fait avec wazo-js-sdk packagé dans electron comme le softphone officiel , un outils de stress test, test de charge, un debut d’outils de detection de fraud via le forward des cdr dans graylog via webhook pour detection de fraude, un outils de générations d’annonce multi tts, une appli d’administration desktop avec import en masse,modifications en masse. Mais les 95% ne sont pas finis et en mode privés

… lol :smiley:

J’ai créé un autre github , celui la est accessible à tous GitHub - greenvi2026/WAZODOCS · GitHub !!! :smiley:

Je viens d’ajouter une collection postman , avec la doc, c’est utile pour tester des trucs vite fait via l’api. j’ai pas tester mais je n’ai pas de doute que cela fonctionne.

Il existe une version docker de wazo ou chaque micro service tourne dans un container docker. Bon ça a été plus ou moins volontairement sabré, le service wazo-confd est mock par défaut ,avec l’ia j’ai pu recréé le service mock pour le rendre fonctionnel, mais bon c’est pas ma tasse de thé docker. J’ai pas ma langue dans ma poche et vais dire ce que je pense, selon moi il y a une volonté évidente de ne pas documenter la version communautaire pour mettre des batons dans les roues et vendre du wazo portal. Personnellement j’ai juste étudié wazo platform pour avoir toute les infos avant de valider l’utilisation de wazo portal dans l’entreprise qui m’emploi. Après j’avoue que certaines choses ne me plaisent pas trop comparé à d’autres solution comme asterisk,vitalpbx, freepbx… L’histoire des conf en bdd et généré par confgend je ne suis pas trop fan, des fois juste pour changer une option ça complexifie enormement les choses. De plus par expérience avec une solution propriétaire, je déteste etre l’esclave d’un prestataire. J’aime bien quand les choses sont claires, bien documentées et faciles à modifier!!! Enfin cela ne reste que mon point de vue personnel. Je ne m’étalerais pas trop sur le dernier point mais un softphone basé sur nodejs embarqué dans electron ça se patch meme si le code est offusqué, c’est pas du code compilé et c’est assez facile à analyser, debug et leurrer!!! lol :smiley:

Le softphone pro peut meme fonctionné avec un wazo communautaire!!! lol :smiley:

Cétait pas mon but à la base, mais devoir croire ce qu’on me disait sans pouvoir test , ça me dérangeait!!! Et en ayant fais bosser l’ia sur des appli nodejs packagés dans electronjs j’avais vu certains trucs!!! Pourquoi ne pas redeveloppé le softphone avec flutter? C 'est du taf mais bon c’est multi plateforme aussi!!!

Ce qui m’intéresse c’est d’avancer ,les PR c’est pas mon truc!!! Perdre mon temps à faire des PR qui potentiellement seraient pris en compte alors que je pourrais avancer !! J’ai pas d action chez Wazo !!! lol :smiley: