A customer asks for a refund, and an assistant identifies the order, interprets the complaint, and proposes returning the full purchase price. The response arrives as valid JSON. Every required field is present, the amount is a number, and the order identifier has the expected format. None of that establishes whether the customer owns the order, whether a refund has already been issued, or whether the assistant is allowed to authorise one.
Connecting a language model to a business system requires a deliberate transition between interpretation and action. The model can help explain a request and propose an operation. The application needs independent grounds for accepting that proposal, and the system that owns the transaction must enforce its rules when the operation takes effect.
Azure API Management and Semantic Kernel can support this design. Their usefulness depends on where they sit in the request path and which decisions remain with the application. A gateway policy cannot establish facts it has never retrieved, and an orchestration library does not make a generated command authoritative.
Begin with a proposal that carries limited authority
For a refund workflow, give the model a small output contract: an order reference, a requested amount, a currency, and an explanation. Keep operational authority outside that contract. The application should obtain the authenticated customer identity from its security context, rather than accept a customer identifier supplied by the model as evidence of ownership.
An illustrative proposal might look like this:
{
"orderReference": "ORDER-4821",
"requestedAmountMinor": 4500,
"currency": "NZD",
"reason": "Customer reports a missing item"
}
The amount represents 4,500 minor units, or NZ$45 in this example. The business must define the permitted currency and amount representation; an integer alone does not establish that the amount is refundable. The explanation is a claim to examine, not a verified record of what happened.
Avoid giving the model fields such as approved, administratorOverride, or an arbitrary destination URL unless the workflow has an explicit, separately enforced reason for accepting them. A compact proposal makes the permitted action easier to understand and reduces the number of generated values that downstream code might accidentally treat as instructions.
Constrain output, then validate what arrived
Structured output features can reduce malformed responses. OpenAI’s structured outputs documentation distinguishes schema-constrained output from JSON mode, which primarily addresses JSON validity. Supported schemas have restrictions, and applications still need to handle refusals and incomplete responses. Azure deployments should use the capabilities documented for their particular model and API combination in Microsoft’s structured outputs guidance.
These features improve a boundary; they do not replace it. Validate the response that actually reaches the application, including responses affected by an integration change or a fallback path. Set limits on body size and field lengths, reject unexpected properties where the contract requires it, and distinguish a missing response from an empty proposal.
A successful structural check establishes the properties expressed by the configured validator. It does not establish that the order exists, that a reason is accurate, or that the requested operation is permitted. The distinction between JSON Schema and TypeScript types is useful here because a typed object can otherwise create more confidence than the boundary deserves.
Put checks where they can prevent the consequence
The request path should make it clear which component can still reject the operation before it changes business state.
In Azure API Management’s content validation policy, the action matters: detection records a problem, while prevention blocks processing at that policy stage. Incoming and outgoing validation also operate at different points. Validate a request before forwarding it if the purpose is to stop an invalid command reaching the business service.
An outbound policy can protect a consumer from a malformed response. It cannot undo a refund already issued by the backend. If the gateway wraps a model-only endpoint, outbound validation can prevent an invalid proposal reaching the application, provided that endpoint has no business side effects. Trace the actual route before deciding what the policy protects.
The gateway and application validators also need compatible contracts. Do not assume every validator supports the same schema dialect, references, or optional features. Keep a small set of accepted and rejected fixtures and run them through each configured boundary. A schema accepted by one component is insufficient evidence that another enforces the same constraints.
Keep business decisions with authoritative state
After structural validation, the refund service should load the order through a customer-scoped query and calculate the remaining refundable balance. It should also apply the relevant policy: whether the item qualifies, whether approval is required, and whether the proposed amount exceeds the authority granted to this workflow.
The resulting checks answer different questions:
| Check | Evidence used | Example reason to reject |
|---|---|---|
| Response structure | Declared proposal contract | Currency field is missing |
| Access | Authenticated identity and permissions | Order belongs to another customer |
| Eligibility | Order history and refund policy | No refundable balance remains |
| Approval | Workflow authority and amount | Request requires a supervisor |
| Commit condition | Current transaction state | Another refund consumed the balance |
A successful read is not a reservation. Two workers can both observe the same remaining balance and attempt to use it. Enforce the relevant invariant during the write, using the transaction or conditional update mechanisms available in the system of record. The design should make simultaneous valid-looking proposals resolve into permitted outcomes.
This is also where tool access needs attention. If an assistant can invoke a refund tool directly, that tool must enforce the same permissions and state conditions. Validating the final conversational response is too late to govern actions taken earlier in the turn. Restrict tool capabilities and inspect their execution path as part of the integration review.
Separate reasoning retries from transaction recovery
A malformed proposal may justify another model attempt within a bounded budget. A denied refund usually requires a different response: explain the policy, request missing information, or refer the case for review. Repeatedly sampling until a proposal passes can turn an intended rejection into a search for an acceptable-looking answer.
Once an operation has been submitted, recovery becomes a transaction problem. Suppose the payment service accepts the refund but the caller times out before receiving confirmation. Asking the model to generate another proposal does not resolve whether money moved. The application needs a durable operation identifier, the payment provider’s documented idempotency behaviour, and a way to retrieve or reconcile the outcome.
Store enough information to distinguish a proposal attempt from a business operation. A regenerated explanation may belong to the same customer request, while an approved second refund may be a genuinely different operation. Choose the idempotency scope around the business intent and persist it across worker restarts. This connects directly to why timeouts do not cancel work.
If a database update and an external payment call cannot share a transaction, document their intermediate states. A pending operation, durable dispatch record, and reconciliation worker may be appropriate. The customer interface should reflect what is known: submitted, confirmed, rejected, or awaiting reconciliation. A transport failure should not automatically become a business failure.
Make orchestration preserve the boundary
Semantic Kernel’s chat completion services provide integration with model services. The surrounding application still decides which context and tools are available and how their results enter the workflow. Keep those decisions visible rather than burying permission checks inside prompt wording.
For example, an order lookup can return only the fields needed to assess the request. A refund proposal tool can create a pending record without having permission to release funds. An approval service can then evaluate that record using ordinary application controls. Separating these capabilities makes it easier to change a model without silently changing its authority.
It also gives reviewers specific failure cases to exercise. Test an invented order reference, a valid order owned by someone else, an amount above the balance, and a proposal replayed after a successful refund. Include a concurrent request and a payment response lost after acceptance. These cases examine the consequences of the integration rather than just whether the model emits tidy JSON.
Operational records should connect the proposal, contract version, checks performed, approval decision, operation identifier, and final outcome. Retain sensitive prompts or customer text only where the purpose and retention controls justify it. Often a structured decision record is more useful for investigation than an unrestricted transcript.
Measure where proposals leave the workflow. A rise in structural rejections after a model change calls for a different investigation from a rise in policy denials or unresolved payment operations. Keep those categories separate in monitoring. Otherwise a high acceptance rate can reward a boundary that has become more permissive, while a genuine improvement in fraud detection can appear to make the assistant less successful.
The resulting boundary allows model behaviour to evolve while business authority remains explicit. A better explanation or a more accurate proposal can improve the customer experience. Permission to move money still comes from the application’s policy, current state, and transaction controls, with enough evidence to recover when the response path fails.





