Restrict ritm creation via table API
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
The above requirement is not getting achieved with before insert BR, I need to block ritm creation via table api and only authorized channels like service catalog/approved API should be able to create.
Please help, if anyone has implemented such requirement.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hey @Mujahid Sharief
Before Insert Business Rule alone cannot reliably block RITM creation via Table API. The reason is that Table API requests are already authorized at the platform level before the Business Rule executes.
To properly enforce this requirement, you need a layered control approach.
Root Cause
The Table API allows record creation if the user has required roles/ACL access
Business Rules execute after access is granted, so they are not a secure control point
There is no built-in differentiation between:
- Service Catalog (UI/Portal)
- External API calls
Solution
1. Restrict Table API Access via ACL (Primary Control)
Create a Create ACL on sc_req_item to strictly control who can insert records.
Example:
Table: sc_req_item
Operation: create
answer = gs.hasRole('your_integration_role');
Only users with this role can create RITM via API
Remove unnecessary roles like rest_service from general users
2. Use Scripted REST API for Approved Integrations
Instead of exposing the Table API:
Create a Scripted REST API
Add validation (headers, tokens, source system)
Example:
(function process(request, response) {
var source = request.headers['x-source'];
if (source != 'approved_system') {
response.setStatus(403);
response.setBody({ error: 'Unauthorized source' });
return;
}
var ritm = new GlideRecord('sc_req_item');
ritm.initialize();
ritm.short_description = request.body.data.short_description;
var sysId = ritm.insert();
response.setBody({ result: 'success', sys_id: sysId });
})();Full control over who can create records
Ability to validate payload and enforce business logic
3. Keep Business Rule as Defensive Layer (Optional)
You can still use a Business Rule as a fallback safeguard:
if (!gs.getSession().isInteractive() && !gs.hasRole('your_integration_role')) {
gs.addErrorMessage('Unauthorized RITM creation');
current.setAbortAction(true);
} Helps catch misconfigurations
Should not be the primary control
How to Differentiate Channels
gs.getSession().isInteractive()
true - UI / Service Portal
false - API / background
Use this only as a supporting check, not a security control.
*************************************************************************************************************************************
If this response helps, please mark it as Accept as Solution and Helpful.
Doing so helps others in the community and encourages me to keep contributing.
Regards
Vaishali Singh
Servicenow Developer
Linkedin - https://www.linkedin.com/in/vaishali-singh-2273361bb
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Thank you for the solution, let me try one of these and confirm.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago - last edited 2 weeks ago
hey @Mujahid Sharief
Hope you are doing well.
Did my previous reply answer your question?
If it was helpful, please mark it as correct ✓ and close the thread . This will help other readers find the solution more easily.
Thankyou & Regards
Vaishali Singh
Servicenow Developer
Linkedin - https://www.linkedin.com/in/vaishali-singh-2273361bb
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Monday
(function executeRule(current, previous /*null when async*/) {
if (!current.subcategory.nil()) {
valCatSubcat(current.category, current.subcategory);
} else {
valCat(current.category);
}
function valCatSubcat(category, subcategory) {
var qry = gs.getMessage(
"name=incident^element=category^ORelement=subcategory^dependent_value={0}^value={1}",
[category, subcategory]
);
var ch = new GlideRecord('sys_choice');
ch.addEncodedQuery(qry);
ch.query();
var result = ch.hasNext();
if (!result) {
// Allow transition from Pending → Work in Progress even if invalid
if (!(previous.incident_state == 'On Hold' && current.incident_state == 'Work in Progress')) {
current.setAbortAction(true);
}
}
}
function valCat(category) {
var qry = gs.getMessage(
"name=incident^element=category^value={0}",
[category]
);
var ch = new GlideRecord('sys_choice');
ch.addEncodedQuery(qry);
ch.query();
var result = ch.hasNext();
if (!result) {
// Allow transition from Pending → Work in Progress even if invalid
if (!(previous.incident_state == 'On Hold' && current.incident_state == 'Work in Progress')) {
current.setAbortAction(true);
}
}
}
})(current, previous);