Integrating Frappe with SAP for invoice automation — approach & patterns

Wanted to share how we connected Frappe to SAP for an invoice-automation workflow, and open a discussion on integration patterns others have used.

The integration, in short Frappe is the front-of-house system (intake, AI extraction, human review, routing) and SAP is the system of record for posting. Frappe talks to SAP over REST for every step:

  • Master/data lookups — pull PO, GRN, and service-entry (SES) data to validate an invoice before posting.
  • Draft build — call an SAP endpoint that assembles the full posting draft (header + line items) from a few inputs (company code, vendor, PO, GRN, year), so SAP does the heavy lifting.
  • Post — submit the draft to SAP’s MIRO posting endpoint; on success SAP returns the MIRO + Accounting document numbers, which we store back on the Frappe record.
  • Document verify — for manual/edge cases, validate a document against SAP (company code + vendor match) before applying a payment block.

How it’s built on the Frappe side

  • All SAP calls live behind whitelisted server methods — the desk pages never call SAP directly.
  • Instant page loads from a stored draft; SAP is only hit on an explicit “Fetch” action (behind a loading overlay), so slow round-trips never block the UI.
  • SAP client is resolved dynamically per call (derived from the company code) — never hardcoded.
  • Credentials/base URLs in an encrypted Settings singleton, not in code or git.
  • Fail-safe routing — if SAP rejects a post, the record goes to a human queue with the SAP error logged, instead of failing silently.

A few things I learned

  • SAP pads/normalizes values (e.g. vendor codes to 10 digits) — doing padding-agnostic comparisons on the Frappe side avoids false mismatches (0000010034 == 10034).
  • Year handling matters: consumed/posted GRNs behave differently, so seeding the right fiscal year on lookups saves a lot of slow retries.
  • Treat SAP’s response contract as the source of truth for “success” (e.g. “a document number came back”), rather than just HTTP 200.

Questions for the community

  • How are you authenticating Frappe → SAP in production — token/basic auth stored in Settings, an API gateway, or something else?
  • Anyone using SAP’s OData services vs custom REST endpoints from Frappe? Trade-offs?
  • Best practice for long-running SAP calls in Frappe — synchronous whitelisted method, background job + realtime update, or a queue?

Happy to share more on any part. Curious how others have wired Frappe to SAP (or other ERPs).

2 Likes

This is a great integration pattern. We have used something similar for AP automation, where the invoicing layer processes the extraction, validation, PO/GRN matching, exception handling, and manual review, while the ERP system continues to act as the primary data source.

Here are some points to keep in mind:

1. Keep ERP calls on the server side.

It is always a good idea to have SAP/ERP credentials and integration logic present in server-side methods instead of exposing them to the front-end clients. This leads to easier logging, retries, and access management.

2. Apply asynchronous processing for long calls

For processes such as master-data fetching, multi-document validation, posting process, using background jobs would be far better than keeping the synchronous request open. The status of the invoice will be ‘Pending Validation → Ready to Post → Posting → Posted/Exception’ while the SAP response is saved against the transaction.

3. Idempotency should be considered

For invoice posting, an external transaction/reference ID and posting status are essential to ensure that timeouts/retries don’t create duplicate SAP documents. A successful HTTP response does not have to be proof of the posting.

4. Standardize ERP Identifiers

The issue of padding vendor codes is vital. We’ve faced similar problems with comparisons of master data in ERP systems and, therefore, normalization should take place prior to validation (and not regard different formally formatted values as being unique objects).

5. Use OData vs Custom REST

The use of OData is beneficial when necessary SAP business functions and related processes are already easily accessible. On the other hand, it makes sense to implement custom REST endpoints if the posting logic is customized for the organization or if SAP’s business logic requires complicated processing.

With TYASuite AP Automation, we noticed that integrating technology seems to be more dependable once one separates the different processes like validating, matching, posting, retrying, and responding to exceptions instead of treating the entire process as one API transaction.

This type of architecture can be used with SAP and other ERP systems as well, helping keep the accounts payable automation system relatively ERP-independent whereas the ERP still retains its status of the main record holder.

Another area of major interest is how this pattern operates when it comes to higher volumes of invoices.

I’d like to add that the line between synchronous and asynchronous processing might vary depending on what you’re doing.

Take, for instance, a user-initiated lookup where you need an immediate response. That could work well synchronously, as long as the SAP response time is reliable. On the other hand, tasks like posting, retries, bulk validation, and anything that might take longer are better suited for background jobs. These should have a clear status model and provide real-time updates.

I’m also curious about how others manage reconciliation when the result of a request is uncertain. For example, if Frappe times out after sending a posting request, but SAP actually finishes the posting, simply retrying could lead to duplicates. It seems crucial to have a separate method to check with SAP using an external reference or correlation ID before retrying.

The point about identifier normalisation is a good reminder that the integration layer should have clear rules for comparing and transforming ERP data, rather than having this logic spread out across different API calls.

@BlakeWest and @Vishva i have also implemented this project with tight validation and security so no duplication occur

(post deleted by author)

Great