Aller au contenu

API Agents

Endpoints REST dédiés à l'application agents, localisés dans /api/agents/.

Authentification

Le pairing QR remplace l'authentification par email/mot de passe. Le token de pairing est généré dans le backoffice admin (via pairing_generate.php) et scanné par l'application mobile.

POST /api/agents/auth/pair.php

Authentifie un agent en utilisant un token de pairing QR et retourne les tokens JWT.

{
  "token": "a1b2c3d4e5f6...",
  "device_id": "<uuid>",
  "device_name": "Samsung SM-G991B"
}
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
    "expires_in": 3600,
    "agent": {
      "id": 1,
      "email": "agent@collectivite.test",
      "nom": "Dupont",
      "prenom": "Jean",
      "collectivite_id": 1,
      "collectivite_name": "Ville de Nice",
      "collectivite_type": "MAIRIE",
      "role": "agent",
      "code_insee": "06088",
      "mairie_latitude": 43.7009,
      "mairie_longitude": 7.2683
    }
  }
}
{
  "success": false,
  "error": "PAIRING_TOKEN_INVALID",
  "message": "QR code invalide ou expiré"
}
{
  "success": false,
  "error": "ACCOUNT_DISABLED",
  "message": "Compte agent désactivé"
}

POST /api/agents/auth/refresh.php

Renouvelle les tokens avec le refresh token.

{
    "refresh_token": "dGhpcyBpcyBhIHJlZnJl..."
}
{
    "success": true,
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "bmV3IHJlZnJlc2ggdG9r...",
    "expires_in": 3600
}

POST /api/agents/auth/logout.php

Invalide le refresh token côté serveur.

Header requis

Authorization: Bearer {access_token}

{
    "success": true,
    "message": "Déconnexion réussie"
}

Incidents

Authentification

Tous les endpoints incidents requièrent le header Authorization: Bearer {access_token}.

GET /api/agents/incidents/list.php

Liste les incidents de la collectivité avec filtres optionnels.

Paramètres query :

Paramètre Type Description
status string Filtrer par statut (NEW, ASSIGNED, IN_PROGRESS, RESOLVED, CLOSED)
priority string Filtrer par priorité (LOW, NORMAL, HIGH, URGENT)
search string Recherche textuelle (adresse, description)
page int Page (défaut: 1)
limit int Limite par page (défaut: 20, max: 100)
GET /api/agents/incidents/list.php?status=NEW&priority=HIGH&page=1&limit=20
{
    "success": true,
    "incidents": [
        {
            "id": 123,
            "type_id": 1,
            "type_name": "Voirie",
            "type_color": "#ef4444",
            "adresse": "12 Rue de la Paix, 06000 Nice",
            "latitude": 43.7102,
            "longitude": 7.2620,
            "description": "Nid-de-poule dangereux",
            "status": "NEW",
            "priority": "HIGH",
            "assigned_agent_id": null,
            "assigned_agent_name": null,
            "photo_count": 2,
            "comment_count": 0,
            "created_at": "2026-01-15T10:30:00Z",
            "updated_at": "2026-01-15T10:30:00Z"
        }
    ],
    "pagination": {
        "page": 1,
        "limit": 20,
        "total": 45,
        "total_pages": 3
    }
}

GET /api/agents/incidents/detail.php

Détail complet d'un incident avec photos et commentaires.

Paramètres query :

Paramètre Type Requis Description
id int Oui ID de l'incident
{
    "success": true,
    "incident": {
        "id": 123,
        "type_id": 1,
        "type_name": "Voirie",
        "type_color": "#ef4444",
        "adresse": "12 Rue de la Paix, 06000 Nice",
        "latitude": 43.7102,
        "longitude": 7.2620,
        "description": "Nid-de-poule dangereux sur la chaussée",
        "status": "IN_PROGRESS",
        "priority": "HIGH",
        "assigned_agent_id": 5,
        "assigned_agent_name": "Marie Martin",
        "assigned_agents": [
            { "id": 5, "nom": "Martin", "prenom": "Marie", "is_referent": true },
            { "id": 8, "nom": "Petit", "prenom": "Luc", "is_referent": false }
        ],
        "attente_motif_nom": null,
        "attente_note": null,
        "attente_started_at": null,
        "attente_contact_nom": null,
        "attente_contact_telephone": null,
        "attente_contact_telephone_urgence": null,
        "declarant_device_id": "abc123...",
        "declarant_email": null,
        "created_at": "2026-01-15T10:30:00Z",
        "updated_at": "2026-01-16T14:20:00Z"
    },
    "photos": [
        {
            "id": 456,
            "type": "DECLARANT",
            "url": "/uploads/incidents/123_1.jpg",
            "latitude": 43.7102,
            "longitude": 7.2620,
            "created_at": "2026-01-15T10:30:00Z"
        },
        {
            "id": 789,
            "type": "INTERVENTION",
            "agent_id": 5,
            "agent_name": "Marie Martin",
            "url": "/uploads/incidents/123_intervention_1.jpg",
            "latitude": 43.7103,
            "longitude": 7.2621,
            "created_at": "2026-01-16T14:20:00Z"
        }
    ],
    "comments": [
        {
            "id": 101,
            "agent_id": 5,
            "agent_name": "Marie Martin",
            "content": "Intervention prévue demain matin.",
            "is_internal": false,
            "created_at": "2026-01-16T09:00:00Z"
        }
    ]
}

Équipe assignée (assigned_agents)

Modèle many-to-many (incident_agents : incident_id, agent_id, is_referent) remplaçant l'ancien assigné unique. assigned_agent_id/assigned_agent_name restent renvoyés (premier agent de assigned_agents, ou colonne legacy incidents.assigned_agent_id en fallback) pour compatibilité clients existants.

POST /api/agents/incidents/update_status.php

Met à jour le statut et/ou la priorité d'un incident. Le passage au statut ATTENTE requiert motif_id.

{
    "incident_id": 123,
    "status": "IN_PROGRESS"
}
{
    "incident_id": 123,
    "status": "ATTENTE",
    "motif_id": 3,
    "note": "En attente de pièces détachées",
    "contact_id": 7
}
{
    "success": true,
    "message": "Statut mis a jour"
}
{
    "success": false,
    "message": "motif_id requis pour le statut ATTENTE"
}

Historique

Sortir du statut ATTENTE (quel que soit le nouveau statut) clôture la période ouverte dans incident_wait_periods, fusionnée avec le reste de l'historique dans l'affichage de l'incident.

GET /api/agents/incidents/motifs_attente.php

Liste les motifs de mise en attente actifs de la mairie de l'incident (configurables par mairie).

Paramètres query : incident_id (int, requis)

{
    "success": true,
    "data": [
        { "id": 1, "nom": "Attente pièces", "est_autre": false },
        { "id": 9, "nom": "Autre", "est_autre": true }
    ]
}

POST /api/agents/incidents/update_priority.php

Met à jour la priorité d'un incident. Rejeté (403) si mairies.priorite_agents_autorise = 0 pour la mairie de l'incident — revalidé côté serveur indépendamment de l'état affiché côté client.

{
    "incident_id": 123,
    "priority": "HIGH"
}
{
    "success": true,
    "message": "Priorite mise a jour"
}
{
    "success": false,
    "message": "Modification de priorite non autorisee pour votre mairie"
}

POST /api/agents/incidents/add_comment.php

Ajoute un commentaire à un incident.

{
    "incident_id": 123,
    "content": "Intervention terminée, rebouchage effectué.",
    "is_internal": false
}
{
    "success": true,
    "comment": {
        "id": 102,
        "agent_id": 5,
        "content": "Intervention terminée, rebouchage effectué.",
        "is_internal": false,
        "created_at": "2026-01-17T11:30:00Z"
    }
}

Commentaires internes

Les commentaires avec is_internal: true ne sont visibles que par les agents, pas par les citoyens.

POST /api/agents/incidents/add_photo.php

Ajoute une photo d'intervention à un incident.

{
    "incident_id": 123,
    "photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
    "latitude": 43.7103,
    "longitude": 7.2621
}
{
    "success": true,
    "photo": {
        "id": 790,
        "type": "INTERVENTION",
        "url": "/uploads/incidents/123_intervention_2.jpg",
        "created_at": "2026-01-17T11:35:00Z"
    }
}

Contacts (intervenants)

GET /api/agents/intervenants/list.php

Liste les intervenants actifs (carnet de contacts). Sans incident_id : intervenants de la mairie de l'agent (ou de toutes les mairies de sa collectivité si EPCI) — utilisé par l'onglet "Contacts". Avec incident_id : intervenants de la mairie de cet incident — utilisé par le sélecteur du dialogue ATTENTE.

Paramètres query :

Paramètre Type Description
incident_id int Optionnel — restreint à la mairie de cet incident
{
    "success": true,
    "data": [
        {
            "id": 1,
            "nom": "Garage Dupont",
            "categorie": "Mécanique",
            "telephone": "0493000000",
            "telephone_urgence": null,
            "email": "contact@garage-dupont.test",
            "notes": "Intervention rapide sur voirie"
        }
    ]
}

Statistiques

GET /api/agents/stats/dashboard.php

Statistiques du tableau de bord pour la collectivité.

{
    "success": true,
    "stats": {
        "total": 150,
        "by_status": {
            "NEW": 25,
            "ASSIGNED": 30,
            "IN_PROGRESS": 45,
            "RESOLVED": 40,
            "CLOSED": 10
        },
        "assigned_to_me": 12,
        "resolved_today": 5,
        "resolved_this_week": 28
    },
    "recent_incidents": [
        {
            "id": 123,
            "type_name": "Voirie",
            "adresse": "12 Rue de la Paix",
            "status": "NEW",
            "priority": "HIGH",
            "created_at": "2026-01-18T08:00:00Z"
        }
    ]
}

Codes d'erreur

Code Signification
200 Succès
400 Requête invalide (paramètres manquants)
401 Non authentifié (token invalide/expiré)
403 Non autorisé (accès refusé)
404 Ressource non trouvée
500 Erreur serveur

Sécurité

  • TLS 1.3 obligatoire
  • JWT avec expiration courte (1h access, 7j refresh)
  • Rate limiting : 100 requêtes/minute par agent
  • Validation de toutes les entrées
  • Logs sans données sensibles