Aller au contenu

Documentation API

Branchez votre système sur OSCARIO EXPRESS

Une API REST en JSON pour créer vos colis, suivre leur état et lire vos factures, et des webhooks signés pour être prévenu d'un changement sans interroger le serveur en boucle.

01Démarrer

L'accès API ne s'active pas tout seul : il est demandé, puis approuvé. Voici le parcours complet, de l'inscription au premier appel.

  1. Ouvrez un compte. L'API est ouverte aux comptes client, agence et livreur. Un compte marchand doit avoir son identité (KYC) validée : sans elle, le tableau de bord reste fermé et la création de colis par l'API est refusée.
  2. Demandez l'accès API depuis la page Paramètres API du tableau de bord. La demande est transmise aux administrateurs de la plateforme et, si votre compte est rattaché à une agence, à cette agence.
  3. Attendez l'approbation. Elle est donnée par un administrateur, par le commercial qui suit votre compte ou par votre agence de rattachement lorsqu'elle y est habilitée. Tant qu'elle manque, la génération de clé répond 403.
  4. Générez une clé dans Paramètres API : un nom, et une expiration facultative de 1 à 3 650 jours. La clé complète n'est affichée qu'une fois ; vous pouvez la révoquer à tout moment.
  5. Appelez l'API avec l'en-tête x-api-key. Commencez par GET /api/external/cities : le cityId d'une ville desservie est obligatoire pour créer un colis.
  6. Abonnez un webhook, facultatif, pour recevoir les changements d'état au lieu de les demander.

02URL de base et authentification

Toutes les routes décrites ici se trouvent sous cette adresse.

URL de base
https://api.oscario.pro

Chaque appel authentifié porte la clé dans l'en-tête x-api-key. Sans cet en-tête, ou avec une clé invalide ou expirée, la réponse est 401.

En-tête
x-api-key: osc_VOTRE_CLE

Une clé commence par osc_, suivi de 64 caractères hexadécimaux. Traitez-la comme un mot de passe : elle ne doit jamais apparaître dans le code d'une page web ou d'une application mobile.

Le compte, son type et ses droits se déduisent de la clé, jamais de la requête. Une clé client travaille sur les colis de son compte ; une clé agence ou livreur, sur les colis qui lui sont affectés.

03API marchande

Les routes du quotidien d'un e-commerçant : créer un colis, retrouver ses colis, lire leur état, leurs messages et ses factures.

Dates. Les paramètres de date attendent un horodatage UTC au format ISO 8601 terminé par « Z », par exemple 2026-07-01T00:00:00Z. Une date seule, une date sans fuseau ou une date avec décalage (+01:00) renvoie 400.

POST/api/external/colisClé client

Crée un colis. Réservé aux clés client : une clé agence ou livreur reçoit 403. fullname, phone et cityId sont obligatoires. Le téléphone est un mobile marocain de 10 chiffres commençant par 06 ou 07 : un numéro au format +212 est refusé. cityId doit désigner une ville desservie. Le montant à encaisser (price) ne peut pas dépasser le plafond de paiement à la livraison fixé par la plateforme, s'il en existe un. change=true crée un échange facturé au plein tarif. Avec l'en-tête Idempotency-Key, une reprise dans les 24 heures renvoie le même colis au lieu d'en créer un second.

Requête
curl -X POST https://api.oscario.pro/api/external/colis \
  -H "x-api-key: osc_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: commande-10432" \
  -d '{
    "fullname": "Ahmed Bennani",
    "phone": "0612345678",
    "cityId": "9b0c6a2e-4f1d-4c7b-8a3e-2d5f6b7c8d9e",
    "address": "12 Rue Hassan II",
    "price": 349.00,
    "product": "Montre connectée",
    "quantity": 1,
    "importRef": "10432",
    "note": "Appeler avant livraison"
  }'
Réponse
201 Created
{
  "success": true,
  "data": {
    "code": "OE-627313072514",
    "fullname": "Ahmed Bennani",
    "phone": "0612345678",
    "cityId": "9b0c6a2e-4f1d-4c7b-8a3e-2d5f6b7c8d9e",
    "price": "349.00",
    "stateId": 1,
    "cfees": "39.00",
    "createdAt": "2026-07-25T20:14:14.000Z"
  }
}

GET/api/external/colisClé API

Liste les colis de la clé, du plus récent au plus ancien, par pages. Tous les paramètres sont facultatifs.

ParamètreRôle
pageNuméro de page, à partir de 1.
limitColis par page, de 1 à 100 ; 20 par défaut.
stateIdUn seul état.
stateIdsPlusieurs états séparés par des virgules, par exemple 1,17.
searchRecherche dans le code de suivi, le nom et le téléphone ; 100 caractères au plus.
cityIdVille de destination (UUID).
importRefVotre propre numéro de commande, envoyé à la création.
from / toFenêtre sur la date de création. UTC terminé par « Z », sinon 400.
updatedFrom / updatedToFenêtre sur la date de dernière modification, pour ne reprendre que les colis modifiés depuis votre dernier passage. UTC terminé par « Z », sinon 400.
availabletrue ne garde que les colis qu'une agence peut livrer elle-même. Réservé aux clés agence : available=true avec une autre clé renvoie 400.
sortSeule valeur admise : tour, l'ordre de la tournée. Réservé aux clés agence et livreur : une clé client reçoit 400.
Requête
curl "https://api.oscario.pro/api/external/colis?page=1&limit=20&stateIds=1,17&from=2026-07-01T00:00:00Z" \
  -H "x-api-key: osc_VOTRE_CLE"
Réponse
200 OK
{
  "success": true,
  "data": [
    {
      "code": "OE-627313072514",
      "fullname": "Ahmed Bennani",
      "phone": "0612345678",
      "address": "12 Rue Hassan II",
      "cityId": "9b0c6a2e-...",
      "cityName": "Tiznit",
      "price": "349.00",
      "cfees": "39.00",
      "product": "Montre connectée",
      "quantity": 1,
      "importRef": "10432",
      "stateId": 17,
      "stateName": "Livré",
      "gpsLat": "29.6974210",
      "gpsLng": "-9.7359240",
      "createdAt": "2026-07-25T20:14:14.000Z",
      "updatedAt": "2026-07-27T15:02:00.000Z"
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 1,
  "totalPages": 1
}

GET/api/external/colis/:codeClé API

Renvoie l'état et le détail d'un colis de votre périmètre : destinataire, adresse, ville, montant, position GPS (gpsLat, gpsLng) partagée par le destinataire ou relevée à la livraison, null sinon et état de sa facturation (billingStatus : none, invoiced ou paid). Un code hors de votre périmètre renvoie 404.

Requête
curl https://api.oscario.pro/api/external/colis/OE-627313072514 \
  -H "x-api-key: osc_VOTRE_CLE"
Réponse
200 OK
{
  "success": true,
  "data": {
    "code": "OE-627313072514",
    "fullname": "Ahmed Bennani",
    "phone": "0612345678",
    "address": "12 Rue Hassan II",
    "cityName": "Tiznit",
    "price": "349.00",
    "cfees": "39.00",
    "stateId": 17,
    "stateName": "Livré",
    "gpsLat": "29.6974210",
    "gpsLng": "-9.7359240",
    "createdAt": "2026-07-25T20:14:14.000Z",
    "updatedAt": "2026-07-27T15:02:00.000Z",
    "billingStatus": "paid",
    "factureCode": "FAC-260728-0012",
    "facturePaidAt": "2026-07-29T10:00:00.000Z"
  }
}

GET/api/external/citiesClé API

Référentiel des villes desservies (id, name). Seul un cityId de cette liste est accepté à la création d'un colis.

Requête
curl https://api.oscario.pro/api/external/cities \
  -H "x-api-key: osc_VOTRE_CLE"
Réponse
200 OK
{
  "success": true,
  "data": [
    { "id": "e455712f-bcf0-4827-b579-b34b52026b0f", "name": "Casablanca" },
    { "id": "9b0c6a2e-4f1d-4c7b-8a3e-2d5f6b7c8d9e", "name": "Tiznit" }
  ]
}

GET/api/external/facturesClé API

Liste vos factures par pages (limit jusqu'à 100), avec les filtres type, paid (true ou false), dateFrom et dateTo. Une clé client ne voit que ses factures de type FAC. Aucun frais interne n'est exposé.

Requête
curl "https://api.oscario.pro/api/external/factures?page=1&limit=20&paid=false" \
  -H "x-api-key: osc_VOTRE_CLE"
Réponse
200 OK
{
  "success": true,
  "data": [
    {
      "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "code": "FAC-260728-0012",
      "type": "FAC",
      "totalAmount": "349.00",
      "totalFees": "39.00",
      "netAmount": "310.00",
      "paid": false,
      "paidAt": null,
      "createdAt": "2026-07-28T10:00:00.000Z",
      "updatedAt": "2026-07-28T10:00:00.000Z",
      "colisCount": 1
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 1,
  "totalPages": 1
}

GET/api/external/colis/:code/messagesClé API

Liste les messages du fil d'un colis de votre périmètre, du plus ancien au plus récent (limit jusqu'à 100, 50 par défaut). Les noms d'expéditeur sont masqués et les liens des pièces jointes ne sont pas exposés : hasAttachment indique seulement leur présence.

Requête
curl "https://api.oscario.pro/api/external/colis/OE-627313072514/messages?page=1&limit=50" \
  -H "x-api-key: osc_VOTRE_CLE"
Réponse
200 OK
{
  "success": true,
  "data": [
    {
      "id": "6f1e2d3c-4b5a-6978-8b9c-0d1e2f3a4b5c",
      "message": "Colis prêt, en attente de ramassage.",
      "messageType": "text",
      "senderType": "agency",
      "senderName": "Agence Casa",
      "hasAttachment": false,
      "createdAt": "2026-07-25T21:15:00.000Z",
      "deletedAt": null
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
}

Les réponses ci-dessus sont des extraits : certains champs facultatifs sont omis pour la lisibilité.

04API opérationnelle : agences et livreurs

Une clé agence ou livreur ne crée pas de colis. Elle lit les colis qui lui sont affectés et fait évoluer leur état, à l'unité ou par lot ; elle lit aussi les messages et les factures qui la concernent. Une agence consulte en plus ses bons de groupage et ses bons de retour, et réceptionne ses groupages entrants.

Ces routes ne sont pas décrites sur cette page publique. Leur documentation, avec les états autorisés et des exemples, s'affiche dans Paramètres API, selon le type de votre compte, une fois votre accès API approuvé : connectez-vous pour la consulter.

05Routes publiques

Les trois routes ci-dessous répondent sans clé. Elles sont limitées par adresse IP, à 30 ou 20 requêtes par minute selon la route : voir la section Limites de débit.

GET/api/external/track/:codeSans clé

Suivi public d'un colis par son code. Le nom du destinataire est masqué : « Ahmed Bennani » devient « Ahmed B. ». L'historique donne, du plus récent au plus ancien, chaque état, sa date, le type et la ville de l'acteur ; les notes internes n'y figurent jamais. Limité à 30 requêtes par minute par IP, compteur partagé avec l'envoi d'avis.

Requête
curl https://api.oscario.pro/api/external/track/OE-627313072514
Réponse
200 OK
{
  "success": true,
  "data": {
    "code": "OE-627313072514",
    "fullname": "Ahmed B.",
    "stateId": 17,
    "createdAt": "2026-07-25T20:14:14.000Z",
    "timeline": [
      {
        "stateId": 17,
        "stateName": "Livré",
        "userType": "dlm",
        "userCity": "Tiznit",
        "createdAt": "2026-07-27T15:02:00.000Z"
      }
    ]
  }
}

GET/api/external/public-feesSans clé

Grille publique des frais de livraison. Sans paramètre, elle renvoie les agences de départ et la liste des villes. Avec agencyId (prioritaire) ou sourceCityId, chaque ville porte son tarif (price) et seules les villes desservies sont renvoyées. Un agencyId inconnu est ignoré, sans erreur. Limité à 20 requêtes par minute par IP.

Requête
curl "https://api.oscario.pro/api/external/public-fees?agencyId=3f2b9c1e-..."
Réponse
200 OK
{
  "success": true,
  "data": {
    "agencies": [
      { "id": "3f2b9c1e-...", "name": "Agence Casa", "cityId": "e455712f-..." }
    ],
    "cities": [
      { "id": "e455712f-...", "name": "Casablanca", "price": 20 }
    ]
  }
}

POST/api/external/feedbackSans clé

Enregistre un avis sur une livraison : code, rating (entier de 1 à 5) et comment facultatif (1 000 caractères au plus). La réponse est identique que le code existe ou non, et un seul avis est retenu par colis. Limité à 30 requêtes par minute par IP, compteur partagé avec le suivi.

Requête
curl -X POST https://api.oscario.pro/api/external/feedback \
  -H "Content-Type: application/json" \
  -d '{ "code": "OE-627313072514", "rating": 5, "comment": "Livraison rapide" }'
Réponse
201 Created
{ "success": true, "data": { "received": true } }

06Webhooks

Un compte déclare une URL de webhook, depuis Paramètres API, et choisit les événements qu'il veut recevoir. L'URL doit être en HTTPS en production ; les adresses locales et privées sont refusées. Le secret de signature n'est affiché qu'à la création et à chaque régénération. Le bouton Tester envoie un événement ping signé.

Un événement ne part que vers les comptes concernés par l'objet, et seulement si leur accès API est encore actif.

ÉvénementEnvoyé quandDestinataires
colis.createdUn colis est créé.Client, agence et livreur du colis
colis.state_changedLe colis change d'état.Client, agence et livreur du colis
colis.location_updatedLe destinataire partage sa position GPS.Client, agence et livreur du colis
colis.messageUn nouveau message arrive dans le fil du colis.Client, agence et livreur du colis
groupage.incomingUn bon de groupage part du hub vers votre agence.Agence destinataire
facture.createdUne facture est émise.Titulaire de la facture et agence émettrice
facture.paidLa facture est réglée.Titulaire de la facture et agence émettrice
facture.reversedLe règlement est annulé : la facture redevient impayée.Titulaire de la facture et agence émettrice
return_bon.assignedUn bon de retour entrant vous est affecté et expédié.Agence destinataire
return_bon.shippedMême signal que return_bon.assigned, sous un nom plus explicite.Agence destinataire
return_bon.sealedUn bon de retour renvoyé vers votre agence d'origine est scellé.Agence destinataire

Événements de retrait. payout.approved, payout.tranche.created et payout.tranche.deleted sont acceptés à l'abonnement, mais aucun envoi ne les délivre aujourd'hui à un compte client, agence ou livreur. Ne construisez pas d'intégration sur eux.

Ce que reçoit votre serveur

Une requête POST en JSON, avec les champs event, timestamp et data, et les en-têtes X-OSCARIO-Event et X-OSCARIO-Signature. Le contenu de data dépend de l'événement et ne contient jamais de frais internes.

Exemple
POST https://votre-serveur.example/webhooks/oscario
Content-Type: application/json
X-OSCARIO-Event: colis.state_changed
X-OSCARIO-Signature: t=1785164520,v1=9f2c4e...

{
  "event": "colis.state_changed",
  "timestamp": "2026-07-27T15:02:00.000Z",
  "data": {
    "code": "OE-627313072514",
    "stateId": 17,
    "stateName": "Livré",
    "price": "349.00",
    "cityName": "Tiznit",
    "gpsLat": "29.6974210",
    "gpsLng": "-9.7359240",
    "updatedAt": "2026-07-27T15:02:00.000Z"
  }
}

Vérifier la signature

L'en-tête X-OSCARIO-Signature contient t, l'heure d'envoi en secondes Unix, et v1, le HMAC-SHA256 en hexadécimal calculé avec votre secret sur t, un point, puis le corps brut de la requête. Calculez-le sur le corps tel qu'il est reçu, avant de le décoder.

Node.js
const crypto = require('crypto');

const entete = req.get('X-OSCARIO-Signature');
const parts = Object.fromEntries(entete.split(',').map((p) => p.split('=')));
const attendu = crypto
  .createHmac('sha256', SECRET_WEBHOOK)
  .update(parts.t + '.' + corpsBrut)
  .digest('hex');

const valide = attendu.length === parts.v1.length
  && crypto.timingSafeEqual(Buffer.from(attendu), Buffer.from(parts.v1));

Échecs et reprises

Une livraison échoue si votre serveur ne répond pas dans les 10 secondes, répond hors de la plage 2xx ou renvoie une redirection, qui n'est jamais suivie. Elle est alors retentée, jusqu'à 5 tentatives au total, avec un délai qui double à partir de 30 secondes. Après 20 livraisons consécutives dont toutes les tentatives ont échoué, le webhook est désactivé automatiquement ; une livraison ou un test réussi remet ce compteur à zéro.

07Limites de débit

Cinq plafonds s'appliquent, chacun sur une fenêtre de durée fixe et avec sa propre clé de comptage. Les valeurs ci-dessous sont celles appliquées par défaut ; l'exploitant de la plateforme peut modifier les deux limites par compte.

PortéePlafondCompté par
Lectures authentifiées300 / minuteCompte (toutes ses clés confondues)
Écritures authentifiées, dont la création de colis60 / minuteCompte (toutes ses clés confondues)
/track/:code et /feedback, ensemble30 / minuteAdresse IP
/public-fees20 / minuteAdresse IP
Toute requête sous /api/external, avant l'authentification12 000 / 15 minutesAdresse IP

Ce qui vous limite en premier dépend de la route. Un serveur qui appelle avec une clé bute sur les limites par compte, bien avant la garde par IP. Les trois routes publiques, elles, s'arrêtent à 30 ou 20 requêtes par minute par IP : dimensionnez un suivi en masse sur GET /api/external/colis avec une clé, pas sur /track.

Chaque réponse porte les en-têtes RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset. Au-delà du plafond, la réponse est 429 et son champ code nomme le limiteur atteint.

Exemple
429 Too Many Requests
{
  "success": false,
  "message": "Too many requests, please try again later",
  "code": "rate_limited:publicTrackLimiter"
}

08Erreurs fréquentes

Le corps de chaque erreur porte un champ message qui en donne la cause. Pour un corps ou un paramètre mal formé, message vaut « Validation error » et la liste errors indique, pour chaque champ refusé, son chemin et la règle non respectée.

StatutCause la plus fréquente
400Corps ou paramètre invalide : téléphone hors format, cityId qui n'est pas un UUID, date sans « Z », limit au-delà de 100. Ville non desservie. Compte client rattaché à aucune agence. Montant au-delà du plafond de paiement à la livraison. available ou sort=tour avec une clé qui n'y a pas droit.
401En-tête x-api-key absent, clé invalide ou expirée.
403Accès API non activé sur le compte, ou retiré. Route réservée à un autre type de clé, par exemple une création de colis avec une clé agence. Identité (KYC) non validée lors d'une création de colis par une clé client.
404Colis inexistant ou hors du périmètre de la clé : les deux cas reçoivent la même réponse.
429Limite de débit atteinte : attendez la fin de la fenêtre indiquée par RateLimit-Reset.

Prêt à intégrer ?

Ouvrez un compte, faites valider votre identité, puis demandez l'accès API depuis Paramètres API. Une fois la demande approuvée, vous créez et révoquez vos clés vous-même.

Déjà un compte ? Se connecter