Die API
JSON über HTTPS auf der eigenen Subdomain der Firma, authentifiziert mit einem API-Schlüssel als Authorization: Bearer fdr_…. Der Scope eines Schlüssels begrenzt ihn: read-Schlüssel dürfen nur GET, write-Schlüssel dürfen alles außer eine Bestellung genehmigen oder versenden, approve-Schlüssel dürfen auch das. Dezimalzahlen (Mengen, Geldbeträge) sind Strings – JSON-Zahlen sind Gleitkommazahlen, diese Werte nicht. Fehler kommen als {"error": "…"} mit aussagekräftigem Statuscode.
Eine Anwendung, die viele Firmen nutzen (Syncomatic, eine Smartphone-App), verwendet statt eines Schlüssels OAuth 2.0, damit jeder Benutzer sie selbst erlaubt und unter Verbundene Anwendungen wieder trennen kann. Siehe OAuth am Ende dieser Seite; ihre Zugriffstoken (fdo_…) werden genauso wie Schlüssel gesendet und unterliegen denselben Scopes.
Lesen
| Endpunkt | Liefert |
|---|---|
GET /api/parts · /api/parts/{id} |
Artikel, seitenweise (?page=&per=); ?category=504 schränkt auf eine Kategorie ein, ?q= durchsucht Nummer, Name und Beschreibung; ein Artikel enthält on_hand |
GET /api/parts/{id}/barcode.png |
Die Artikelnummer als Code-128-Barcode im PNG-Format, mit der Nummer darunter; ?scale=1–10, ?height= (Pixel), ?text=0 nur für die Striche. Das JSON jedes Artikels gibt diesen Pfad als barcode_url an |
GET /api/parts/{id}/bom |
Die Stücklistenpositionen des Artikels: Komponente, Menge pro Einheit, Referenz, Arbeitsgang |
GET /api/parts/{id}/routing |
Die Arbeitsgänge des Artikels, mit Summen |
GET /api/parts/{id}/images |
Die Bilder des Artikels: ID, Dateiname, Kennzeichen für das Hauptbild |
GET /api/suppliers |
Die Lieferanten |
GET /api/parts/{id}/sourcing |
Wer den Artikel verkauft, unter welcher Nummer, zu welchem Preis |
GET /api/demand?status=open&channel= |
Bedarfe mit Vertriebskanal, Referenz, Menge und versendeter Menge |
GET /api/channels |
Die Vertriebskanäle |
GET /api/channels/{code}/listings |
Jeder auf dem Kanal gelistete Artikel mit seiner Kanal-Artikelnummer; null bedeutet: noch nicht übertragen |
GET /api/stock |
Eine Zeile pro Artikel, Lagerort und Charge (lot_id, lot_code, expires_at; nicht chargengeführter Bestand hat keine) |
GET /api/lots?q= |
Chargen und Seriennummern, die neuesten zuerst: Code, Artikel, verfügbarer Bestand, Verfallsdatum, Lieferantencharge |
GET /api/lots/{id}/barcode.png |
Der Code einer Charge oder Seriennummer als Code-128-PNG, wie bei einem Artikel; barcode_url im JSON jeder Charge |
GET /api/sourcing?supplier=&supplier_part_number=&part_number= |
Welcher unserer Artikel einer Lieferantennummer entspricht, oder umgekehrt. Groß- und Kleinschreibung spielt keine Rolle |
GET /api/purchase-orders?status=&number=PO-00004 |
Bestellungen mit Lieferant, Zwischensumme und Link. number findet eine Bestellung so, wie Menschen sie nennen |
GET /api/purchase-orders/{id} |
Eine Bestellung mit ihren Positionen |
GET /api/workstations · /api/workstations/{id} |
Arbeitsplätze mit Gruppe und Status |
GET /api/workstations/{id}/next |
Der Auftrag, den dieser Arbeitsplatz jetzt erledigen soll, mit seinen Materialien – null, außer der Arbeitsplatz ist frei und ein Arbeitsgang ist bereit. ?material=<part id> beschränkt auf Aufträge, die diesen Artikel verbrauchen. Die Antwort enthält current_material und requires_material_change des Arbeitsplatzes |
GET /api/work-orders?status=&number=WO-00023&external_ref= · /api/work-orders/{id} |
Aufträge; ein einzelner Auftrag enthält seine Arbeitsgänge sowie Materialien und Startbereitschaft jedes Arbeitsgangs. Jeder offene Auftrag trägt die Prognose der Vorwärtsterminierung: expected_start, expected_finish, late (Fertigstellung nach due_at) und delay_reasons; vergleichen Sie expected_finish mit due_at, um eine Verzögerung zu erkennen. Abgeschlossene und stornierte Aufträge haben keine Prognose (null) |
GET /api/{parts,workstations,work-orders}/{id}/custom |
Das undurchsichtige JSON oder null |
Schreiben
| Endpunkt | Body | Bewirkt |
|---|---|---|
POST /api/purchase-orders |
{supplier, currency?, exchange_rate?, expected_at?, external_ref?, notes?, lines:[{supplier_part_number | part_id, quantity, unit_price?}]} |
Legt einen Entwurf an; Positionen mit Lieferantennummer werden über die Bezugsquellen aufgelöst. Eine unbekannte Nummer lässt die ganze Anfrage scheitern, mit Nennung der Position. Ein Mensch genehmigt |
PUT /api/workstations/{id}/status |
{"status": "idle|busy|blocked|offline", "note"?} |
Spiegelt den Zustand einer Maschine |
POST /api/work-order-steps/{id}/start |
{"workstation_id"?} |
Übernimmt einen Arbeitsgang |
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"} |
Bucht gefertigte Einheiten; entnimmt Materialien retrograd zur Stücklistenmenge oder, für jeden in consumed genannten Artikel, zur gemeldeten Ist-Menge; der letzte Arbeitsgang bucht den Zugang des Ergebnisses. Für chargengeführte Artikel: used nennt die verbrauchten Chargen oder Seriennummern, made die Seriennummern des Ergebnisses (eine pro Einheit, Pflicht) oder seine Charge (Standard: die Auftragsnummer), expires das Verfallsdatum einer neuen Zielcharge |
POST /api/work-order-steps/{id}/scrap |
{"quantity": "9", "consumed"?: […], "used"?: […], "note"?} |
Ein Durchlauf ohne Ergebnis: Materialien verbraucht, kein Zugang, Zählerstand des Arbeitsgangs unverändert |
POST /api/work-order-steps/{id}/pause |
Ein laufender Arbeitsgang mit Restmenge geht zurück auf ausstehend, der Fortschritt bleibt erhalten, der Arbeitsplatz wird freigegeben – so wird der nächste Auftrag neu gewählt. Die Druckverwaltung tut das nach jedem Druckbett | |
PUT /api/workstations/{id}/material |
{"part_id": 934} oder {"part_id": null} |
Womit der Arbeitsplatz gerüstet ist – das eingelegte Filament eines Druckers |
PUT /api/tasks/{source} |
[{key, priority, message, href, actions?: [{key, label, tone?}]}] |
Ersetzt die Aufgaben dieser Quelle vollständig; alles Weggelassene gilt als erledigt. Quellnamen sind Slugs in Kleinbuchstaben. Eine Aufgabe kann bis zu sechs Aktionen anbieten – Schaltflächen, die ein Mensch drücken kann; ein Druck erledigt die Aufgabe mit diesem Schlüssel als outcome, den die Quelle zurückliest. Eine Aufgabe mit Ergebnis wird nie wieder geöffnet: Verwenden Sie für ein neues Vorkommen einen neuen Schlüssel |
GET /api/tasks/{source} |
Die offenen Aufgaben dieser Quelle und die kürzlich erledigten mit einem outcome – so erfährt eine Integration, welche Schaltfläche gedrückt wurde |
|
PUT /api/{kind}/{id}/custom |
Das JSON selbst | Ersetzt den undurchsichtigen Block (höchstens 64 KB) |
POST /api/parts |
{category_code, name, part_type, description?, notes?, unit_of_measure?, is_stocked?, minimum_stock?, lead_time_days?, tracking?, custom?} |
Legt einen Artikel an; die Lagernummer wird in der Kategorie vergeben. Liefert den Artikel |
POST /api/parts/{id}/bom/lines |
{child_part_id, quantity, reference?, notes?, routing_step_id?} |
Fügt eine Stücklistenposition hinzu; die Regeln gegen Zyklen gelten |
PUT /api/bom/lines/{id} |
{quantity?, reference?, notes?} |
Bearbeitet eine Position |
POST /api/parts/{id}/routing/steps |
{name, group_code?, seq?, setup_minutes?, run_minutes_per_unit?, batch_size?, instructions?} |
Fügt einen Arbeitsgang hinzu; ein leeres seq nimmt den nächsten Zehnerschritt |
POST /api/parts/{id}/images |
multipart, Feld file |
Lädt ein Bild hoch (höchstens 12 MB); das erste wird zum Hauptbild |
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?} |
Legt die Bezugsquelle des Lieferanten für den Artikel an (201) oder aktualisiert sie (200); nicht gesendete Felder behalten ihren Wert. Die erste Bezugsquelle eines Artikels wird bevorzugt |
POST /api/demand |
{channel, reference, part_id | part_number | channel_part_number, quantity, due_at?, kind?, notes?} |
Bedarf eingegangen. Idempotent über (channel, reference): Eine Wiederholung, solange offen, aktualisiert Menge und Datum und antwortet mit 200; ein erstes Eintreffen antwortet mit 201. Eine geschlossene oder stornierte Referenz bleibt unberührt |
POST /api/demand/{id}/ship |
{quantity?, location_id? | location_code?, lots?} |
Bedarf versendet. Bucht diese Menge (leer: den Rest) vom Lagerort oder vom größten Vorrat des Artikels ab, als Lagerbewegung mit Verweis auf den Bedarf; schließt den Bedarf, sobald alles versendet ist. lots nennt die versendeten Chargen oder Seriennummern, wo auch immer sie liegen: Pflicht für ein seriennummerngeführtes Produkt. 409, wenn nicht genug verfügbar ist |
POST /api/demand/{id}/cancel |
Bedarf storniert | |
PUT /api/channels/{code}/listings/{part_id} |
{channel_part_number?, is_listed?, notes?} |
Listet einen Artikel auf dem Kanal oder speichert die Nummer des Kanals für ihn |
Um benachrichtigt zu werden, wenn etwas geschieht, statt nachzufragen, siehe Webhooks.
Für einen Assistenten
Was ein Chat-Agent braucht, um eine Person auf Kurs zu halten: ein Aufruf für „Was braucht mich?“, Suchen nach den Namen, die Menschen verwenden, und die Schaltflächen. Jeder Eintrag trägt eine url, die ein Mensch anklicken kann.
| Endpunkt | Liefert |
|---|---|
GET /api/brief |
Alles mit aktuellem Handlungsbedarf: offene Aufgaben, Zustände der Arbeitsplätze, Fertigungsaufträge im Entwurf und offene, Bestellungen, die auf Genehmigung warten, genehmigte, aber nicht versendete und überfällige, zu fixierende Planaufträge, nötige Umlagerungen, Fehlmengen und Aufträge, die laut Kapazitätsprüfung verspätet fertig werden. headline sagt dasselbe in wenigen Sätzen, das Dringendste zuerst |
GET /api/tasks |
Jede offene Aufgabe, die dringendste zuerst, mit ihren actions |
GET /api/planning |
Die Planungsseite: Planaufträge proposed und firmed (jeder sagt, because wovon), und die Terminplanung jeder Arbeitsplatzgruppe mit ihren verspäteten Aufträgen |
GET /api/demand/{id}/progress |
„Wo ist meine Bestellung?“: der Bedarf, Bestand und eingehende Zugänge des Artikels, die Vorschläge des Planungslaufs für diesen Bedarf, die Fertigungsaufträge, die ihn herstellen, und welche davon verspätet terminiert sind. Zugänge gehören zum Artikel, nicht zum Bedarf: Zwei Bedarfe für denselben Artikel sehen denselben Bestand |
Aktionen ausführen
| Endpunkt | Body | Erfordert | Bewirkt |
|---|---|---|---|
POST /api/tasks/{id}/act |
{"action": "ready"} |
operator (Bediener) | Drückt eine der Schaltflächen der Aufgabe |
POST /api/tasks/{id}/snooze |
{"days": 2} |
operator (Bediener) | Blendet die Aufgabe aus; eine Woche, wenn keine Zahl angegeben ist |
POST /api/tasks/{id}/resolve |
operator (Bediener) | Schließt die Aufgabe von Hand | |
POST /api/work-orders |
{part_number | part_id, location, quantity?, external_ref?, due_at?, priority?, notes?, output_location?, release?} |
planner (Planer) | Legt einen Fertigungsauftrag an: als Entwurf oder freigegeben, wenn release true ist. location ist der Code des Lagerorts, von dem er Teile entnimmt (und an den er das Ergebnis bucht, sofern output_location nichts anderes sagt). external_ref ist Ihr Name für das, worum es beim Auftrag geht. Senden Sie einen Idempotency-Key, sonst legt eine Wiederholung einen zweiten Auftrag an |
POST /api/work-orders/{id}/issue |
{part_number | part_id, quantity?, note?, lots?} |
operator (Bediener) | Entnimmt einen Artikel für den Auftrag aus dem Lager, außerhalb seiner Stückliste; lots nennt seine Chargen oder Seriennummern. 409, wenn nicht genug da ist |
POST /api/work-orders/{id}/release |
planner (Planer) | Vom Entwurf zu freigegeben. Antwortet mit dem Auftrag und der Startbereitschaft jedes Arbeitsgangs | |
POST /api/work-orders/{id}/cancel |
planner (Planer) | Storniert einen nicht abgeschlossenen Auftrag | |
POST /api/planning/run |
planner (Planer) | Startet die Planung sofort. Antwortet mit den Vorschlägen | |
POST /api/planning/firm |
{"ids": [12, 13], "location": "BHILL"} |
planner (Planer) | Macht aus Vorschlägen Fertigungsaufträge und Bestellungen im Entwurf. location (oder location_id) ist nötig, wenn eine Eigenfertigung darunter ist |
POST /api/purchase-orders/{id}/cancel |
planner (Planer) | Storniert die Bestellung | |
POST /api/purchase-orders/{id}/approve |
admin (Administrator), approve-Schlüssel |
Vom Entwurf zu genehmigt | |
POST /api/purchase-orders/{id}/send |
planner (Planer), approve-Schlüssel |
Bucht die Bestellung als versendet. Foudra übermittelt sie nicht an den Lieferanten |
Für jeden dieser Aufrufe gelten vier Regeln:
- Wer. Senden Sie
X-On-Behalf-Of: someone@example.com, und die Änderung erfolgt im Namen dieses Benutzers, mit dessen Rolle. Ohne den Header handelt der Ersteller des Schlüssels. Beide müssen die Rolle haben: Ein Schlüssel kann die Befugnis seines Erstellers nicht an jemand anderen verleihen und nicht mehr ausleihen, als sein Ersteller hat. Der Header wirkt auch bei den lesenden Endpunkten, sodass das Briefing das Briefing dieses Benutzers ist. - Vorschau. Hängen Sie
?dry_run=1an, und die Anfrage führt alles tatsächlich aus und macht es dann rückgängig. Die Antwort ist genau das, was geschehen wäre, mit"dry_run": true; nichts wird geändert. Verwenden Sie das, bevor Sie einen Menschen fragen: „Soll ich?“ - Einmal. Senden Sie einen Header
Idempotency-Key(eine beliebige Zeichenkette bis 200 Zeichen, eindeutig pro beabsichtigter Aktion). Eine Wiederholung innerhalb einer Woche erhält die erste Antwort zurück, mit dem HeaderIdempotent-Replay: true, und nichts wird doppelt ausgeführt. Derselbe Schlüssel bei einer anderen Anfrage wird abgelehnt (422). - Protokoll. Jede Änderung schreibt eine Zeile ins Prüfprotokoll, die den Schlüssel nennt und, falls angegeben, für wen er gehandelt hat.
Jede Antwort enthält summary, einen Satz, den man einem Menschen so wiedergeben kann, data und acted_as. Ablehnungen: 403 für den Scope eines Schlüssels oder die Rolle eines Benutzers, 404, 409, wenn sich die Sache nicht in einem Zustand befindet, der das erlaubt (die Meldung sagt, warum), 422 für eine ungültige Aktion oder einen wiederverwendeten Schlüssel.
Das Druckfarm-Muster
Fragen Sie /next?material=<loaded> ab, solange der Drucker frei ist – ist mit dem eingelegten Material etwas bereit? Wenn nicht, sagt /next ohne Filter, was der oberste Auftrag braucht; spielen Sie eine Aufgabe Filament wechseln ein und gehen Sie auf blockiert. Der Mensch setzt die Spule ein, setzt Loaded material (eingelegtes Material) auf der Arbeitsplatzseite und tippt auf Ready (bereit). Dann start; drucken; complete mit der Menge und dem tatsächlich verbrauchten Filament; status wieder frei. Ein fehlgeschlagener Druck ist scrap, kein Abschluss mit null. Das MRP weiß nichts über Drucker.
Schreibzugriffe über die API werden dem Benutzer zugeschrieben, der den Schlüssel angelegt hat, sofern X-On-Behalf-Of niemand anderen nennt (siehe oben).
OAuth
Für eine Anwendung, die für Benutzer beliebiger Firmen handelt. Anwendungen werden von Foudra registriert (fragen Sie über den Support): Sie erhalten eine client_id, ein Geheimnis für eine serverseitige Anwendung, und Ihre exakten Redirect-URIs werden hinterlegt. Der Ablauf ist der Authorization Code Grant mit PKCE (RFC 6749, RFC 7636), und PKCE mit S256 ist für jeden Client Pflicht.
- Leiten Sie den Browser zu
https://foudra.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=read%20write&state=…&code_challenge=…&code_challenge_method=S256. Der Benutzer gibt an, welche Firma, meldet sich dort an und sieht, was Sie angefordert haben. - Bei Allow (zulassen) kommt der Browser zurück zu
redirect_uri?code=…&state=…; bei Deny (ablehnen) zu?error=access_denied. Ein Code gilt 10 Minuten und funktioniert einmal. POST https://foudra.com/oauth/token, formularkodiert, Client-Authentifizierung per HTTP Basic (ein öffentlicher Client sendet stattdessenclient_idim Formular):grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…. Die Antwort:
{"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"}}
Rufen Sie die API unter api_base_url mit Authorization: Bearer fdo_… auf: Das Token funktioniert nur dort. Ein abgelaufenes oder widerrufenes Token erhält 401 mit WWW-Authenticate: Bearer error="invalid_token".
- Erneuern Sie vor oder nach Ablauf der Stunde mit
grant_type=refresh_token&refresh_token=fdor_…. Refresh-Token rotieren: Jede Verwendung liefert ein neues und verbraucht das alte. Wird ein verbrauchtes Refresh-Token vorgelegt, gilt das als Diebstahl und widerruft jedes Token dieser Anmeldung; der Benutzer muss Sie erneut erlauben. Speichern Sie daher das neue Refresh-Token, bevor Sie das neue Zugriffstoken verwenden, und wiederholen Sie eine Erneuerung nie mit dem alten. Ein 90 Tage lang nicht verwendetes Refresh-Token läuft ab. POST https://foudra.com/oauth/revokemittoken=…(RFC 7009), wenn der Benutzer auf Ihrer Seite trennt. Das Widerrufen eines Refresh-Tokens widerruft auch seine Zugriffstoken.
Scopes sind die Scopes der API-Schlüssel: read, write, approve, wobei jeder den vorherigen einschließt. Was Sie erhalten, ist durch Ihre Registrierung und die Rolle des Benutzers begrenzt (Betrachter und Bediener read; Planer write; Administratoren und Inhaber approve), und die Begrenzung wird bei jeder Anfrage erneut angewendet, sodass ein Benutzer, der herabgestuft oder entfernt wird, Ihren Zugriff mitnimmt. Das Feld scope der Token-Antwort sagt, was gewährt wurde. Schreibzugriffe werden dem Benutzer zugeschrieben, und Aktionen melden "acted_as": {"user": …, "application": …}. X-On-Behalf-Of funktioniert wie bei Schlüsseln.
Fehler des Token-Endpunkts folgen RFC 6749: {"error": "invalid_grant", "error_description": "…"} mit Status 400 oder invalid_client mit 401.