sailing Foudra MRP
Référence chevron_rightapi/index

L'API

Du JSON sur HTTPS, sur le sous-domaine propre à l'entreprise, authentifié par une clé d'API sous la forme Authorization: Bearer fdr_…. La portée d'une clé la limite : les clés read ne peuvent faire que des GET, les clés write peuvent tout faire sauf approuver ou envoyer une commande d'achat, les clés approve peuvent faire cela aussi. Les décimaux (quantités, montants) sont des chaînes — les nombres JSON sont des flottants, ce que ces valeurs ne sont pas. Les erreurs ont la forme {"error": "…"}, avec un code de statut parlant.

Une application utilisée par de nombreuses entreprises (Syncomatic, une application mobile) utilise OAuth 2.0 plutôt qu'une clé, pour que chaque utilisateur l'autorise lui-même et puisse la déconnecter depuis les applications connectées. Voir OAuth à la fin de cette page ; ses jetons d'accès (fdo_…) s'envoient comme les clés et obéissent aux mêmes portées.

Lecture

Point d'accès Renvoie
GET /api/parts · /api/parts/{id} Les articles, paginés (?page=&per=) ; ?category=504 restreint à une catégorie, ?q= cherche dans la référence, le nom et la description ; un article inclut on_hand
GET /api/parts/{id}/barcode.png La référence sous forme de code-barres Code 128 en PNG, la référence imprimée en dessous ; ?scale=1–10, ?height= (en pixels), ?text=0 pour les barres seules. Le JSON de chaque article donne ce chemin dans barcode_url
GET /api/parts/{id}/bom Les lignes de nomenclature de l'article : composant, quantité unitaire, repère, opération de la gamme
GET /api/parts/{id}/routing Les opérations de la gamme de fabrication de l'article, avec les totaux
GET /api/parts/{id}/images Les images de l'article : identifiant, nom de fichier, indicateur d'image principale
GET /api/suppliers Les fournisseurs
GET /api/parts/{id}/sourcing Qui vend l'article, sous quelle référence, à quel prix
GET /api/demand?status=open&channel= Les demandes, avec canal, référence, quantité et quantité expédiée
GET /api/channels Les canaux
GET /api/channels/{code}/listings Chaque article référencé sur le canal, avec son numéro propre au canal ; null signifie pas encore publié
GET /api/stock Une ligne par article, emplacement et lot (lot_id, lot_code, expires_at ; le stock non suivi n'en a pas)
GET /api/lots?q= Les lots et numéros de série, les plus récents d'abord : code, article, disponible, péremption, lot du fournisseur
GET /api/lots/{id}/barcode.png Le code d'un lot ou d'un numéro de série en PNG Code 128, comme pour un article ; barcode_url dans le JSON de chaque lot
GET /api/sourcing?supplier=&supplier_part_number=&part_number= Lequel de nos articles correspond à une référence fournisseur, ou l'inverse. Insensible à la casse
GET /api/purchase-orders?status=&number=PO-00004 Les commandes, avec fournisseur, sous-total et lien. number en trouve une telle que les gens la nomment
GET /api/purchase-orders/{id} Une commande avec ses lignes
GET /api/workstations · /api/workstations/{id} Les postes, avec groupe et état
GET /api/workstations/{id}/next La tâche que ce poste doit faire maintenant, avec ses matières — null sauf si le poste est libre et qu'une opération est prête. ?material=<part id> restreint aux tâches qui consomment cet article. La réponse inclut le current_material du poste et requires_material_change
GET /api/work-orders?status=&number=WO-00023&external_ref= · /api/work-orders/{id} Les ordres ; un ordre seul inclut ses opérations, avec les matières et l'état de préparation de chacune. Chaque ordre ouvert porte la prévision de l'ordonnancement au plus tôt : expected_start, expected_finish, late (fin après due_at) et delay_reasons ; comparez expected_finish à due_at pour voir un glissement. Les ordres terminés ou annulés n'ont pas de prévision (null)
GET /api/{parts,workstations,work-orders}/{id}/custom Le JSON opaque, ou null

Écriture

Point d'accès Corps Effet
POST /api/purchase-orders {supplier, currency?, exchange_rate?, expected_at?, external_ref?, notes?, lines:[{supplier_part_number | part_id, quantity, unit_price?}]} Crée un brouillon ; les lignes données par leur référence fournisseur sont résolues via l'approvisionnement. Une référence inconnue fait échouer toute la requête en indiquant la ligne. Un humain approuve
PUT /api/workstations/{id}/status {"status": "idle|busy|blocked|offline", "note"?} Reflète l'état d'une machine
POST /api/work-order-steps/{id}/start {"workstation_id"?} Prend en charge une opération
POST /api/work-order-steps/{id}/complete {"quantity": "9", "consumed"?: [{"part_id", "quantity"}], "used"?: [{"part_id", "lots": ["R-2291"]}], "made"?: ["G0088"], "expires"?: "2027-03-31"} Enregistre les unités produites ; consomme automatiquement les matières selon la nomenclature, ou selon le réel déclaré pour tout article cité dans consumed ; la dernière opération réceptionne la production. Pour les articles suivis : used désigne les lots ou numéros de série consommés, made les numéros de série produits (un par unité, obligatoire) ou le lot produit (par défaut : le numéro de l'ordre), expires la péremption d'un nouveau lot produit
POST /api/work-order-steps/{id}/scrap {"quantity": "9", "consumed"?: […], "used"?: […], "note"?} Un passage qui n'a rien produit : matières consommées, aucune production, compteur de l'opération inchangé
POST /api/work-order-steps/{id}/pause Une opération en cours à laquelle il reste de la quantité repasse en attente, avancement conservé, poste libéré — la tâche suivante est donc choisie à nouveau. Le gestionnaire d'impression fait cela après chaque plateau
PUT /api/workstations/{id}/material {"part_id": 934} ou {"part_id": null} Ce avec quoi le poste est équipé — le filament chargé dans une imprimante
PUT /api/tasks/{source} [{key, priority, message, href, actions?: [{key, label, tone?}]}] Remplace en bloc l'ensemble des tâches de cette source ; tout ce qui est omis est résolu. Les noms de source sont des identifiants en minuscules. Une tâche peut proposer jusqu'à six actions — des boutons sur lesquels un humain peut appuyer ; un appui résout la tâche avec cette clé comme outcome, que la source relit. Une tâche qui a un résultat n'est jamais rouverte : utilisez une nouvelle clé pour une nouvelle occurrence
GET /api/tasks/{source} Les tâches ouvertes de cette source et celles récemment résolues avec un outcome — c'est ainsi qu'une intégration apprend quel bouton a été pressé
PUT /api/{kind}/{id}/custom Le JSON lui-même Remplace le bloc opaque (64 Ko au maximum)
POST /api/parts {category_code, name, part_type, description?, notes?, unit_of_measure?, is_stocked?, minimum_stock?, lead_time_days?, tracking?, custom?} Crée un article ; le numéro de stock est attribué dans la catégorie. Renvoie l'article
POST /api/parts/{id}/bom/lines {child_part_id, quantity, reference?, notes?, routing_step_id?} Ajoute une ligne de nomenclature ; les règles contre les cycles s'appliquent
PUT /api/bom/lines/{id} {quantity?, reference?, notes?} Modifie une ligne
POST /api/parts/{id}/routing/steps {name, group_code?, seq?, setup_minutes?, run_minutes_per_unit?, batch_size?, instructions?} Ajoute une opération à la gamme ; seq vide prend la dizaine suivante
POST /api/parts/{id}/images multipart, champ file Téléverse une image (12 Mo au maximum) ; la première devient l'image principale
PUT /api/parts/{id}/sourcing/{supplier} {supplier_part_number, unit_price?, currency?, product_url?, minimum_order_qty?, pack_size?, lead_time_days?, is_preferred?, notes?} Crée la ligne du fournisseur pour l'article (201) ou la met à jour (200) ; les champs non envoyés gardent leur valeur. La première source d'un article devient la source préférée
POST /api/demand {channel, reference, part_id | part_number | channel_part_number, quantity, due_at?, kind?, notes?} Demande reçue. Idempotent sur (canal, référence) : une répétition tant que la demande est ouverte met à jour la quantité et la date et répond 200 ; une première arrivée répond 201. Une référence close ou annulée n'est pas touchée
POST /api/demand/{id}/ship {quantity?, location_id? | location_code?, lots?} Demande expédiée. Sort cette quantité (vide : le reste) de l'emplacement, ou du plus gros tas de l'article, par un mouvement du registre qui renvoie à la demande ; clôt la demande quand tout est parti. lots désigne les lots ou numéros de série expédiés, où qu'ils soient : obligatoire pour un produit suivi par numéro de série. 409 si le disponible est insuffisant
POST /api/demand/{id}/cancel Demande annulée
PUT /api/channels/{code}/listings/{part_id} {channel_part_number?, is_listed?, notes?} Référence un article sur le canal, ou enregistre le numéro que le canal lui donne

Pour être prévenu quand quelque chose se produit plutôt que de le demander, voir les webhooks.

Pour un assistant

Ce dont un agent conversationnel a besoin pour aider quelqu'un à tenir le cap : un appel pour « ce qui m'attend », des recherches par les noms qu'emploient les gens, et les boutons. Chaque élément porte une url sur laquelle un humain peut cliquer.

Point d'accès Renvoie
GET /api/brief Tout ce qui demande de l'attention maintenant : tâches ouvertes, états des postes de travail, ordres de fabrication en brouillon et ouverts, commandes d'achat en attente d'approbation, approuvées mais non envoyées, et en retard, ordres planifiés à confirmer, transferts nécessaires, manques, et tâches que la vérification de capacité annonce en retard. headline dit la même chose en quelques phrases, le plus urgent d'abord
GET /api/tasks Chaque tâche ouverte, la plus urgente d'abord, avec ses actions
GET /api/planning La page de planification : ordres planifiés proposed (proposés) et firmed (confirmés), chacun disant because (à cause de) quoi, et l'ordonnancement de chaque groupe de postes avec ses tâches en retard
GET /api/demand/{id}/progress « Où en est ma commande ? » : la demande, le stock et les approvisionnements attendus de l'article, les propositions du calcul de planification pour cette commande, les ordres de fabrication qui la produisent, et lesquels sont planifiés en retard. Les approvisionnements appartiennent à l'article, pas à la commande : deux commandes du même article voient le même stock

Agir

Point d'accès Corps Rôle requis Effet
POST /api/tasks/{id}/act {"action": "ready"} opérateur Appuie sur l'un des boutons de la tâche
POST /api/tasks/{id}/snooze {"days": 2} opérateur Masque la tâche ; une semaine si aucun nombre n'est donné
POST /api/tasks/{id}/resolve opérateur Clôt la tâche à la main
POST /api/work-orders {part_number | part_id, location, quantity?, external_ref?, due_at?, priority?, notes?, output_location?, release?} planificateur Crée un ordre de fabrication : un brouillon, ou lancé si release vaut true. location est le code de l'endroit où il prélève les articles (et dépose la production, sauf si output_location diffère). external_ref est votre nom pour l'objet du travail. Envoyez un Idempotency-Key, sinon une nouvelle tentative crée un second ordre
POST /api/work-orders/{id}/issue {part_number | part_id, quantity?, note?, lots?} opérateur Prélève un article en magasin pour l'ordre, hors nomenclature ; lots désigne ses lots ou numéros de série. 409 si la quantité est insuffisante
POST /api/work-orders/{id}/release planificateur Passe de brouillon à lancé. Répond avec l'ordre et l'état de préparation de chaque opération
POST /api/work-orders/{id}/cancel planificateur Annule un ordre non terminé
POST /api/planning/run planificateur Lance la planification maintenant. Répond avec les propositions
POST /api/planning/firm {"ids": [12, 13], "location": "BHILL"} planificateur Transforme des propositions en ordres de fabrication et commandes d'achat en brouillon. location (ou location_id) est nécessaire quand une fabrication en fait partie
POST /api/purchase-orders/{id}/cancel planificateur Annule la commande
POST /api/purchase-orders/{id}/approve administrateur, clé approve Passe de brouillon à approuvée
POST /api/purchase-orders/{id}/send planificateur, clé approve Enregistre la commande comme envoyée. Foudra ne la transmet pas au fournisseur

Quatre règles s'appliquent à chacun de ces appels :

Chaque réponse comporte summary, une phrase à répéter telle quelle à un humain, data et acted_as. Refus : 403 pour la portée d'une clé ou le rôle d'un utilisateur, 404, 409 quand l'objet n'est pas dans un état qui le permet (le message dit pourquoi), 422 pour une action invalide ou une clé réutilisée.

Le cas de la ferme d'impression

Interrogez /next?material=<loaded> tant que le poste est libre — y a-t-il quelque chose de prêt avec ce qui est chargé ? Sinon, /next sans filtre indique ce dont a besoin la tâche en tête ; envoyez une tâche change filament (changer de filament) et passez à l'état bloqué. L'humain installe la bobine, renseigne Loaded material (matière chargée) sur la page du poste de travail, appuie sur Ready (prêt). Puis start ; impression ; complete avec la quantité et le filament réellement utilisé ; status de nouveau libre. Une impression ratée est un scrap, pas une fin d'opération avec zéro unité. Le MRP ne sait rien des imprimantes.

Les écritures via l'API sont enregistrées au nom de l'utilisateur qui a créé la clé, sauf si X-On-Behalf-Of désigne quelqu'un d'autre (voir plus haut).

OAuth

Pour une application qui agit au nom d'utilisateurs de n'importe quelle entreprise. Les applications sont enregistrées par Foudra (demandez-le via le support) : vous recevez un client_id, un secret pour une application côté serveur, et vos URI de redirection exactes sont enregistrées. Le flux est l'octroi par code d'autorisation avec PKCE (RFC 6749, RFC 7636), et PKCE avec S256 est exigé de tout client.

  1. Envoyez le navigateur vers https://foudra.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=read%20write&state=…&code_challenge=…&code_challenge_method=S256. L'utilisateur indique son entreprise, s'y connecte, et voit ce que vous avez demandé.
  2. S'il accepte (Allow), le navigateur revient sur redirect_uri?code=…&state=… ; s'il refuse (Deny), sur ?error=access_denied. Un code dure 10 minutes et ne sert qu'une fois.
  3. POST https://foudra.com/oauth/token, encodé en formulaire, authentification du client par HTTP Basic (un client public envoie plutôt client_id dans le formulaire) : grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…. La réponse :
{"access_token": "fdo_…", "token_type": "Bearer", "expires_in": 3600,
 "refresh_token": "fdor_…", "scope": "read write",
 "company": {"slug": "acme", "name": "Acme Widgets", "api_base_url": "https://acme.foudra.com"}}

Appelez l'API à l'adresse api_base_url avec Authorization: Bearer fdo_… : le jeton ne fonctionne que là. Un jeton expiré ou révoqué reçoit 401 avec WWW-Authenticate: Bearer error="invalid_token".

  1. Renouvelez avant ou après l'heure avec grant_type=refresh_token&refresh_token=fdor_…. Les jetons de renouvellement tournent : chaque utilisation en renvoie un nouveau et consomme l'ancien. Présenter un jeton de renouvellement déjà consommé est considéré comme un vol et révoque tous les jetons issus de cette connexion ; l'utilisateur doit vous autoriser à nouveau. Enregistrez donc le nouveau jeton de renouvellement avant d'utiliser le nouveau jeton d'accès, et ne retentez jamais un renouvellement avec l'ancien. Un jeton de renouvellement inutilisé pendant 90 jours expire.
  2. POST https://foudra.com/oauth/revoke avec token=… (RFC 7009) quand l'utilisateur se déconnecte de votre côté. Révoquer un jeton de renouvellement révoque aussi ses jetons d'accès.

Les portées sont celles des clés d'API : read, write, approve, chacune incluant la précédente. Ce que vous obtenez est plafonné par votre enregistrement et par le rôle de l'utilisateur (lecteurs et opérateurs read ; planificateurs write ; administrateurs et propriétaires approve), et ce plafond est réappliqué à chaque requête : un utilisateur rétrogradé ou retiré emporte votre accès avec lui. Le scope de la réponse du jeton indique ce qui a été accordé. Les écritures sont attribuées à l'utilisateur, et les appels d'action renvoient "acted_as": {"user": …, "application": …}. X-On-Behalf-Of fonctionne comme avec les clés.

Les erreurs du point d'accès des jetons sont celles de la RFC 6749 : {"error": "invalid_grant", "error_description": "…"} avec le statut 400, ou invalid_client avec 401.