HTTP Status Codes in ServiceNow Integrations: What Each One Means and How to Handle It

Every REST response has a status code. Knowing what each code means — and how to handle it correctly in ServiceNow scripts — is the difference between integrations that fail gracefully and ones that silently corrupt data or spam error logs. This guide covers every relevant status code with specific handling guidance for ServiceNow scripts.

2xx Success codes

  • 200 OK — request succeeded, body contains the result. Standard for GET and successful PATCH/PUT responses.
  • 201 Created — resource was created. Standard for POST. Check the Location header for the URL of the new resource. The created record is typically in the response body.
  • 204 No Content — success with no response body. Standard for DELETE. Do not try to parse the body — there is none. response.getBody() returns an empty string.

4xx Client error codes

  • 400 Bad Request — your request is malformed. Check: JSON syntax errors, missing required fields, invalid field value formats. This is a code error — fix the request, do not retry.
  • 401 Unauthorized — authentication failed. Token is missing, expired, or invalid. If using OAuth, refresh the token. If using Basic Auth, check credentials. Retry with fresh credentials.
  • 403 Forbidden — authenticated but not permitted. The user/service has no ACL access to this resource. Not fixable with retry — requires a permission change.
  • 404 Not Found — the resource does not exist. The sys_id was deleted, the URL has a typo, or you are querying the wrong instance. Do not retry.
  • 422 Unprocessable Entity — request syntax is correct but semantic validation failed. A required reference does not exist, a field value is not in the allowed list. Fix the data, do not retry.
  • 429 Too Many Requests — rate limited. Check the Retry-After header. Implement exponential backoff. Do not hammer the endpoint — that makes it worse.

5xx Server error codes

  • 500 Internal Server Error — the remote server crashed processing your request. Usually a bug on the remote end. Log it, retry with backoff after a delay.
  • 503 Service Unavailable — service is down or overloaded. Retry with exponential backoff. Alert your team if it persists.
  • 504 Gateway Timeout — the request took too long. May indicate a server performance issue or your request is too complex. Retry with backoff.

Handling codes in ServiceNow scripts

try {
    var response = rm.execute();
    var status = response.getStatusCode();
    var body = response.getBody();

    if (status >= 200 && status < 300) {
        // Success — process response
        return JSON.parse(body);

    } else if (status == 401) {
        // Re-authenticate
        gs.warn('Token expired, refreshing', 'MyIntegration');
        refreshOAuthToken();
        return null; // Caller should retry

    } else if (status == 429) {
        // Rate limited — queue for retry
        gs.warn('Rate limited — will retry', 'MyIntegration');
        gs.eventQueue('my.integration.retry', current, '', '');
        return null;

    } else if (status >= 500) {
        // Server error — log and retry logic
        gs.error('External server error: ' + status + ' | ' + body, 'MyIntegration');
        return null; // Implement retry elsewhere

    } else {
        // 4xx client error — log, do NOT retry
        gs.error('Request error: ' + status + ' | ' + body.substring(0, 200), 'MyIntegration');
        return null;
    }

} catch (e) {
    // Network failure — connection refused, timeout
    gs.error('REST call failed: ' + e.getMessage(), 'MyIntegration');
    return null;
}

Related: RESTMessageV2 guide · OAuth 2.0 · Flow Designer error handling · IntegrationHub spokes

Handling status codes in RESTMessageV2

Every RESTMessageV2 response has a status code. Your integration logic must handle the full range — not just 200. The minimal correct pattern:

var rm = new sn_ws.RESTMessageV2('MyAPIMessage', 'GET Record');
rm.setStringParameterNoEscape('sys_id', recordSysId);
try {
    var response = rm.execute();
    var status = response.getStatusCode();
    var body = response.getBody();
    
    if (status == 200) {
        var data = JSON.parse(body);
        processSuccess(data);
    } else if (status == 404) {
        gs.warn('Record not found in external system: ' + recordSysId);
        // don't throw — log and continue
    } else if (status == 429) {
        // Rate limited — queue for retry
        scheduleRetry(recordSysId, 'Rate limited by API');
    } else if (status >= 500) {
        // Server error — log as error, queue for retry
        gs.error('API server error ' + status + ' for record ' + recordSysId);
        scheduleRetry(recordSysId, 'API server error ' + status);
    } else {
        gs.error('Unexpected status ' + status + ': ' + body.substring(0, 200));
    }
} catch(e) {
    // Network failure, timeout, DNS failure
    gs.error('Integration network error: ' + e.getMessage());
    scheduleRetry(recordSysId, 'Network error: ' + e.getMessage());
}

The retry queue pattern

For production integrations, transient failures (429, 503, timeouts) should not result in lost data — they should result in a retry. Build a lightweight queue table: custom fields for the record sys_id, the operation, the retry count, the next retry time, and the last error message. A Scheduled Job runs every few minutes, queries the queue for records past their retry time with retry count under the maximum, and attempts the operation again. Each failure increments retry count and sets next retry time with exponential backoff (1 min, 5 min, 15 min, 60 min). This pattern is the difference between an integration that loses records under load and one that is reliably eventually-consistent.

Related: RESTMessageV2 · OAuth 2.0 · Flow Designer error handling · IntegrationHub spokes · Scheduled Jobs

Status codes that require special handling in ServiceNow integrations

Beyond the basics, several status codes require specific handling patterns in production ServiceNow integrations:

401 Unauthorized — Authentication failed or token expired. For OAuth integrations, attempt a token refresh first before treating as a hard failure. If the refresh also returns 401, the credentials are invalid and the integration needs human intervention.

403 Forbidden — The credentials are valid but the user/application does not have permission for this operation. This is a configuration issue on the external system side — no amount of retry will fix it. Log as an error requiring investigation, do not retry automatically.

404 Not Found — The specific record does not exist in the external system. This may be expected (the record was deleted externally) or unexpected (a sync issue). Handle based on your integration's logic — for a one-way sync pushing data to the external system, a 404 may mean you need to create the record rather than update it.

409 Conflict — A concurrent modification conflict. Common in optimistic locking scenarios. Retry with the latest version of the record after a brief pause.

422 Unprocessable Entity — The request was well-formed but contained semantic errors (a required field was missing, a value failed validation). Log the response body — it usually contains specific validation error details that tell you exactly what was wrong with the payload.

503 Service Unavailable — The external system is down or overloaded. Always retry with backoff. Add the retry time in your response logging so you can measure how long external systems are unavailable when troubleshooting integration SLAs.

Logging status codes for operational visibility

Every integration in production should log outbound call outcomes in a structured way that enables monitoring and SLA tracking. At minimum, log: the target endpoint, the HTTP method, the status code received, the response time in milliseconds, and the outcome (success, retry, failure). Write these to a custom integration log table rather than just the System Log — the System Log fills quickly in high-volume environments and lacks the queryable structure needed for integration health dashboards. A custom table lets you query "what percentage of calls to the Salesforce API succeeded in the last 24 hours?" or "how many 429s did we receive from the HR system today?" — operational questions that the System Log cannot answer efficiently.

Implementing Status Code Handling in RESTMessageV2

When using RESTMessageV2 for outbound calls, the response object provides getStatusCode() to retrieve the HTTP status. A production integration script always checks this value before processing the response body. The pattern is: execute the REST message, check the status code, branch on success/failure, and implement retry logic for transient failures (5xx and 429). For 4xx responses from external APIs, log the full response body alongside the status code — 4xx errors from well-designed APIs include error details in the body that identify the specific problem (invalid parameter, missing required field, authentication failure). Logging only the status code and discarding the body is a common cause of difficult-to-diagnose integration failures. The debugging guide covers how to surface these logs efficiently.

5xx Retry Strategy

Server errors (5xx responses) from external APIs are generally transient — the target system may be restarting, temporarily overloaded, or experiencing a brief outage. A robust outbound integration implements exponential backoff retry for 5xx responses: wait 1 second before the first retry, 2 seconds before the second, 4 seconds before the third, then give up and create an incident or alert for human review. ServiceNow's Flow Designer error handling supports retry configuration at the flow level for IntegrationHub calls. For RESTMessageV2 calls in scripts, implement retry logic manually with a loop counter and gs.sleep(milliseconds). Never retry 4xx responses — they represent logical errors that will produce the same failure on retry and only add unnecessary traffic to the target system.

Custom Error Codes Within Status Classes

The HTTP status code tells the calling system the category of result; the response body provides the details. Well-designed integration APIs use application-specific error codes in the response body to communicate precise failure reasons within a status class. A 400 response with body {"error_code": "MISSING_REQUIRED_FIELD", "field": "priority"} is far more actionable than a 400 with body "Bad request". Define your application error codes as constants in a Script Include so they are reused consistently across all your Scripted REST API endpoints — and pair well with the Table API reference, enabling calling systems to handle specific error conditions programmatically rather than parsing human-readable error messages that may change between releases.

Logging Status Codes for Integration Health Monitoring

Capturing HTTP status codes from outbound integration calls in a custom log table provides the data foundation for integration health dashboards. A simple log record capturing: timestamp, target system, endpoint, status code, response time, and error message (if applicable) enables several valuable monitoring capabilities. A Performance Analytics indicator tracking 4xx error rate by target system surfaces degrading integration health before it becomes a user-reported incident. A scheduled report showing integrations with no successful calls in the last 24 hours identifies silently broken integrations. The logging overhead is minimal — a GlideRecord insert per integration call is trivial compared to the diagnostic value it provides. Store log records with a 90-day retention policy to balance historical analysis capability against table growth.

The complete integrations reference

The NowSpectrum Integrations Guide covers REST, error handling, OAuth 2.0, MID Server, and 50 interview Q&As in 26 pages.

Get the Integrations Guide →
← Back to all posts