Microsoft Dynamics 365 F&O and SFTP / Flat Files integration
Microsoft Dynamics 365 F&O is a cloud ERP with a modern OData API, while many trading partners and legacy systems can only exchange data as files over SFTP. This connection lets D365 trade with those partners without anyone re-keying data. Records such as purchase orders and vendor invoices read out of D365 and land as X12 or CSV files in a partner's outbound folder, and inbound files drop back in as D365 entities. Because SFTP is a pull-only transport with no push, the connector polls for new files on a schedule. The general ledger and posted records stay authoritative in Microsoft Dynamics 365 F&O.
What moves between them
Direction depends on the partner relationship and is usually two-way. Outbound, ml-connector reads D365 purchase orders, vendor invoices, vendors, items, and journal entries over OData and writes them as X12 or CSV files into the partner's outbound folder. Inbound, it polls the inbound folder, parses partner files such as 810 invoices or 850 purchase orders, and creates the matching D365 entities like VendorInvoiceHeaders or PurchaseOrderHeadersV2. Reference data such as vendor IDs, item numbers, and GL account codes is aligned so every file line resolves to a real D365 record. Cadence is the SFTP poll interval, commonly 5 to 60 minutes, and D365 Business Events can trigger an outbound file as soon as a record posts.
How ml-connector handles it
ml-connector stores both credential sets encrypted. For Microsoft Dynamics 365 F&O it requests a Microsoft Entra ID client-credentials token scoped to the tenant host, sends it as a Bearer token, and refreshes it when a call returns 401, since the token expires roughly hourly. It accepts the full operations.dynamics.com host per customer because there is no shared base URL, and it scopes reads to the right legal entity with dataAreaId and cross-company. For SFTP / Flat Files it authenticates with the partner's SSH key, verifies the server host key to block man-in-the-middle, and writes outbound files with deterministic names so a retried FlowRun produces the same filename. Inbound, it polls the folder, skips partial uploads by waiting for a sentinel file or a stable size, and dedupes on a filename plus content hash so a re-listed file is never imported twice. Each inbound FlowRun carries a jobId of sftp plus filename and a hash prefix for BullMQ-level dedup. D365 OData has no idempotency header, so it relies on natural keys such as InvoiceNumber to avoid duplicate-key errors, and it posts vendor invoices only before they are posted because posted records are read-only. Real edge cases handled include X12 delimiter characters in vendor names, CRLF versus LF line endings, server-local file timestamps, and the financial dimension display-string format D365 requires before a write. D365 429 responses honor Retry-After, and EDI files get a 997 acknowledgment written back to the acks folder.
A real-world example
A mid-sized consumer goods manufacturer with roughly 600 employees runs Microsoft Dynamics 365 F&O for procurement and finance and sells to several big-box retailers. Those retailers will only trade by X12 EDI dropped on an SFTP server, not by API. Before the integration, a coordinator exported purchase orders from a retailer portal, retyped them into D365, then manually built invoice files to upload back, which was slow and error-prone and left orders sitting for a day. With Microsoft Dynamics 365 F&O and SFTP / Flat Files connected, inbound 850 purchase orders are parsed into D365 purchase orders within the poll window, and confirmed invoices are written back as 810 files with a 997 acknowledgment. The re-keying step is gone and orders move the same day.
What you can do
- Read D365 purchase orders, vendor invoices, vendors, and items over OData and write them as X12 or CSV files to a partner's SFTP folder.
- Poll the inbound SFTP folder and create matching D365 entities such as VendorInvoiceHeaders and PurchaseOrderHeadersV2 from 810 and 850 files.
- Authenticate D365 with Microsoft Entra ID OAuth 2.0 and the SFTP server with an SSH key, verifying the server host key.
- Dedupe inbound files by filename and content hash and write deterministic outbound filenames so a retry never doubles a record.
- Return X12 997 acknowledgments for inbound EDI and honor D365 Retry-After backoff, with a full audit trail and replay on every file.
Questions
- Which direction does data move between Microsoft Dynamics 365 F&O and SFTP / Flat Files?
- It is usually two-way and depends on the trading partner. Outbound, the connector reads D365 records over OData and writes them as X12 or CSV files to the partner's folder; inbound, it polls the folder and creates D365 entities such as vendor invoices and purchase orders from partner files. The general ledger and posted records stay authoritative in D365, so file data never overwrites a posted entry.
- How does the connector know when a new file has arrived if SFTP has no webhooks?
- SFTP is a pull-only transport with no native push, so the connector polls the inbound folder on a schedule, commonly every 5 to 60 minutes. It compares the listing against a processed-files log and dedupes on filename plus content hash so a re-listed file is never imported twice. To avoid reading a file that is still uploading, it waits for a sentinel file or a stable size before processing.
- How are D365 entity writes handled to avoid duplicates and posting errors?
- D365 OData has no idempotency key header and uses natural keys such as InvoiceNumber plus VendorAccount, so creating the same record twice returns a duplicate-key error that the connector handles gracefully. Vendor invoices are written only before they are posted, since posted records are read-only over OData. The connector also formats financial dimensions as the display string D365 expects so a write does not silently fail.
Related integrations
More Microsoft Dynamics 365 F&O integrations
Other systems that connect to SFTP / Flat Files
Connect Microsoft Dynamics 365 F&O and SFTP / Flat Files
Free to use. Add your credentials, ping your real systems, and see if we fit.
Get started