Développeurs · API v1

API publique d'ApsionScan

Connectez votre ERP, votre boutique en ligne ou vos scripts à ApsionScan. Cette page décrit l'état actuel de l'API, comment s'authentifier, et ce qui arrive ensuite.

Dernière mise à jour : Publiée le :

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’IPDisponible
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 OpenAPIDisponible
Produits, entrepôts et emplacements, niveaux de stock, mouvements, transfertsDisponible
Clients, commandes clients, fournisseurs, commandes fournisseurs et réceptionsPrévu
Gestion des clés dans Paramètres › Clés APIDisponible
Webhooks sortants (notifications d’événements)Prévu

Obtenir une clé API

Disponible

Une 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

Disponible

Une 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.

Tester sa clébash
curl https://apsionscan.com/api/v1/me \
  -H "Authorization: Bearer aps_live_AbCd1234_votreSecretDe32Caracteres000000"
Réponse 200json
{
  "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 (403 api_key_not_allowed).
  • À l’inverse, /api/v1 n’accepte que des clés : une session de l’application ou un jeton utilisateur y est refusé (401 api_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

Disponible

Chaque 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.

ScopeDonne accès à
products:read · products:writeCatalogue produits
warehouses:readEntrepôts et emplacements
stock:read · stock:writeNiveaux de stock, mouvements et transferts
orders:read · orders:writeClients et commandes clients
purchasing:read · purchasing:writeFournisseurs, commandes fournisseurs et réceptions
webhooks:manageAbonnements 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

Disponible

Les 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.

Exemple : scope manquantjson
{
  "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"]
}
HTTPcodeSignification
401invalid_api_keyClé inconnue, mal formée ou secret incorrect
401api_key_revokedClé révoquée : utilisez une nouvelle clé
401api_key_expiredDate d’expiration dépassée
401api_key_requiredRoute /api/v1 appelée sans clé API
403api_key_not_allowedClé utilisée en dehors de /api/v1
403ip_not_allowedAdresse IP absente de la liste autorisée de la clé
403insufficient_scopeLa clé n’a pas le scope requis (liste dans requiredScopes)
403forbiddenPermission insuffisante du compte de service, ou abonnement de l’entreprise inactif
400validation_failedCorps ou paramètres invalides : détail champ par champ dans errors
400idempotency_key_requiredÉcriture sans en-tête Idempotency-Key
422idempotency_key_reusedMême Idempotency-Key réutilisée pour une requête différente
409request_in_progressUne requête avec la même Idempotency-Key est encore en cours
400invalid_cursorCurseur de pagination invalide
400invalid_jsonCorps de requête qui n’est pas du JSON valide
413payload_too_largeCorps de requête au-delà de 1 Mo : découpez l’envoi
429rate_limitedLimite 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

Disponible

Chaque 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

Disponible

Chaque é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.
Exemplehttp
POST /api/v1/stock-movements HTTP/1.1
Authorization: Bearer aps_live_…
Idempotency-Key: 4f9a6c1e-2b7d-4c55-9a1e-0c3f8d2b6e71
Content-Type: application/json

Pagination et synchronisation

Disponible

Les 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.

Réponse paginéejson
{
  "data": [ … ],
  "nextCursor": "WyIyMDI2LTEwLTE2VDEwOjAwOjAwLjAwMFoiLCJiIl0"
}

Spécification OpenAPI

Disponible

La 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

Disponible

Vos 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éthodeRouteScope
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}/locationswarehouses: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-movementsstock:read
POST/v1/stock-transfers · /{id}/ship · /{id}/receivestock:write
GET/v1/stock-transfers/{id}stock:read
Entrée de 50 unités avec lotbash
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

Disponible

Clients, 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éthodeRouteScope
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}/cancelorders: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}/sendpurchasing:write
Créer et confirmer une commande clientbash
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

Disponible

GET /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éthodeRouteScope
GET/v1/identify?code=…products:read
GET/v1/kits/{kitId}/receive-templatestock:read
POST/v1/kit-operations/receive · /v1/kit-operations/transferstock:write
GET/v1/kit-operations (filtre kitId) · /v1/kit-operations/{id}stock:read
Réceptionner 3 palettes d’un kitbash
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 → 400 invalid_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-Key sur les écritures, pagination par curseur et updatedSince, 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.

Un projet d’intégration ?

Nous accompagnons chaque nouvelle intégration. Décrivez-nous votre besoin.