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
Understand the OutSystems exception hierarchy for API calls
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.
Add a Communication Exception handler to every API-calling Server Action
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.
Inspect HTTP status codes in OnAfterResponse
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.
Build a configurable retry pattern for transient failures
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.
Log all integration errors to Service Center
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.
Define a consistent error response structure for exposed APIs
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
1/* Pattern 1: Server Action with Communication Exception handler2 Input: ProductId (Long Integer)3 Output: ProductData (Product_Response structure), IsSuccess (Boolean), ErrorMessage (Text)4*/56Start7 |8 v9[GetProductFromAPI] -- REST method10 ProductId = ProductId11 |12 v13[Assign]14 ProductData = GetProductFromAPI.Response15 IsSuccess = True16 ErrorMessage = ""17 |18 v19End2021[Exception Handler: Communication Exception]22 |23 v24[LogError]25 Module = "ProductsModule"26 Message = "GetProduct API call failed"27 ErrorDetail = CommException.ExceptionMessage28 |29 v30[Assign]31 IsSuccess = False32 ErrorMessage = "Unable to reach the product service. Please try again."33 ProductData = Default(Product_Response)34 |35 v36End3738[Exception Handler: ResourceNotFound User Exception]39 |40 v41[Assign]42 IsSuccess = False43 ErrorMessage = "Product not found."44 |45 v46End4748/* Pattern 2: OnAfterResponse HTTP status code handler49 Attached to consumed REST API node.50 Input: Response (HTTPResponse)51 Output: CustomizedResponse (HTTPResponse)52*/5354Start55 |56 v57[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 = Response64 |65 v66EndCommon 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.
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.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation