Skip to main content
RapidDev - Software Development Agency
outsystems-tutorial

Customizing REST Calls with OnBeforeRequest and OnAfterResponse Callbacks

OutSystems REST API callbacks (OnBeforeRequest and OnAfterResponse) are action hooks that run before every outgoing request and after every incoming response for a consumed REST API. Use OnBeforeRequest to inject dynamic headers (Authorization, correlation IDs, tenant identifiers) and modify URLs. Use OnAfterResponse to inspect HTTP status codes, transform response payloads, raise typed exceptions for error status codes, and log response metadata. Both callbacks apply to all methods in the REST API node.

What you'll learn

  • Understand the difference between simple and advanced callbacks and when to use each
  • Implement OnBeforeRequest to dynamically inject Authorization headers and custom request identifiers
  • Use OnAfterResponse to inspect status codes, raise typed exceptions, and extract response headers
  • Compose multiple header modifications in a single callback without overwriting existing headers
  • Apply the advanced callback pattern for payload transformation using Integration Studio (O11) or External Libraries (ODC)
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced10 min read25-30 minOutSystems 11 and ODCMarch 2026RapidDev Engineering Team
TL;DR

OutSystems REST API callbacks (OnBeforeRequest and OnAfterResponse) are action hooks that run before every outgoing request and after every incoming response for a consumed REST API. Use OnBeforeRequest to inject dynamic headers (Authorization, correlation IDs, tenant identifiers) and modify URLs. Use OnAfterResponse to inspect HTTP status codes, transform response payloads, raise typed exceptions for error status codes, and log response metadata. Both callbacks apply to all methods in the REST API node.

The REST API Callback Pipeline in OutSystems

When OutSystems calls an external REST API, the request goes through a two-stage pipeline: OnBeforeRequest (outgoing) and OnAfterResponse (incoming). These callbacks let you intercept and modify every HTTP interaction at the transport level, regardless of which specific method is being called. This is the correct place to handle cross-cutting concerns like authentication header injection, request tracing, response normalization, and HTTP error detection. Without callbacks, each method call would need its own header injection logic — callbacks centralize it once.

Prerequisites

  • A consumed REST API already imported under Logic tab → Integrations → REST with at least one working method
  • Site Properties or Secret Settings configured for any credentials you plan to inject
  • Understanding of OutSystems exception types (User Exception, Communication Exception, Security Exception)
  • For advanced callbacks: Integration Studio installed (O11) or External Libraries SDK familiarity (ODC)

Step-by-step guide

1

Understand the two callback types: simple vs advanced

OutSystems provides two versions of each callback: Simple callbacks (OnBeforeRequest, OnAfterResponse): - Implemented as regular OutSystems Server Action logic - Work with the HTTPRequest/HTTPResponse OutSystems structures - Can read/write headers, URL, status code, response body (as Text) - Available to all developers, no additional tooling - Limitation: cannot access the raw binary request/response stream Advanced callbacks (OnBeforeRequestAdvanced, OnAfterResponseAdvanced): - Implemented in C# code via Integration Studio (O11) or External Libraries (ODC) - Have direct access to the .NET HttpRequestMessage and HttpResponseMessage objects - Required for: binary payload manipulation, streaming, custom TLS, non-standard serialization - Most integrations only need the simple callbacks To add a simple callback, click your REST API node → Properties panel → On Before Request → select (New OnBeforeRequest). For advanced, select (New OnBeforeRequestAdvanced).

Expected result: You understand when to use simple vs advanced callbacks and have chosen the appropriate type for your use case.

2

Add OnBeforeRequest to inject a dynamic Bearer token

Click your consumed REST API node. In the Properties panel, click On Before Request → (New OnBeforeRequest). Service Studio creates an OnBeforeRequest action inside your REST API node. Open it. The action has two pre-defined parameters: - Request (HTTPRequest, input) — the current request before sending - CustomizedRequest (HTTPRequest, output) — the modified request to send You must assign CustomizedRequest at the end, even if you make no changes. Build this flow: Start → GetAccessToken [Server Action that returns current valid token — see oauth tutorial] → Assign NewAuthHeader NewAuthHeader.Name = "Authorization" NewAuthHeader.Value = "Bearer " + GetAccessToken.Token → ListAppend (Request.Headers, NewAuthHeader) → Assign CustomizedRequest = Request → End IMPORTANT: Use ListAppend on Request.Headers — do NOT replace the entire Headers list. Replacing it removes all pre-existing headers including Content-Type.

Expected result: Every request sent through this REST API now includes the Authorization header. Verify in Service Center → Monitoring → Integrations that the header appears in the logged request.

3

Conditionally modify the URL in OnBeforeRequest

OnBeforeRequest can also modify the request URL. This is useful for: - Appending environment-specific query parameters - Routing to different API versions based on a feature flag - Injecting a tenant identifier into the URL path URL modification example for multi-tenant API: Start → GetSiteProperty (TenantId) → Assign Request.URL = Replace(Request.URL, "/api/", "/api/" + TenantId + "/") → Assign CustomizedRequest = Request → End For query parameter appending: Start → GetSiteProperty (ApiVersion) → Assign Request.URL = Request.URL + "?api-version=" + ApiVersion → Assign CustomizedRequest = Request → End Note: If the URL already contains query parameters, use "&api-version=" + ApiVersion instead of "?api-version=" to avoid malforming the URL. Check using the Index() expression function: If(Index(Request.URL, "?", 0, False, False) >= 0, "&", "?")

Expected result: The Request.URL is modified before sending. Log entries in Service Center show the modified URL.

4

Implement OnAfterResponse to handle HTTP error status codes

Click your REST API node. In Properties panel, set On After Response → (New OnAfterResponse). The action parameters: - Response (HTTPResponse, input) — the received response with StatusCode (Integer), Headers (List), ResponseText (Text) - CustomizedResponse (HTTPResponse, output) — the modified response to return to the caller Build HTTP status code handling: Start → Switch Case: Response.StatusCode = 200 OR Response.StatusCode = 201 → [pass through] Case: Response.StatusCode = 401 → Raise Exception (TokenExpired, User Exception) with message "Authentication token expired. Please re-authenticate." Case: Response.StatusCode = 403 → Raise Exception (InsufficientPermissions, Security Exception) Case: Response.StatusCode = 404 → Raise Exception (ResourceNotFound, User Exception) Case: Response.StatusCode = 429 → GetResponseHeader ("Retry-After") → Assign RetryAfterSeconds → Raise Exception (RateLimitExceeded, User Exception) with message "Rate limit exceeded. Retry after " + RetryAfterSeconds + " seconds." Case: Response.StatusCode >= 500 → Raise Exception (ExternalServerError, Communication Exception) Default → [Assign CustomizedResponse = Response → End] Create named User Exceptions first via Logic tab → Exceptions → right-click → Add Exception.

Expected result: HTTP 4xx and 5xx responses raise typed exceptions that can be individually caught in calling Server Actions. 2xx responses pass through normally.

5

Extract and use response headers in OnAfterResponse

Some APIs return important metadata in response headers (pagination links, rate limit remaining, request IDs, correlation IDs). Access them in OnAfterResponse using the GetResponseHeader action from HTTPRequestHandler. First, add the HTTPRequestHandler reference: Module → Manage Dependencies (Ctrl+Q) → search HTTPRequestHandler → check GetResponseHeader → Apply. Inside OnAfterResponse: Start → GetResponseHeader (Name = "X-Request-Id") → Assign RequestTrackingId = GetResponseHeader.Value → GetResponseHeader (Name = "X-RateLimit-Remaining") → If (TextToInteger(GetResponseHeader.Value) < 10) → [True] LogWarning (Module = "MyModule", Message = "API rate limit nearly exhausted: " + GetResponseHeader.Value + " remaining") → [False] pass → Assign CustomizedResponse = Response → End For headers that must be passed to the calling action, store them in a Site Property or a short-lived cache entity. Callbacks cannot directly return values to the calling method — they only modify the request/response objects.

Expected result: Response header values are inspected and acted upon. Rate limit warnings appear in Service Center logs before hitting the limit.

6

Test and debug callbacks with the Service Studio debugger

Callbacks run as part of the server-side flow and are fully debuggable. To step through OnBeforeRequest: 1. Open the OnBeforeRequest action in the Logic tab 2. Click any node to set a breakpoint (the node gets a red dot) 3. Press F6 (Debug) in Service Studio 4. Trigger an action from the screen that calls the REST API method 5. The Debugger tab (lower pane) pauses at your breakpoint 6. Use the Scope panels to inspect Request.Headers, URL, and any local variables 7. Press F8 (Step Over) to advance through each node Common callback bugs found via debugging: - CustomizedRequest not assigned at End → outgoing request is null, causing an error - Headers being replaced (= assignment) instead of appended (ListAppend) → Content-Type header lost - Switch Case order wrong → 200 OK caught by a >= 500 default

Expected result: You can step through the callback logic and inspect the HTTPRequest/HTTPResponse structures to verify modifications are correct.

Complete working example

RestAPI_Callbacks_Combined_Pattern.txt
1/* OnBeforeRequest Multi-header injection pattern
2 Injects Bearer token, correlation ID, and API version header
3 Input: Request (HTTPRequest)
4 Output: CustomizedRequest (HTTPRequest)
5*/
6
7Start
8 |
9 v
10[GetOrRefreshToken] -- Server Action (see oauth-authentication tutorial)
11 Returns: AccessToken (Text)
12 |
13 v
14[Assign] AuthHeader
15 AuthHeader.Name = "Authorization"
16 AuthHeader.Value = "Bearer " + AccessToken
17 |
18 v
19[ListAppend] Request.Headers += AuthHeader
20 |
21 v
22[Assign] CorrelationHeader
23 CorrelationHeader.Name = "X-Correlation-Id"
24 CorrelationHeader.Value = GenerateGuid()
25 |
26 v
27[ListAppend] Request.Headers += CorrelationHeader
28 |
29 v
30[Assign] VersionHeader
31 VersionHeader.Name = "X-API-Version"
32 VersionHeader.Value = Site.ApiVersion
33 |
34 v
35[ListAppend] Request.Headers += VersionHeader
36 |
37 v
38[Assign] CustomizedRequest = Request
39 |
40 v
41End
42
43
44/* OnAfterResponse Status code handling + rate limit monitoring
45 Input: Response (HTTPResponse)
46 Output: CustomizedResponse (HTTPResponse)
47*/
48
49Start
50 |
51 v
52[Switch on Response.StatusCode]
53 |
54 Case = 401 -->
55 [Assign] NeedsTokenRefresh = True (update Site Property)
56 [Raise Exception: TokenExpired (User Exception)]
57 |
58 Case = 403 -->
59 [Raise Exception: AccessDenied (Security Exception)]
60 |
61 Case = 404 -->
62 [Raise Exception: ResourceNotFound (User Exception)]
63 |
64 Case = 429 -->
65 [GetResponseHeader "Retry-After"] RetryAfterSecs
66 [Raise Exception: RateLimitExceeded (User Exception)
67 Message = "Rate limited. Retry after " + RetryAfterSecs + "s"]
68 |
69 Case >= 500 -->
70 [LogError]
71 Module = "IntegrationModule"
72 Message = "External API server error: " + IntegerToText(Response.StatusCode)
73 Detail = Response.ResponseText
74 [Raise Exception: ExternalServerError (Communication Exception)]
75 |
76 Default (2xx) -->
77 [GetResponseHeader "X-RateLimit-Remaining"] RateLimitRemaining
78 [If TextToInteger(RateLimitRemaining) < 10]
79 [True] [LogWarning Message = "Rate limit low: " + RateLimitRemaining]
80 [False] (continue)
81 [Assign] CustomizedResponse = Response
82 |
83 v
84End

Common mistakes

Why it's a problem: Replacing Request.Headers entirely instead of appending to it

How to avoid: Use ListAppend(Request.Headers, NewHeader) to add headers. Assigning a new list to Request.Headers removes all headers previously set by OutSystems, including Content-Type: application/json and Accept headers, breaking the API call.

Why it's a problem: Forgetting to assign CustomizedRequest = Request at the end of OnBeforeRequest

How to avoid: CustomizedRequest is a required output parameter. If not assigned, OutSystems cannot proceed with the request. TrueChange will highlight this as a warning — do not ignore it. Every code path through OnBeforeRequest must assign CustomizedRequest before reaching End.

Why it's a problem: Calling a database Aggregate inside OnBeforeRequest on every single API call

How to avoid: OnBeforeRequest runs synchronously before every HTTP call. A database query here adds database round-trip latency to every API call. Cache frequently-needed values (tokens, config) in Site Properties and only hit the database when the cached value is stale.

Why it's a problem: Using OnBeforeRequest for logic that only applies to one specific method rather than all methods

How to avoid: OnBeforeRequest applies to ALL methods in the REST API node. If the logic should only apply to one method, implement it directly in the Server Action that calls that method, not in the callback. Check Request.URL in the callback if you absolutely must differentiate.

Best practices

  • Always assign CustomizedRequest at the end of OnBeforeRequest, even in exception paths — failing to do so raises a runtime error on every API call.
  • Use ListAppend to add headers, not assignment — replacing the entire Headers list removes Content-Type, Accept, and other headers set by OutSystems automatically.
  • Keep callback logic stateless where possible — avoid reading from the database in OnBeforeRequest to keep latency low. Cache authentication tokens in Site Properties instead.
  • Centralize all cross-cutting HTTP concerns in callbacks (auth, correlation IDs, logging) rather than repeating the logic in each Server Action that calls the API.
  • For APIs with multiple REST nodes in the same module, consider creating a reusable Server Action for the shared callback logic and calling it from each node's callback.
  • Test callbacks by setting breakpoints inside the OnBeforeRequest/OnAfterResponse actions in the Service Studio Debugger — they participate fully in the debug session.
  • Use the OnAfterResponse callback to transform malformed response bodies (e.g., non-standard date formats) into normalized OutSystems-compatible formats before the generated structure deserialization occurs.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm building an OutSystems integration with a SaaS API that uses rotating API keys. Each API key expires after 24 hours, and I get the new key from a key management endpoint. Help me design the OnBeforeRequest and OnAfterResponse callbacks to: 1) check if the cached API key is expiring within 1 hour, fetch a new one if needed, and inject it as an X-API-Key header, 2) handle 401 responses by triggering a key refresh and raising an exception for the caller to retry.

OutSystems Prompt

I have a consumed REST API in OutSystems called PaymentGatewayAPI with methods: CreatePayment, GetPayment, RefundPayment. The API requires: 1) Authorization: Bearer {token} header (token changes every 30 min), 2) X-Idempotency-Key: {guid} header on POST calls only, 3) Any 402 response should raise a PaymentDeclined User Exception with the response body as the message. Write the complete OnBeforeRequest and OnAfterResponse action flows using arrow notation.

Frequently asked questions

Can I have both OnBeforeRequest and OnAfterResponse active on the same REST API node simultaneously?

Yes. Both callbacks are independent and both run on every request/response cycle. OnBeforeRequest fires before the HTTP call is made; OnAfterResponse fires after the response is received. They do not interfere with each other. You can and should use both together for complete request/response control.

How do I access the response body as a text string in OnAfterResponse to parse error messages from APIs that return non-standard error formats?

The Response.ResponseText attribute on the HTTPResponse input parameter contains the full response body as a Text string. You can use JSON parsing logic (or the JSONDeserialize built-in) to extract specific fields from non-standard error responses. After extracting the error detail, include it in the message of the raised exception so calling Server Actions can surface the specific error to users.

Do callbacks apply to REST API calls made from ODC apps the same way as O11?

Yes. The simple callback mechanism (OnBeforeRequest, OnAfterResponse) works identically in ODC Studio. The difference is advanced callbacks: in O11 they use Integration Studio (.xif extensions in C#); in ODC they use External Libraries (C# .NET 8 SDK, deployed as ZIP packages via ODC Portal). For most use cases, simple callbacks are sufficient in both platforms.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.