The Table API covers most data access scenarios, but sometimes you need a custom endpoint — one that runs business logic, aggregates data from multiple tables, or exposes an operation that doesn't map cleanly to CRUD. Scripted REST APIs are how you build those endpoints on the ServiceNow platform.
When to Build a Scripted REST API
Build a Scripted REST API when:
- You need to execute business logic as part of the API call (not just data retrieval)
- You need to aggregate data from multiple tables into a single response
- You're exposing ServiceNow functionality to an external system that expects a specific API contract
- You need custom authentication or rate limiting beyond what the Table API provides
Creating a Scripted REST API
Navigate to System Web Services > Scripted REST APIs > New
Key settings:
- API ID — used in the URL path:
/api/<scope>/<api_id> - API Namespace — your application scope prefix
- Requires Authentication — almost always true for production APIs
Resource Design
A Scripted REST API is made up of Resources — individual endpoints with their own path and HTTP method handling.
// API: /api/nowspec/incident_metrics
// Resources:
// GET /summary → overall metrics
// GET /by_group/{id} → metrics for specific group
// POST /refresh → trigger metrics recalculation
Use path parameters for resource identifiers and query parameters for filters:
// Path parameter: /api/nowspec/incidents/{sys_id}
var sys_id = request.pathParams.sys_id;
// Query parameter: /api/nowspec/incidents?priority=1&limit=50
var priority = request.queryParams.priority;
var limit = parseInt(request.queryParams.limit) || 25;
Request and Response Handling
(function process(request, response) {
// Read request body (for POST/PUT)
var body = JSON.parse(request.body.dataString);
// Set response status
response.setStatus(200); // or 201, 400, 404, 500
// Set content type
response.setContentType('application/json');
// Build response object
var result = {
status: 'success',
data: {},
meta: {
timestamp: new Date().toISOString(),
version: '1.0'
}
};
// Execute business logic
var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^priority=1');
gr.query();
var incidents = [];
while (gr.next()) {
incidents.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue('number'),
short_description: gr.getValue('short_description'),
assigned_to: gr.getDisplayValue('assigned_to')
});
}
result.data = incidents;
result.meta.count = incidents.length;
response.setBody(JSON.stringify(result));
})(request, response);
Error Handling Pattern
(function process(request, response) {
try {
var sys_id = request.pathParams.sys_id;
if (!sys_id) {
response.setStatus(400);
response.setBody(JSON.stringify({
error: 'sys_id is required',
code: 'MISSING_PARAMETER'
}));
return;
}
var gr = new GlideRecord('incident');
if (!gr.get(sys_id)) {
response.setStatus(404);
response.setBody(JSON.stringify({
error: 'Incident not found',
code: 'RECORD_NOT_FOUND'
}));
return;
}
// Success response
response.setStatus(200);
response.setBody(JSON.stringify({
sys_id: gr.getUniqueValue(),
number: gr.getValue('number')
}));
} catch(e) {
gs.error('Scripted REST API error: ' + e.message);
response.setStatus(500);
response.setBody(JSON.stringify({
error: 'Internal server error',
code: 'SERVER_ERROR'
}));
}
})(request, response);
Versioning
ServiceNow Scripted REST APIs support versioning through the API definition. When you need to make a breaking change:
- Create a new version of the API (v2)
- Build the new version alongside the existing one
- Communicate a deprecation timeline to API consumers
- Remove v1 only after all consumers have migrated
The URL includes the version: /api/nowspec/incidents/v2/summary
Security Considerations
- Always require authentication — never expose a Scripted REST API without it
- Validate all input parameters before using them in queries
- Never concatenate user input directly into encoded queries — use addQuery() instead
- Limit what data is returned based on the calling user's roles, not just authentication
- Log API calls including the caller's user ID for audit purposes