Documentation de l'API
Connectez votre ERP ou logiciel métier : lisez les ventes, clients et factures de votre organisation, créez des ventes et recevez des notifications en temps réel. Testez chaque requête directement depuis cette page.
URL de base : https://api.egicore.fr/api/v1/external
Authentification
Chaque requête porte la clé dans l'en-tête x-api-key. Un administrateur de votre société crée les clés dans Mon compte → API en choisissant leurs scopes. La clé complète (egk_…) n'est affichée qu'une seule fois, à la création ; Egicore n'en conserve qu'une empreinte.
Une clé est valable 365 jours par défaut (730 jours au maximum). Une clé révoquée ou expirée est refusée immédiatement (401).
curl https://api.egicore.fr/api/v1/external/orders \
-H "x-api-key: egk_votre_cle"Scopes
Chaque route exige un scope. Une clé sans le scope requis reçoit 403 INSUFFICIENT_SCOPE.
| Scope | Autorise |
|---|---|
orders:read | Lire les ventes (GET /orders, GET /orders/{id}) |
orders:write | Créer des ventes (POST /orders) |
clients:read | Lire les clients (GET /clients, GET /clients/{id}) |
invoices:read | Lire les factures (GET /invoices, GET /invoices/{id}) |
Réponses & pagination
Les réponses sont en JSON. Une collection est enveloppée dans data et meta ; une ressource unique renvoie { "data": { … } }.
Paramètres de pagination : page (défaut 1) et limit (défaut 10, maximum 100 ; une valeur supérieure est ramenée à 100). Les montants sont en euros, les dates au format ISO 8601 (UTC) et les identifiants sont des UUID.
{ "data": [ … ], "meta": { "page": 1, "limit": 10, "total": 42 } }Statuts de vente
Valeurs possibles du champ status des ventes et des factures, et du filtre status de GET /orders et GET /invoices :
| Valeur | Signification |
|---|---|
DRAFT | Brouillon |
PENDING_PAYMENT | En attente de paiement |
PAYMENT_RECEIVED | Paiement reçu |
IN_ESCROW | Fonds cantonnés |
DELIVERED | Livrée |
CLOSED | Clôturée (fonds libérés) |
DISPUTED | Litige en cours |
RESOLVED | Litige résolu |
REFUNDED | Remboursée |
Limites de débit
120 requêtes par minute et par clé (fenêtre fixe de 60 s). Au-delà, l'API répond 429 avec le code RATE_LIMIT_EXCEEDED jusqu'à la minute suivante.
Une protection générale de 100 requêtes par minute et par adresse IP s'applique aussi à toute l'API : si toutes vos requêtes partent de la même adresse, c'est cette limite qui est atteinte en premier. Elle renvoie également 429 avec le code RATE_LIMIT_EXCEEDED.
Chaque appel (clé, méthode, chemin, code de réponse) est journalisé pour audit.
Erreurs
Toute erreur renvoie le statut HTTP approprié et un corps au format :
{ "error": { "code": "INSUFFICIENT_SCOPE", "message": "Scope(s) manquant(s) : orders:write" } }Erreur de validation (400) : message est un tableau.
{
"error": {
"code": "VALIDATION_ERROR",
"message": ["clientId must be a UUID", "depositRate doit être 0 (sans acompte) ou compris entre 0.1 et 0.4"]
}
}| HTTP | Code | Cas |
|---|---|---|
| 400 | VALIDATION_ERROR | Corps de POST /orders invalide ou champ inconnu. message est alors un tableau de messages de validation. |
| 400 | INVALID_STATUS_FILTER | Filtre status de GET /orders ou GET /invoices hors de la liste des statuts de vente. |
| 400 | INVALID_PAGINATION | page ou limit n’est pas un entier supérieur ou égal à 1. |
| 401 | API_KEY_MISSING | En-tête x-api-key absent. |
| 401 | API_KEY_INVALID | Clé inconnue ou révoquée. |
| 401 | API_KEY_EXPIRED | Clé arrivée à expiration. Créez-en une nouvelle dans Mon compte → API. |
| 403 | INSUFFICIENT_SCOPE | La clé ne porte pas le scope requis par la route (le message liste les scopes manquants). |
| 404 | ORDER_NOT_FOUND | Vente introuvable dans votre organisation, ou identifiant qui n’est pas un UUID (GET /orders/{id}, GET /invoices/{id}). |
| 404 | CLIENT_NOT_FOUND | Client introuvable dans votre organisation, ou identifiant qui n’est pas un UUID (GET /clients/{id}) ; clientId inconnu dans POST /orders. |
| 404 | NOT_FOUND | Route inconnue (chemin ou méthode). |
| 404 | NO_SELLER_IN_ORG | POST /orders : aucun utilisateur de votre organisation auquel rattacher la vente. |
| 429 | RATE_LIMIT_EXCEEDED | Quota de requêtes dépassé, par clé ou par adresse IP (voir Limites de débit). |
| 500 | INTERNAL_ERROR | Erreur inattendue. Aucun détail technique n’est renvoyé ; contactez le support avec l’heure de l’appel. |
Les messages sont destinés au débogage et peuvent évoluer : basez votre logique sur le statut HTTP et le champ code.
Ventes
Corps de POST /orders
Les champs non listés sont refusés (400 VALIDATION_ERROR). Le devis signé n'est pas transmis par l'API : le vendeur le joint dans Egicore avant de confirmer la vente.
| Champ | Type | Requis | Règle |
|---|---|---|---|
clientId | string (UUID) | Oui | Identifiant d'un client de votre organisation (voir GET /clients). |
totalHt | number | Oui | Montant total HT en euros, supérieur ou égal à 0. |
totalTtc | number | Oui | Montant total TTC en euros, supérieur ou égal à 0. |
depositRate | number | Oui | Taux d'acompte : 0 (sans acompte) ou entre 0.1 et 0.4. |
title | string | Non | Intitulé de la vente. Facultatif ici, mais demandé au vendeur pour confirmer le brouillon. |
balanceEscrow | boolean | Non | Met aussi le solde sous cantonnement jusqu’à la livraison. Défaut : true. |
paymentMethod | string | Non | BANK_TRANSFER, PAY_BY_BANK, CARD ou SEPA_DIRECT_DEBIT. |
deliveryDeadline | string (date ISO) | Non | Échéance de livraison : date future, au plus tard dans 365 jours. Facultative ici, mais demandée au vendeur pour confirmer le brouillon. |
notes | string | Non | Notes internes, non visibles du payeur. |
Clients
Factures
Webhooks sortants
Enregistrez une URL de réception dans Mon compte → API → Webhooks sortants et choisissez les événements à recevoir. L'URL doit être en https et publique (pas de localhost ni d'adresse IP privée). Un secret de signature whsec_… s'affiche une seule fois à la création : conservez-le. Depuis le même écran, vous envoyez un événement de test, consultez le journal des 20 dernières livraisons et relancez une livraison en échec.
Événements
| Événement | Émis quand | Champs de data |
|---|---|---|
order.created | Une vente est créée. Vente saisie dans Egicore : à sa confirmation (status PENDING_PAYMENT). Vente créée par POST /orders : immédiatement, au statut DRAFT. | orderId, orderNumber, status, totalTtc, clientId |
order.status_changed | À chaque changement de statut d’une vente, quelle qu’en soit l’origine : action du vendeur ou du payeur, fonds reçus ou libérés par le prestataire de paiement, litige, remboursement, intervention de l’équipe Egicore. | orderId, orderNumber, from, to |
test.ping | Envoyé à la demande depuis Mon compte → API (« Envoyer un test ») vers un seul endpoint. Non abonnable. | message, endpointId, sentAt |
Requête envoyée
Egicore envoie un POST JSON à votre URL. Le corps contient le nom de l'événement, ses données et l'identifiant unique de la livraison :
{
"event": "order.status_changed",
"data": {
"orderId": "3f2b8c1e-7a4d-4e9b-9c21-5d8e6f0a1b2c",
"orderNumber": "EGI-202609-00042",
"from": "IN_ESCROW",
"to": "DELIVERED"
},
"deliveryId": "6a1f0c9e-2d3b-4e5f-8a7b-1c2d3e4f5a6b"
}Autres exemples (order.created, test.ping)
{
"event": "order.created",
"data": {
"orderId": "b7e1d2c3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
"orderNumber": "EGI-202610-00007",
"status": "DRAFT",
"totalTtc": 14400,
"clientId": "8d0c4f6a-2b1e-4c7d-9a3f-1e5b7c9d2f40"
},
"deliveryId": "0e9d8c7b-6a5f-4e3d-9c2b-1a0f9e8d7c6b"
}{
"event": "test.ping",
"data": {
"message": "Test event sent from Egicore.",
"endpointId": "5c4b3a29-1807-4f6e-9d5c-4b3a2918f7e6",
"sentAt": "2026-10-08T09:45:12.345Z"
},
"deliveryId": "9b8a7f6e-5d4c-4b3a-8f1e-0d9c8b7a6f5e"
}| En-tête | Valeur |
|---|---|
Content-Type | application/json |
X-Egicore-Signature | HMAC-SHA256 du corps brut, clé = votre secret whsec_… (utilisé tel quel), encodé en hexadécimal minuscule |
Aucun horodatage n'est signé : protégez-vous du rejeu en dédoublonnant sur deliveryId, identique pour toutes les tentatives d'une même livraison, relance manuelle comprise.
Vérifier la signature
Recalculez le HMAC sur le corps brut reçu et comparez-le en temps constant à l'en-tête avant tout traitement :
import crypto from 'node:crypto'
import express from 'express'
const app = express()
const SECRET = process.env.EGICORE_WEBHOOK_SECRET // whsec_…
// Corps BRUT : la signature porte sur les octets reçus, pas sur un JSON re-sérialisé.
app.post('/egicore-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const received = req.get('X-Egicore-Signature') ?? ''
const expected = crypto
.createHmac('sha256', SECRET)
.update(req.body)
.digest('hex')
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
if (!valid) return res.status(401).end()
const { event, data, deliveryId } = JSON.parse(req.body.toString('utf8'))
// Ignorez un deliveryId déjà traité, puis traitez l'événement en asynchrone.
res.status(200).end()
})Réponse attendue et nouvelles tentatives
Répondez par un code 2xx dès réception, puis traitez l'événement en asynchrone. Chaque tentative attend votre réponse 10 secondes au maximum. Un dépassement de ce délai, tout autre code, une erreur réseau ou une redirection (3xx, jamais suivie) compte comme un échec.
En cas d'échec, Egicore réessaie automatiquement : 5 tentatives au total, espacées de 15 s, 30 s, 1 min puis 2 min. Après la 5e tentative, la livraison passe en Échec ; vous pouvez alors la relancer depuis le journal (mêmes corps et deliveryId). Statuts du journal : En cours, Livrée, Échec. L'ordre d'arrivée des événements n'est pas garanti : appuyez-vous sur les champs from / to et sur l'état renvoyé par GET /orders/{id}.
Versioning & support
Le préfixe /api/v1/ identifie la version ; les ajouts rétrocompatibles (nouveaux champs, nouveaux événements) n'en changent pas. Ignorez les champs que vous ne connaissez pas. Support : support@egicore.fr.