APEX AI Agent Handlers: Intercepting & Updating Requests and Responses

Introduction
In my previous post, I looked at logging APEX AI Agent requests and responses with Request and Response handlers. Logging is the first step because it shows you what is actually moving through the agent loop: prompts, messages, tool definitions, tool calls, tool results, token counts, and final responses.
Once you can see those values, the next question is obvious:
Can I change them?
Yes. That is where handlers become more than instrumentation. They let you inspect and update selected values before and after the AI service call.
In this post, I will show how you can use APEX AI Agent handlers to:
Add runtime application context to the request.
Redact or normalize user input before sending it to the model.
Inspect pending tool calls before execution.
Reject unsafe arguments to tool calls before the tool runs.
Modify the final assistant response before the user sees it.
The Mental Model
The most important handler pattern is simple:
p_paramtells you what APEX passes to the handler.p_resultis where you make changes.
The handler procedures use specific APEX signatures:
PROCEDURE agent_request_handler
(p_param in apex_ai.t_chat_request_handler_param,
p_result in out nocopy apex_ai.t_chat_request_handler_result);
PROCEDURE agent_response_handler
(p_param in apex_ai.t_chat_response_handler_param,
p_result in out nocopy apex_ai.t_chat_response_handler_result);
The request handler runs before the request is sent to the AI service. This is where you shape the outgoing request.
The response handler runs after the AI service responds. This is where you inspect the model response, validate pending tool calls, stop the loop early, or modify the assistant's final response.
Oracle's APEX 26.1 APEX_AI docs describe the normalized chat request, chat response, response handler parameter, response handler result, chat message, tool call, and tool result types in the APEX_AI data types documentation. The response handler can also return a tool result directly with APEX_AI.SET_TOOL_RESULT signature 2, but that behaves differently from returning a final assistant response, as shown below.
Request Handler Uses
Use the request handler when you want to change what the model sees.
Common examples:
Add the current APEX application context to the system prompt.
Add user, datetime, timezone, project, or customer context.
Redact sensitive values from user messages.
Change temperature or other request-level values when exposed through the request record.
The request handler is especially useful because it operates before the model has a chance to make a decision.
Example 1: Add Runtime Context to the System Prompt
The system prompt defines the agent's operating rules, but some context is only known at runtime. For example, the current app user, customer, project, security role, or selected record may depend on APEX session state.
For stable application context, I would normally use the agent's Augment System Prompt configuration. I am using the request handler here to show that the handler can also add context when the value needs to be calculated at request time.
You can append that context in the request handler.
procedure agent_request_handler
(p_param in apex_ai.t_chat_request_handler_param,
p_result in out nocopy apex_ai.t_chat_request_handler_result)
as
l_context clob;
begin
l_context :=
CHR(10) || CHR(10)
|| 'Runtime application context:' || CHR(10)
|| '- Current date and time: ' || TO_CHAR(LOCALTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') || CHR(10)
|| '- User timezone: ' || 'America/Los_Angeles' || CHR(10)
|| '- App username: ' || apex_util.get_session_state('APP_USER') || CHR(10);
p_result.request.system_prompt :=
p_result.request.system_prompt
|| l_context;
end agent_request_handler;
The declarative agent configuration can remain stable while the handler adds request-specific operational context.
Note: You can also use the 'Augment System Prompt' tools section of the APEX Agent to augment the system prompt with context.
Example 2: Redact Sensitive User Input
If the user prompt may contain values you do not want to send to the AI provider, you can inspect the outgoing chat messages and redact those values.
procedure redact_user_messages
(p_messages in out nocopy apex_ai.t_chat_messages)
as
l_idx pls_integer;
begin
l_idx := p_messages.first;
while l_idx is not null loop
if p_messages(l_idx).chat_role = apex_ai.c_role_user then
p_messages(l_idx).message :=
regexp_replace(
p_messages(l_idx).message,
'([0-9]{3})-([0-9]{2})-([0-9]{4})',
'[redacted-ssn]' );
p_messages(l_idx).message :=
regexp_replace(
p_messages(l_idx).message,
'([[:alnum:]_.-]+)@([[:alnum:]_.-]+)',
'[redacted-email]' );
end if;
l_idx := p_messages.next(l_idx);
end loop;
end redact_user_messages;
Then call it from the request handler:
procedure agent_request_handler
(p_param in apex_ai.t_chat_request_handler_param,
p_result in out nocopy apex_ai.t_chat_request_handler_result)
as
begin
redact_user_messages(p_result.request.messages);
end agent_request_handler;
This also gives you a clear place to centralize prompt normalization rules.
This is intentionally simple. Production redaction should be tested against your real data patterns and should avoid logging the unredacted source text.
A Note About Tool Authorization
In the first version of this post, I considered including an example in which the request handler removes tools based on the current user's authorization. After thinking about it more, I do not think that is the right primary example for APEX.
APEX already gives you declarative controls at the AI Agent tool level. You can use Authorization Schemes and Server-side Conditions to determine whether a tool is available. That is the better first place to handle tool availability because it keeps security close to the APEX component configuration and uses the same declarative model APEX developers already use elsewhere in the application.
Handlers can still inspect p_result.request.tools, and there may be advanced cases where modifying the tool list is useful. For example, you might be building tools dynamically through lower-level APEX_AI.CHAT calls rather than using a declarative APEX Agent configuration. But for normal APEX AI Agent tools, prefer the built-in Authorization Scheme and Server-side Condition properties.
Response Handler Uses
Use the response handler when you want to inspect or change what the model returned.
Common examples:
Validate pending tool calls before they execute.
Validate tool arguments before execution.
Return a controlled tool result without running the declarative tool.
Stop the agent loop early.
Replace a refusal or error message with a user-friendly response.
Add standard disclaimers, links, or formatting to the final answer.
Oracle documents t_chat_response_handler_param as containing the handler invocation number, the normalized request, and pending_tool_calls. It documents t_chat_response_handler_result as containing the mutable response, messages, and early_exit.
The key field for tool interception is:
p_param.pending_tool_calls
Each pending tool call has: id, name, args, args_json
Example 3: Validate a Pending Tool Call
Suppose the model asks to run search_open_cases, but it passes MAX_ROWS as 1000. That may be valid JSON and still be a bad request. In this example, search_open_cases is the tool name, and MAX_ROWS is a tool parameter used by the query to limit the number of rows returned.
The response handler can inspect the pending tool call before it executes.
procedure agent_response_handler
(p_param in apex_ai.t_chat_response_handler_param,
p_result in out nocopy apex_ai.t_chat_response_handler_result)
as
l_call apex_ai.t_chat_message_tool_call;
l_max_rows number;
begin
if p_result.response.type <> apex_ai.c_response_type_tool_calls then
return;
end if;
for i in 1 .. p_param.pending_tool_calls.count loop
l_call := p_param.pending_tool_calls(i);
if l_call.name = 'search_open_cases' then
l_max_rows := coalesce(l_call.args_json.get_number('MAX_ROWS'), 25);
if l_max_rows > 50 then
p_result.response.type := apex_ai.c_response_type_complete;
p_result.response.message.chat_role := apex_ai.c_role_assistant;
p_result.response.message.message :=
'I cannot run that search because MAX_ROWS cannot exceed 50.';
p_result.early_exit := true;
return;
end if;
end if;
end loop;
end agent_response_handler;
This does not execute the original tool. It stops the normal agent loop and returns a specific assistant message to the user.
This is different from calling apex_ai.set_tool_result with an error payload. set_tool_result creates a tool-result message in the conversation history. That can be useful when you want the model to see the failed tool result and decide how to respond on the next turn, but it does not necessarily show that error directly in the agent UI. For a validation failure that should be shown to the user immediately, return a complete assistant response and set p_result.early_exit to true.
That distinction matters. If a tool call should not run, reject it before execution. The tool procedure should still validate its inputs because it may be callable from paths outside the AI Agent loop.
Example 4: Modify the Final Assistant Response
The response handler also sees complete responses. That means you can standardize or enrich the final answer before it is returned to the user.
For example, you might append a standard note when the answer was generated from operational data.
procedure agent_response_handler
(p_param in apex_ai.t_chat_response_handler_param,
p_result in out nocopy apex_ai.t_chat_response_handler_result)
as
begin
if p_result.response.type = apex_ai.c_response_type_complete then
p_result.response.message.message :=
p_result.response.message.message
|| chr(10) || chr(10)
|| '_This answer was generated from current service data. '
|| 'Open the linked records before making customer commitments._';
end if;
end agent_response_handler;
I would not use this for heavy rewriting. If the model is consistently producing the wrong shape of answer, fix the system prompt or tool result shape first. Use response modification for small, deterministic post-processing.
Request Handler vs Response Handler
Here is how I think about the two handlers:
| Need | Better Handler |
|---|---|
| Add current app context | Request handler |
| Redact user input before the provider call | Request handler |
| Inspect model-selected tools | Response handler |
| Validate tool call arguments | Response handler |
| Stop the agent loop early | Response handler |
| Modify the final assistant message | Response handler |
If you can provide a stable context before the model is called, use the request handler. If you need to react to what the model actually chose, use the response handler.
Practical Guidance
Handlers are powerful, but they can also make behavior harder to reason about if you are not disciplined.
The rules I would follow:
Keep handlers small. Delegate real logic to package functions and procedures.
Make handler changes deterministic. Do not introduce random behavior or hidden state.
Log what you changed. If you redact input, limit arguments, stop a tool call, or modify a final response, record it in your agent log.
Use APEX Authorization Schemes and Server-side Conditions for declarative tool availability.
Prefer validating arguments before execution over cleaning up after execution.
Keep tool results small. Anything returned from a tool can become model context on the next turn.
Test multi-turn conversations. A handler change on invocation
1can affect what happens on invocation2.
Conclusion
APEX AI Agent handlers are not just logging hooks. They are control points inside the agent loop.
The request handler lets you shape what the model sees: prompts, messages, and runtime context. The response handler lets you shape what happens after the model responds: pending tool calls, early exits, and final assistant messages.
The important pattern is to keep the model advisory and keep the application deterministic. Let the model decide what it wants to do, but use APEX configuration, handlers, tools, and database rules to decide what is allowed to happen.
That is the practical boundary for production APEX agents:
The model can suggest.
The handler can intercept.
APEX configuration and tool code enforcement.
Database rules protect the data.
Once you treat handlers as part of the agent workflow, you can build agents that are easier to debug, safer to operate, and better aligned with the business rules already living in your APEX application.
Updated Sample Package
I updated the sample PL/SQL package from my previous post to include examples from this post. Link.




