Documentation développeur
Intégrez les notifications push Android, iOS et Web en quelques minutes avec notre API REST simple et sécurisée.
Introduction
SoftAfrik Push est une plateforme SaaS de notifications push destinée au marché africain. Elle prend en charge :
- Android via Firebase Cloud Messaging (FCM) HTTP v1
- iOS via Apple Push Notification Service (APNS)
- Web Push via FCM
Toutes les requêtes API (sauf les URLs de tracking publiques) nécessitent une clé API passée dans le header X-API-Key.
Authentification
Toutes les requêtes API doivent inclure l'en-tête suivant :
Content-Type: application/json
X-API-Key: sk_live_votre_cle_api_ici
Obtenir une clé API
- Créez un compte sur SoftAfrik Push
- Connectez-vous au dashboard client ou admin
- Générez ou copiez votre clé API (64 caractères hexadécimaux)
Sécurité
- La clé est liée à un
api_client_idunique - Le client doit avoir le statut
active - Un client soft-deleted est rejeté
Base URL
https://push.softafrik.com/api/v1
En développement local (Docker) :
http://localhost:8080/api/v1
Formats & Erreurs
Réponse succès (HTTP 200)
{
"success": true,
"message": "Push created successfully",
"data": { ... }
}
Réponse erreur
{
"success": false,
"error": "title and message are required",
"details": { ... }
}
Codes HTTP courants
| HTTP | Cause |
|---|---|
| 200 | Succès |
| 400 | Requête invalide (paramètres manquants ou incorrects) |
| 401 | Authentification échouée (clé API manquante, invalide ou suspendue) |
| 404 | Ressource introuvable |
| 429 | Rate limit ou quota dépassé |
| 500 | Erreur serveur (FCM, base de données, etc.) |
Quota & Santé API
/quota
Vérifie la santé de l'API, les limites de votre plan et la consommation actuelle.
Réponse (200)
{
"success": true,
"message": "OK",
"data": {
"plan": { "code": "pro", "name": "Pro" },
"limits": {
"devices": 50000,
"pushes_per_month": 500000,
"api_requests_per_minute": 1000
},
"current_month": {
"devices_count": 12,
"pushes_sent": 340,
"api_requests": 1200
},
"totals": {
"active_devices": 45,
"total_pushes": 1280
}
}
}
/health
Public
Health check public (pas d'authentification requise).
{ "success": true, "message": "OK", "timestamp": "2026-05-02T13:17:50+00:00" }
Devices
/devices
Enregistre ou met à jour un device (upsert par push_token).
Payload
| Champ | Type | Requis | Description |
|---|---|---|---|
push_token | string | ✅ | Token FCM ou APNS |
platform | string | ✅ | android, ios ou web |
external_user_id | string | ❌ | ID utilisateur côté client |
email | string | ❌ | Email utilisateur |
phone | string | ❌ | Téléphone |
country | string | ❌ | Code ISO-3166 alpha-2 (ex: CI, TG, BJ) |
language | string | ❌ | Langue (défaut fr) |
device_model | string | ❌ | Modèle de l'appareil |
browser | string | ❌ | Navigateur |
user_agent | string | ❌ | User-Agent |
timezone | string | ❌ | Fuseau horaire |
permission_status | string | ❌ | Statut permission notif |
os_version | string | ❌ | Version OS |
app_version | string | ❌ | Version app |
metadata | object | ❌ | JSON libre |
Exemple
{
"push_token": "dGhpcyBpcyBhIGZha2UgdG9rZW4...",
"platform": "web",
"external_user_id": "customer_12345",
"email": "client@example.com",
"phone": "+22897417951",
"country": "TG",
"language": "fr",
"device_model": "Chrome Desktop",
"os_version": "Windows 11",
"app_version": "1.0.0"
}
Réponse
{
"success": true,
"message": "Device registered",
"data": { "device_id": 123, "platform": "web", "token_status": "valid" }
}
/devices?page=1&platform=web&status=valid&country=TG
Liste les devices avec pagination et filtres.
Réponse
{
"success": true,
"message": "OK",
"data": {
"devices": [
{
"id": 123,
"platform": "web",
"push_token": "dGhpcyBpcyBhIGZha2UgdG9rZW4...",
"token_status": "valid",
"external_user_id": "customer_12345",
"last_active_at": "2026-04-25 14:30:00",
"created_at": "2026-04-20 10:00:00"
}
],
"pagination": {
"total": 45,
"per_page": 20,
"current_page": 1,
"last_page": 3
}
}
}
/devices/{id}
Met à jour un device. Champs acceptés : device_model, language, country, token_status.
{
"device_model": "Chrome 124",
"language": "fr",
"country": "TG",
"token_status": "valid"
}
/devices/{id}
Révoque un device (soft delete). Le token_status passe à revoked.
{
"success": true,
"message": "Device revoked",
"data": { "device_id": 123 }
}
Push / Notifications
/push/send
Crée une notification push et la met en file d'attente pour envoi asynchrone.
Payload
| Champ | Type | Requis | Description |
|---|---|---|---|
title | string | ✅ | Titre de la notification |
message | string | ✅ | Corps de la notification |
target_type | string | ❌ | all (défaut), segment, device_ids, external_user_id |
target_value | mixed | ❌ | Plateforme, IDs ou PUID selon target_type |
image_url | string | ❌ | URL image |
action_url | string | ❌ | URL ou deep link au clic |
custom_data | object | ❌ | Données JSON libres transmises au device |
scheduled_at | string | ❌ | Planification YYYY-MM-DD HH:MM:SS |
Exemple — Envoi à tous
{
"title": "Nouvelle promo !",
"message": "Profitez de -30% sur tous les articles",
"target_type": "all",
"image_url": "https://cdn.example.com/banner.jpg",
"action_url": "https://example.com/promos/summer",
"custom_data": { "promo_id": 42, "expires": "2026-05-01" }
}
Exemple — Ciblage par external_user_id
{
"title": "Commande confirmée",
"message": "Votre commande #123 a été confirmée.",
"target_type": "external_user_id",
"target_value": "customer_12345",
"action_url": "https://example.com/orders/123",
"custom_data": { "order_reference": "ORD-123", "type": "order_status" }
}
Exemple — Ciblage par device IDs
{
"title": "Message privé",
"message": "Un vendeur vous a répondu.",
"target_type": "device_ids",
"target_value": [123, 124],
"action_url": "https://example.com/messages/42"
}
Réponse
{
"success": true,
"message": "Push created successfully",
"data": {
"push_id": 128,
"recipients": 342,
"status": "queued"
}
}
/push/{id}/status
Consulte l'état agrégé d'un envoi (délivrés, ouverts, échoués, en attente).
{
"success": true,
"message": "OK",
"data": {
"push_id": 128,
"title": "Nouvelle promo !",
"status": "sent",
"total_recipients": 342,
"sent_at": "2026-04-25 20:00:00",
"delivery": {
"delivered": 320,
"opened": 89,
"failed": 22,
"pending": 0
}
}
}
Campagnes
/campaigns
Crée une campagne structurée avec segmentation et planification.
Payload
| Champ | Type | Requis | Description |
|---|---|---|---|
name | string | ✅ | Nom interne |
title | string | ✅ | Titre du push |
message | string | ✅ | Corps du push |
description | string | ❌ | Description interne |
segment_criteria | string | ❌ | Défaut all |
platform | string | ❌ | android, ios, web |
country | string | ❌ | Filtre pays |
image_url | string | ❌ | Image |
action_url | string | ❌ | URL cible |
scheduled_at | string | ❌ | Planification |
Exemple
{
"name": "Campagne Ramadan 2026",
"title": "Promo Ramadan",
"message": "Des offres exceptionnelles pendant le mois béni",
"platform": "android",
"country": "CI",
"scheduled_at": "2026-03-01 20:00:00"
}
Réponse
{
"success": true,
"message": "Campaign created",
"data": {
"campaign_id": 45,
"push_id": 129,
"recipients": 120,
"status": "active"
}
}
/campaigns
Liste les campagnes.
/campaigns/{id}
Annule une campagne.
Tracking
1. Tracking authentifié (backend)
/push/{id}/opened
POST
/push/{id}/clicked
Headers : X-API-Key + Content-Type: application/json
{ "device_id": 123 }
{ "success": true, "message": "Event tracked" }
2. URLs publiques signées (Service Worker)
/track/open?t=TOKEN&s=SIGNATURE
Public
Tracking d'ouverture sans authentification. La signature HMAC-SHA256 garantit l'authenticité.
GET /api/v1/track/open?t=eyJ...&s=abc123
/track/click?t=TOKEN&s=SIGNATURE&redirect=URL
Public
Tracking de clic avec redirection.
GET /api/v1/track/click?t=eyJ...&s=abc123&redirect=https%3A%2F%2Fexample.com
Dans le Service Worker
self.addEventListener('notificationclick', function(event) {
event.notification.close();
const data = event.notification.data;
fetch(data.tracking_url_click);
event.waitUntil(clients.openWindow(data.action_url));
});
Webhooks
SoftAfrik Push peut notifier votre backend à chaque événement important. Configurez votre URL webhook dans le dashboard.
| Événement | Déclencheur |
|---|---|
push.created | Création d'un push (avant envoi) |
push.delivered | Job traité avec succès par le worker |
push.failed | Job échoué définitivement (dead letter) |
Payload push.delivered
{
"event": "push.delivered",
"push_id": 128,
"device_id": 123,
"platform": "web",
"status": "delivered",
"timestamp": "2026-04-25T20:01:15+00:00"
}
Rate Limits & Quotas
Les limites dépendent de votre plan :
| Plan | Devices | Pushes/mois | Requêtes/min |
|---|---|---|---|
| Free | 1 000 | 10 000 | 100 |
| Pro | 50 000 | 500 000 | 1 000 |
| Enterprise | Illimité | Illimité | 10 000 |
En cas de dépassement, l'API renvoie HTTP 429 avec un message explicite.
Exemples cURL complets
1. Tester l'API (quota)
curl -s -X GET \
-H "X-API-Key: sk_live_votre_cle_api" \
"https://push.softafrik.com/api/v1/quota" | jq .
2. Enregistrer un device
curl -s -X POST \
-H "X-API-Key: sk_live_votre_cle_api" \
-H "Content-Type: application/json" \
-d '{
"push_token": "dGhpcyBpcyBhIGZha2UgdG9rZW4...",
"platform": "web",
"external_user_id": "customer_12345",
"email": "client@example.com",
"phone": "+22897417951",
"country": "TG",
"language": "fr"
}' \
"https://push.softafrik.com/api/v1/devices" | jq .
3. Envoyer un push (tous)
curl -s -X POST \
-H "X-API-Key: sk_live_votre_cle_api" \
-H "Content-Type: application/json" \
-d '{
"title": "Nouvelle promo !",
"message": "Profitez de -30% sur tous les articles",
"target_type": "all",
"image_url": "https://cdn.example.com/banner.jpg",
"action_url": "https://example.com/promos/summer",
"custom_data": { "promo_id": 42 }
}' \
"https://push.softafrik.com/api/v1/push/send" | jq .
4. Statut d'un push
curl -s -X GET \
-H "X-API-Key: sk_live_votre_cle_api" \
"https://push.softafrik.com/api/v1/push/128/status" | jq .
5. Créer une campagne planifiée
curl -s -X POST \
-H "X-API-Key: sk_live_votre_cle_api" \
-H "Content-Type: application/json" \
-d '{
"name": "Campagne Ramadan 2026",
"title": "Promo Ramadan",
"message": "Des offres exceptionnelles",
"platform": "android",
"country": "CI",
"scheduled_at": "2026-03-01 20:00:00"
}' \
"https://push.softafrik.com/api/v1/campaigns" | jq .
6. Révoquer un device
curl -s -X DELETE \
-H "X-API-Key: sk_live_votre_cle_api" \
"https://push.softafrik.com/api/v1/devices/123" | jq .
Exemple d'intégration PHP
<?php
class SoftAfrikPushClient {
private string $apiKey;
private string $baseUrl = 'https://push.softafrik.com/api/v1';
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
private function request(string $method, string $endpoint, array $data = null): array {
$ch = curl_init($this->baseUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-API-Key: ' . $this->apiKey
]);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
if ($method === 'POST') curl_setopt($ch, CURLOPT_POST, true);
if ($method === 'PUT') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
if ($method === 'DELETE') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true) ?? [];
}
public function registerDevice(array $device): array {
return $this->request('POST', '/devices', $device);
}
public function sendPush(array $push): array {
return $this->request('POST', '/push/send', $push);
}
public function getQuota(): array {
return $this->request('GET', '/quota');
}
}
// Utilisation
$client = new SoftAfrikPushClient('sk_live_votre_cle_api');
$client->registerDevice([
'push_token' => 'token_ici',
'platform' => 'web',
'external_user_id' => 'user_123'
]);
$client->sendPush([
'title' => 'Bienvenue !',
'message' => 'Merci de nous avoir rejoint.',
'target_type' => 'all'
]);
?>
Service Worker (Web Push)
Exemple de firebase-messaging-sw.js pour recevoir les notifications Web Push :
importScripts('https://www.gstatic.com/firebasejs/10.0.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.0.0/firebase-messaging-compat.js');
firebase.initializeApp({
apiKey: "YOUR_FIREBASE_API_KEY",
projectId: "your-project-id",
messagingSenderId: "123456789"
});
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function(payload) {
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: '/icon.png',
data: payload.data
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
const data = event.notification.data;
if (data && data.tracking_url_click) {
fetch(data.tracking_url_click);
}
event.waitUntil(clients.openWindow(data.action_url || '/'));
});