A finance systems lead at a European manufacturer once described the moment the whole project went sideways. The AI extraction layer was working. Invoices came in as PDFs, the model pulled vendor, PO number, line items, tax codes, and net amounts with high confidence, and the validation rules caught the handful of documents that needed a human look. Everything up to that point was clean.
Then came posting. The team had picked a standard OData service to create the supplier invoice in S/4HANA because it was the modern option and the API hub listed it first. Within two weeks they discovered that the service did not expose the field their tax team needed for a specific reverse-charge scenario. They also found that posting 4,000 invoices during month-end close through individual OData calls took longer than the batch window allowed. The extraction was flawless. The last hundred meters were a mess.
This is the part of SAP document automation nobody puts in the demo. Getting data out of a document is a solved problem now. Getting it into SAP correctly, at volume, with the right error handling and the right audit trail, is where projects stall. The choice between OData, BAPI, and RFC is not a matter of taste or modernity. Each one behaves differently under load, exposes different fields, returns errors in different shapes, and creates different obligations for your basis and security teams.
Why the Posting Route Decides Your Architecture
Most teams treat the posting interface as an implementation detail. They pick whatever the SAP team suggests in the kickoff meeting and move on to the interesting work of tuning the extraction model. That ordering is backwards.
The posting route determines your throughput ceiling. It determines whether you can post a document with a custom field your business added in 2014. It determines whether a failed posting gives you a clean message you can show a clerk, or a raw dump you have to decode. It determines whether your integration survives the next S/4HANA upgrade. And it determines how much of your automation logic lives outside SAP versus inside it.
Consider what an AI document automation platform actually needs from SAP. It needs to read master data to validate what it extracted, so vendor numbers, material numbers, cost centers, tax codes, and company codes all need lookup access. It needs to match against open items, usually purchase orders and goods receipts. It needs to create the business document. It needs to attach the source file. And it needs to know, precisely and quickly, whether the posting succeeded and what document number came back.
Those five needs do not have to use the same protocol. Some of the strongest architectures use OData for master data reads, BAPI over RFC for the posting itself, and a separate service for attachment handling. Understanding what each route does well is what lets you make that split intelligently.
RFC: The Foundation Everything Else Sits On
Remote Function Call is the oldest of the three and the least glamorous. It is SAP's proprietary protocol for calling ABAP function modules from outside the system. Your integration opens a connection to an application server or a message server, authenticates, calls a function module by name, passes parameters in a structured format, and gets structured data back.
RFC is a transport mechanism, not a business API. This distinction matters and confuses people constantly. When somebody says "we call SAP via RFC," they mean they are using the RFC protocol to invoke something. What they invoke could be a BAPI, a standard function module, or custom ABAP code written specifically for the interface.
The characteristics that matter for document automation come down to speed, breadth, and statefulness. RFC connections are fast. Binary serialization over a persistent connection beats HTTP with JSON payloads by a wide margin, especially for large table parameters. If you need to pull 50,000 open purchase order line items to build a matching cache, RFC does it in seconds where a REST equivalent would need pagination and repeated round trips.
RFC also gives you access to everything. Any RFC-enabled function module in the system is callable, including custom ones. When your business has a validation routine written in ABAP that encodes fifteen years of accumulated rules, you can call it directly rather than reimplementing it in Python. That is often the difference between an automation project that fits the business and one that fights it.
Statefulness is where RFC gets interesting for posting. RFC supports stateful sessions, which means a sequence of calls can share the same ABAP session context. You can call one function to build data, another to validate, and a third to commit, all within the same logical unit of work. HTTP-based interfaces are stateless by default, so achieving the same thing requires either a wrapper that does everything in one call or a service that manages a draft state.
The costs are real. RFC needs network connectivity into the SAP application server on specific ports, usually 33xx, which security teams do not love. It needs an SAP-provided library, either the classic NetWeaver RFC SDK or the newer implementations, installed and maintained on the calling side. Language support is uneven, with strong options in Java and Python through PyRFC and .NET, and weaker options elsewhere. RFC also crosses SAP's own architectural direction, since SAP is steering integration toward HTTP-based services for cloud deployments. On SAP Cloud ALM and RISE landscapes, direct RFC access is frequently unavailable or heavily restricted.
BAPI: Business Logic With a Contract
Business Application Programming Interfaces are a specific category of RFC-enabled function modules. SAP designed them as stable, documented, business-object-oriented entry points. BAPI_INCOMINGINVOICE_CREATE1 creates a supplier invoice. BAPI_ACC_DOCUMENT_POST creates an accounting document. BAPI_SALESORDER_CREATEFROMDAT2 creates a sales order.
What separates a BAPI from a random function module is the contract. BAPIs are supposed to be upgrade-stable, meaning SAP commits to keeping the interface working across releases. They follow conventions for parameter naming and structure. They return errors through a standard RETURN table containing message type, message class, message number, and formatted text. And critically, most BAPIs do not commit on their own. You call BAPI_TRANSACTION_COMMIT explicitly, which gives you a chance to inspect the result and roll back before anything hits the database.
For AI-driven document automation, that explicit commit pattern is genuinely valuable. Your platform can call the posting BAPI in test mode, evaluate the messages that come back, decide whether the confidence in the extracted data plus the absence of blocking errors justifies an automatic post, and only then commit. Documents that fail go to a human queue with the exact SAP message attached rather than a generic failure notice.
The error semantics deserve more attention than they usually get. A BAPI RETURN table can contain dozens of messages of type S for success, I for information, W for warning, and E for error. A well-built automation layer classifies these. Message F5 704 about a missing tax code is different from message M8 534 about a quantity variance beyond tolerance, and both are different from an authorization failure. Mapping SAP message classes to your own exception categories is one of the highest-value pieces of work in an AP automation build, because it determines whether your exception queue is actionable or just a pile of red rows.
BAPI field coverage is broad but not infinite. The invoice creation BAPI exposes header data, item data, tax data, withholding tax, account assignments, and a generous EXTENSIONIN structure for custom fields. That extension mechanism is how most customers push data into Z-fields on the invoice. It works, though it requires a BAdI implementation on the SAP side to actually read the extension and write it to the right place. Teams frequently discover this halfway through testing.
Performance on BAPIs is strong when used correctly. Calling a BAPI over RFC for a single invoice takes a few hundred milliseconds in a healthy system. Where teams get into trouble is calling BAPIs in a loop from an external process with a new connection each time, which turns connection setup into the dominant cost. Connection pooling and batching multiple documents per stateful session solve this, and any serious integration layer should do both.
OData: The HTTP Path SAP Is Betting On
OData services expose SAP business objects as RESTful resources over HTTP. You authenticate with OAuth or basic credentials or a certificate, you GET a collection of purchase orders, you POST a JSON payload to create a supplier invoice, and you get an HTTP status code plus a response body back.
The appeal is obvious to anyone building modern software. No proprietary libraries. No special ports. Standard tooling for authentication, logging, rate limiting, and monitoring. API gateways understand it. Developers who have never touched SAP can be productive quickly. And SAP's own roadmap points here, with the SAP Business Accelerator Hub publishing OData and increasingly REST services as the sanctioned integration surface for S/4HANA Cloud.
The technical model has genuine strengths. The metadata document at $metadata describes every entity, property, and relationship in the service, which means your integration layer can validate payloads before sending them. Query options like $filter, $expand, $select, and $top let you shape reads precisely. Pulling a purchase order with its items and its schedule lines in one call using $expand is cleaner than three separate BAPI calls.
Batch support through $batch lets you bundle multiple operations into a single HTTP request, and changesets inside a batch give you atomicity, so either all operations in the changeset succeed or all roll back. That covers a meaningful part of what stateful RFC sessions provide.
Now the difficulties. OData services expose exactly the fields SAP chose to expose. If a field exists in the underlying table but is not part of the service definition, you cannot post it without extending the service, which means development work in the SAP system by someone with the right skills. On S/4HANA Cloud public edition, that extension has to go through approved extensibility mechanisms rather than classic ABAP changes. Teams that assume OData will cover everything the BAPI covered are usually wrong on their first project.
Throughput is the other constraint. Each OData call carries HTTP overhead, TLS handshake cost unless connections are reused, JSON serialization, and gateway processing. For a thousand documents this is fine. For an overnight batch of sixty thousand invoices in a shared services center, the arithmetic gets uncomfortable fast. Batching helps significantly, though batch payload size limits and gateway timeouts introduce their own tuning work.
Error handling in OData is thinner than BAPI RETURN tables by default. You get an HTTP status and an error object with a code and message, sometimes with details in an inner error array. Many standard services surface the underlying SAP message properly, and some do not, leaving you with a generic 400 and a message that reads like a translation of a translation. Testing error paths, not just happy paths, is essential before committing to an OData route for a high-volume process.
Draft handling adds a wrinkle. Several standard S/4HANA OData services follow a draft pattern where creating a document means creating a draft entity, patching it with data, and calling an Activate action. That is three or more round trips per document instead of one. It exists for good reasons in a Fiori context where users edit incrementally, and it is pure overhead for machine-to-machine posting.
Matching the Route to the Document Process
Abstract comparisons only get you so far. The right answer changes by process, volume, and landscape.
For accounts payable invoice posting at high volume, BAPI over RFC remains the strongest default on on-premise and private cloud S/4HANA. BAPI_INCOMINGINVOICE_CREATE1 has deep field coverage, mature error messages, explicit commit control, and the throughput to handle month-end surges. An AI platform that extracts invoice data, matches it against the PO and goods receipt, and posts through this BAPI can process thousands of documents in a batch window with granular per-document error capture.
For sales order creation from customer purchase orders, the calculus shifts. Sales orders often need pricing simulation, availability checks, and credit checks before the customer sees a confirmation. The relevant OData services expose these as readable operations, and the volumes in most order-to-cash processes are lower than AP invoice volumes. OData works well here, particularly when the automation needs to show a user a preview of the order before it posts.
For master data creation, vendor onboarding, customer setup, and material master extension, OData is usually the better choice. Volumes are low, the data is complex and relational, and the validation feedback matters more than raw speed. Business Partner services in S/4HANA are mature and expose the role-based structure clearly.
For goods receipt and inventory postings triggered by delivery notes or packing lists, RFC with the material document BAPI tends to win. These postings happen in bursts tied to physical events, they need to complete quickly, and the field requirements are well-defined.
For journal entry posting from bank statements, expense reports, or intercompany documents, BAPI_ACC_DOCUMENT_POST handles the complexity of multi-line, multi-currency, multi-company-code entries better than most OData equivalents. The ability to build the complete document in memory and post it as one atomic unit fits how accounting works.
For landscapes on S/4HANA Cloud public edition, the decision is largely made for you. RFC access is not available. OData and the newer REST APIs are the path, and your architecture has to work within their field coverage and throughput characteristics. This is a good reason to test posting volumes early rather than discovering the ceiling in production.
Building an Automation Layer That Does Not Care
The best architecture decision most teams can make is to stop treating this as a single choice. Build an abstraction layer inside your document automation platform that separates the extracted business document from the mechanics of posting it.
The pattern looks like this. Your AI layer produces a canonical document object containing everything extracted and validated, in a structure that reflects the business meaning rather than any SAP interface. A posting adapter takes that object and maps it to whatever route the target system supports. The adapter handles connection management, retries, error translation, and idempotency. Everything above the adapter stays the same whether you post through BAPI, OData, or a queue.
This costs perhaps two weeks of extra design work at the start. It pays for itself the first time a customer runs S/4HANA Cloud instead of ECC, or the first time you need to switch a high-volume process from OData to BAPI because the batch window shrank.
Idempotency deserves special mention. Network timeouts happen. When a posting call times out, you do not know whether SAP created the document. Posting again risks a duplicate invoice, which is exactly the kind of error that erodes trust in automation permanently. Building a reliable check, either through a reference field carrying your platform's document ID that you can query before retrying, or through SAP's own duplicate invoice check configured properly, is not optional at production volume.
Error translation is where the automation earns its keep. SAP messages are written for SAP users. An AP clerk who has never used SAP directly should see something like "The invoice quantity of 120 units exceeds the goods receipt quantity of 100 units on purchase order line 20" rather than message M8 081 with raw variable substitution. Mapping the messages you actually encounter into clear language, with a suggested action for each, turns your exception queue from a bottleneck into a workflow.
Attachments, Async, and the Things People Forget
Two operational details break more implementations than protocol choice.
The first is attachments. Nearly every document automation requirement includes storing the source PDF against the posted SAP document so auditors can trace back. This is a separate integration from the posting itself. Depending on your landscape you might use ArchiveLink with a content repository, the Generic Object Services attachment services, an OData attachment service, or an external content platform with a link stored in SAP. Each option has different implications for storage cost, retention policy, and retrieval performance. Deciding this late, after the posting integration is built, causes rework.
The second is asynchronous posting. Synchronous calls are simple to reason about and terrible under load spikes. A hybrid pattern works better for most high-volume processes. Your platform queues validated documents, a worker pool pulls from the queue and posts at a controlled rate, results come back into the queue system, and the user interface reflects status as it changes. This decouples your extraction throughput from SAP's posting capacity and gives you natural retry semantics. It also means a slow SAP system during month-end degrades your latency rather than breaking your pipeline.
Queued RFC and transactional RFC exist in the SAP world for exactly this reason and have their place, particularly when guaranteed delivery matters more than immediate feedback. The tradeoff is that you lose synchronous error visibility, so the document number and any errors arrive later through a different channel. For processes where a human is waiting on the result, that is usually the wrong trade.
What Good Looks Like in Practice
A well-designed AI document automation integration for SAP has a few visible traits.
Master data validation happens before posting, not during. The platform maintains a synchronized cache of vendors, materials, cost centers, tax codes, and open purchase orders, refreshed on a schedule appropriate to change frequency. Extracted values get validated against this cache, so a bad vendor match surfaces in seconds rather than as a posting failure minutes later.
The posting call itself carries complete, pre-validated data. Test-mode calls confirm the document will post cleanly before the commit. Documents that generate warnings but not errors post automatically if configured to do so, with the warnings recorded.
Errors return classified and actionable, with the SAP message preserved for support and a clear explanation surfaced to the business user. Every attempt is logged with a correlation ID that ties the source document, the extraction result, the posting payload, and the SAP response together.
Throughput scales by adding workers rather than by tuning individual calls, because the connection layer pools properly and the queue absorbs bursts.
And the whole posting mechanism sits behind an interface, so moving a customer from ECC to S/4HANA, or from private to public cloud, is a configuration change and an adapter swap rather than a rebuild.
Getting the Decision Right the First Time
Start by profiling the actual process. Count documents per day and per peak hour. List every field that has to reach SAP, including the custom ones nobody mentions until user acceptance testing. Identify the landscape and its constraints on network access and extensibility. Find out whether a human waits on the result or whether the process runs overnight.
Then test both routes with real data before committing. Post a hundred documents through the OData service and a hundred through the BAPI. Measure latency and throughput. Deliberately break things, wrong tax code, quantity mismatch, closed period, blocked vendor, and read what comes back. The route with better error messages for your actual failure modes often matters more than the one with better benchmark numbers.
Artificio works with SAP teams on exactly this layer, taking documents from raw PDF through extraction, validation against live SAP master data, and posting through whichever route fits the landscape. The extraction gets the attention in demos. The posting architecture is what determines whether the automation holds up at close.
The teams that get this right treat OData, BAPI, and RFC as tools with different shapes rather than as generations of technology where newer wins. A modern integration might use OData for master data reads, BAPI over RFC for high-volume posting, and a queue in between to absorb load. That is not a compromise. That is the design.Â
