API REST v1

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.

La clé API ne doit JAMAIS être exposée dans du JavaScript frontend. Elle doit rester exclusivement côté backend.

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
  1. Créez un compte sur SoftAfrik Push
  2. Connectez-vous au dashboard client ou admin
  3. Générez ou copiez votre clé API (64 caractères hexadécimaux)
Sécurité
  • La clé est liée à un api_client_id unique
  • 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
HTTPCause
200Succès
400Requête invalide (paramètres manquants ou incorrects)
401Authentification échouée (clé API manquante, invalide ou suspendue)
404Ressource introuvable
429Rate limit ou quota dépassé
500Erreur serveur (FCM, base de données, etc.)

Quota & Santé API

GET /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
    }
  }
}
GET /health Public

Health check public (pas d'authentification requise).

{ "success": true, "message": "OK", "timestamp": "2026-05-02T13:17:50+00:00" }

Devices

POST /devices

Enregistre ou met à jour un device (upsert par push_token).

Payload
ChampTypeRequisDescription
push_tokenstring✅Token FCM ou APNS
platformstring✅android, ios ou web
external_user_idstring❌ID utilisateur côté client
emailstring❌Email utilisateur
phonestring❌Téléphone
countrystring❌Code ISO-3166 alpha-2 (ex: CI, TG, BJ)
languagestring❌Langue (défaut fr)
device_modelstring❌Modèle de l'appareil
browserstring❌Navigateur
user_agentstring❌User-Agent
timezonestring❌Fuseau horaire
permission_statusstring❌Statut permission notif
os_versionstring❌Version OS
app_versionstring❌Version app
metadataobject❌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" }
}
GET /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
    }
  }
}
PUT /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"
}
DELETE /devices/{id}

Révoque un device (soft delete). Le token_status passe à revoked.

{
  "success": true,
  "message": "Device revoked",
  "data": { "device_id": 123 }
}

Push / Notifications

POST /push/send

Crée une notification push et la met en file d'attente pour envoi asynchrone.

Payload
ChampTypeRequisDescription
titlestring✅Titre de la notification
messagestring✅Corps de la notification
target_typestring❌all (défaut), segment, device_ids, external_user_id
target_valuemixed❌Plateforme, IDs ou PUID selon target_type
image_urlstring❌URL image
action_urlstring❌URL ou deep link au clic
custom_dataobject❌Données JSON libres transmises au device
scheduled_atstring❌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"
  }
}
GET /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

POST /campaigns

Crée une campagne structurée avec segmentation et planification.

Payload
ChampTypeRequisDescription
namestring✅Nom interne
titlestring✅Titre du push
messagestring✅Corps du push
descriptionstring❌Description interne
segment_criteriastring❌Défaut all
platformstring❌android, ios, web
countrystring❌Filtre pays
image_urlstring❌Image
action_urlstring❌URL cible
scheduled_atstring❌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"
  }
}
GET /campaigns

Liste les campagnes.

DELETE /campaigns/{id}

Annule une campagne.


Tracking

1. Tracking authentifié (backend)
POST /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)
GET /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
GET /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énementDéclencheur
push.createdCréation d'un push (avant envoi)
push.deliveredJob traité avec succès par le worker
push.failedJob é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"
}
Les webhooks sont envoyés en mode fire-and-forget avec un timeout de 10 secondes. Aucun retry automatique n'est implémenté pour l'instant.

Rate Limits & Quotas

Les limites dépendent de votre plan :

PlanDevicesPushes/moisRequêtes/min
Free1 00010 000100
Pro50 000500 0001 000
EnterpriseIllimité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 || '/'));
});
Besoin d'aide ? Contactez-nous à dak@softafrik.com ou consultez la FAQ.