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

Complete Guide to REST API Error Handling in OutSystems

Every REST API call in OutSystems can fail. Add a Communication Exception handler to every Server Action that makes an external API call. Check HTTP status codes via the OnAfterResponse callback. Log all integration errors with the LogError system action. For transient failures, implement a retry pattern using a For loop with a counter and Wait node. Service Center → Monitoring → Integrations shows the complete request/response history for debugging.

What you'll learn

  • Add Communication Exception handlers to Server Actions that call external REST APIs
  • Use the OnAfterResponse callback to inspect HTTP status codes and raise typed exceptions
  • Implement a configurable retry pattern for transient API failures
  • Log integration errors to Service Center using the LogError system action
  • Design a consistent error response structure for exposed API error handling
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate9 min read20-25 minOutSystems 11 and ODCMarch 2026RapidDev Engineering Team
TL;DR

Every REST API call in OutSystems can fail. Add a Communication Exception handler to every Server Action that makes an external API call. Check HTTP status codes via the OnAfterResponse callback. Log all integration errors with the LogError system action. For transient failures, implement a retry pattern using a For loop with a counter and Wait node. Service Center → Monitoring → Integrations shows the complete request/response history for debugging.

Why API Error Handling Is Non-Negotiable

External REST API calls can fail for many reasons: network timeouts, server overloads, authentication expiry, rate limits, or malformed responses. Without explicit error handling, a failed API call crashes your entire server action with an unhandled exception — the user sees a generic error page and you have no log entry to debug with. OutSystems provides several layers for catching and handling API errors: the exception handler in the action flow, the OnAfterResponse callback for HTTP-level inspection, and Service Center's integration logs for post-mortem analysis. This tutorial builds a robust error handling framework you can apply to any API integration.

Prerequisites

  • A consumed REST API already imported in Logic tab → Integrations → REST
  • Basic understanding of OutSystems exception handling (User Exception, Communication Exception types)
  • Familiarity with Server Actions and action flow notation
  • Service Center access for reviewing integration logs (O11) or ODC Portal for traces (ODC)

Step-by-step guide

1

Understand the OutSystems exception hierarchy for API calls

Before adding handlers, understand which exception types apply: - Communication Exception: triggered when the HTTP request cannot complete — DNS failure, connection refused, TCP timeout. This is the most common error for external API calls. - User Exception: can be raised manually in OnAfterResponse when you detect a bad HTTP status code (4xx, 5xx). - Security Exception: raised when authentication/authorization fails — use this in OnAuthentication callbacks. - AllExceptions: catch-all — use as a fallback after more specific handlers. In OutSystems, unhandled exceptions bubble up to the nearest parent action and eventually to the screen, showing the default error page. Adding explicit handlers breaks this bubble and lets you control the user experience.

Expected result: You understand when each exception type fires and which to handle in which context.

2

Add a Communication Exception handler to every API-calling Server Action

Open the Server Action that calls your REST API method. In the action flow, right-click anywhere in the flow canvas → Add Exception Handler → select Communication Exception. This creates a handler branch below the main flow. Inside the handler: 1. Add an Assign node to set meaningful output parameters: IsSuccess = False, ErrorMessage = "Unable to reach the service. Please try again later." 2. Add a LogError node (from System module via Manage Dependencies): Module = your module name, Message = "API call failed: " + CommException.Message, ErrorDetail = CommException.StackTrace 3. Add an End node Action flow with handler: Start → CallExternalAPI (REST method) → Assign (process response) → End [Communication Exception] → Assign (IsSuccess=False, ErrorMessage) → LogError → End

Expected result: When the external API is unreachable, the action returns IsSuccess=False with a user-friendly message instead of crashing.

3

Inspect HTTP status codes in OnAfterResponse

The Communication Exception only fires for network-level failures. HTTP errors (401, 403, 404, 429, 500) from the server are NOT automatically raised as exceptions — OutSystems treats them as successful HTTP responses with an error status code. You must inspect the status code explicitly. To do this: 1. Click your consumed REST API node in Logic tab → Integrations → REST 2. In Properties panel, find On After Response → New OnAfterResponse 3. Open the generated OnAfterResponse action The action receives Response (HTTPResponse) with a StatusCode (Integer) attribute. Build this flow: Start → Switch Case: Response.StatusCode = 401 → Raise Exception (UnauthorizedAccess, Security Exception) Case: Response.StatusCode = 403 → Raise Exception (Forbidden, User Exception) Case: Response.StatusCode = 404 → Raise Exception (ResourceNotFound, User Exception) Case: Response.StatusCode = 429 → Raise Exception (RateLimitExceeded, User Exception) Case: Response.StatusCode >= 500 → Raise Exception (ServerError, Communication Exception) Default → End (2xx responses pass through normally) Create the named User Exceptions first: Logic tab → Exceptions → right-click → Add Exception.

Expected result: HTTP 4xx and 5xx responses are raised as typed exceptions that can be caught individually in calling actions.

4

Build a configurable retry pattern for transient failures

For transient failures (network blips, 503 Service Unavailable, 429 Rate Limited), implement a retry pattern rather than immediately failing. Create a Server Action named CallAPIWithRetry with: - Input: (whatever inputs your API method needs) - Local variables: RetryCount (Integer, default 0), MaxRetries (Integer, default 3), RetryDelayMs (Integer, default 1000), LastError (Text), IsSuccess (Boolean) Action flow: Start → [For Loop: While RetryCount < MaxRetries] → CallExternalAPI → [On Success] Assign (IsSuccess=True) → Exit Loop → [Communication Exception or RateLimitExceeded Exception] → Assign (RetryCount = RetryCount + 1, LastError = exception message) → If (RetryCount >= MaxRetries) → [True] End [False] → Wait (RetryDelayMs milliseconds) → continue loop The Wait action (from System module) pauses the server-side flow. For exponential backoff, set RetryDelayMs = RetryDelayMs * 2 on each iteration.

Expected result: Transient API failures are retried automatically up to 3 times with delays, improving reliability without user intervention.

5

Log all integration errors to Service Center

OutSystems has a built-in logging system accessible in Service Center → Monitoring → Error Log and → Integrations. To write to the Error Log from your exception handlers: 1. In Service Studio, go to Module → Manage Dependencies (Ctrl+Q) 2. Search for (System) module → expand → check LogError and LogWarning actions 3. Click Apply In your exception handlers, add a LogError call: - Module: your module name (Text literal, e.g., "ProductsModule") - Message: descriptive error summary - ErrorDetail: full exception stack trace or response body - StackTrace: exception StackTrace attribute For integration-specific logs, Service Center → Monitoring → Integrations shows every outgoing and incoming REST call with timestamp, URL, HTTP status, request headers, response body, and duration — invaluable for debugging without adding extra logging code.

Expected result: All API errors appear in Service Center → Monitoring → Error Log with enough context to diagnose root causes without re-running the scenario.

6

Define a consistent error response structure for exposed APIs

When exposing REST APIs, return consistent error responses that consumers can parse reliably. Create a Structure under Data tab → Structures → right-click → Add Structure. Name it ErrorResponse with attributes: - Code (Text) — machine-readable error code, e.g., "RESOURCE_NOT_FOUND" - Message (Text) — human-readable description - Details (Text) — optional extended information In your exposed API exception handlers (Logic tab → Exceptions per method): - Catch each typed exception - Assign the ErrorResponse structure - Use SetResponseStatusCode to set the appropriate HTTP status - The ErrorResponse output parameter is automatically serialized to JSON by OutSystems Consumers then always receive: { "Code": "RESOURCE_NOT_FOUND", "Message": "Product with Id 42 was not found.", "Details": "" }

Expected result: Consumers of your exposed API receive structured, predictable error responses for all failure conditions.

Complete working example

APIErrorHandling_ServerAction_patterns.txt
1/* Pattern 1: Server Action with Communication Exception handler
2 Input: ProductId (Long Integer)
3 Output: ProductData (Product_Response structure), IsSuccess (Boolean), ErrorMessage (Text)
4*/
5
6Start
7 |
8 v
9[GetProductFromAPI] -- REST method
10 ProductId = ProductId
11 |
12 v
13[Assign]
14 ProductData = GetProductFromAPI.Response
15 IsSuccess = True
16 ErrorMessage = ""
17 |
18 v
19End
20
21[Exception Handler: Communication Exception]
22 |
23 v
24[LogError]
25 Module = "ProductsModule"
26 Message = "GetProduct API call failed"
27 ErrorDetail = CommException.ExceptionMessage
28 |
29 v
30[Assign]
31 IsSuccess = False
32 ErrorMessage = "Unable to reach the product service. Please try again."
33 ProductData = Default(Product_Response)
34 |
35 v
36End
37
38[Exception Handler: ResourceNotFound User Exception]
39 |
40 v
41[Assign]
42 IsSuccess = False
43 ErrorMessage = "Product not found."
44 |
45 v
46End
47
48/* Pattern 2: OnAfterResponse HTTP status code handler
49 Attached to consumed REST API node.
50 Input: Response (HTTPResponse)
51 Output: CustomizedResponse (HTTPResponse)
52*/
53
54Start
55 |
56 v
57[Switch on Response.StatusCode]
58 Case = 401 --> [Raise Exception: UnauthorizedAccess (Security)]
59 Case = 403 --> [Raise Exception: Forbidden (User)]
60 Case = 404 --> [Raise Exception: ResourceNotFound (User)]
61 Case = 429 --> [Raise Exception: RateLimitExceeded (User)]
62 Case >= 500 --> [Raise Exception: ExternalServerError (Communication)]
63 Default --> [Assign] CustomizedResponse = Response
64 |
65 v
66End

Common mistakes

Why it's a problem: Assuming HTTP 4xx and 5xx responses automatically trigger a Communication Exception

How to avoid: They do not. Communication Exceptions only fire for network-level failures (can't connect, timeout). You must check HTTP status codes explicitly in the OnAfterResponse callback and raise exceptions manually for error status codes.

Why it's a problem: Retrying on all exception types, including 401 Unauthorized and 400 Bad Request

How to avoid: Retrying on authentication failures burns retry budget and delays the eventual failure. Only retry on RateLimitExceeded (429), ServiceUnavailable (503), and Communication Exceptions. Check the exception type or status code before deciding to retry.

Why it's a problem: Showing raw exception messages or stack traces directly to end users

How to avoid: Log the full technical details to Service Center using LogError, and return a generic, user-friendly message to the screen. Exposing stack traces to users is a security risk and provides a poor user experience.

Why it's a problem: Forgetting to set the Timeout in Seconds property on REST methods, leaving the 100-second default

How to avoid: Click the REST method node → Properties panel → Timeout in Seconds. Set an appropriate value for the use case: 5-10 seconds for real-time UI calls, 30-60 seconds for batch operations, 5-15 seconds for health checks.

Best practices

  • Add a Communication Exception handler to every Server Action that makes an external REST API call — no exceptions (pun intended).
  • Inspect HTTP status codes in the OnAfterResponse callback rather than checking them in each calling action — centralizing this logic means you fix it once.
  • Only retry on transient errors (429, 503, Communication Exception). Never retry on 400, 401, or 403 — these are client errors that will not resolve themselves.
  • Log every API error with the full exception message, stack trace, and relevant input parameters so you can diagnose failures from logs without reproducing them.
  • Return user-friendly error messages to the UI (e.g., 'Service temporarily unavailable') rather than raw exception messages or HTTP status codes.
  • Set realistic Timeout in Seconds values on each REST method — the default 100 seconds is too long for interactive requests. For read endpoints, 10-15 seconds is more appropriate.
  • Monitor Service Center → Monitoring → Integrations daily during the first week after deploying an API integration — catch silent failures before users report them.
  • Test error paths explicitly: temporarily change the endpoint URL to an invalid host to trigger a Communication Exception, and verify your handler produces the expected output.

Still stuck?

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

ChatGPT Prompt

I'm building an OutSystems 11 integration with an external payment API. The API can return these HTTP status codes: 200 (success), 400 (validation error with error body), 401 (token expired), 422 (unprocessable entity), 429 (rate limited, Retry-After header included), 500 (server error). Help me design: 1) the OnAfterResponse callback to handle each status code, 2) the exception types I should create, and 3) a retry pattern that respects the Retry-After header value.

OutSystems Prompt

In my OutSystems module I call an external inventory REST API from a Server Action UpdateInventory. The call occasionally fails with Communication Exceptions due to network instability. Write the complete action flow (using arrow notation) for a retry wrapper Server Action that: attempts the call up to 3 times, waits 2 seconds between attempts with exponential backoff, logs each failure attempt to Service Center, and returns IsSuccess (Boolean) and FinalErrorMessage (Text) to the caller.

Frequently asked questions

How do I see the full HTTP request and response body for a failed API call in OutSystems?

Go to Service Center → Monitoring → Integrations. Filter by module name and the time range of the failure. Each log entry shows the full request URL, headers, request body, HTTP status code, response body, and duration in milliseconds. This is the fastest debugging tool for API integration issues. In ODC, open ODC Portal → Monitoring → Traces and filter for the relevant app.

Does OutSystems automatically retry failed API calls?

No. OutSystems does not have built-in retry logic for REST API calls. You must implement retry patterns manually in Server Actions using a loop with a counter and the Wait system action. This gives you full control over retry conditions, max attempts, and delay strategy — which is preferable since blind retries can worsen rate-limiting situations.

What is the difference between handling errors in OnAfterResponse vs handling them in the calling Server Action?

OnAfterResponse runs once for the entire REST API and applies to every method call — it is the right place for protocol-level error handling like HTTP status code checking. The calling Server Action exception handler is method-call-specific and handles the business-level response to that error (showing a message, logging context-specific data, deciding whether to retry). Use both layers together for complete coverage.

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.