API de retrait laboratoire KebecPhoto v1
Guide d'intégration machine à machine pour Photolab Yves Thomas — version 1.1 — juillet 2026
1. Introduction
L'API de retrait permet à votre système de récupérer les lots d'impression de KebecPhoto
par HTTPS — le ou les manifestes plus chaque fichier image prêt à imprimer — au lieu
d'attendre notre dépôt SFTP dans votre dossier /incoming. Le manifeste servi par cette
API est le XML SoumissionLabo détaillé (voir la §7).
Il diffère du CSV Timestone simplifié que nous déposons sur SFTP : les fichiers
image et leurs noms sont identiques octet pour octet sur les deux canaux, mais pas le
manifeste — le SFTP expédie le CSV Timestone, cette API expédie le XML
SoumissionLabo.
Pendant la période de transition, les deux canaux fonctionnent en parallèle : nous continuons de déposer sur votre SFTP exactement comme aujourd'hui, et les mêmes lots sont simultanément disponibles via cette API. Une fois votre intégration validée en production, nous cesserons (d'un commun accord) le dépôt SFTP; l'API deviendra alors le seul canal de livraison. La bascule est instantanément réversible de notre côté.
SoumissionLabo détaillé — mêmes commandes, enveloppe différente.
Chaque fichier (et manifeste) est accompagné de sa taille et de son empreinte SHA-256 pour
vérification.
Ce que l'API vous offre :
- Lister les lots en attente de retrait (ou déjà confirmés), les plus anciens d'abord.
- Inspecter un lot : son ou ses manifestes et l'inventaire complet des fichiers avec empreintes.
- Télécharger chaque manifeste (XML
SoumissionLabo) et chaque fichier image (JPEG) individuellement — retéléchargeables à volonté. - Confirmer la réception d'un lot une fois que vous avez tout, ce qui le retire de votre liste « à récupérer ».
2. Démarrage rapide
# 1. Lister les lots en attente (les plus anciens d'abord)
curl -H "Authorization: Bearer $CLE" \
"$BASE/batches?status=UPLOADED"
# 2. Obtenir l'inventaire des fichiers d'un lot
curl -H "Authorization: Bearer $CLE" \
"$BASE/batches/{batchId}"
# 3. Télécharger le ou les manifestes
curl -OJ -H "Authorization: Bearer $CLE" \
"$BASE/batches/{batchId}/manifests/{name}"
# 4. Télécharger chaque fichier (max 4 en parallèle)
curl -OJ -H "Authorization: Bearer $CLE" \
"$BASE/batches/{batchId}/files/{filename}"
# 5. Confirmer la réception (idempotent)
curl -X POST -H "Authorization: Bearer $CLE" \
"$BASE/batches/{batchId}/confirm"
3. Environnements et URL de base
| Environnement | URL de base | Préfixe de clé API |
|---|---|---|
| Staging (tests d'intégration) | https://staging.kebecphoto.com/api/lab/v1 | kplab_stg_… |
| Production | https://app.kebecphoto.com/api/lab/v1 | kplab_prod_… |
Les clés sont liées à l'environnement : une clé staging est cryptographiquement inerte contre la production et vice-versa (le serveur rejette une étiquette d'environnement non concordante avant toute consultation). Vous recevrez d'abord une clé staging; nous passerons à la production une fois votre flux de bout en bout validé contre des lots de test staging.
4. Authentification
Chaque requête porte votre clé API dans l'en-tête Authorization :
Authorization: Bearer kplab_prod_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- Format :
kplab_+ étiquette d'environnement (prod/stg) +_+ 43 caractères base64 URL-compatibles. Envoyez-la exactement telle que fournie. - Garde : traitez la clé comme un mot de passe — coffre de secrets uniquement; jamais dans un dépôt de code, des journaux, des URL ni du code côté client. Nous ne conservons qu'une empreinte SHA-256 (plus les premiers caractères, pour l'affichage dans notre console d'administration); la clé ne peut jamais être réaffichée. Si elle est perdue, nous en créons une nouvelle.
- Rotation : deux clés peuvent être actives simultanément. Nous vous remettons la nouvelle, vous basculez à votre convenance, nous révoquons l'ancienne — aucune interruption.
- Compromission : contactez Francis immédiatement; la révocation prend effet dès la requête suivante.
Échecs d'authentification
Tout échec d'authentification — en-tête absent, clé mal formée, clé inconnue, clé révoquée, clé expirée — retourne la même réponse uniforme (volontairement : aucune information ne fuit sur l'état de la clé) :
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
Content-Type: application/json
{"error":"unauthorized"}
429 avec en-tête
Retry-After. (Les requêtes sans clé syntaxiquement valide reçoivent toujours un
simple 401.) Un client correctement configuré ne rencontre jamais ce verrouillage;
si cela vous arrive, cessez les tentatives, vérifiez la clé et respectez Retry-After.
5. Conventions
- TLS obligatoire. HTTPS uniquement; le HTTP simple n'est pas servi.
- JSON, UTF-8 pour toutes les réponses structurées; octets bruts pour les téléchargements de manifestes/fichiers.
- Horodatages en ISO 8601 UTC avec millisecondes, p. ex.
2026-06-10T14:05:12.000Z. - Identifiants (ids de lots) : chaînes opaques (style
cmk…, 25 caractères). Traitez-les comme opaques; ne les interprétez jamais. - Erreurs : toujours l'enveloppe
{"error":"<code_en_snake_case>"}, parfois avec des champs additionnels (p. ex.retry_after,supersededByBatchId). Aiguillez sur le codeerror, pas seulement sur le statut HTTP. - Des champs JSON inconnus peuvent s'ajouter en tout temps (évolution additive) — votre parser doit ignorer les champs qu'il ne reconnaît pas.
- Les corps de requête sont partout optionnels.
POST …/confirmfonctionne avec un corps vide exactement comme avant; il PEUT porter un rapport de vérification optionnel (voir 6.5). Aucun autre point d'accès de lots ne lit de corps. - Les paramètres de chemin n'exigent aucun échappement. Les ids de lots, noms de manifestes et noms de fichiers ne contiennent que
A–Z a–z 0–9 . _ -— l'encodage pourcent est un no-op inoffensif. Utilisez les valeurs exactement telles que retournées par l'API. - L'inventaire d'un lot UPLOADED est immuable. Une fois listé, ses entrées
files[]/manifests[], tailles et empreintes ne changent jamais sur place. Toute correction de notre côté passe par un NOUVEAU lot (l'ancien disparaissant ou passant410).
6. Référence des points d'accès
6.1 Lister les lots
GET/batches
Retourne les lots visibles pour votre laboratoire, les plus anciens d'abord par date de
création (createdAt croissant, départage déterministe par id), avec
pagination par curseur. Notez que c'est l'ordre de création, pas l'ordre de disponibilité — un lot
d'école différé qui devient disponible ce soir peut apparaître avant des lots créés après lui.
Seuls les lots livrables sont listés — vous ne verrez jamais de lots internes, échoués ou
remplacés.
| Paramètre | Valeurs | Défaut | Description |
|---|---|---|---|
status | UPLOADED · CONFIRMED · all | UPLOADED | UPLOADED = disponibles, pas encore confirmés par vous. CONFIRMED = déjà accusés réception. Toute autre valeur retombe sur le défaut. |
since | ISO 8601 | — | Seulement les lots dont les fichiers sont devenus disponibles à partir de cet instant. C'est la date de disponibilité, pas la date de création : un lot d'école créé il y a trois jours mais téléversé ce soir APPARAÎTRA. Une valeur non interprétable est ignorée. |
cursor | id du lot | — | Reprendre après ce lot — n'envoyez jamais autre chose que le nextCursor de la réponse immédiatement précédente. Une valeur mal formée est ignorée (vous obtenez la première page); un id inconnu donne une page vide (hasMore: false) — repartez sans curseur au prochain sondage. |
limit | 1–100 | 20 | Taille de page (les valeurs hors plage sont ramenées dans la plage). |
Réponse 200 :
{
"batches": [
{
"id": "cmkabc1230000xyz456def789",
"status": "UPLOADED",
"createdAt": "2026-06-10T14:00:00.000Z",
"submittedAt": "2026-06-10T14:05:12.000Z",
"confirmedAt": null,
"orderCount": 3,
"itemCount": 12,
"isConsolidated": false,
"trigger": "DATED_DEADLINE",
"filesAvailable": true
}
],
"nextCursor": null,
"hasMore": false
}
| Champ | Type | Signification |
|---|---|---|
id | chaîne | Identifiant du lot — utilisez-le dans tous les autres points d'accès. |
status | chaîne | UPLOADED ou CONFIRMED (voir le cycle de vie). |
createdAt | horodatage | Création du lot de notre côté (peut précéder la disponibilité de plusieurs jours pour la consolidation des livraisons à l'école). |
submittedAt | horodatage · null | Moment où le lot a fini son téléversement / est devenu livrable. |
confirmedAt | horodatage · null | Moment où VOUS avez confirmé la réception (null d'ici là). |
orderCount / itemCount | nombre | Commandes et lignes d'impression dans le lot. |
isConsolidated | booléen | Informatif : vrai pour un lot produit par la consolidation nocturne ou déclenché manuellement pour traitement immédiat (croisez avec trigger : NIGHTLY_RUN vs MANUAL). Vous pouvez l'ignorer. |
trigger | chaîne · null | Cause de la soumission : DATED_DEADLINE, NIGHTLY_RUN, SAME_DAY_HOME, MANUAL, ou null (héritage). |
filesAvailable | booléen | false = rien n'a jamais été publié pour téléchargement API (lot antérieur à l'API) — sautez-le. Note : dans la LISTE, ce champ ne reflète pas la purge de rétention; la purge est rapportée par le DÉTAIL (purged: true, dont le filesAvailable passe alors à false). |
nextCursor / hasMore | chaîne · null / booléen | Pagination : repassez nextCursor comme cursor tant que hasMore est vrai. |
6.2 Détail d'un lot (inventaire des fichiers)
GET/batches/{batchId}
Tous les champs de la liste ci-dessus, plus l'inventaire complet : manifestes et fichiers image, chacun avec taille et SHA-256.
{
"id": "cmkabc1230000xyz456def789",
"status": "UPLOADED",
"…": "…(tous les champs de la liste)…",
"filesAvailable": true,
"purged": false,
"manifests": [
{
"name": "BATCH-2026-06-10-cmkabc1230000xyz456def789.xml",
"groupKey": "batch",
"sizeBytes": 8421,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
],
"files": [
{
"filename": "KP-20260610-A1B2C3_001_YT-4X6-MAT.jpg",
"groupKey": "batch",
"orderRef": "KP-20260610-A1B2C3",
"quantity": 5,
"lineType": "STANDARD",
"side": null,
"sizeBytes": 2048312,
"sha256": "ab12cd…"
},
{
"filename": "KP-20260610-A1B2C3_007_YT-CARTE_CARD.jpg",
"side": null, "…": "…"
},
{
"filename": "KP-20260610-A1B2C3_012_YT-CARTE_FRONT.jpg",
"side": "FRONT", "…": "…"
},
{
"filename": "KP-20260610-A1B2C3_012_YT-CARTE_BACK.jpg",
"side": "BACK", "…": "…"
}
]
}
| Champ | Type | Signification |
|---|---|---|
manifests[].name | chaîne | Nom du manifeste — passez-le tel quel au point d'accès de téléchargement de manifeste. Un par groupe de livraison (voir groupKey). |
files[].filename | chaîne | Nom de fichier exact — identique au dépôt SFTP et à l'élément <FichierImage> / <FichierImageVerso> du manifeste. Passez-le tel quel au point d'accès de téléchargement de fichier. |
groupKey | chaîne | Groupe de livraison, reflétant les dossiers SFTP : batch = commandes groupées au studio (votre dossier batch/); ORDER-… = une expédition directe par groupe (votre dossier orders/). |
orderRef | chaîne · null | Numéro de commande lisible auquel appartient le fichier (aussi intégré au nom de fichier). |
quantity | nombre · null | Quantité d'impression pour la ligne (un fichier peut s'imprimer N fois — élément <Quantite> du manifeste). |
lineType | chaîne · null | STANDARD (impression simple / carte sportive) ou SHEET (feuille imposée pré-rendue — imprimer telle quelle). |
side | chaîne · null | Faces de cartes sportives seulement : FRONT / BACK pour la paire recto/verso héritée (une carte recto seul porte FRONT). Null pour le fichier fusionné _CARD (le suffixe du nom de fichier l'identifie) et pour tout le reste. |
sizeBytes / sha256 | nombre / chaîne | Taille exacte en octets + SHA-256 (hex) du fichier — vérifiez après téléchargement. |
purged | booléen | Vrai lorsque les fichiers ont dépassé la rétention et ont été retirés (voir la rétention) — les métadonnées d'inventaire restent, les téléchargements répondent 404. |
Code de statut particulier — 410 Gone : si un lot que vous aviez vu
a été remplacé par un lot consolidé avant le retrait, son détail retourne :
{"error":"batch_superseded","supersededByBatchId":"cmk…"}
supersededByBatchId, chaîne
ou null) — il apparaît dans votre liste normale. Si supersededByBatchId est null,
jetez simplement le lot et fiez-vous à votre sondage status=UPLOADED habituel. Si des
téléchargements de fichiers se mettent à répondre 404 en plein retrait, relisez le détail du lot —
un 410 à cet endroit correspond à ce cas.6.3 Télécharger un manifeste
GET/batches/{batchId}/manifests/{name}
Transmet le manifeste — le XML SoumissionLabo détaillé (voir la
§7 pour le schéma). UTF-8, ouvrant sur une déclaration
<?xml version="1.0" encoding="UTF-8"?>. C'est le format de manifeste de cette API;
le dépôt SFTP, lui, transporte le CSV Timestone simplifié.
En-têtes de réponse en cas de succès (200) :
Content-Type: application/xml
Content-Length: 8421
Content-Disposition: attachment; filename="BATCH-2026-06-10-….xml"
X-Checksum-SHA256: 9f86d081…
Cache-Control: private, no-store
404 {"error":"not_found"} pour un nom inconnu, un lot purgé, ou un lot qui n'est
pas (ou plus) livrable.
6.4 Télécharger un fichier
GET/batches/{batchId}/files/{filename}
Transmet un JPEG prêt à imprimer, identique octet pour octet au dépôt SFTP. Mêmes en-têtes que
ci-dessus avec Content-Type: image/jpeg.
- Concurrence : maximum 16 téléchargements simultanés par laboratoire (les deux clés actives pendant une rotation partagent les créneaux). Une 17e requête parallèle reçoit
429 {"error":"too_many_concurrent_streams"}avecRetry-After— attendez la fin d'un transfert, puis réessayez. - Chaque transfert est limité à 90 secondes au total (il coupe les transferts lents, pas seulement les bloqués). Les fichiers font quelques Mo, donc un transfert sain se termine en quelques secondes — si un téléchargement est coupé à répétition, vérifiez votre débit effectif. Retélécharger est toujours sans danger. Réglez votre propre délai client légèrement au-dessus (p. ex. 120 s).
- Vérifiez le SHA-256 contre la valeur d'inventaire; en cas de discordance (hautement improbable — corruption de transit), retéléchargez.
- Un créneau de téléchargement est libéré dès la fermeture de votre connexion (y compris interruptions/plantages); un cas pathologique s'auto-guérit en au plus 120 s. Un retrait planté ne vous verrouille jamais.
- Pas de
HEADni de repriseRange— la récupération est toujours un re-téléchargement complet (les fichiers sont assez petits pour que ce soit le contrat le plus simple et le plus sûr). - Tout est retéléchargeable à volonté pendant la fenêtre de rétention — re-récupérer est toujours sans danger.
6.5 Confirmer la réception
POST/batches/{batchId}/confirm
À appeler quand TOUS les fichiers du lot sont téléchargés et vérifiés. Le lot passe à
CONFIRMED (et sort de votre liste status=UPLOADED par défaut). Corps de
requête vide.
Réponse 200 :
{
"id": "cmkabc1230000xyz456def789",
"status": "CONFIRMED",
"confirmedAt": "2026-06-10T15:42:08.000Z",
"alreadyConfirmed": false
}
- Idempotent : reconfirmer un lot déjà confirmé retourne
200avec"alreadyConfirmed": true(et leconfirmedAtd'origine) — jamais une erreur. Deux confirmations concurrentes reçoivent toutes deux200. 404 not_found: lot inconnu (ou non livrable à votre laboratoire).409 invalid_status: le lot existe mais ne peut pas être confirmé (p. ex. lot antérieur à l'API sans fichiers récupérables, ou lot dans un état d'erreur interne). Si persistant, contactez Francis.410 batch_superseded: voir la §6.2 — ne pas imprimer; utilisezsupersededByBatchId.
Rapport de vérification optionnel (depuis le 2026-06-12). Un client qui vérifie ses téléchargements (comme l'application de bureau KebecPhoto Labo) peut joindre un corps JSON (≤ 1 Ko) :
{
"agentVersion": "1.0.0",
"mode": "auto",
"imagesVerified": 135,
"manifestsVerified": 2,
"bytesVerified": 287345234,
"sha256MismatchCount": 0,
"durationMs": 184233
}
- Le serveur valide le rapport contre SON inventaire (comptes par type + octets
totaux). Toute divergence, ou
sha256MismatchCount > 0⇒422 verification_failedet le lot N'EST PAS confirmé — retéléchargez puis reconfirmez. - Un rapport accepté est conservé; la réponse porte alors un champ
report. Une reconfirmation retourne le rapport DÉJÀ enregistré (jamais écrasé). - Corps malformé ⇒
400 invalid_report. Un corps VIDE garde la réponse héritée identique au octet près (aucune cléreport). - La réponse de liste porte aussi un champ additif
serverTime(ISO 8601), capturé AVANT la lecture en base — utilisez-le (moins la marge d'une heure recommandée) comme prochaine bornesinceplutôt que l'horloge de votre poste. - Les téléchargements de manifestes comptent désormais dans les mêmes créneaux simultanés et la même limite de 90 s par transfert que les fichiers.
7. Référence des données
Grammaire des noms de fichiers
{refCommande}_{seq}_{codeLab}[_{FRONT|BACK|CARD}].jpg
KP-20260610-A1B2C3 _ 001 _ YT-4X6-MAT .jpg → impression standard
KP-20260610-A1B2C3 _ 007 _ YT-CARTE _ CARD .jpg → carte recto/verso FUSIONNÉE (défaut)
KP-20260610-A1B2C3 _ 012 _ YT-CARTE _ FRONT .jpg → carte, recto (paire héritée / recto seul)
KP-20260610-A1B2C3 _ 012 _ YT-CARTE _ BACK .jpg → carte, verso (paire héritée)
refCommande— numéro de commande lisible (aussi dans le champorderRefde l'inventaire).seq— index de ligne dans la commande, complété de zéros, base 1.codeLab— VOTRE code de produit, tel que configuré par produit (élément<CodeProduit>du manifeste). Un code de produit ne se termine JAMAIS par_FRONT,_BACKni_CARD— ces jetons terminaux sont réservés au rôle du fichier ci-dessous.
Cartes sportives (recto/verso)
Défaut — fichier fusionné _CARD : une carte recto/verso dépose
désormais UN seul fichier, le recto et le verso côte à côte sur une même image de largeur double
(p. ex. une carte 2.5×3.5 po → un fichier de classe 5×3.5 po). La densité (ppp) inscrite
dans le JPEG est la vérité physique du format — le fichier EST la feuille finale, pré-rendue aux
dimensions d'impression : imprimer tel quel, sans recadrage ni redimensionnement, puis
couper au trait central. Dans le XML SoumissionLabo, la ligne fusionnée
est UN <Article> avec un seul <FichierImage> (jamais de
<FichierImageVerso>) et sans balises de taille
(<TailleCm>/<TaillePo> omises — la taille mono-face serait
fausse pour la feuille double). Dans l'inventaire API : une seule entrée, side: null,
identifiée par le suffixe _CARD.
Héritage — paire _FRONT/_BACK : les lignes de cartes
antérieures à la migration déposent encore DEUX fichiers formant UN seul
<Article> portant à la fois <FichierImage> (recto) et
<FichierImageVerso> (verso) : une carte facturable reste un seul article. Dans
l'inventaire API, ce sont deux entrées partageant le même orderRef, distinguées par
side: "FRONT" / "BACK". Les deux sont pré-rendus aux dimensions
d'impression — imprimer tels quels, sans recadrage. (Une carte recto seul reste un unique fichier
_FRONT.)
Feuilles imposées
Les entrées lineType: "SHEET" sont des JPEG composites pré-imposés
(300 ppp, prêts à imprimer tels quels) — exactement comme convenu dans notre échange du
2 juin sur les feuilles imposées.
Groupes de livraison (groupKey)
groupKey | Équivalent SFTP | Signification |
|---|---|---|
batch | dossier batch/ | Commandes en livraison à l'école, groupées pour le studio (un manifeste pour le groupe; adresse du point de groupage). |
ORDER-{date}-{ref} | dossier orders/ | Une commande en expédition directe au parent (son propre manifeste; adresse du parent). |
Un lot mixte contient le groupe batch ET un groupe ORDER-… par commande
en expédition directe — téléchargez chaque manifeste listé dans manifests[].
Le manifeste (XML SoumissionLabo)
Le manifeste de l'API est le XML SoumissionLabo détaillé — plus riche que le CSV
Timestone simplifié déposé sur SFTP; les deux ne sont pas interchangeables. Élément
racine <SoumissionLabo>; un <Article> par ligne
d'impression (une carte fusionnée _CARD est un article avec un seul
<FichierImage> et sans balises de taille; une paire héritée est un article
portant à la fois <FichierImage> et
<FichierImageVerso>). Les commandes familiales
mélangent les enfants sous une même <Commande>, chaque article portant son propre
élève. UTF-8, avec une déclaration XML.
| Élément | Emplacement | Contenu |
|---|---|---|
<Lot> | racine | En-tête du lot : <NumeroLot>, <DateCreation>, <NombreCommandes>, <NombreArticles>, <NombreTirages>. |
<Studio> | racine | Photographe : <Nom>, <CodeStudio>, <CompteLaboYT> (votre numéro de compte labo pour ce studio). |
<Commande> | sous <Commandes> | Une par commande : <NumeroCommande> + les blocs ci-dessous. |
<Client> | commande | <Nom>, <Courriel>, <Telephone> du client. |
<Livraison type="…"> | commande | ecole (groupage école/studio — le groupe batch), expeditionDirecte (directe au parent — un groupe ORDER-…), ou numerique (numérique, sans adresse). Éléments d'adresse : <Ligne1>, <Ville>, <Province>, <CodePostal>, <Pays>. |
<Article> | sous <Articles> | Un par ligne d'impression. Enfants clés : <CodeProduit> (VOTRE code de produit), <Quantite>, <FichierImage> (+ <FichierImageVerso> pour les versos de paires de cartes HÉRITÉES seulement — jamais sur une ligne fusionnée _CARD), <Recadrage applique="oui|non"> avec <X>/<Y>/<Largeur>/<Hauteur> quand recadré. Les balises <TailleCm>/<TaillePo> sont omises sur les lignes fusionnées _CARD (et sur les feuilles imposées). |
| balises d'article optionnelles | article | <Decor> (nom du décor/arrière-plan), <GroupeSource> (groupe source de photo de groupe), <CommentairesCommande> (commentaire de commande du parent, si activé). |
Le manifeste obtenu suit les groupes de livraison (groupKey) : le manifeste du groupe
batch porte <Livraison type="ecole">; chaque manifeste de groupe
ORDER-… porte <Livraison type="expeditionDirecte">. (Le CSV Timestone
simplifié est le format que nous déposons sur votre SFTP; cette API sert toujours le XML
SoumissionLabo détaillé. Nous vous aviserions avant tout changement de l'un ou
l'autre.)
8. Flux d'intégration recommandé
Sondez toutes les 5 minutes; persistez deux choses de votre côté : l'ensemble des ids de lots entièrement traités, et l'horodatage de votre dernier sondage réussi.
toutes les 5 minutes :
since = dernier_sondage_réussi − 1 heure # chevauchement volontaire; dédoublonnez par id
page = GET /batches?status=UPLOADED&since={since}
boucle :
pour chaque lot de page.batches :
si lot.id déjà traité ou non lot.filesAvailable : continuer
détail = GET /batches/{lot.id} # 410 → marquer annulé, imprimer le lot remplaçant
pour chaque m de détail.manifests : télécharger + vérifier sha256
pour chaque f de détail.files : télécharger + vérifier sha256 # ≤ 4 en parallèle
remettre à la chaîne de production
POST /batches/{lot.id}/confirm # idempotent — sûr à répéter après un plantage
marquer lot.id traité
si non page.hasMore : sortir
page = GET /batches?…&cursor={page.nextCursor}
dernier_sondage_réussi = maintenant
- Reprise après plantage : tout est re-récupérable et confirm est idempotent — après un plantage, retraitez simplement le lot du début.
- Dédoublonnez par id de lot, pas par temps : le chevauchement d'une heure garantit zéro trou; l'ensemble d'ids garantit zéro double impression.
- Backoff : sur
429, honorezRetry-After; sur5xx, réessayez avec backoff exponentiel (5 s, 10 s, 20 s… plafond 5 min). Ne bouclez jamais à plein régime. - N'interprétez pas les noms de fichiers pour le routage quand les métadonnées d'inventaire (
groupKey,orderRef,side,lineType) répondent déjà à la question. - Filet de sécurité : une fois par jour (ou au redémarrage), exécutez un
GET /batches?status=UPLOADEDcomplet sanssinceet réconciliez avec votre ensemble d'ids traités —sinceest une optimisation, jamais la source de vérité. (Un lot jamais confirmé y reste listé indéfiniment.) - Autorité d'horloge : dérivez votre repère
sincede NOS horodatages (p. ex. le plus récentsubmittedAtque vous avez vu, moins le chevauchement d'une heure), pas de votre horloge locale — le décalage d'horloge devient sans effet. - Échec terminal de fichier : si un fichier listé à l'inventaire répond 404, ou si son SHA-256 reste discordant après quelques re-téléchargements — deux situations qui ne devraient jamais se produire — ne confirmez pas le lot; il reste
UPLOADED(rien n'expire) et visible dans votre liste filet-de-sécurité. Contactez Francis.
9. Catalogue des erreurs
| HTTP | Code error | Signification | Quoi faire |
|---|---|---|---|
| 401 | unauthorized | Clé absente/invalide/révoquée/expirée (uniforme — aucun détail, par conception). | Vérifier la clé et l'environnement; contacter Francis si persistant. |
| 404 | not_found | Lot/manifeste/fichier inconnu, fichiers purgés, ou ressource hors de votre périmètre. | Sauter; re-lister. Pour un lot purgé, rien à récupérer. |
| 405 | method_not_allowed | Mauvaise méthode HTTP (p. ex. GET sur confirm). | Corriger la requête. |
| 409 | invalid_status | Le lot existe mais ne peut pas être confirmé pour l'instant. | Revérifier via le détail; contacter Francis si persistant. |
| 410 | batch_superseded | Ne pas imprimer. Lot remplacé par supersededByBatchId. | Annuler localement; traiter le lot remplaçant. |
| 413 | payload_too_large | Un corps de requête dépasse 1 Ko (plafond du rapport de confirmation). | Garder le rapport optionnel sous 1 Ko; corps vides ailleurs. |
| 429 | rate_limited | Limite de débit dépassée (ou verrouillage d'échecs d'authentification). | Attendre Retry-After secondes (aussi dans le JSON : retry_after). |
| 429 | too_many_concurrent_streams | Plus de 16 téléchargements simultanés. | Attendre la fin d'un transfert; réessayer après Retry-After. |
| 503 | too_many_concurrent_streams | Indisponibilité transitoire du suivi des transferts côté serveur. | Réessayer après Retry-After (secondes). |
| 5xx | — | Erreur serveur transitoire. | Backoff exponentiel et réessayer; nous alerter si soutenu. |
10. Limites de débit et concurrence
Les limites s'appliquent par laboratoire (toutes vos clés actives partagent les mêmes
compteurs), par minute (fenêtre fixe). Les limites de sondage/confirmation sont bien au-dessus de
la cadence documentée. Le compteur de fichiers (600/min) est le mécanisme de régulation voulu pour
les gros lots — en drainant un gros lot à 16 transferts parallèles, vous pouvez voir un
429 rate_limited occasionnel; honorez Retry-After et continuez.
| Point d'accès | Limite |
|---|---|
GET /batches (liste) | 120 / min |
GET /batches/{id} (détail) | 240 / min |
GET …/manifests/{name} | 120 / min |
GET …/files/{filename} | 600 / min + max 16 transferts simultanés |
POST …/confirm | 60 / min |
Le découpage des lots utilise un plafond de 500 lignes image, auquel s'ajoutent les manifestes et certains fichiers supplémentaires; la durée du téléchargement dépend du volume de données et du débit.
Un plafond de sécurité additionnel par IP source (dimensionné bien au-dessus de la somme des compteurs) protège l'API en amont de l'authentification; il est hors d'atteinte aux limites documentées mais s'applique par IP — faites tourner votre sondeur comme un seul client coordonné plutôt que comme plusieurs processus parallèles derrière une même IP de sortie.
11. Conservation des données et cycle de vie des lots
- Un lot apparaît avec
status: "UPLOADED"dès que son ensemble d'artefacts est complet et livrable. - Votre
POST /confirmle fait passer àCONFIRMED. - Les fichiers restent téléchargeables pendant au moins 60 jours après votre confirmation; après quoi ils peuvent être purgés — le détail du lot rapporte alors
filesAvailable: false, purged: true(lefilesAvailablede la liste ne reflète pas la purge) et les téléchargements répondent 404. Les métadonnées d'inventaire (noms, tailles, empreintes) sont conservées. - Les lots que vous ne confirmez jamais restent disponibles — mais confirmez, s'il vous plaît : c'est notre accusé de réception.
- Vous ne verrez jamais d'états internes (création/empaquetage/échec). Le seul changement d'état post-listage à gérer est le
410 batch_superseded(un lot d'école consolidé avant retrait), qui porte l'id du lot remplaçant.
12. Versionnement et soutien
- Le contrat est versionné dans le chemin (
/api/lab/v1). À l'intérieur de la v1, les changements sont additifs seulement (nouveaux champs optionnels, nouveaux points d'accès); un changement non rétrocompatible passerait par/v2avec une période de chevauchement. - Cette page est la référence française officielle; la version anglaise est maintenue en parallèle. Les deux évoluent au même rythme que l'API.
- Soutien : Francis Cossette (KebecPhoto) — votre fil de courriel existant. Urgences (clé compromise, production bloquée) : par téléphone.