Vue d'ensemble
L'API publique permet à un système tiers (ERP, e-commerce, outil métier, script) de lire et d'écrire les données de votre entreprise dans ApsionScan, de machine à machine, sans passer par l'interface.
- URL de base :
https://apsionscan.com/api/v1 - Format : JSON en UTF-8, dates ISO 8601 en UTC, propriétés en camelCase
- Authentification : une clé API par intégration, envoyée dans l’en-tête
Authorization - Versionnement dans l’URL : toute rupture de contrat passera par
/api/v2; l’ajout de champs optionnels n’est pas une rupture
État des lieux
L'API est construite par étapes. Le socle d'authentification est en production ; les ressources métier arrivent dans les prochaines versions. Cette page est mise à jour à chaque étape (voir la date en haut de page et l'historique en bas).
| Fonctionnalité | Statut |
|---|---|
| Clés API, scopes, expiration, révocation, liste d’IP | Disponible |
GET /v1/me : identité de la clé (test de connexion) | Disponible |
Format d’erreur standard (RFC 7807), en-tête Idempotency-Key, pagination par curseur, limite de débit par clé, spécification OpenAPI | Disponible |
| Produits, entrepôts et emplacements, niveaux de stock, mouvements, transferts | Disponible |
| Clients, commandes clients, fournisseurs, commandes fournisseurs et réceptions | Prévu |
| Gestion des clés dans Paramètres › Clés API | Disponible |
| Webhooks sortants (notifications d’événements) | Prévu |
Obtenir une clé API
DisponibleUne clé appartient à une entreprise. Elle est rattachée à un compte de service technique, qui n'occupe pas de siège et ne peut pas se connecter à l'interface. Seuls le propriétaire et les administrateurs de l'entreprise peuvent créer ou révoquer des clés.
- Dans ApsionScan, ouvrez Paramètres › Clés API (onglet visible avec la permission « Gérer les clés API », accordée par défaut au propriétaire et aux administrateurs).
- Cliquez sur « Nouvelle clé », donnez-lui un nom, une source et les droits nécessaires, puis copiez le secret affiché.
- Le secret n’est affiché qu’une seule fois, à la création. ApsionScan n’en garde qu’une empreinte et ne peut pas vous le renvoyer.
- Chaque clé porte un nom, une source (l’identifiant de votre intégration, par exemple
erp-x) et une liste de scopes. - Options : date d’expiration et liste d’adresses IP ou de plages CIDR autorisées. Le nom, les droits, les IP et l’expiration restent modifiables ensuite ; la source, non.
S'authentifier
DisponibleUne clé a la forme aps_live_<préfixe>_<secret>. Envoyez-la à chaque requête dans l’en-tête Authorization, avec le schéma Bearer.
curl https://apsionscan.com/api/v1/me \
-H "Authorization: Bearer aps_live_AbCd1234_votreSecretDe32Caracteres000000"{
"company": { "id": "7c0b…", "name": "Votre entreprise" },
"apiKey": {
"id": "1f3e…",
"name": "ERP production",
"prefix": "aps_live_AbCd1234",
"source": "erp-x",
"scopes": ["products:read", "stock:read"],
"expiresAt": null
},
"rateLimit": { "requestsPerMinute": 600 }
}- Les clés ne sont acceptées que sous
/api/v1. Ailleurs, la requête est refusée (403api_key_not_allowed). - À l’inverse,
/api/v1n’accepte que des clés : une session de l’application ou un jeton utilisateur y est refusé (401api_key_required). - Appelez l’API uniquement depuis un serveur. Une clé ne doit jamais apparaître dans un navigateur, une application mobile ou un dépôt de code.
Scopes et permissions
DisponibleChaque requête doit passer deux contrôles : le scope de la clé (ce que la clé a le droit de faire) et les permissions du compte de service dans l'entreprise (ce que l'entreprise l'autorise à faire). Accordez à chaque clé le minimum nécessaire.
| Scope | Donne accès à |
|---|---|
products:read · products:write | Catalogue produits |
warehouses:read | Entrepôts et emplacements |
stock:read · stock:write | Niveaux de stock, mouvements et transferts |
orders:read · orders:write | Clients et commandes clients |
purchasing:read · purchasing:write | Fournisseurs, commandes fournisseurs et réceptions |
webhooks:manage | Abonnements aux webhooks (à venir) |
GET /v1/me ne demande aucun scope. Les scopes métier seront contrôlés dès la publication des ressources correspondantes.
Erreurs
DisponibleLes erreurs suivent la RFC 7807 (Content-Type: application/problem+json). Utilisez le champ code, stable, dans votre logique ; detail est un texte lisible qui peut évoluer. requestId (aussi dans l’en-tête X-Request-Id) identifie la requête auprès du support.
{
"type": "https://apsionscan.com/en/developers/api#erreurs",
"title": "Forbidden",
"status": 403,
"code": "insufficient_scope",
"detail": "Cette clé API n'a pas le(s) scope(s) requis : stock:write.",
"instance": "/api/v1/stock-movements",
"requestId": "6f1c2d9e-…",
"requiredScopes": ["stock:write"]
}| HTTP | code | Signification |
|---|---|---|
| 401 | invalid_api_key | Clé inconnue, mal formée ou secret incorrect |
| 401 | api_key_revoked | Clé révoquée : utilisez une nouvelle clé |
| 401 | api_key_expired | Date d’expiration dépassée |
| 401 | api_key_required | Route /api/v1 appelée sans clé API |
| 403 | api_key_not_allowed | Clé utilisée en dehors de /api/v1 |
| 403 | ip_not_allowed | Adresse IP absente de la liste autorisée de la clé |
| 403 | insufficient_scope | La clé n’a pas le scope requis (liste dans requiredScopes) |
| 403 | forbidden | Permission insuffisante du compte de service, ou abonnement de l’entreprise inactif |
| 400 | validation_failed | Corps ou paramètres invalides : détail champ par champ dans errors |
| 400 | idempotency_key_required | Écriture sans en-tête Idempotency-Key |
| 422 | idempotency_key_reused | Même Idempotency-Key réutilisée pour une requête différente |
| 409 | request_in_progress | Une requête avec la même Idempotency-Key est encore en cours |
| 400 | invalid_cursor | Curseur de pagination invalide |
| 400 | invalid_json | Corps de requête qui n’est pas du JSON valide |
| 413 | payload_too_large | Corps de requête au-delà de 1 Mo : découpez l’envoi |
| 429 | rate_limited | Limite de la clé atteinte : attendez la durée indiquée par Retry-After |
Les champs propres à une erreur (ex. requiredScopes, retryAfter) sont ajoutés au corps, comme le permet la RFC 7807. Le texte de detail est aujourd’hui en français.
Sécurité et cycle de vie des clés
Disponible- Révocation immédiate : une clé révoquée est refusée dès la requête suivante.
- Rotation sans interruption : une nouvelle clé peut être émise avec les mêmes réglages pendant que l’ancienne reste active. Basculez votre intégration, puis révoquez l’ancienne.
- Liste d’IP : si elle est renseignée, seules ces adresses ou plages peuvent utiliser la clé.
- Expiration : une clé peut avoir une date de fin, après laquelle elle est refusée.
- Traçabilité : la date et l’adresse IP de la dernière utilisation sont conservées ; la création et la révocation sont inscrites au journal d’activité de l’entreprise.
- Une clé par intégration et par environnement : en cas de fuite, vous ne révoquez que celle-là.
Limites de débit
DisponibleChaque clé a sa propre limite (par défaut 600 requêtes par minute, indiquée par GET /v1/me). Chaque réponse porte RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset (secondes avant la fin de la fenêtre). Au-delà, l’API répond 429 rate_limited avec Retry-After.
La limite générale de la plateforme s’applique aussi : 100 requêtes par minute et par adresse IP.
Idempotence des écritures
DisponibleChaque écriture (POST, PUT, PATCH) exige l’en-tête Idempotency-Key : un identifiant unique par opération (un UUID par exemple), de 1 à 255 caractères. En cas de coupure réseau, renvoyez la même requête avec la même clé : vous obtenez la même réponse, sans doublon.
- Même clé, même requête : la réponse d’origine est renvoyée, avec l’en-tête
Idempotent-Replayed: true. - Même clé, requête différente : 422
idempotency_key_reused. - Même clé pendant que la première requête est en cours : 409
request_in_progress. - Une erreur 4xx est conservée et renvoyée à l’identique ; après une erreur 5xx, vous pouvez réessayer avec la même clé.
- Les clés sont conservées 7 jours.
POST /api/v1/stock-movements HTTP/1.1
Authorization: Bearer aps_live_…
Idempotency-Key: 4f9a6c1e-2b7d-4c55-9a1e-0c3f8d2b6e71
Content-Type: application/jsonPagination et synchronisation
DisponibleLes listes sont paginées par curseur : limit (1 à 200, 50 par défaut) et cursor. La réponse contient data et nextCursor ; passez nextCursor à la requête suivante jusqu’à ce qu’il vaille null. L’ordre est stable (date de modification, puis identifiant).
Pour une synchronisation incrémentale, ajoutez updatedSince (ISO 8601) : seuls les éléments modifiés depuis cette date sont renvoyés. Conservez la date de votre dernier passage et réutilisez-la au suivant.
{
"data": [ … ],
"nextCursor": "WyIyMDI2LTEwLTE2VDEwOjAwOjAwLjAwMFoiLCJiIl0"
}Spécification OpenAPI
DisponibleLa spécification OpenAPI de l’API publique est disponible sur https://apsionscan.com/api/v1/openapi.json, et une console de test interactive sur https://apsionscan.com/api/v1/docs. Elles ne décrivent que les routes /api/v1.
Catalogue et stock
DisponibleVos produits peuvent être désignés par votre propre identifiant (externalId), propre à la source de la clé : PUT /v1/products/by-external/{externalId} crée le produit s’il est inconnu (201, sans stock initial) ou le met à jour (200). Le SKU n’est jamais modifié. Dans un mouvement ou un transfert, désignez le produit par productId, productExternalId ou sku (un seul).
| Méthode | Route | Scope |
|---|---|---|
| GET | /v1/products · /v1/products/{id} · /v1/products/by-external/{externalId} | products:read |
| PUT | /v1/products/by-external/{externalId} | products:write |
| GET | /v1/warehouses · /v1/warehouses/{id}/locations | warehouses:read |
| GET | /v1/stock-levels (filtres productId, warehouseId, locationId, lotId) | stock:read |
| POST | /v1/stock-movements (IN, OUT, ADJUSTMENT, TRANSFER) | stock:write |
| GET | /v1/stock-movements | stock:read |
| POST | /v1/stock-transfers · /{id}/ship · /{id}/receive | stock:write |
| GET | /v1/stock-transfers/{id} | stock:read |
curl -X POST https://apsionscan.com/api/v1/stock-movements \
-H "Authorization: Bearer $APSIONSCAN_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"productExternalId":"ERP-VIS-1","type":"IN","quantity":50,"locationId":"<uuid>","unitPrice":0.12,"batchNumber":"L-2026-42"}'Stock insuffisant : 422 insufficient_stock. La sortie d’un kit crée une sortie par composant : la réponse data liste tous les mouvements créés.
Ventes et achats
DisponibleClients, commandes client, fournisseurs et bons de commande se créent ou se mettent à jour par votre identifiant (PUT …/by-external/{externalId}) : 201 à la création, 200 à la mise à jour. Un appel identique ne modifie rien, vous pouvez donc renvoyer vos données à chaque synchronisation. Une commande se rattache à un client par customerId ou customerExternalId ; ses coordonnées sont alors reprises du client.
| Méthode | Route | Scope |
|---|---|---|
| GET | /v1/customers · /v1/customers/{id} · /v1/customers/by-external/{externalId} | orders:read |
| PUT | /v1/customers/by-external/{externalId} | orders:write |
| GET | /v1/customer-orders (filtre status) · /{id} · /by-external/{externalId} | orders:read |
| PUT | /v1/customer-orders/by-external/{externalId} | orders:write |
| POST | /v1/customer-orders/{id}/cancel | orders:write |
| GET | /v1/suppliers · /v1/suppliers/{id} · /v1/suppliers/by-external/{externalId} | purchasing:read |
| PUT | /v1/suppliers/by-external/{externalId} | purchasing:write |
| GET | /v1/purchase-orders (filtre status) · /{id} · /{id}/receipts · /by-external/{externalId} | purchasing:read |
| PUT | /v1/purchase-orders/by-external/{externalId} | purchasing:write |
| POST | /v1/purchase-orders/{id}/send | purchasing:write |
curl -X PUT https://apsionscan.com/api/v1/customer-orders/by-external/WEB-10045 \
-H "Authorization: Bearer $APSIONSCAN_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"customerExternalId":"CLI-88","lines":[{"sku":"VIS-M6-20","quantity":10}],"confirm":true}'Règles de mise à jour : une commande client garde son en-tête modifiable tant qu’elle est DRAFT ou CONFIRMED, ses lignes seulement en brouillon. Un bon de commande reste modifiable tant qu’il est DRAFT ou SENT et sans réception. Sinon : 409 order_locked (avec orderStatus). Les réceptions se font au scan dans ApsionScan et se lisent par GET /v1/purchase-orders/{id}/receipts. Une commande client lue renvoie aussi ses expéditions (transporteur, n° de suivi, quantités, lots) et ses numéros de série. POST /v1/purchase-orders/{id}/send envoie le bon de commande au fournisseur (email s’il a une adresse) ; pour une clé créée avant le 18/10/2026, cochez d’abord la permission « Envoyer un BC » du rôle « Intégration API » (Paramètres › Rôles).
Kits et codes scannés
DisponibleGET /v1/identify?code=… retrouve le produit ou le kit d’un code lu par votre terminal : code ApsionScan, code associé, SKU, EAN/UPC, ou étiquette fournisseur GS1 (GS1-128, DataMatrix, QR). Pour une étiquette GS1, la réponse contient aussi gs1 : GTIN, lot, dates et quantité.
Un kit se réceptionne ou se transfère en une seule opération : une entrée (ou un transfert) par composant, regroupés et annulables d’un coup dans ApsionScan. Désignez le kit par kitId, kitExternalId ou kitSku. Sans lines, la composition du kit × kitCount est utilisée, aux prix proposés par GET /v1/kits/{kitId}/receive-template. Le transfert prend d’abord les lots qui périment en premier ; tout ou rien.
| Méthode | Route | Scope |
|---|---|---|
| GET | /v1/identify?code=… | products:read |
| GET | /v1/kits/{kitId}/receive-template | stock:read |
| POST | /v1/kit-operations/receive · /v1/kit-operations/transfer | stock:write |
| GET | /v1/kit-operations (filtre kitId) · /v1/kit-operations/{id} | stock:read |
curl -X POST https://apsionscan.com/api/v1/kit-operations/receive \
-H "Authorization: Bearer $APSIONSCAN_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"kitSku":"PAL-VIS","kitCount":3,"locationId":"<uuid>"}'Stock insuffisant pour un transfert : 422 insufficient_stock, avec missing (composant, quantité demandée, disponible). Rien n’est déplacé.
À venir
Prévu- Webhooks sortants signés pour être notifié des changements de stock et de statut des commandes.
Historique des modifications
Commandes : expéditions (transporteur, n° de suivi, lots) et numéros de série dans la lecture d’une commande client ; envoi d’un bon de commande au fournisseur (
POST /v1/purchase-orders/{id}/send).Sécurité : corps de requête limité à 1 Mo (413
payload_too_large), JSON invalide → 400invalid_json; le journal d’activité indique la clé API à l’origine de chaque écriture (« API — nom de la clé ») ; deux envois simultanés du même identifiant externe ne créent plus de doublon.Kits et codes : identification d’un code scanné (dont étiquettes GS1), réception et transfert de kit en une opération, historique des opérations de kit.
Ventes et achats : clients, commandes client (création, mise à jour, confirmation, annulation), fournisseurs, bons de commande et leurs réceptions, par identifiant externe.
Catalogue et stock : produits (lecture, upsert par identifiant externe), entrepôts et emplacements, niveaux de stock, mouvements, transferts entre entrepôts.
Conventions v1 en production : erreurs RFC 7807 (
application/problem+json),Idempotency-Keysur les écritures, pagination par curseur etupdatedSince, limite de débit par clé, spécification OpenAPI publique (/api/v1/openapi.json).Gestion des clés en libre-service dans Paramètres › Clés API (création, modification, renouvellement, révocation).
Première publication : authentification par clé API, scopes,
GET /v1/me, codes d’erreur, feuille de route.