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).