Webhooks
Recevez des notifications en temps reel lors des changements de statut de vos expeditions.
Webhooks
Les webhooks envoient une requete HTTP POST vers votre serveur des qu'un evenement se produit dans Swaloo. C'est le moyen recommande de synchroniser vos systemes plutot que d'interroger l'API en boucle (polling).
Prerequis
Les webhooks font partie d'un module d'abonnement. Seuls les plans Pro, Business et Entreprise incluent les webhooks. Le plan Decouverte n'y a pas acces.
Une notification n'est envoyee que si les trois conditions sont reunies :
- Le forfait de l'organisation inclut les webhooks (
hasWebhooks = true). - Une URL de destination (
webhookUrl) en HTTPS est renseignee. - Le webhook est active (
isWebhookEnabled = true).
Configuration
Le webhook se configure au niveau de l'organisation, via l'API ou le dashboard :
curl -X PATCH https://api.swaloo.com/api/v1/organizations/{id} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/merge-patch+json" \
-d '{"webhookUrl": "https://votre-serveur.com/webhooks/swaloo", "isWebhookEnabled": true}'| Champ | Type | Description |
|---|---|---|
webhookUrl | string | URL HTTPS de votre endpoint. Doit etre accessible publiquement. |
webhookSecret | string | Secret HMAC genere automatiquement (whsec_ + 40 hex). Lecture seule. |
isWebhookEnabled | boolean | Active/desactive l'envoi des notifications. |
Le webhookSecret est genere automatiquement a la creation de l'organisation. Il n'est pas modifiable. Conservez-le en variable d'environnement.
Evenements
Swaloo emet un seul type d'evenement : shipment.status_changed, emis a chaque transition de workflow d'une tache :
| Transition | Description | Statut expedition |
|---|---|---|
assign | Tache assignee a un chauffeur | pending |
start_approach | Chauffeur en route vers la collecte | pickup_in_progress |
pick_up | Colis recupere | picked_up |
start_delivery_approach | Chauffeur en route vers la livraison | in_transit |
start_transit | Livraison en cours | in_transit |
deliver | Colis remis au destinataire | delivered |
fail | Echec de collecte ou de livraison | depend du contexte |
cancel | Tache annulee | cancelled |
reactivate | Tache reactivee apres un echec | pending |
Une meme expedition genere plusieurs notifications au fil de son cycle de vie.
Format du payload
En-tetes : Content-Type: application/json et X-Swaloo-Signature: sha256=<hmac_hex>
| Champ | Type | Description |
|---|---|---|
event | string | Toujours "shipment.status_changed" |
shipmentId | string (uuid) | Identifiant de l'expedition |
trackingNumber | string | Numero de suivi (SWL-...) |
externalReference | string | null | Votre reference de commande |
status | string | Statut expedition : pending_approval, pending, pickup_in_progress, picked_up, in_transit, delivered, returned, cancelled |
taskType | string | Type de tache : pickup, delivery, transfer, return |
taskStatus | string | Statut tache : waiting_for_pickup, assigned_waiting, pending, assigned, pickup_in_progress, picked_up, delivery_in_progress, in_transit, delivered, failed, cancelled |
driverName | string | null | Nom du chauffeur ou null |
updatedAt | string (ISO 8601) | Horodatage |
Exemple : colis livre
{
"event": "shipment.status_changed",
"shipmentId": "550e8400-e29b-41d4-a716-446655440000",
"trackingNumber": "SWL-20260616-A1B2C3",
"externalReference": "CMD-20260616-001",
"status": "delivered",
"taskType": "delivery",
"taskStatus": "delivered",
"driverName": "Jean Dupont",
"updatedAt": "2026-06-16T15:02:00+00:00"
}Cycle de vie typique
1. assign (pickup) → status: pending, taskStatus: assigned
2. start_approach → status: pickup_in_progress, taskStatus: pickup_in_progress
3. pick_up → status: picked_up, taskStatus: picked_up
4. assign (delivery) → status: picked_up, taskStatus: assigned
5. start_delivery → status: in_transit, taskStatus: delivery_in_progress
6. deliver → status: delivered, taskStatus: deliveredVerifier la signature
Chaque requete inclut X-Swaloo-Signature: sha256=<hmac> — le HMAC-SHA256 du corps brut avec votre webhookSecret comme cle.
Calculez le HMAC sur le corps brut (avant parsing JSON). Re-serialiser l'objet modifierait les octets.
Node.js
const crypto = require('crypto');
app.post('/webhooks/swaloo', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('X-Swaloo-Signature') || '';
const expected = 'sha256=' +
crypto.createHmac('sha256', process.env.SWALOO_WEBHOOK_SECRET)
.update(req.body).digest('hex');
const valid = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.status(401).end();
const event = JSON.parse(req.body.toString());
// Traiter l'evenement (idempotent)...
res.status(200).json({ received: true });
});PHP
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SWALOO_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, getenv('SWALOO_WEBHOOK_SECRET'));
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
$event = json_decode($payload, true);
// Traiter selon $event['status']...
http_response_code(200);Politique de retry
| Code de reponse | Comportement |
|---|---|
2xx | Succes — livraison validee |
4xx | Rejet definitif — aucune nouvelle tentative |
5xx ou timeout/erreur reseau | Retry avec backoff exponentiel |
Strategie : jusqu'a 3 tentatives (delai : 1s, 2s, 4s). Apres epuisement, le message part en dead-letter queue. Votre endpoint a 10 secondes pour repondre.
Tester en local
# 1. Tunnel HTTPS avec ngrok
ngrok http 3000
# → https://abc123.ngrok.io
# 2. Configurer l'URL
curl -X PATCH https://api.swaloo.com/api/v1/organizations/{id} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/merge-patch+json" \
-d '{"webhookUrl": "https://abc123.ngrok.io/webhooks/swaloo", "isWebhookEnabled": true}'
# 3. Simuler un webhook manuellement
SECRET="whsec_3f9a8b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a"
PAYLOAD='{"event":"shipment.status_changed","shipmentId":"550e8400-...","trackingNumber":"SWL-20260616-A1B2C3","externalReference":"CMD-001","status":"delivered","taskType":"delivery","taskStatus":"delivered","driverName":"Test","updatedAt":"2026-06-16T15:00:00+00:00"}'
SIGNATURE="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"
curl -X POST http://localhost:3000/webhooks/swaloo \
-H "Content-Type: application/json" \
-H "X-Swaloo-Signature: $SIGNATURE" \
-d "$PAYLOAD"Bonnes pratiques
- Verifiez la signature sur chaque requete avant tout traitement.
- Idempotence : de-dupliquez sur
shipmentId+taskStatus+updatedAt. - Repondez vite en
2xxpuis traitez en arriere-plan. - HTTPS uniquement pour l'URL de destination.
- Utilisez
updatedAtpour ordonner les evenements (pas l'ordre d'arrivee). - Gerez les
null:externalReferenceetdriverNamepeuvent etre absents.
Erreurs courantes
| Probleme | Cause | Solution |
|---|---|---|
| Aucun webhook recu | Plan sans webhooks, URL vide, ou desactive | Verifiez les 3 conditions |
| Signature invalide | Body parse avant HMAC | Utilisez le corps brut |
| Webhooks en double | Retry apres timeout | Rendez votre handler idempotent |
| Pas de retry apres erreur | Votre serveur repond 4xx | Renvoyez 5xx pour forcer un retry |
Voir aussi : API externe | Endpoints | Erreurs | Statuts