Passer au contenu principal

DZBuild POS

Comment la caisse Windows gratuite DZBuild POS se relie à une boutique, et les appels qu'elle fait pour les produits, photos, stock, documents de vente, commandes, clients et le flux d'événements.

Écrit par Support

DZBuild POS est la caisse Windows gratuite pour les magasins qui vendent aussi en ligne. Elle fonctionne hors ligne et, une fois reliée, reste synchronisée avec une ou plusieurs boutiques DZBuild. Cette page liste les appels d'une caisse reliée, pour que le support et les partenaires sachent ce qu'une caisse peut faire et ne peut pas faire.

Ces endpoints ne répondent qu'aux jetons délivrés à la caisse. Une clé API personnelle ou un jeton d'application reçoit 403 sur les chemins réservés au POS, et un jeton de caisse reçoit 403 sur tout chemin hors de sa liste.

À qui elle s'adresse

  • Aux propriétaires qui vendent en magasin et sur leur boutique DZBuild. Seul le propriétaire de la boutique peut relier une caisse ; les membres de l'équipe ne le peuvent pas.

  • À tous les plans. Une caisse n'est pas une clé API : elle ne demande pas de plan Enterprise et ne prend aucune place de clé.

  • Dès que la première caisse s'enregistre, l'extension DZBuild POS apparaît dans le tableau de bord avec les caisses reliées, un bouton de déconnexion et les dernières ventes, retours et clôtures Z. https://dzbuild.com/dashboard/connected-devices ouvre cette page.

Comment une caisse se relie

  • Découverte : GET https://dzbuild.com/.well-known/oauth-authorization-server (RFC 8414).

  • La caisse est un client OAuth 2.0 public, dzbuild-pos-windows, sans secret. Seul PKCE S256 est accepté, et la redirection est http://127.0.0.1:PORT/oauth/callback sur n'importe quel port.

  • Le propriétaire se connecte dans le navigateur du système et choisit les boutiques que la caisse peut utiliser, jusqu'à 10. Une caisse peut aussi se relier avec un code : elle en affiche un, et le propriétaire le saisit sur https://dzbuild.com/device depuis un téléphone.

  • Le jeton d'accès commence par dzpos_ et dure 15 minutes. Le jeton de rafraîchissement est remplacé à chaque utilisation et expire après 30 jours sans usage ou 180 jours au total. Renvoyer un ancien jeton de rafraîchissement met fin à la liaison.

  • Chaque appel porte X-DZ-Store avec l'id de la boutique, sauf GET /v1/me. Une boutique que le propriétaire n'a pas choisie répond 403.

  • Chaque POST, PATCH et DELETE porte une Idempotency-Key, avec les règles de Idempotence.

  • Limites : 120 requêtes par minute par caisse et 600 par boutique. Le quota API mensuel ne s'applique pas aux caisses.

  • Déconnecter la caisse dans le tableau de bord met fin à la liaison : le rafraîchissement suivant répond 400 invalid_grant et le heartbeat suivant 410 device_revoked.

Les 19 scopes

La caisse demande toujours les 19, et DZBuild les accorde toujours tous.

Scopes

Ce que la caisse peut faire

openid, profile, offline_access

Savoir qui l'a reliée et rester reliée

store:read

Lire les boutiques de la liaison, leur langue, leur plan et leur plafond de produits

products:read, products:write

Lire les produits, les créer et les modifier par lots, ajouter des photos

inventory:read, inventory:write

Lire et ajuster le stock des produits

orders:read, orders:write

Recevoir les commandes de la boutique, en prendre une, la faire avancer, l'annuler

customers:read

Vérifier un numéro de téléphone avant une vente

pos:sales:read, pos:sales:write

Enregistrer ventes, retours et clôtures Z

locations:read, locations:write

Enregistrer le magasin où se trouve la caisse

backups:read, backups:write

Réservés : les sauvegardes ne sont pas proposées

devices:self

Enregistrer la caisse, envoyer des heartbeats, se délier

events:read

Lire le flux des changements

Endpoints

Méthode et chemin

Rôle

GET /v1/me

Le propriétaire et les boutiques de la liaison

GET /v1/store

La boutique choisie : nom, langue, devise DZD, moment de déduction du stock, plan et plafond de produits

POST /v1/devices, GET /v1/devices

Enregistrer la caisse (une ligne par caisse et par boutique), lister les caisses

POST /v1/devices/{id}/heartbeat

Toutes les 15 minutes : version, envois en attente et en échec

DELETE /v1/devices/{id}

Délier cette caisse

GET /v1/locations, POST /v1/locations

Le magasin où se trouve la caisse

POST /v1/products/batch

Créer ou modifier jusqu'à 100 produits

GET /v1/products, GET /v1/products/{id}

Produits modifiés depuis une date, un produit

POST /v1/media/uploads, POST /v1/products/{id}/images

Ticket photo, puis rattacher la photo envoyée

POST /v1/inventory/adjustments/batch

Jusqu'à 500 fixations ou variations de stock

POST /v1/pos/sales, POST /v1/pos/sales/{sale_id}/refunds, POST /v1/pos/closures

Ventes, retours, clôtures Z

GET /v1/orders, GET /v1/orders/{id}

Les commandes de la boutique au format de la caisse

POST /v1/orders/{id}/claim, PATCH /v1/orders/{id}, POST /v1/orders/{id}/cancel

Prendre une commande, la faire avancer, l'annuler

GET /v1/customers?phone=

Les indicateurs d'un numéro de téléphone

GET /v1/events

Le flux des changements

Certains chemins sont partagés avec les clés API (produits, commandes, clients). Une caisse reçoit les formats de cette page ; une clé API garde les formats de la section Ressources.

Lot de produits

POST /v1/products/batch prend items, jusqu'à 100. Chaque élément porte le external_id propre à la caisse, name, un sku et un barcode facultatifs, pricing.price en chaîne à deux décimales ("4500.00"), inventory.track_stock, status (active, draft ou archived) et une category facultative avec son propre external_id et son name.

  • Un élément modifie le produit déjà relié à son external_id. Sinon, il reprend un produit sans variantes qui a le même sku et aucune liaison. Sinon, il crée un produit avec 0 en stock.

  • Une catégorie est retrouvée par son external_id, ou créée à partir de son nom.

  • La réponse liste chaque élément : external_id, id, status (created ou updated) et error: null. Un élément refusé porte un objet error et ni id ni status.

  • Plafond du plan : quand la boutique a déjà autant de produits actifs que son plan le permet, un lot qui créerait un produit, brouillon ou non, répond 402 product_limit_reached. Les éléments placés avant restent écrits. Passer un produit existant en active au-delà du plafond est une erreur sur cet élément seulement.

  • Le lot ne fixe jamais le stock. Le stock passe par l'appel d'ajustements.

Photos

  1. POST /v1/media/uploads avec filename, content_type (image/jpeg, image/png ou image/webp), size (jusqu'à 8 Mio) et sha256. La réponse porte media_id et une upload_url signée valable 10 minutes.

  2. La caisse envoie les octets bruts en PUT sur upload_url, sans les en-têtes DZBuild.

  3. POST /v1/products/{id}/images avec media_id et position rattache la photo. La taille et le sha256 doivent correspondre au ticket, sinon l'appel répond 422 media_mismatch. Un ticket inconnu ou expiré répond 404 media_not_found. Un produit garde jusqu'à 20 photos.

Stock

POST /v1/inventory/adjustments/batch prend jusqu'à 500 éléments. Chaque élément indique product_external_id, target: "product", soit set (pièces entières) soit delta, une reason (pos_sale, pos_return, restock, count ou loss) et une ref unique.

  • Seuls les produits qui suivent le stock au niveau du produit peuvent être ajustés. Un produit avec un stock par variante répond l'erreur d'élément variant_product, un produit sans suivi de stock not_tracked, un produit qui n'existe plus unknown_product.

  • La réponse liste chaque élément par ref avec status ok ou error.

  • Les changements de stock de la caisse n'apparaissent jamais dans l'historique des modifications du tableau de bord et ne proposent jamais d'annulation.

Documents POS

POST /v1/pos/sales enregistre un ticket, une facture ou un bon de livraison ; POST /v1/pos/sales/{sale_id}/refunds un bon de retour ou un avoir sur cette vente ; POST /v1/pos/closures une clôture Z avec ses totaux et ses ancres d'empreinte.

  • Les documents sont gardés tels qu'envoyés et ne changent jamais : il n'existe aucun chemin de modification ou de suppression.

  • Une vente répond 201 avec {"id": 99120, "stock_applied": false}. Le stock passe par l'appel d'ajustements, jamais par un document.

  • Les documents POS sont séparés des commandes. Ils ne comptent pas dans la limite mensuelle de commandes de la boutique, ne notifient personne et n'atteignent jamais Google Sheets ni les webhooks.

  • Les montants sont des chaînes à deux décimales, les quantités des nombres à 3 décimales au plus, avec jusqu'à 500 lignes et 20 paiements par document.

  • Envoyer un document dont le external_id est déjà enregistré répond 409 already_exists avec l'id enregistré dans error.details.id. La caisse le lit comme un succès.

  • Un retour sur une vente d'une autre boutique répond 404 not_found.

Commandes

  • GET /v1/orders?updated_since=... et GET /v1/orders/{id} donnent les commandes de la boutique au format de la caisse, avec leurs articles. order_number est le numéro court de la boutique quand il existe.

  • POST /v1/orders/{id}/claim avec device_id et terminal : la première caisse l'emporte. La même caisse qui la reprend reçoit 200 ; une autre caisse reçoit 409 order_claimed.

  • PATCH /v1/orders/{id} avec status demande la prise (409 claim_required sinon). Passages permis : de pending ou confirmed vers processing, shipped ou delivered, de processing vers shipped ou delivered, et de shipped vers delivered. Tout autre passage répond 409 transition_not_allowed.

  • POST /v1/orders/{id}/cancel prend reason (out_of_stock, customer_unreachable, duplicate ou other) et une note facultative de 500 caractères au plus. Le stock revient selon les règles de stock de la boutique. Si une autre caisse tient la prise, l'annulation répond 409 order_claimed.

  • Un changement de statut depuis la caisse déclenche les mêmes suites qu'un changement fait dans le tableau de bord, notifications et mise à jour Google Sheets comprises.

Clients

GET /v1/customers?phone=0550123456 répond une page d'un élément au plus : id, is_banned et fraud_score. Elle ne regarde que les clients de la boutique, accepte le numéro avec ou sans +213, et ne donne ni nom, ni adresse, ni historique. Un numéro inconnu donne une page vide.

Événements

GET /v1/events?wait=25&limit=200&cursor=... renvoie items, next_cursor et has_more. next_cursor est toujours présent, même sur une page vide ; la caisse le renvoie à l'appel suivant.

  • La périphérie garde l'appel jusqu'à 25 secondes et répond dès qu'un changement arrive.

  • Le premier appel, sans curseur, commence par un order.updated pour chaque commande pending, confirmed et processing.

  • Types : order.created, order.updated, product.updated, product.deleted, inventory.level_changed, customer.updated et device.revoked. Chaque événement a un id stable, ce qui permet à la caisse d'ignorer les doublons.

  • inventory.level_changed porte old, new, delta et une source : order quand une commande a fait bouger le stock (avec le numéro de commande), dashboard pour tout autre changement fait hors de la caisse, et pos quand une autre caisse de la même boutique l'a changé (la caisse ignore cette source).

  • Les écritures de produits et de stock de la caisse elle-même ne lui sont pas renvoyées.

  • device.revoked porte device_id en chaîne, une seule fois, après la déconnexion de la caisse.

  • Un curseur que cette API n'a pas émis répond 400 bad_request.

Sauvegardes

DZBuild ne garde pas les sauvegardes des caisses. GET /v1/backups, POST /v1/backups, POST /v1/backups/{id}/complete et GET /v1/backups/{id}/download répondent toujours 501 not_implemented, et la caisse garde ses sauvegardes sur le PC.

Codes d'erreur

Statut

code

Quand

400

bad_request

Un updated_since incorrect ou un curseur que cette API n'a pas émis

401

unauthorized

Jeton absent, faux ou expiré, ou liaison terminée

402

product_limit_reached

Un lot créerait un produit au-delà du plafond du plan

403

forbidden

Un chemin hors de la liste de la caisse, ou une boutique que le propriétaire n'a pas choisie

404

not_found

Produit, vente ou commande absent de cette boutique

404

device_not_found

Un id de caisse qui n'est pas cette caisse sur cette boutique

404

media_not_found

Ticket photo inconnu ou expiré

409

already_exists

Un document avec ce external_id est enregistré ; son id est dans details.id

409

order_claimed

Une autre caisse a pris la commande

409

claim_required

Faire avancer une commande que cette caisse n'a pas prise

409

transition_not_allowed

Le passage n'est pas permis depuis le statut de la commande

410

device_revoked

La caisse a été déconnectée

413

payload_too_large

Corps de plus de 1 Mio

422

validation_error

Un champ est incorrect ; details.field le nomme

422

media_mismatch

La photo envoyée ne correspond pas à son ticket

422

idempotency_key_reuse

Même Idempotency-Key avec un autre corps

429

rate_limited

Limite dépassée ; attendre Retry-After

501

not_implemented

Les chemins de sauvegarde

503

storage_unavailable

Le stockage des photos est injoignable ; réessayer plus tard

Avez-vous trouvé la réponse à votre question ?