Integration Objects and OData: The Hands-On Guide to Commerce Integration APIs
Modeling Integration Objects that hold up: item and attribute definitions in ImpEx, inbound channels with OAuth, virtual and runtime attributes for payload shaping, the MetaAPI for environment consistency, monitoring inbound and outbound traffic, and the reuse-versus-scope judgment call.
The integration options guide surveys the channels; this is the workshop manual for the one that should be your first consideration for system-to-system data exchange: Integration Objects exposed as OData services. An Integration Object (IO) is a declared, business-level projection of your type system: which types, which attributes, which keys, exposed as a self-describing OData API with CRUD semantics, authentication, and monitoring included. Done well, IOs turn integration from bespoke endpoint code into modeling. Done casually, they become the widest unaudited door into your database. The difference is everything below.
Modeling: The Three-Layer Declaration#
An IO is data, not code: IntegrationObject (the API), IntegrationObjectItem (each exposed type, exactly one marked root), and IntegrationObjectItemAttribute (each exposed attribute, with unique flags forming the upsert key). You can click them together in the Backoffice Integration UI, but production IOs live in ImpEx so environments agree:
INSERT_UPDATE IntegrationObject; code[unique = true]; integrationType(code)
; InboundProduct ; INBOUND
INSERT_UPDATE IntegrationObjectItem; integrationObject(code)[unique = true]; code[unique = true]; type(code) ; root[default = false]
; InboundProduct ; Product ; Product ; true
; InboundProduct ; CatalogVersion ; CatalogVersion ;
; InboundProduct ; Catalog ; Catalog ;
INSERT_UPDATE IntegrationObjectItemAttribute; integrationObjectItem(integrationObject(code), code)[unique = true]; attributeName[unique = true]; attributeDescriptor(enclosingType(code), qualifier); returnIntegrationObjectItem(integrationObject(code), code); unique[default = false]
; InboundProduct:Product ; code ; Product:code ; ; true
; InboundProduct:Product ; name ; Product:name ; ;
; InboundProduct:Product ; catalogVersion ; Product:catalogVersion ; InboundProduct:CatalogVersion ; true
; InboundProduct:CatalogVersion ; version ; CatalogVersion:version ; ; true
; InboundProduct:CatalogVersion ; catalog ; CatalogVersion:catalog ; InboundProduct:Catalog ; true
; InboundProduct:Catalog ; id ; Catalog:id ; ; true
Modeling judgment that separates good IOs from database dumps:
- Design from the consumer's model inward, not from your items.xml outward. The IO is a contract; the third party's required fields define it, and your type system merely implements it. Fields nobody asked for are liability, not generosity.
- Keys are the contract's spine. The
uniqueattributes compose the upsert identity for inbound; get them wrong and you create duplicates or overwrite strangers. They should mirror the business identity (code plus catalog version for catalog data), which usually mirrors your ImpEx unique keys, and for the same reason. - Reuse versus scope is a real tension: the CX Works guidance says minimize IO count through reuse (a consumer that tolerates extra fields can share), the side-by-side guide says scope per consumer for revocability. Reconcile by intent: reuse when consumers genuinely share the same business projection and trust level; scope when lifecycle, access, or data sensitivity differ. Never let reuse hand a low-trust consumer a high-trust projection because it was already there.
Shaping Payloads: Virtual and Runtime Attributes#
Two mechanisms cover "the model does not quite match":
- Virtual attributes compute values at serialization time through a script (
IntegrationObjectVirtualAttributeDescriptorpointing at amodel://Groovy script): formatting, flattening, light enrichment. Keep them pure and cheap; they execute per entity per request, and a virtual attribute doing a FlexibleSearch per row is an N+1 you designed on purpose. - Runtime attributes add persisted attributes to types without an items.xml change, which makes them deployment-free and dangerously convenient. The operational caveat from the source material deserves bold: virtual and runtime attribute definitions are runtime configuration; they do not survive initialization and do not propagate across environments by themselves. Export them as ImpEx alongside the IO definition, or use the MetaAPI to synchronize, and treat a runtime attribute that became load-bearing as a signal it belongs in items.xml at the next release (the type system guide's governance).
The Backoffice Meta view (EDMX schema, sample JSON payload, composition graph) is the review artifact: attach it to the integration's documentation so consumers and reviewers see the same contract.
Exposure: Channels, Auth, and Access Rights#
IOs are not exposed by existing; three explicit steps gate the door:
- Inbound Channel Configuration binds the IO to an authentication type (Basic or OAuth2; choose OAuth2 client-credentials for systems, and let Basic die with the legacy integrations that demanded it).
- An OAuth client per consumer, per the side-by-side rule: revocable, observable, rate-attributable.
- Access rights per IO: the type-and-attribute permissions (user rights blocks, per the ImpEx guide) for the integration user behind the client. The Backoffice Integration UI perspective itself should be restricted to integration owners; it is an API-definition console, which is to say a security console.
The endpoints then follow the OData convention: POST /odata2webservices/InboundProduct/Products with a JSON payload nesting the association path (catalog version inside product), GET with $filter, $expand, $top for reads, and $batch for grouped operations. Inbound POSTs perform the upsert against the key attributes; deep inserts create the association chain when the payload carries it.
Outbound and the Fleet Around It#
The same IO modeling drives the outbound family: OutboundSync (change-detection-driven replication with retry bookkeeping, the right tool where webhooks are too casual), webhooks (notification with IO payloads, per the webhooks guide), and direct OutboundService calls from your code to ConsumedDestinations. One model, several transports, which is exactly the payoff of modeling once: when the ERP wants the same product projection the middleware gets, it is a destination configuration, not a new integration.
On middleware: the pre-built SAP integration packages (S/4HANA and friends) ride these same Integration APIs through SAP Integration Suite iFlows, and the release note discipline matters because an iFlow can exist in the package while the matching inbound or outbound service arrives in a later Commerce integration extension pack. Check both ends' availability before committing a date to the ERP team.
Operating: Monitoring, Performance, Consistency#
- Monitoring is built in and underused: inbound and outbound request monitoring in Backoffice shows payloads, outcomes, and errors per IO. Wire its signals into the estate dashboard (the integration monitoring guide automates the alerting side) and set retention on the monitoring items themselves, which are data like any other (data maintenance guide).
- Performance: OData calls execute on the serving aspect with the persistence costs the FlexibleSearch guide describes; deep
$expandon wide IOs is join fan-out you can read in the translated SQL. Bulk loads belong in batched requests during agreed windows, and if legitimate integration traffic saturates the aspect, that is the scaling conversation with SAP the source anticipates, backed by your monitoring numbers. - Environment consistency: the MetaAPI reads and writes IO definitions over HTTP, which makes it the mechanism for promoting integration models across environments and for drift detection in CI (export from stage, diff against the repo's ImpEx, fail on surprises). IOs that exist only in one Backoffice are the integration equivalent of untracked schema.
The Checklist#
- Every IO in version-controlled ImpEx (definitions, channels, access rights, scripts), promoted like code
- Keys mirror business identity; payload fields justified by a named consumer
- OAuth2 per consumer; Backoffice Integration UI restricted; access rights per IO
- Virtual attributes pure and cheap; runtime attributes exported and reviewed for items.xml graduation
- Inbound/outbound monitoring alerted and retained; reconciliation for the flows that matter
- MetaAPI drift check in the pipeline; sample payloads and EDMX archived with the contract
Integration Objects reward the same discipline as every declared surface in this platform: model deliberately, expose minimally, version everything, monitor the seams. The estates that treat IOs as schema-grade artifacts integrate new systems in days; the ones that treat the Integration UI as a convenience click away from production get to rediscover, one incident at a time, that an API is a promise.