CAD Guide

REST API Integrations: Outbound and Inbound

Introduction

REST integrations are the backbone of modern enterprise connectivity. ServiceNow sits at the center of most IT ecosystems, needing to both consume external APIs (outbound) and expose its own data and processes to external systems (inbound). This guide covers both directions with production-ready code examples.


Outbound REST: Calling External APIs

RESTMessageV2 — The Modern API

var sm = new sn_ws.RESTMessageV2();

// Option 1: Direct URL (for ad-hoc calls)
sm.setHttpMethod('GET');
sm.setEndpoint('https://api.example.com/v2/incidents');
sm.setRequestHeader('Authorization', 'Bearer ' + 
    gs.getProperty('myapp.api.token'));
sm.setRequestHeader('Content-Type', 'application/json');
sm.setRequestHeader('Accept', 'application/json');

// Add query parameters
sm.setQueryParameter('status', 'open');
sm.setQueryParameter('limit', '100');

var response = sm.execute();
var statusCode = response.getStatusCode();
var body = response.getBody();

if (statusCode == 200) {
    var data = JSON.parse(body);
    // Process data
} else {
    gs.error('API call failed. Status: ' + statusCode + 
             ' Body: ' + body);
}

POST with JSON Body

var sm = new sn_ws.RESTMessageV2();
sm.setHttpMethod('POST');
sm.setEndpoint('https://api.example.com/v2/tickets');
sm.setRequestHeader('Content-Type', 'application/json');
sm.setRequestHeader('Authorization', 'Bearer ' + apiToken);

var payload = {
    title: current.getValue('short_description'),
    priority: current.getValue('priority'),
    description: current.getValue('description'),
    external_id: current.getValue('number')
};

sm.setRequestBody(JSON.stringify(payload));

var response = sm.execute();
var result = JSON.parse(response.getBody());

// Store external system ID back on our record
current.setValue('correlation_id', result.id);
current.update();

REST Message Records (Best Practice)

For reusable, maintainable integrations, define REST Messages in the platform:

System Web Services > Outbound > REST Message > New
// Cleaner: Reference the REST Message record
var sm = new sn_ws.RESTMessageV2(
    'ExternalTicketSystem',  // REST Message name
    'Create Ticket'           // HTTP Method name
);
sm.setStringParameterNoEscape('short_description', 
    current.getValue('short_description'));
sm.setStringParameterNoEscape('priority', 
    current.getValue('priority'));

var response = sm.execute();

This approach separates endpoint configuration from code, making environment changes (dev → prod URLs) manageable through records rather than code changes.

Asynchronous Execution

For integrations that shouldn't block the transaction:

// In an async Business Rule or scheduled job
var sm = new sn_ws.RESTMessageV2('ExternalSystem', 'Sync Incident');
sm.setStringParameterNoEscape('sys_id', current.getUniqueValue());

// executeAsync() returns immediately; response handled by callback
sm.executeAsync();

Error Handling and Retry Logic

function callWithRetry(restMessage, maxRetries) {
    var attempts = 0;
    var response;
    while (attempts < maxRetries) {
        try {
            response = restMessage.execute();
            if (response.getStatusCode() < 500) {
                return response; // Success or 4xx (no retry)
            }
        } catch(e) {
            gs.warn('REST call attempt ' + (attempts+1) + 
                   ' failed: ' + e.message);
        }
        attempts++;
        // Exponential backoff
        gs.sleep(Math.pow(2, attempts) * 1000);
    }
    throw new Error('REST call failed after ' + maxRetries + ' attempts');
}

Inbound REST: Exposing ServiceNow APIs

The Table API (OOTB)

ServiceNow exposes all tables through the built-in Table API:

GET  /api/now/table/incident          → List incidents
GET  /api/now/table/incident/{sys_id} → Get specific incident
POST /api/now/table/incident          → Create incident
PUT  /api/now/table/incident/{sys_id} → Full update
PATCH /api/now/table/incident/{sys_id}→ Partial update
DELETE /api/now/table/incident/{sys_id}→ Delete incident

Powerful for generic integrations, but exposes all fields by default. Use query parameters to control output:

?sysparm_fields=number,short_description,state,priority
&sysparm_query=active=true^priority=1
&sysparm_limit=100
&sysparm_display_value=true

Scripted REST APIs: Custom Endpoints

For custom business logic or controlled data exposure:

System Web Services > Scripted REST APIs > New

Example: Create a custom incident intake endpoint

// Resource: POST /api/mycompany/v1/incident
(function process(request, response) {
    
    try {
        var body = JSON.parse(request.body.dataString);
        
        // Validate required fields
        if (!body.title || !body.caller_email) {
            response.setStatus(400);
            response.setBody({
                error: 'Missing required fields: title, caller_email'
            });
            return;
        }
        
        // Find caller by email
        var caller = new GlideRecord('sys_user');
        if (!caller.get('email', body.caller_email)) {
            response.setStatus(404);
            response.setBody({
                error: 'User not found: ' + body.caller_email
            });
            return;
        }
        
        // Create incident
        var inc = new GlideRecord('incident');
        inc.initialize();
        inc.setValue('short_description', body.title);
        inc.setValue('description', body.description || '');
        inc.setValue('caller_id', caller.getUniqueValue());
        inc.setValue('priority', body.priority || '3');
        inc.setValue('category', body.category || 'inquiry');
        var sysId = inc.insert();
        
        // Fetch the created record for response
        inc.get(sysId);
        
        response.setStatus(201);
        response.setBody({
            sys_id: sysId,
            number: inc.getValue('number'),
            state: inc.getDisplayValue('state'),
            link: gs.getProperty('glide.servlet.uri') + 
                  'incident.do?sys_id=' + sysId
        });
        
    } catch(e) {
        gs.error('Incident API error: ' + e.message);
        response.setStatus(500);
        response.setBody({ error: 'Internal server error' });
    }
    
})(request, response);

Authentication Options

Method Use Case
Basic Auth Simple integrations; avoid in production
OAuth 2.0 Preferred for external applications
API Key (mutual auth) Machine-to-machine integrations
SAML Enterprise SSO-based integrations

OAuth 2.0 Setup (Inbound):

System OAuth > Application Registry > New → OAuth API endpoint for external clients

External clients authenticate with client_id + client_secret to receive a token, then include Authorization: Bearer {token} in API calls.


Testing and Debugging REST Integrations

REST API Explorer

System Web Services > REST API Explorer

Test any Scripted REST API or Table API call directly from the browser with your current session's authentication.

Outbound HTTP Request Log

System Logs > Outbound HTTP Requests

Shows every outbound REST/SOAP call with full request/response detail. Invaluable for debugging failed integrations.

Integration Debugger

System Diagnostics > Session Debug > Outbound HTTP Logging

Enable detailed logging for the current session to capture integration traffic.


Best Practices

  • Always use REST Message records for reusable integrations (not hardcoded URLs)
  • Store API credentials in Credential records, not System Properties
  • Implement error handling for every HTTP call (check status codes)
  • Use async execution for non-critical integrations to avoid blocking
  • Version your Scripted REST APIs (v1, v2)
  • Validate input on inbound APIs before processing
  • Return meaningful HTTP status codes (201 for creates, 404 for not found, etc.)
  • Log integration failures with enough context to debug
  • Set reasonable connection and read timeouts on outbound calls
  • Use OAuth 2.0 for external application authentication

Conclusion

REST integrations are where ServiceNow earns its place as an enterprise platform hub. The RESTMessageV2 API gives you full control over outbound calls, while Scripted REST APIs let you expose precisely the right interface to external consumers. Invest in proper credential management, error handling, and logging from day one—integrations that fail silently are worse than integrations that fail loudly.

Keep reading this guide

Log in to access the full study guide and supercharge your preparation.