Réceptions et retraits
Les offres en mode de préparation stocked vivent dans un entrepôt Wamia : l’entrepôt les compte, la boutique les réapprovisionne sur rendez-vous et récupère du stock par un retrait. Cette page est le versant intégration de ce flux — Wamia Shop et l’application mobile pilotent les mêmes opérations.
Quelles offres
Seules les offres dont le fulfilmentMode est stocked peuvent être déclarées, réapprovisionnées ou retirées. Les offres cross-dock restent dans le stock de la boutique et voyagent avec chaque commande ; un réapprovisionnement qui en nomme une est refusé en bloc avec VALIDATION_FAILED et les SKU fautifs dans le message.
Pour les offres stockées, le comptage de l’entrepôt fait foi. apiDeclareStocks accepte la ligne mais n’écrit rien : la ligne revient ok: true avec skipped: true et skipReason: stocked_offer. Reflétez plutôt apiStockedPositions.availableQty dans la boutique.
Déclarer un réapprovisionnement
apiDeclareInbound prend les lignes (offerId, qty), l’entrepôt (optionnel : l’entrepôt par défaut de la boutique s’applique), le inboundMode — SELLER_DROPOFF quand la boutique apporte la marchandise, WAMIA_PICKUP quand un chauffeur Wamia la collecte, avec le contact et l’adresse dans pickup — une sellerReference et une note. L’appel est idempotent : rejouer la même idempotencyKey renvoie la même expédition. La réponse est une InboundShipmentView d’origin: SELLER_DECLARED au statut DRAFT.
apiInboundShipments(origin: SELLER_DECLARED) liste les réapprovisionnements ; ORDER_DRIVEN liste les transferts cross-dock que Wamia a créés à partir des commandes. apiInboundShipment renvoie les lignes, le rendez-vous, la tâche d’enlèvement et les écarts d’une expédition.
Réserver et déplacer le rendez-vous
Un dépôt a besoin d’un créneau de quai. apiInboundSlotGrid(facilityId, from, to) renvoie les créneaux de chaque jour sur une fenêtre de 14 jours au plus, avec capacity, booked et remaining ; apiInboundSlots(facilityId, day) est la forme à un jour. Réservez avec apiBookInboundAppointment(shipmentId, dockId, startsAt) : l’expédition passe en SCHEDULED et inbound.scheduled part. Les créneaux passés, hors horaires du quai, dans le délai de prévenance de l’entrepôt ou déjà pleins sont refusés avec VALIDATION_FAILED.
Pour déplacer un rendez-vous réservé, appelez apiRescheduleInboundAppointment avec le nouveau créneau et une note facultative ; l’ancien créneau est libéré et inbound.rescheduled part avec previousAppointmentId. Ces deux appels n’ont pas de clé et le SDK ne les rejoue jamais : après un délai dépassé, relisez apiInboundShipment et regardez appointment avant de rappeler. La boutique reçoit un rappel la veille ; un créneau manqué devient inbound.no_show et l’expédition repasse en DRAFT pour être re-réservée.
Retraits
apiRequestWithdrawal demande à l’entrepôt de rendre du stock : facilityCode, des lignes par sku ou offerId avec qty, un reason et une note facultatifs. L’appel est idempotent. La demande démarre en REQUESTED ; l’entrepôt l’approuve ou la refuse (withdrawal.decided ; l’approbation réserve les unités), vous réservez un créneau de quai avec apiBookWithdrawalAppointment (créneaux de apiInboundSlots ; apiRescheduleWithdrawalAppointment le déplace ; withdrawal.scheduled), le terrain la prépare (withdrawal.ready quand elle attend au quai) et enregistre la remise (withdrawal.handed_over, avec une raison d’écart par ligne si elle est partielle). Le bon de retrait est signé sur papier. apiWithdrawals(status) liste les demandes de la boutique avec leur appointment. Les quantités au-dessus de availableQty sont refusées : les unités réservées appartiennent aux commandes ouvertes.
Refléter les positions, ne pas les déclarer
apiStockedPositions(updatedSince, facilityCode) est paginé par curseur comme les listes. Interrogez-le au rythme de votre synchronisation de stock et écrivez availableQty dans la boutique ; onHandQty moins reservedQty est ce que la vitrine peut vendre. inbound.received et withdrawal.handed_over sont les moments où les positions bougent ; un webhook de l’une ou l’autre famille est un bon déclencheur de rafraîchissement incrémental.
Exemple
Déclarer un réapprovisionnement
mutation DeclareTopUp($input: DeclareInboundInput!, $idempotencyKey: String!) {
apiDeclareInbound(input: $input, idempotencyKey: $idempotencyKey) {
id reference status origin inboundMode facilityId facilityCode expectedUnits dueAt
}
}{
"idempotencyKey": "inbound:PO-77:3f9c…",
"input": {
"inboundMode": "SELLER_DROPOFF",
"sellerReference": "PO-77",
"lines": [{ "offerId": "0f6a…", "qty": 120 }]
}
}Créneaux, réservation et déplacement
query Slots($facilityId: String!, $from: DateTime!, $to: DateTime!) {
apiInboundSlotGrid(facilityId: $facilityId, from: $from, to: $to) {
day
slots { dockId dockCode startsAt endsAt capacity booked remaining closedReason }
}
}
mutation Book($shipmentId: String!, $dockId: String!, $startsAt: DateTime!) {
apiBookInboundAppointment(shipmentId: $shipmentId, dockId: $dockId, startsAt: $startsAt) {
id status scheduledAt appointmentId
}
}
mutation Move($shipmentId: String!, $dockId: String!, $startsAt: DateTime!, $note: String) {
apiRescheduleInboundAppointment(shipmentId: $shipmentId, dockId: $dockId, startsAt: $startsAt, note: $note) {
id status scheduledAt appointmentId
}
}Demander un retrait
mutation Withdraw($input: ApiWithdrawalInput!, $idempotencyKey: String!) {
apiRequestWithdrawal(input: $input, idempotencyKey: $idempotencyKey) {
id status facilityId reason createdAt
}
}{
"idempotencyKey": "withdrawal:2026-10-10:8a1b…",
"input": {
"facilityCode": "TUN-1",
"reason": "end_of_season",
"lines": [{ "sku": "TSHIRT-RED-M", "qty": 30 }]
}
}Lire le comptage de l’entrepôt
query StockedPositions($cursor: String, $updatedSince: DateTime) {
apiStockedPositions(cursor: $cursor, limit: 200, updatedSince: $updatedSince) {
items { sku offerId facilityCode onHandQty reservedQty availableQty updatedAt }
nextCursor
}
}Avec le SDK Node
@wamia/connect-sdk fournit des méthodes typées pour chaque opération ci-dessus, idempotentes là où l’API l’est, plus isInboundEvent et isWithdrawalEvent pour typer les charges utiles des webhooks.
import { ConnectClient, idempotencyKeyFor } from '@wamia/connect-sdk';
const client = new ConnectClient({ apiKey: process.env.WAMIA_API_KEY! });
// 1. The warehouse count wins for stocked offers: mirror it, never declare it.
for await (const position of client.allStockedPositions({ updatedSince: lastSync })) {
await setShopStock(position.sku, position.availableQty);
}
const result = await client.declareStocks(crossDockRows, idempotencyKeyFor('stock', crossDockRows));
for (const item of result.items) {
if (item.skipped) log(`${item.sku}: kept the warehouse count (${item.skipReason})`);
}
// 2. Declare a top-up, then book the first free slot of the next two weeks.
const shipment = await client.declareInbound(
{ lines: [{ offerId, qty: 120 }], inboundMode: 'SELLER_DROPOFF', sellerReference: 'PO-77' },
idempotencyKeyFor('inbound:PO-77', [offerId]),
);
const days = await client.inboundSlotGrid(shipment.facilityId, from, to);
const slot = days.flatMap((day) => day.slots).find((s) => s.remaining > 0);
if (slot) await client.bookInboundAppointment(shipment.id, slot.dockId, slot.startsAt);
// 3. Ask for stock back; the warehouse approves, picks, then hands it over.
await client.requestWithdrawal(
{ facilityCode: 'TUN-1', lines: [{ sku: 'TSHIRT-RED-M', qty: 30 }], reason: 'end_of_season' },
idempotencyKeyFor('withdrawal:2026-10-10', ['TSHIRT-RED-M']),
);Statuts des réceptions
| État | Signification |
|---|---|
| DRAFT | Déclarée, sans rendez-vous (ou le dernier a été manqué). |
| SCHEDULED | Un créneau de quai est réservé ; scheduledAt et appointmentId sont posés. |
| PICKED_UP | WAMIA_PICKUP seulement : le chauffeur a collecté la marchandise. |
| DROPPED_OFF | La marchandise est au quai et attend le comptage. |
| RECEIVING | L’entrepôt compte ; receivedUnits monte ligne par ligne. |
| RECEIVED | Comptée ; les écarts éventuels attendent la réponse de la boutique. |
| CLOSED | Le stock est vendable ; les positions ont bougé. |
| CANCELLED | Annulée par la boutique avant l’arrivée ou par l’entrepôt. |
Statuts des retraits
| État | Signification |
|---|---|
| REQUESTED | En attente de décision de l’entrepôt. |
| APPROVED | Acceptée ; les unités sont réservées. Réservez un créneau de quai. |
| SCHEDULED | Un créneau de quai est réservé ; appointment le décrit. |
| PREPARING | L’entrepôt prépare les unités. |
| READY | Préparée et en attente au quai pour la boutique. |
| ARRIVED | La boutique est au quai ; remise en cours. |
| CLOSED | Remise en totalité à shopRepresentative ; les positions ont bougé. |
| HANDED_OVER | Remise en partie ; chaque ligne porte un shortfallReason. |
| NO_SHOW | Le créneau est passé sans la boutique ; re-réservez avant holdUntil (stagedHoldHours). |
| REJECTED | Refusée ; lisez decisionNote. |
| CANCELLED | Annulée par la boutique avant la préparation, par l’entrepôt, ou après l’expiration de la garde. |
Événements
| Type | Signification |
|---|---|
| inbound.declared | Une expédition a été déclarée (shipmentId). |
| inbound.scheduled | Premier rendez-vous réservé (appointmentId). |
| inbound.rescheduled | Déplacée vers un autre créneau (previousAppointmentId). |
| inbound.slot_closed | L’entrepôt a fermé sur le créneau réservé ; re-réservez (appointmentId). |
| inbound.arrived | Pointée au quai. |
| inbound.received | Comptée ; relisez les positions. |
| inbound.discrepancy | Un manquant, un surplus ou une casse attend une réponse. |
| inbound.no_show | Le créneau est passé sans arrivée ; re-réservez. |
| inbound.cancelled | Annulée. |
| withdrawal.requested | Un retrait a été demandé (withdrawalId). |
| withdrawal.decided | Approuvé ou refusé ; relisez la demande pour status et decisionNote. |
| withdrawal.scheduled | Un créneau de quai a été réservé ou déplacé (appointmentId). |
| withdrawal.slot_closed | L’entrepôt a fermé sur le créneau réservé ; re-réservez (appointmentId). |
| withdrawal.ready | Préparé et en attente au quai. |
| withdrawal.handed_over | Remis (en totalité ou avec un écart) ; relisez les positions. |
| withdrawal.no_show | Le créneau est passé sans la boutique ; re-réservez. |
| withdrawal.cancelled | Annulé ; les unités sont libérées. |