- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-04-2023 09:39 AM
I need to write a REST API to accept an incoming PDF (Employee contract) from another system which generates it. I want to attach it to the user record in ServiceNow.
The external system can easily call a given REST endpoint with a POST request and can also send the employee number as a parameter.
I have tried (unsuccessfully) to read the binary file sent using Postman (as binary data) - I can create the attachment, give it a name and relate it to the relevant record by doing a lookup and setting the record on the attachment, but the data is corrupt or I am reading the wrong values.
I have spoken to ServiceNow Support and they tell me that this is not supported and impossible - If that's the case I am extremely surprised - but I'll have to accept it - so I throw that challenge to the community.
Note: I cannot use the out of the box attachment API because the external system doesn't (and shouldn't) know the table name and sys_id - it only knows the employee number - I want a custom endpoint that handles this gracefully and confirms back success when the record is found and the attachment created.
Here is an example of code I've tried in the REST API:
var grUser = new GlideRecord('sys_user');
grUser.get('employee_number', params.ref); //This is from the REST parameters
var attachment = new GlideSysAttachment();
//set up inputs
var fileName = 'sample';
var contentType = 'application/pdf';
var agr = attachment.write(grUser, fileName+'.pdf', contentType, request.body.dataStream);
I think the issue is with the last line - request.body.dataStream - it doesn't appear to hold the actual data.
Does anyone know of a way to do this? I really don't want to have to give an external system access to write attachments with the attachments API as it may do so in an uncontrolled way.
Note: This also has to be possible in scope as it's a scoped application.
Solved! Go to Solution.
- Labels:
-
API
-
Integrations
-
REST

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-05-2023 06:33 PM - edited 07-05-2023 08:48 PM
Hi eddy,
I just did up a quick proof of concept that accepts an employee number as a path parameter along with a JSON body that contains a file name, content type and base64 encoded string of the file contents.
The scripted REST api then uses the GlideSysAttachment API's writeBase64() method (note I believe this is only available in scoped applications) for global scope you would need to decode the base64 string first to create the attachment against the target record.
I agree with Tony's comments around base64 payloads though and also considering having your Scripted REST API or Table API return the target record details and then have the source system use the Attachment API with those details.
Here's a basic example:
(function process( /*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
try {
var responseObj = {};
var errorMessages = [];
var fileName = request.body.data.fileName;
var contentType = request.body.data.contentType;
var base64EncodedContent = request.body.data.content;
var employeeNumber = request.pathParams.employee_number;
var attachment = new GlideSysAttachment();
var employeeGr = new GlideRecord('sys_user');
if (employeeGr.get('employee_number', employeeNumber)) {
var attachmentGr = attachment.writeBase64(employeeGr, fileName, contentType, base64EncodedContent);
} else throw 'Unable to find employee with employee number of ' + employeeNumber;
responseObj.employeeName = employeeGr.getValue('name');
responseObj.employeeNumber = employeeNumber;
responseObj.employee_sys_id = employeeGr.getUniqueValue();
response.setBody(responseObj);
response.setStatus(200);
} catch (err) {
response.setStatus(500);
errorMessages.push(err);
responseObj.errorMessages = errorMessages;
response.setBody(responseObj);
}
})(request, response);
The Request Body would look something like:
And the result:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-04-2023 02:13 PM
Hi, in a scenario like this I would consider best practice to be making an initial get request using table API to sys_user table to query for a match to the 'employee number', then use the attachment API to post to sys_user utilizing the sys_id returned by the initial get query.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-05-2023 01:18 AM
OK thanks Tony. This again is requiring the real work to be done in the external system which is extremely limited. This is why we pay for ServiceNow (to do the integrations and logic for us) - but it seems they are now telling me I have to do the logic in a 3rd party tool.
I'm not saying your suggestion won't work - it will - it's just that I am then having to use other products in the middle to sort all of that out - and if I'm using something else, why don't I just use something else period.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-05-2023 02:09 PM
Hi, I have worked on a number of platform integrations and am yet to encounter one that allows attachment payloads to be posted to the target, without first knowing what/where the target is and I would think that using a get request to identify the target, then an attachment post based on details returned by the get is a universal solution.
- There are also commercial solutions where attachments cannot just be posted (even with target details) but require that you first create a container related to the target to hold the attachment, so I don't see the SNC solution as complicated or non standard.
If you consider using a base64 solution, be aware that base64 payloads can get very large vary quickly and this can result in issues with payload size.
Also, if you can share details of your source platform and the functionality available to you, the community would be in a better position to evaluate and assist.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-07-2023 03:22 AM
Thanks but I do know what the target is - as I've stated we have the employee number and I have a record in ServiceNow with that employee number. I need to be able to write an endpoint that understands that employee number and imports the file but ServiceNow insist I can only attach files by specifying table name and sys_id.
My only option is to write a middleman in another solution to do do a get request to get those details and then call the attachment api to upload - which I have done and it works, it's just messy and shouldn't be necessary.