The IProductOrderPOST Apex interface enables customization of POST operations using Apex lifecycle hooks. One handler routes into four mutually exclusive sub-flows from productOrderItem[].action (and product.id). Implementations can validate and enrich creation, amendment, renewal, or cancellation payloads, and run side effects after the pipeline, while the client-visible order is re-fetched via GET.
This interface supports the following hooks.
transformRequest
configureDefaultValidations
applyCustomValidations
preServiceHook
handlePostOperation
Note
These hooks are not used for Product Order POST. If implemented, the methods are required to compile, but they are never invoked (constructGraphQL / constructMutationGraphQL return null).
customiseGraphQLQuery
customiseMutationPayload
Rewrite the outbound payload in preServiceHook via modifiedPayload.
Sub-flows
Mixed actions (add+renew, cancel+anything, and similar) are rejected by validateSingleActionTypebefore any Apex hook.
Sub-flow
How it is detected
Pipeline steps
Standard create/submit
Default when no amend/renew/cancel action
orderIngestion → activateOrder → submitOrder (last two skipped if the org has Quotes Tax)
Amendment
Any item action=amend, or a non-cancel/non-renew item with existing product.id
preServiceHook fires up to three times per request (once per step) using finalPayload re-invocation: build payload → Apex → execute with modifiedPayload. Switch on serviceName.
Creation Lifecycle Use Cases and Hook Mapping
The following table lists common use cases for the POST operation.
Use Case
Hook(s)
Description
Example Scenarios
Benefit
Force a sub-flow
transformRequest
Runs before sub-flow detection. Mutating productOrderItem[].action (or injecting product.id) changes which pipeline is built.
• Set action=amend when product.id is present • Route cancel vs renew vs amend to a single action
Avoid mixed-action rejection and pick the correct pipeline
Enforce create rules
applyCustomValidations
Only loud HTTP error path. Return top-levelvalidationStatus / validationMessage. Nested result is silently dropped.
• Require requestedStartDate on add items • Duplicate amendment/renewal checks the handler does not run
Prevents invalid orders before service calls
Inject ingestion preferences
preServiceHook (orderIngestion)
Add scalar keys on the initiating payload. orderGraph is a live Java object — prefer scalars.
• pricingPref, taxPref
Channel-specific pricing/tax without forking the handler
Override amendment quantity deltas
preServiceHook (initiateAmendment)
Mutate quantityChanges before execute.
• Org rounding rules
Keep quantity math consistent
Hold activated but unsubmitted
preServiceHook (submitOrder)
skipCall: true is not an HTTP error. Skipping an initiating step leaves a null order id. Skipping submitOrder is the only coherent partial skip.
• Nightly batch submit
Partial pipeline without a broken order
Audit after POST
handlePostOperation
Return is discarded. Client body is GET re-fetch. A non-empty map fires telemetry (up to 3× from preServiceHook).
• Platform events, correlation ids
Side effects without shaping the HTTP body
transformRequest
This hook transforms the request context before processing any operation. It is invoked before sub-flow detection.
Default spec (all true): orderLevelAttributesValidation, relatedPartyValidation, productOrderItemsValidation. Return a map toggling those flags.
Handler validateAmendmentItems / validateRenewalItems / validateCancellationItems do not run in the normal request flow (isXRequest is still false during validation). Duplicate critical checks in applyCustomValidations. Missing product.id is enforced later in payload builders as HTTP 400.
1global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {2 Map<String, Object> body = (Map<String, Object>) context.get('requestBody');3 if (body == null) {4 return new Map<String, Object>();5 }6 Object items = body.get('productOrderItem');7 if (items instanceof List<Object> && body.get('requestedStartDate') == null) {8 for (Object itemObj : (List<Object>) items) {9 Map<String, Object> item = (Map<String, Object>) itemObj;10 if ('add' == item.get('action') || item.get('action') == null) {11 return new Map<String, Object>{12 'validationStatus' => 'fail',13 'validationMessage' => 'requestedStartDate is required when adding new items'14 };15 }16 }17 }18 return new Map<String, Object>();19}
preServiceHook
This hook runs once per pipeline step (up to three times). Return {skipCall, modifiedPayload}. Exceptions are swallowed per step (WARNING); the original payload is used. skipCall is not an HTTP error.
serviceName
Payload shape
Notes
orderIngestion
{orderGraph}
Standard create. Prefer scalar keys such as pricingPref
graphQLQueryResultAsMap is synthetic {"data": flowResult} from orderIngestionResult / initiateAmendmentResult / initiateRenewalResult / initiateCancellationResult. constructedTMFResponse is the request body. Return is discarded. The client representation is GET retrieveResource.