It worked in the test system. It broke on the first day in production, and nobody could see the one-line difference in the body.
The story starts in the same place in most integration projects. A developer pulls one sample response from the source, flattens it into a JSON body, builds the contract on that body and sends it to the frontend team. The sample is accurate. The contract is not.
The gap in a schema derived from a sample body
The reason is simple: SAP function parameters are not flat. A structure can contain other structures or tables. A schema derived from sample data tells you which fields happened to be filled in that sample, not the real type of the parameter.
The bill arrives accordingly. The day the function returns a non-empty table parameter, the consumer either never sees the field or receives a list where it expected a single value. The error message usually comes from the parser on the other side rather than from SAP, which is why teams also start looking in the wrong place.
Tying the type decision to the parameter definition
The API flow can process SAP table, program, transaction code, function and iDataView objects. For function parameters the parser reads the EXID value and converts character, numeric, structure and table results into the matching type. The decision rests on the parameter definition in the catalog rather than on a sample body, and the service output is prepared as JSON or XML in line with that structure.
The order that works in practice: read the input and output parameters from real catalog information, mark structure and table types separately, then generate the example body you hand over from those types. The example should come out of the definition, not out of a document.
Narrowing the scope of what a function returns
There is a scope decision too. You are not obliged to expose every field a function returns. A narrower field set shrinks the response and keeps the contract readable; record limits and fixed parameters serve the same purpose.
The question to ask is not technical but commercial: what will the consumer do with this field? A field with no answer does not belong in the contract, because taking it out later is a contract change. Removing a field from a contract always costs more than adding one.
The limits of deep structure support
- The business rules of a custom function are not understood on their own. Which input combination produces which error still needs its own test.
- Handling nested structures is not a commitment about payload size or response time, and we have no published measurement to offer there.
- Configuration fields exist on the SOAP side, but the call core uses the shared HTTP path. WSDL import and WS-Security coverage are not verified.
You can run a quick comparison in your own environment: call the same function once with a selection that leaves the table parameter empty, and once with a selection that fills it. If the two responses have the same shape, your contract holds. If they do not, you wrote down the sample rather than the contract.
If you have a function whose types you are unsure about, let us read its parameter list together before the contract goes to the other side. Marking the structure and table parameters is half a day of work now, instead of the same work in the final week of the integration.