API
JSON przez HTTPS w subdomenie firmy, z uwierzytelnieniem kluczem API w postaci Authorization: Bearer fdr_…. Klucz jest ograniczony swoim zakresem: klucze read mogą tylko GET, klucze write mogą wszystko poza zatwierdzeniem lub wysłaniem zamówienia zakupu, a klucze approve mogą także to. Liczby dziesiętne (ilości, kwoty) są napisami — liczby w JSON są zmiennoprzecinkowe, a te takie nie są. Błędy mają postać {"error": "…"} z odpowiednim kodem statusu.
Aplikacja używana przez wiele firm (Syncomatic, aplikacja na telefon) zamiast klucza korzysta z OAuth 2.0, aby każdy użytkownik sam na nią pozwalał i mógł ją odłączyć w Connected apps (połączone aplikacje). Zob. OAuth na końcu tej strony; jej tokeny dostępu (fdo_…) wysyła się tak samo jak klucze i obowiązują je te same zakresy.
Odczyt
| Punkt końcowy | Zwraca |
|---|---|
GET /api/parts · /api/parts/{id} |
Części, stronicowane (?page=&per=); ?category=504 zawęża do kategorii, ?q= przeszukuje numer, nazwę i opis; część zawiera on_hand |
GET /api/parts/{id}/barcode.png |
Numer części jako kod kreskowy Code 128 w PNG, z numerem wydrukowanym pod spodem; ?scale=1–10, ?height= (piksele), ?text=0 dla samych kresek. JSON każdej części podaje tę ścieżkę jako barcode_url |
GET /api/parts/{id}/bom |
Pozycje zestawienia materiałowego części: komponent, ilość na sztukę, oznaczenie referencyjne, operacja marszruty |
GET /api/parts/{id}/routing |
Operacje marszruty części, z sumami |
GET /api/parts/{id}/images |
Obrazy części: id, nazwa pliku, znacznik obrazu głównego |
GET /api/suppliers |
Dostawcy |
GET /api/parts/{id}/sourcing |
Kto sprzedaje część, pod jakim numerem i za ile |
GET /api/demand?status=open&channel= |
Zapotrzebowania z kanałem, numerem referencyjnym, ilością i ilością wysłaną |
GET /api/channels |
Kanały |
GET /api/channels/{code}/listings |
Wszystkie części wystawione w kanale z numerem części w kanale; null oznacza, że jeszcze nie wysłano |
GET /api/stock |
Jeden wiersz na część, lokalizację i partię (lot_id, lot_code, expires_at; zapas nieśledzony ich nie ma) |
GET /api/lots?q= |
Partie i numery seryjne, od najnowszych: kod, część, stan dostępny, data ważności, partia dostawcy |
GET /api/lots/{id}/barcode.png |
Kod partii lub numeru seryjnego jako PNG z Code 128, tak jak dla części; barcode_url w JSON każdej partii |
GET /api/sourcing?supplier=&supplier_part_number=&part_number= |
Która z naszych części odpowiada numerowi dostawcy, albo odwrotnie. Wielkość liter nie ma znaczenia |
GET /api/purchase-orders?status=&number=PO-00004 |
Zamówienia z dostawcą, sumą częściową i linkiem. number znajduje zamówienie tak, jak nazywają je ludzie |
GET /api/purchase-orders/{id} |
Zamówienie z pozycjami |
GET /api/workstations · /api/workstations/{id} |
Stanowiska z grupą i statusem |
GET /api/workstations/{id}/next |
Praca, którą to stanowisko powinno teraz wykonać, z materiałami — null, chyba że stanowisko jest wolne i jakaś operacja jest gotowa. ?material=<part id> ogranicza do pracy zużywającej tę część. Odpowiedź zawiera current_material stanowiska i requires_material_change |
GET /api/work-orders?status=&number=WO-00023&external_ref= · /api/work-orders/{id} |
Zlecenia; pojedyncze zlecenie zawiera operacje oraz materiały i gotowość każdej z nich. Każde otwarte zlecenie ma prognozę z harmonogramu w przód: expected_start, expected_finish, late (zakończenie po due_at) i delay_reasons; porównaj expected_finish z due_at, aby zobaczyć poślizg. Zlecenia zakończone i anulowane nie mają prognozy (null) |
GET /api/{parts,workstations,work-orders}/{id}/custom |
Nieprzezroczysty JSON albo null |
Zapis
| Punkt końcowy | Treść | Działanie |
|---|---|---|
POST /api/purchase-orders |
{supplier, currency?, exchange_rate?, expected_at?, external_ref?, notes?, lines:[{supplier_part_number | part_id, quantity, unit_price?}]} |
Tworzy szkic; pozycje podane numerem dostawcy są rozwiązywane przez źródła zaopatrzenia. Nieznany numer powoduje odrzucenie całego żądania ze wskazaniem pozycji. Zatwierdza człowiek |
PUT /api/workstations/{id}/status |
{"status": "idle|busy|blocked|offline", "note"?} |
Odzwierciedla stan maszyny |
POST /api/work-order-steps/{id}/start |
{"workstation_id"?} |
Podejmuje operację |
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"} |
Rejestruje wykonane sztuki; rozchodowuje materiały automatycznie według normy z zestawienia materiałowego albo według zgłoszonego rzeczywistego zużycia dla części wymienionych w consumed; ostatnia operacja przyjmuje wyrób. Dla części śledzonych: used wskazuje zużyte partie lub numery seryjne, made numery seryjne wyrobu (jeden na sztukę, wymagane) albo jego partię (domyślnie numer zlecenia), expires datę ważności nowej partii wyrobu |
POST /api/work-order-steps/{id}/scrap |
{"quantity": "9", "consumed"?: […], "used"?: […], "note"?} |
Przebieg, który niczego nie wytworzył: materiały zużyte, brak wyrobu, licznik operacji bez zmian |
POST /api/work-order-steps/{id}/pause |
Trwająca operacja z pozostałą ilością wraca do oczekujących, z zachowaniem postępu i zwolnieniem stanowiska — następna praca jest wybierana od nowa. Menedżer druku robi tak po każdym stole | |
PUT /api/workstations/{id}/material |
{"part_id": 934} lub {"part_id": null} |
Czym stanowisko jest uzbrojone — filament załadowany do drukarki |
PUT /api/tasks/{source} |
[{key, priority, message, href, actions?: [{key, label, tone?}]}] |
Zastępuje w całości zestaw zadań tego źródła; wszystko, czego brak, zostaje zamknięte. Nazwy źródeł to identyfikatory pisane małymi literami. Zadanie może oferować do sześciu akcji — przycisków, które człowiek może nacisnąć; naciśnięcie zamyka zadanie z kluczem akcji jako outcome, który źródło odczytuje. Zadanie z wynikiem nigdy nie jest ponownie otwierane: dla nowego wystąpienia użyj nowego klucza |
GET /api/tasks/{source} |
Otwarte zadania tego źródła i niedawno zamknięte z outcome — w ten sposób integracja dowiaduje się, który przycisk naciśnięto |
|
PUT /api/{kind}/{id}/custom |
Sam JSON | Zastępuje nieprzezroczysty blok (maks. 64 KB) |
POST /api/parts |
{category_code, name, part_type, description?, notes?, unit_of_measure?, is_stocked?, minimum_stock?, lead_time_days?, tracking?, custom?} |
Tworzy część; numer magazynowy jest nadawany w kategorii. Zwraca część |
POST /api/parts/{id}/bom/lines |
{child_part_id, quantity, reference?, notes?, routing_step_id?} |
Dodaje pozycję zestawienia materiałowego; obowiązują reguły dotyczące cykli |
PUT /api/bom/lines/{id} |
{quantity?, reference?, notes?} |
Edytuje pozycję |
POST /api/parts/{id}/routing/steps |
{name, group_code?, seq?, setup_minutes?, run_minutes_per_unit?, batch_size?, instructions?} |
Dodaje operację marszruty; puste seq przyjmuje kolejną dziesiątkę |
POST /api/parts/{id}/images |
multipart, pole file |
Wysyła obraz (maks. 12 MB); pierwszy staje się głównym |
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?} |
Tworzy wiersz dostawcy dla części (201) albo go aktualizuje (200); niewysłane pola zachowują wartość. Pierwsze źródło części staje się preferowanym |
POST /api/demand |
{channel, reference, part_id | part_number | channel_part_number, quantity, due_at?, kind?, notes?} |
Zapotrzebowanie przyjęte. Idempotentne względem (channel, reference): powtórzenie, gdy zapotrzebowanie jest otwarte, aktualizuje ilość i datę i odpowiada 200; pierwsze nadejście odpowiada 201. Zamknięty lub anulowany numer referencyjny pozostaje bez zmian |
POST /api/demand/{id}/ship |
{quantity?, location_id? | location_code?, lots?} |
Zapotrzebowanie wysłane. Wydaje tę ilość (puste: resztę) z lokalizacji lub z największej pozycji części jako ruch w rejestrze odwołujący się do zapotrzebowania; zamyka zapotrzebowanie, gdy wszystko zostało wysłane. lots wskazuje wysłane partie lub numery seryjne, gdziekolwiek są: wymagane dla wyrobu śledzonego numerami seryjnymi. 409, gdy stan dostępny jest niewystarczający |
POST /api/demand/{id}/cancel |
Zapotrzebowanie anulowane | |
PUT /api/channels/{code}/listings/{part_id} |
{channel_part_number?, is_listed?, notes?} |
Wystawia część w kanale albo zapisuje dla niej numer kanału |
Aby dostawać powiadomienia o zdarzeniach zamiast pytać, zob. webhooki.
Dla asystenta
Czego potrzebuje agent czatu, aby pilnować, żeby człowiek trzymał się planu: jedno wywołanie „co czeka na mnie”, wyszukiwanie po nazwach, których używają ludzie, i przyciski. Każdy element ma url, który człowiek może kliknąć.
| Punkt końcowy | Zwraca |
|---|---|
GET /api/brief |
Wszystko, co wymaga teraz uwagi: otwarte zadania, stany stanowisk, szkice i otwarte zlecenia produkcyjne, zamówienia zakupu czekające na zatwierdzenie, zatwierdzone, ale niewysłane, i przeterminowane, zlecenia planowane do zatwierdzenia, potrzebne przesunięcia, braki oraz praca, która według kontroli zdolności produkcyjnej skończy się z opóźnieniem. headline mówi to samo w kilku zdaniach, od najpilniejszego |
GET /api/tasks |
Wszystkie otwarte zadania, od najpilniejszego, z ich actions |
GET /api/planning |
Strona planowania: zlecenia planowane proposed i firmed (każde podaje because, z jakiego powodu) oraz harmonogram każdej grupy stanowisk z opóźnioną pracą |
GET /api/demand/{id}/progress |
„Gdzie jest moje zamówienie?”: zapotrzebowanie, zapas części i nadchodzące dostawy, propozycje przebiegu planowania dla tego zamówienia, zlecenia produkcyjne, które je wykonują, i które z nich są zaplanowane z opóźnieniem. Podaż należy do części, a nie do zamówienia: dwa zamówienia na jedną część widzą ten sam zapas |
Działania
| Punkt końcowy | Treść | Wymaga | Działanie |
|---|---|---|---|
POST /api/tasks/{id}/act |
{"action": "ready"} |
operator | Naciska jeden z przycisków zadania |
POST /api/tasks/{id}/snooze |
{"days": 2} |
operator | Ukrywa zadanie; na tydzień, gdy nie podano liczby |
POST /api/tasks/{id}/resolve |
operator | Zamyka zadanie ręcznie | |
POST /api/work-orders |
{part_number | part_id, location, quantity?, external_ref?, due_at?, priority?, notes?, output_location?, release?} |
planista | Tworzy zlecenie produkcyjne: szkic albo uruchomione, gdy release ma wartość true. location to kod miejsca, z którego pobiera części (i do którego odkłada wyrób, chyba że output_location jest inne). external_ref to twoja nazwa tego, czego dotyczy zlecenie. Wyślij Idempotency-Key, inaczej ponowienie utworzy drugie zlecenie |
POST /api/work-orders/{id}/issue |
{part_number | part_id, quantity?, note?, lots?} |
operator | Pobiera część z magazynu dla zlecenia, poza jego zestawieniem materiałowym; lots wskazuje partie lub numery seryjne. 409, gdy jest jej za mało |
POST /api/work-orders/{id}/release |
planista | Ze szkicu do uruchomionego. Odpowiada zleceniem i gotowością każdej operacji | |
POST /api/work-orders/{id}/cancel |
planista | Anuluje niezakończone zlecenie | |
POST /api/planning/run |
planista | Uruchamia planowanie teraz. Odpowiada propozycjami | |
POST /api/planning/firm |
{"ids": [12, 13], "location": "BHILL"} |
planista | Zamienia propozycje w szkice zleceń produkcyjnych i zamówień zakupu. location (lub location_id) jest potrzebne, gdy wśród nich jest produkcja |
POST /api/purchase-orders/{id}/cancel |
planista | Anuluje zamówienie | |
POST /api/purchase-orders/{id}/approve |
administrator, klucz approve |
Ze szkicu do zatwierdzonego | |
POST /api/purchase-orders/{id}/send |
planista, klucz approve |
Rejestruje zamówienie jako wysłane. Foudra nie przekazuje go dostawcy |
Do każdego z nich stosują się cztery zasady:
- Kto. Wyślij
X-On-Behalf-Of: someone@example.com, a zmiana zostanie wykonana w imieniu tego użytkownika i z jego rolą. Bez tego nagłówka działającym jest twórca klucza. Obaj muszą mieć wymaganą rolę: klucz nie może użyczyć uprawnień swojego twórcy komuś innemu ani pożyczyć więcej, niż ma twórca. Nagłówek działa także w punktach końcowych odczytu, więc podsumowanie jest podsumowaniem tego użytkownika. - Podgląd. Dodaj
?dry_run=1, a żądanie wykona wszystko naprawdę, po czym to cofnie. Odpowiedź jest dokładnie tym, co by się stało, z"dry_run": true; nic nie zostaje zmienione. Używaj tego, zanim zapytasz człowieka „czy mam to zrobić?”. - Raz. Wyślij nagłówek
Idempotency-Key(dowolny napis do 200 znaków, unikalny dla zamierzonego działania). Powtórzenie w ciągu tygodnia dostaje z powrotem pierwszą odpowiedź, z nagłówkiemIdempotent-Replay: true, i nic nie jest robione dwa razy. Ten sam klucz w innym żądaniu zostaje odrzucony (422). - Zapis. Każda zmiana zapisuje wiersz w dzienniku zdarzeń ze wskazaniem klucza i, jeśli podano, osoby, w której imieniu działał.
Każda odpowiedź zawiera summary — zdanie, które można powtórzyć człowiekowi — data i acted_as. Odmowy: 403 z powodu zakresu klucza lub roli użytkownika, 404, 409, gdy obiekt nie jest w stanie, który na to pozwala (komunikat mówi dlaczego), 422 dla błędnej akcji lub ponownie użytego klucza.
Schemat farmy drukarek
Gdy stanowisko jest wolne, odpytuj /next?material=<loaded> — czy jest coś gotowego z tego, co załadowane? Jeśli nie, /next bez filtra mówi, czego potrzebuje pierwsza praca w kolejce; przekaż zadanie change filament (zmień filament) i przejdź w stan zablokowany. Człowiek zakłada szpulę, ustawia Loaded material (załadowany materiał) na stronie stanowiska i naciska Ready. Potem start; druk; complete z ilością i rzeczywiście zużytym filamentem; ponownie status idle. Nieudany wydruk to scrap, a nie zakończenie z ilością zero. MRP nic nie wie o drukarkach.
Zapisy przez API są rejestrowane na użytkownika, który utworzył klucz, chyba że X-On-Behalf-Of wskazuje kogoś innego (zob. wyżej).
OAuth
Dla aplikacji działającej w imieniu użytkowników dowolnej firmy. Aplikacje rejestruje Foudra (poproś przez pomoc techniczną): dostajesz client_id, sekret dla aplikacji działającej po stronie serwera, a twoje dokładne adresy przekierowania (redirect URI) zostają zapisane. Przepływ to przyznanie kodu autoryzacji z PKCE (RFC 6749, RFC 7636), a PKCE z S256 jest wymagane od każdego klienta.
- Skieruj przeglądarkę na
https://foudra.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=read%20write&state=…&code_challenge=…&code_challenge_method=S256. Użytkownik wskazuje firmę, loguje się w niej i widzi, o co prosisz. - Po Allow przeglądarka wraca na
redirect_uri?code=…&state=…; po Deny na?error=access_denied. Kod jest ważny 10 minut i działa raz. POST https://foudra.com/oauth/token, kodowany jako formularz, z uwierzytelnieniem klienta przez HTTP Basic (klient publiczny zamiast tego wysyłaclient_idw formularzu):grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…. Odpowiedź:
{"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"}}
Wywołuj API pod api_base_url z Authorization: Bearer fdo_…: token działa tylko tam. Wygasły lub unieważniony token dostaje 401 z WWW-Authenticate: Bearer error="invalid_token".
- Odśwież token przed upływem godziny lub po nim przez
grant_type=refresh_token&refresh_token=fdor_…. Tokeny odświeżania rotują: każde użycie zwraca nowy i zużywa stary. Przedstawienie zużytego tokenu odświeżania jest traktowane jako kradzież i unieważnia wszystkie tokeny z tego logowania; użytkownik musi ponownie na ciebie pozwolić. Dlatego zapisz nowy token odświeżania, zanim użyjesz nowego tokenu dostępu, i nigdy nie ponawiaj odświeżenia starym. Token odświeżania nieużywany przez 90 dni wygasa. POST https://foudra.com/oauth/revokeztoken=…(RFC 7009), gdy użytkownik odłączy się po twojej stronie. Unieważnienie tokenu odświeżania unieważnia także jego tokeny dostępu.
Zakresy to zakresy kluczy API: read, write, approve, każdy obejmujący poprzedni. To, co dostajesz, jest ograniczone twoją rejestracją i rolą użytkownika (przeglądający i operatorzy read; planiści write; administratorzy i właściciele approve), a ograniczenie jest stosowane ponownie przy każdym żądaniu, więc użytkownik zdegradowany lub usunięty zabiera ze sobą twój dostęp. scope w odpowiedzi z tokenem mówi, co przyznano. Zapisy są przypisywane użytkownikowi, a operacje zwracają "acted_as": {"user": …, "application": …}. X-On-Behalf-Of działa tak samo jak przy kluczach.
Błędy punktu końcowego tokenu są zgodne z RFC 6749: {"error": "invalid_grant", "error_description": "…"} ze statusem 400 albo invalid_client z 401.