Skip to main content

Documentation de l'API : /api/app-stats

URL de production​

https://votre-domaine.com/api/app-stats

En local : http://localhost:3000/api/apps-stats ou http://localhost:3000/api/app-stats.


Méthode​

GET uniquement.

Description​

Retourne les statistiques de l'application associée à la clé API (agrégats SMS / OTP par canal, totaux, réseaux, courbe sur 7 jours, journaux récents masqués). Même logique métier que le tableau de bord admin pour une app donnée, mais sans session : uniquement le header api-key.

Exemple :

GET /api/app-stats HTTP/1.1
Host: votre-domaine.com
api-key: VOTRE_CLE_API_ICI

Authentification​

HeaderObligatoireDescription
api-keyOuiClé API de l'application (identique à send-message / send-otp).

Paramètres de requête (query)​

Tous optionnels. Priorité de la période : month → sinon from / to → sinon mois civil en cours (à partir du 1er du mois, sans borne de fin explicite dans period.to).

ParamètreFormatDescription
monthYYYY-MMStatistiques sur le mois civil (1er 00:00:00 → dernier jour 23:59:59).
fromDate parsable par DateDébut (00:00:00).
toIdemFin (23:59:59).
logsLimitEntier 1–200Nombre max d'entrées par liste (logs.sms et logs.otp). Défaut : 50.
  • month invalide → 400 avec success: false et un message explicite.
  • from seul ou to seul est autorisé (intervalle ouvert d'un côté).
  • logs : les entrées sont filtrées par la même période que les agrégats (baseFilter), triées par createdAt décroissant.

Ce que l'API ne renvoie pas​

Aucun champ de prix ou de coût (cost, totalCost, costMinor, etc.) : la réponse est limitée aux volumes, statuts, parties SMS, journaux masqués et métadonnées non sensibles.


Structure de la réponse 200 OK​

Enveloppe :

{
"success": true,
"data": { }
}

data.app​

ChampTypeDescription
appIdstringIdentifiant métier de l'application.
namestring ou objet i18nNom affiché (selon le document en base).

data.period​

ChampTypeDescription
fromstring ISO date ou nullBorne inférieure effective ($gte).
tostring ISO date ou nullBorne supérieure effective ($lte), si définie.
monthstring YYYY-MM ou nullPrésent si le filtre month a été utilisé.

data.stats​

Six blocs homogènes, clés fixes :

  • messageNational, messageInternational, messageWhatsapp : SMS.
  • otpNational, otpInternational, otpWhatsapp : OTP.

Chaque bloc message contient :

ChampTypeDescription
sentnumberMessages envoyés avec succès (statut sent).
failednumberÉchecs (not_sent côté agrégation SMS).
partsnumberSomme des parts sur les envois réussis.
successRatestringPourcentage "0.00" à "100.00" : sent / (sent + failed).

Chaque bloc OTP contient en plus :

ChampTypeDescription
verifiednumberOTP vérifiés (lignes verified).
verificationRatestring"0.00"–"100.00" : verified / sent si sent > 0, sinon "0".

data.totals​

ChampTypeDescription
totalMessagesnumberSomme des sent + failed sur les six blocs stats.
totalSentnumberSomme des sent.
totalFailednumberSomme des failed.
totalQueuednumberDocuments SMS queued dans la période.
totalNotSentnumberDocuments SMS not_sent dans la période.
totalPartsnumberSomme des parts sur les six blocs.
totalOtpVerifiednumberSomme des verified des trois blocs OTP.

data.networkStats​

Compteurs agrégés (SMS + OTP) par réseau détecté :

CléTypeDescription
mauritelnumber
mattelnumber
chinguitellnumber
inconnunumber
internationalnumber

data.chartData​

Tableau de 7 jours calendaires glissants (aujourd'hui inclus), libellés date en locale fr-FR (ex. lun. 7 avr.). Indépendant du filtre month / from / to des agrégats principaux.

ChampTypeDescription
datestringLibellé court du jour.
totalMessagesnumberSMS + OTP (envoyés / échoués / vérifiés selon la logique du graphique).
totalSentnumber
smsSentnumber
otpSentnumberInclut sent et verified pour la courbe.
otpVerifiednumber
deliveryRatenumberPourcentage arrondi à une décimale (nombre, pas une chaîne).

data.logs​

ChampTypeDescription
limitnumberPlafond appliqué (identique à logsLimit clampé 1–200).
smsarrayDernières lignes SmsLog de la période.
otparrayDernières lignes OtpLog de la période.

Élément logs.sms[*] :

ChampTypeDescription
kind"sms"
endpointstringsend-otp ou send-message.
statusstringsent, not_sent, queued.
recipientstringNuméro masqué (même règle que le modèle).
messagePreviewstring | nullAperçu court.
providerstring | null
partsnumber
createdAtstring ISO | null
errorCodestring | null
errorMessagestring | null
messageIdstring | null

Élément logs.otp[*] : comme SMS, avec kind: "otp", plus :

ChampTypeDescription
channelstringsms ou whatsapp.
verifiedAtstring ISO | nullSi vérification.

Les champs sensibles (corps de message complet, OTP, coûts) ne sont pas inclus.


Exemple de réponse statique (data)​

Exemple illustratif (valeurs fictives) pour intégration / tests de parsing :

{
"success": true,
"data": {
"app": {
"appId": "demo-app",
"name": "Application démo"
},
"period": {
"from": "2026-04-01T00:00:00.000Z",
"to": "2026-04-30T23:59:59.999Z",
"month": "2026-04"
},
"stats": {
"messageNational": {
"sent": 120,
"failed": 3,
"parts": 125,
"successRate": "97.56"
},
"otpNational": {
"sent": 80,
"failed": 2,
"verified": 72,
"parts": 80,
"successRate": "97.56",
"verificationRate": "90.00"
},
"messageInternational": {
"sent": 10,
"failed": 1,
"parts": 12,
"successRate": "90.91"
},
"otpInternational": {
"sent": 5,
"failed": 0,
"verified": 4,
"parts": 5,
"successRate": "100.00",
"verificationRate": "80.00"
},
"messageWhatsapp": {
"sent": 0,
"failed": 0,
"parts": 0,
"successRate": 0
},
"otpWhatsapp": {
"sent": 15,
"failed": 1,
"verified": 14,
"parts": 15,
"successRate": "93.75",
"verificationRate": "93.33"
}
},
"totals": {
"totalMessages": 236,
"totalSent": 230,
"totalFailed": 7,
"totalQueued": 2,
"totalNotSent": 5,
"totalParts": 317,
"totalOtpVerified": 90
},
"networkStats": {
"mauritel": 180,
"mattel": 40,
"chinguitell": 25,
"inconnu": 3,
"international": 12
},
"chartData": [
{
"date": "lun. 7 avr.",
"totalMessages": 42,
"totalSent": 40,
"smsSent": 22,
"otpSent": 18,
"otpVerified": 15,
"deliveryRate": 95.2
},
{
"date": "mar. 8 avr.",
"totalMessages": 55,
"totalSent": 52,
"smsSent": 30,
"otpSent": 25,
"otpVerified": 22,
"deliveryRate": 94.5
},
{
"date": "mer. 9 avr.",
"totalMessages": 38,
"totalSent": 36,
"smsSent": 18,
"otpSent": 20,
"otpVerified": 17,
"deliveryRate": 94.7
},
{
"date": "jeu. 10 avr.",
"totalMessages": 61,
"totalSent": 58,
"smsSent": 35,
"otpSent": 26,
"otpVerified": 24,
"deliveryRate": 95.1
},
{
"date": "ven. 11 avr.",
"totalMessages": 48,
"totalSent": 45,
"smsSent": 28,
"otpSent": 20,
"otpVerified": 18,
"deliveryRate": 93.8
},
{
"date": "sam. 12 avr.",
"totalMessages": 22,
"totalSent": 21,
"smsSent": 12,
"otpSent": 10,
"otpVerified": 9,
"deliveryRate": 95.5
},
{
"date": "dim. 13 avr.",
"totalMessages": 30,
"totalSent": 29,
"smsSent": 15,
"otpSent": 15,
"otpVerified": 13,
"deliveryRate": 96.7
}
],
"logs": {
"limit": 50,
"sms": [
{
"kind": "sms",
"endpoint": "send-message",
"status": "sent",
"recipient": "+222****12",
"messagePreview": "Bonjour, v",
"provider": "national",
"parts": 1,
"createdAt": "2026-04-13T10:15:00.000Z",
"errorCode": null,
"errorMessage": null,
"messageId": "msg-static-001"
},
{
"kind": "sms",
"endpoint": "send-message",
"status": "not_sent",
"recipient": "+222****99",
"messagePreview": "Rappel: r",
"provider": "national",
"parts": 1,
"createdAt": "2026-04-13T09:40:00.000Z",
"errorCode": "TIMEOUT",
"errorMessage": "Gateway timeout",
"messageId": null
}
],
"otp": [
{
"kind": "otp",
"endpoint": "send-otp",
"status": "verified",
"recipient": "+222****45",
"messagePreview": "123***",
"provider": "national",
"channel": "sms",
"parts": 1,
"createdAt": "2026-04-13T10:05:00.000Z",
"verifiedAt": "2026-04-13T10:06:30.000Z",
"errorCode": null,
"errorMessage": null,
"messageId": "otp-static-001"
},
{
"kind": "otp",
"endpoint": "send-otp",
"status": "sent",
"recipient": "+222****88",
"messagePreview": "456***",
"provider": "national",
"channel": "whatsapp",
"parts": 1,
"createdAt": "2026-04-13T08:00:00.000Z",
"verifiedAt": null,
"errorCode": null,
"errorMessage": null,
"messageId": "otp-static-002"
}
]
}
}
}

Note : en production, successRate sur un bloc sans trafic peut être le nombre 0 ou la chaîne "0.00" selon le calcul ; prévoir les deux formes côté client si vous affichez sans normaliser.


Erreurs​

CodeConditionExemple de corps
400month mal formé{ "success": false, "message": "Paramètre month invalide. ..." }
401Clé absente{ "message": "API key is missing" }
403Clé invalide{ "success": false, "message": "Invalid API key" } (selon middleware)
403App non résolue{ "success": false, "message": "Application invalide" }
405Méthode ≠ GET{ "success": false, "message": "Method not allowed" }
500Erreur serveur{ "success": false, "message": "Échec de la récupération des statistiques" }

Exemples cURL​

Production — mois en cours (par défaut)​

curl -sS -H "api-key: VOTRE_CLE_API" \
"https://votre-domaine.com/api/app-stats"

Production — mois précis et limite de journaux​

curl -sS -H "api-key: VOTRE_CLE_API" \
"https://votre-domaine.com/api/app-stats?month=2026-03&logsLimit=100"

Production — plage de dates​

curl -sS -H "api-key: VOTRE_CLE_API" \
"https://votre-domaine.com/api/app-stats?from=2026-01-01&to=2026-01-31"

Local (alias possible)​

curl -sS -H "api-key: VOTRE_CLE_API" \
"http://localhost:3000/api/app-stats"
curl -sS -H "api-key: VOTRE_CLE_API" \
"http://localhost:3000/api/apps-stats"

Sécurité​

  • La clé API donne accès aux statistiques et aux aperçus de journaux de l'app : à traiter comme un secret (HTTPS obligatoire en production).
  • Ne pas exposer la clé dans le code front-end public ; appeler l'API depuis un backend ou un job sécurisé.

Environnement local​

Il faut un .env avec notamment MONGODB_URI et, si le cache KV est utilisé par le projet, KV_REST_API_URL et KV_REST_API_TOKEN (sinon la résolution par clé peut fonctionner sans cache selon la configuration du dépôt).