ERP ve Sistem Entegrasyonu Kılavuzu
Bir entegrasyonu inşa etmeden önce kapsamını belirleyin: API'nin nereye eriştiği, nereye erişemediği ve sınırların nerede olduğu
Bu kılavuz, FreeMaint ile başka bir sistem — bir ERP, bir SCADA historian'ı, bir depo ya da muhasebe yazılımı — arasındaki entegrasyonun kapsamını belirleyen geliştirici içindir. Bu bölümdeki diğer makaleler çağrıların nasıl yapılacağını anlatır. Bu makale ise bilerek sınırlardan söz eder, çünkü pahalıya patlayan hata yanlış bir başlık değildir: pahalı olan, geliştirmenin yarısında, var saydığınız bir yeteneğin aslında bulunmadığını fark etmektir. Aşağıdaki her sınır bilinçli olarak yazılmıştır.
Tek anahtar, üç kapı
Bir API anahtarı (fmk_ ile başlar) bir yönetici tarafından Şirket Ayarları > API (/company-settings/api) altında ya da bir kişi tarafından kendisi için Profil > Güvenlik > API anahtarları altında oluşturulur ve Authorization: Bearer fmk_... biçiminde gönderilir. Tam olarak üç şeyin kimliğini doğrular: POST /api/v1/graphql adresindeki GraphQL uç noktası, enerji verisi alma uç noktası ve /public-api/whoami ile /public-api/ping. /api/v1/{resource} altındaki REST yollarının kimliğini doğrulaMAZ — onlar uygulamanın kendi API'sidir ve bir kullanıcı oturumu (JWT) gerektirir. Entegrasyonunuzu GraphQL üzerine kurun: bir anahtarın eriştiği yüzey budur.
Bir API anahtarının size verdikleri
Ham anahtar yalnızca bir kez, oluşturulduğu anda gösterilir ve sonrasında asla geri alınamaz: yalnızca bir SHA-256 özeti saklanır; listenin anahtarı ekranda tanıyabilmesi için fmk_ sonrasındaki 6 karakter de birlikte tutulur.
Ad
İnsanın okuyabileceği bir etiket — “Oracle ERP köprüsü”, “2. hat kiosku”. Her tüketen sistem için ayrı bir anahtar çıkarın; böylece birini, diğerlerini rahatsız etmeden iptal edebilirsiniz.
Erişim düzeyi
READ, READ_STOCK veya FULL; sahibin izinlerinin üzerine konan sıralı bir hiyerarşi. READ yalnızca sorgu çalıştırır ve varsayılandır. READ_STOCK, adjustStock mutasyonunu ekler. FULL, oluşturma, güncelleme ve silme dâhil, sahibin rolünün izin verdiği her işleme izin verir — sahip enerji okumaları oluşturabiliyorsa enerji veri alım uç noktasına da.
Son kullanma
İsteğe bağlı bir expiresAt. O andan sonra anahtar kimlik doğrulamayı bırakır. Kalıcı bir servis entegrasyonu için boş bırakın; bir taşeron ya da pilot çalışma için doldurun.
İptal
isActive, saniyeler içinde etkili olan, anahtar başına bir düğmedir. Kayıt denetim amacıyla saklanır; iptal edilmiş bir anahtar ortadan kaybolmak yerine izlenebilir kalır.
Son kullanım
lastUsedAt, kimliği doğrulanmış en son çağrıyı kaydeder — artık kimsenin kullanmadığı bir anahtarı fark etmeye yeter.
Sahip
Anahtarın adına davrandığı kişi. Her çağrı bu kişinin izinlerini ve veri görünürlüğünü taşır; anahtarın yazdıklarını da bu kişinin adı imzalar. Bir ERP için özel bir kullanıcı oluşturun — “ERP entegrasyonu” — rolü köprünün tam olarak ihtiyaç duyduğu şeyi kapsasın ve anahtarı bu kullanıcıya verin.
Bir API anahtarının size vermedikleri
Bunlar mevcut uygulamanın gerçek sınırlarıdır, bu sayfanın eksikleri değil. Güvenlik incelemeniz bunlardan herhangi birine bağlıysa, tasarımı şimdi buna göre yapın; inşaattan sonra değil.
- Anahtar başına modül listesi yok. Bir anahtar, sahibinin rolünün izin verdiği şeyleri okur ve yazar — kapsamını daraltmak için anahtarı rolü daha dar olan bir sahibe verin. Bunun üzerine yalnızca üç erişim düzeyi uygulanır: anahtarın kendisi üzerinde kullanabileceği modülleri listeleyemezsiniz.
- IP izin listesi yok. Bir anahtar internetin her yerinden geçerlidir. Onu bir sır gibi ele alın: bir kasa ya da platformunuzun gizli bilgi deposu; asla bir kod deposu, paylaşımlı bir sürücü veya bir destek kaydı değil.
- Anahtar başına kullanım günlüğü yok. Yalnızca lastUsedAt saklanır. Anahtar başına çağrı geçmişi, anahtar başına kota ve hangi anahtarın hangi işlemi yaptığına dair bir kayıt yoktur. Günlük kota sayacı ŞİRKET başınadır ve tüm anahtarlarca paylaşılır; dolayısıyla kontrolden çıkan tek bir entegrasyon diğerlerinin tamamının kotasını tüketebilir.
- Başkası adına onay yok. Sahibi onaylayabilecek olsa bile bir anahtar hiçbir zaman bir iş akışı adımını onaylamaz: onaylar uygulamada insan eliyle yapılan bir işlem olarak kalır.
- Anahtar başına tek şirket. Bir anahtar, oluşturulduğu şirkete bağlıdır ve başka bir şirketin verilerine erişemez: başka bir şirketin kaydı basitçe bulunamaz.
Neleri okuyabilirsiniz
GraphQL 16 okuma sorgusu sunar: whoami, workOrders, workOrder(id), assets, asset(id), assetDowntimes, meters(assetId), parts, part (id veya barkod ile), stockMovements, locations, vendors, customers, purchaseOrders, purchaseOrder(id | reference | erpReference) ve floorPlans(locationId). Her liste sonuna kadar sayfalanabilir: workOrders, assets, assetDowntimes, meters, parts, purchaseOrders, locations, vendors ve customers'ın hepsi limit (1 ile 200 arası) artı offset alır; ayrıca workOrders, assets, parts, purchaseOrders, locations, vendors ve customers artımlı eşitlemeler için updatedSince de alır. Varsayılan sayfa 50 satırdır; yalnızca locations, vendors ve customers eski varsayılanları olan 200'ü korur. Her liste eşitlikleri bozmak için id üzerinde ikincil bir sıralamayla sıralanır; böylece bir sayfa sınırı asla bir satırı atlamaz veya tekrarlamaz. stockMovements, mutabakatınızı dayandırdığınız stok defteridir: en eskiden yeniye okunur ve afterId ile sayfalanır (aldığınız son hareketin id'sini verin); bu yöntem yeni hareketler yazılırken bile kesin kalır ve since, geçmiş verileri bir tarihten itibaren geriye dönük doldurmaya başlamanızı sağlar. Çöp Kutusundaki iş emirleri asla döndürülmez. purchaseOrders ayrıca status ve vendorId'ye göre süzülür; tutarlar şirketin para birimindedir ve her sipariş bunu currency alanında taşır.
Anahtarı doğrulayın ve iş emirlerini tek bir çağrıda okuyun: curl -X POST https://freemaint.com/api/v1/graphql -H "Authorization: Bearer $FMK" -H "Content-Type: application/json" -d '{"query":"{ whoami { companyId tier } workOrders(limit: 5, status: \"OPEN\") { id reference title status priority dueDate } }"}'
Neleri yazabilirsiniz
On iki mutasyon: iş emirleri, varlıklar ve yedek parçalar üzerinde oluşturma, güncelleme ve silme; ayrıca işaretli bir stok hareketi için adjustStock. Bunlar, uygulamanın kendi kullandığı servislerin üzerinden çalışır; bu nedenle referanslar (WO-…, ASS-…, PART-…), denetim kayıtları ve bildirimler tam olarak arayüzdeki gibi davranır. Stok için adjustStock'u tercih edin: aynı işlem içinde bir StockMovement denetim kaydı yazar ve bir miktarı sıfırın altına düşürmeyi reddeder; oysa updatePart(quantity) mutlak bir değer belirler ve farkı bir düzeltme olarak kaydeder. Stoğu konuma göre izlenen bir parçada adjustStock'a locationId verin; updatePart(quantity) orada reddedilir. On birincisi olan addMeterReading, sayaç ekranıyla aynı servis üzerinden bir sayaç okuması kaydeder; böylece sayaç tetikleyicileri ve sayaç tabanlı önleyici bakım, uygulamada girilen bir okumadakiyle tamamen aynı şekilde tepki verir. On ikincisi olan setPurchaseOrderErpReference, ERP'nizin belge numarasını bir satın alma siparişine kaydeder; böylece iki kayıt daha sonra purchaseOrder(erpReference: …) ile eşleştirilebilir. FULL erişimli bir anahtar gerektirir ve bilerek hiçbir webhook tetiklemez: ERP'niz kendi yazdığını asla bir olay olarak almaz.
API'nin hiç kapsamadıkları
GraphQL şeması iş emirlerini, varlıkları, makine duruşlarını, yedek parçaları ve bunların stok hareketlerini, sayaçları, satın alma siparişlerini, konumları, tedarikçileri, müşterileri ve kat planlarını içerir. Başka hiçbir şeyi değil. Aşağıdakiler FreeMaint'in tam olarak çalışan ancak bugün HİÇBİR API yüzeyi bulunmayan parçalarıdır: bir entegrasyondan okunamaz, yazılamaz veya abone olunamaz ve mevcut sorguların hiçbir bileşimi bunlara ulaşmaz.
- Denetimler — ne şablonlar, ne planlanmış turlar, ne kaydedilmiş sonuçlar, ne de aksiyon kodları. Denetim sorgusu, denetim mutasyonu ve denetim webhook olayı yoktur.
- Kullanıcılar, roller ve ekipler — bu API üzerinden bir kullanıcıyı listeleyemez, oluşturamaz veya devre dışı bırakamazsınız. Kullanıcı hazırlama uygulama içinde yapılır; Enterprise planında bu, SCIM ile otomatikleştirilebilir; SCIM ayrı bir mekanizmadır ve bu API'nin parçası değildir.
- Projeler — sorgu da yok, mutasyon da.
- Satın alma talepleri — sorgu da yok, mutasyon da. Satın alma siparişleri okunabilir (purchaseOrders, purchaseOrder), ERP'niz bunlara kendi referansını geri yazabilir (setPurchaseOrderErpReference) ve purchase_order.* webhook'ları bunları duyurur; ancak bir sipariş API üzerinden oluşturulamaz, düzenlenemez ve teslim alınamaz: bunlar uygulamada kalır.
- Dosyalar, fotoğraflar ve ekler — bir belge veya fotoğraf yükleyemez, geri de okuyamazsınız. İş emri fotoğrafları, parça görselleri ve denetim kanıtlarının tamamı API'nin dışında kalır. Bir yanıtta görünen bir URL, örneğin bir parçanın imageUrl alanı, bir yükleme yolu değildir.
- Önleyici bakım planları, zaman kayıtları, görevler ve kontrol listeleri — sunulmuyor.
Önemli
Webhook'lar — olay kataloğu
Webhook'lar entegrasyonun “push” tarafıdır: siz sorgulayıp durmak yerine, bir şey olduğu anda FreeMaint sizin URL'nize POST gönderir. Bunları Şirket Ayarları > Webhook'lar (/company-settings/webhooks) altında yapılandırır ve her webhook'u ihtiyaç duyduğunuz olaylara abone edersiniz. Katalog, sabit bir listedir.
- work_order.created — bir iş emri oluşturuldu
- work_order.completed — bir iş emri Tamamlandı durumuna ulaştı
- work_order.status_changed — bir iş emri herhangi bir duruma geçti; yük new_status taşır. Değişmeden kalan work_order.completed'a ek olarak tetiklenir
- request.created — bir bakım talebi gönderildi
- request.status_changed — bir talep onaylandı, reddedildi veya iptal edildi
- part.low_stock — bir yedek parça asgari seviyesine ya da altına düştü
- part.out_of_stock — bir yedek parça sıfıra ulaştı; ayrı bir olaydır, low_stock'un bir çeşidi değildir
- part.low_stock_summary — şirket başına günde bir özet; yeniden sipariş noktasında veya altında olan yedek parçaları, hiç hareket görmemiş olanlar dâhil listeler
- part.out_of_stock_summary — sıfırda duran yedek parçalar için aynı günlük özet
- part.consumed — bir yedek parça bir iş emrinde tüketildi veya iş emrinden iade edildi; quantity işaretlidir
- compliance.chain_divergence — denetim hash zinciri koptu (Enterprise)
- asset.downtime_started — bir makine duruşu açıldı
- asset.downtime_ended — bir makine duruşu kapatıldı; süre artık işlenebilir
- report.daily_summary — günün tamamlanan işleri, seçtiğiniz saatte günde bir kez
- purchase_order.created — bir satın alma siparişi oluşturuldu; yük, siparişi (reference, status, total_cost, currency, expected_date, erp_reference) ve tedarikçisini taşır
- purchase_order.status_changed — bir satın alma siparişinin durumu değişti (onaylandı, sipariş verildi, iptal edildi…); yük previous_status ve new_status taşır
- purchase_order.received — bir satın alma siparişine ait bir teslimat teslim alındı (kısmi olanlar dahil her teslimatta gönderilir); yük her satırı quantity_ordered, quantity_received (bu teslimat), quantity_received_total ve quantity_remaining ile, ayrıca receipt_id, fully_received ve closed_short ile listeler; mal beklenmeye devam ettiği sürece new_status PARTIALLY_RECEIVED, sipariş tamamlandığında veya kapatıldığında RECEIVED olur
Uyarı
Teslimat zarfı
Her GENERIC teslimat, gövdesi aynı üç anahtarlı zarftan oluşan bir POST'tur: event, timestamp ve data; data varlığa göre anahtarlanır. Bunlara üç başlık eşlik eder. X-FreeMaint-Signature, t=<unix saniye>,v1=<hex> biçimindedir; burada v1, o webhook'un gizli anahtarıyla imzalanan "<t>.<ham gövde>" dizesinin tam olarak HMAC-SHA256 değeridir; X-FreeMaint-Event olayın adını yineler; X-FreeMaint-Delivery-Id teslimatı tanımlar. İmzayı, ayrıştırmadan önce HAM gövdeye karşı doğrulayın — JSON'u yeniden serileştirmek baytları değiştirir ve imza tutmaz.
{ "event": "work_order.created", "timestamp": "2026-09-05T08:14:02.371Z", "data": { "work_order": { "id": 5521, "title": "3 numaralı pompada kaçak", "status": "OPEN", "priority": "HIGH", "dueDate": "2026-09-08T00:00:00.000Z" } } }
Güvenilirlik ve izleme
Bir teslimat, 2 saniyelik taban üzerinden üstel geri çekilmeyle 3 kez denenir — kabaca 2 sn, sonra 4 sn, sonra 8 sn — ve her deneme 10 saniye sonra zaman aşımına uğrar. 2xx yanıtı teslimatı “teslim edildi” olarak işaretler. 5xx yanıtları ve ağ hataları yeniden denenir. 4xx ise kalıcı bir ret sayılır ve HİÇ yeniden denenmez: uç noktanız sizin tarafınızdaki bir hata yüzünden 400 ya da 401 dönerse, o teslimat orada durur ve onu yalnızca elle yapılan bir yeniden gönderim kurtarır.
- Her teslimat saklanır: olay, gönderilen tam yük, durum, döndürdüğünüz HTTP kodu, yanıt gövdenizin ilk ~500 karakteri, deneme sayısı ve hata mesajı.
- Durumlar şunlardır: PENDING, DELIVERED, FAILED, RATE_LIMITED (Starter'ın günde 100 teslimat üst sınırına ulaşıldı) ve SKIPPED_TIER (paket artık o hedefe hak vermiyor).
- Saklanan bir teslimat, webhook'un geçmişinden yeniden oynatılabilir. Kaydedilen yükü olduğu gibi yeniden gönderir; böylece içindeki iş anahtarları aynı kalır ve düzgün yinelenen kayıt ayıklaması yapan bir alıcı iki kez işlemez.
- Her webhook lastTriggeredAt, lastSuccessAt, lastFailureAt ve bir failureCount taşır; bu da tüm geçmişi açmadan bir sağlık kontrolü yapmaya yeter.
- Webhook listesi ve teslimat günlüğü her pakette okunabilir; böylece paket düşürmesi nedeniyle duran bir entegrasyon sessizce ölmek yerine görünür kalır.
- Teslimatlar başarısız olmaya başladığında sizi hiçbir şey uyarmaz — ne e-posta ne bildirim. Sessiz bir kesinti sizin için önemliyse, teslimat günlüğünü kendi izleme sisteminizden sorgulayın.
Güvenlik
URL'ler webhook'u kaydettiğinizde doğrulanır ve kurallar bilerek katıdır: bir webhook, sunucularımızın yaptığı giden bir istektir; dolayısıyla gevşek bir kural bir SSRF vektörü olurdu.
- Anahtarlar SHA-256 özetleri olarak saklanır — ham değer, oluşturulduktan sonra FreeMaint'ten geri alınamaz.
- Webhook URL'leri https kullanmalıdır. Düz http ve diğer bütün şemalar reddedilir.
- Yalnızca 443 ve 8443 numaralı bağlantı noktaları kabul edilir.
- Geri döngü (loopback), bağlantı-yerel ve özel aralıktaki adresler (127.x, 10.x, 192.168.x, 172.16–31.x, ::1 ve benzerleri) reddedilir: alıcının genel internetten erişilebilir olması gerekir. Yalnızca tesisinizin içinde var olan bir sistem için genel bir aktarıcı ya da bir tünel gerekir.
- URL içindeki kimlik bilgileri (https://user:pass@host) reddedilir.
- Yönlendirmeler izlenmez — kaydettiğiniz uç nokta, POST'u alan uç noktadır.
- Her webhook'un kendi imzalama gizli anahtarı vardır ve bir gizli anahtar döndürme eylemi bulunur. Döndürme anında etkili olur; bu nedenle yeni gizli anahtarı kendi tarafınızda aynı zaman aralığında devreye alın.
- SLACK ve TEAMS hedefleri, platforma göre biçimlendirilmiş bir mesaj alır ve HİÇBİR FreeMaint HMAC başlığı almaz: onlar için tek kimlik doğrulama, URL'nin gizli kalmasıdır. Kriptografik olarak doğrulamanız gereken her şey için GENERIC kullanın.
Bugün iyi çalışan kalıplar
Bunlar, mevcut yüzeyin temiz biçimde desteklediği entegrasyonlar ve her birini üretimde güvenle çalıştıran ayrıntılardır.
Tüketimi bir ERP'ye yansıtmak
part.consumed olayına abone olun. quantity işaretlidir — parça işe çıkarken negatif, iade edilirken pozitif — ve yük, yeniden denemeler ile yeniden oynatmalar boyunca sabit kalan bir stock_movement_id taşır; bunu idempotency anahtarınız olarak kullanın. adjustStock mutasyonu bu olayı bilerek TETİKLEMEZ; kendi yazdıklarınızın size yankı olarak geri dönüp bir döngü oluşturmasını engelleyen de tam olarak budur.
Stok düzeltmelerini içeri göndermek
adjustStock'u kendi tarafınızdan işaretli bir delta ile çağırın. READ_STOCK anahtarı bunu, hiçbir şey oluşturamadan, güncelleyemeden veya silemeden yapabilir; bu da onu tek yönlü bir stok köprüsü için tam oturan bir seçim yapar.
Başka bir sistemden iş emri oluşturmak
Bir SCADA alarmından, bir yardım masası kaydından ya da bir ERP arıza bildiriminden createWorkOrder. İş emri olağan referansını alır ve diğerleri gibi work_order.created olayını tetikler.
Yedek parça kataloğunu eşzamanlı tutmak
parts(updatedSince:) yalnızca sizin seçtiğiniz andan bu yana değişenleri döndürür. createPart ve updatePart, customReference alanını — parça için kendi kodunuzu — kabul eder; part(barcode:) de sonrasında onunla eşleşir, böylece bir parçayı ERP'nizin zaten kullandığı tanımlayıcıyla arayabilirsiniz. Benzersizlik zorunlu kılınmaz; bunu güvence altına almak size düşer.
Tamamlanmada döngüyü kapatmak
work_order.completed, iş emrini ve onun önceki durumunu taşır; bu da karşı tarafta bir bakım bildirimini kapatmaya yeter.
Hangi pakette olmanız gerekir
Birbirinden ayrı iki kapı: biri API için, biri webhook'lar için. Starter planındaki bir şirket, API'ye sahip olmadan webhook alabilir.
GraphQL API ve API anahtarları
Business ve üzeri. Core ve Starter hiçbir şekilde anahtar oluşturamaz.
Günlük kota
Business'ta günde 5.000 çağrı, Enterprise'da 100.000. Şirket başına ve işlem başına sayılır, 00:00 UTC'de sıfırlanır — birden çok işlemi tek bir HTTP isteğinde toplamak gidiş-dönüşten tasarruf ettirir, kotadan asla.
Webhook'lar
Starter ve üzeri, günde 100 teslimatla sınırlı; Business'tan itibaren sınırsız.
Slack ve Teams hedefleri
Enterprise. Genel bir webhook Starter'dan itibaren kullanılabilir.
İpucu
İlgili makaleler
Bu sayfa yardımcı oldu mu?