Streamlining ServiceNow Operations: A Comprehensive Guide to Copying Attachments Between Instances
- kelly.ryu
- Mar 21
- 5 min read
Updated: Mar 26

In the dynamic landscape of ServiceNow deployments, the necessity to transfer attachments between different instances frequently arises. This could be due to various reasons, such as migrating data during upgrades, synchronizing development and production environments, or integrating data between disparate ServiceNow platforms. Efficiently managing this process is crucial for maintaining data integrity and ensuring smooth operational workflows. This article delves into the primary methods available for copying attachments between ServiceNow instances, providing a detailed understanding of each approach, along with considerations for security and performance.
Understanding the ServiceNow REST Attachment API
The ServiceNow platform offers a robust REST API that enables programmatic interaction with various aspects of the system, including attachments. This API serves as a cornerstone for building integrations and automating tasks across different ServiceNow instances. Specifically, the Attachment API provides a set of endpoints and HTTP methods designed to manage file attachments associated with records. The base URL for the Attachment API is typically structured as /api/now/attachment.
Within this API, several key HTTP methods are fundamental for attachment management. The POST method is used for creating new attachments. There are two primary POST endpoints: /now/attachment/file and /now/attachment/upload. The /now/attachment/file endpoint allows for uploading a binary file by providing query parameters such as creation_time (defaults to the current time), encryption_context (specifying encryption for the attachment), the required file_name, the table_name to which the attachment will be linked, and the table_sys_id of the specific record. The actual binary content of the file is included in the request body. Alternatively, the /now/attachment/upload endpoint facilitates multipart file uploads, where parameters like Content-Type, table_name, table_sys_id, and the file content itself (as uploadFile) are included within the form body of the request.
Conversely, the GET method is used to retrieve information about existing attachments. The endpoint /now/attachment/{sys_id} allows you to fetch the metadata associated with a specific attachment, where {sys_id} represents the unique identifier of the attachment record in the sys_attachment table. To retrieve the actual content of an attachment, the endpoint /now/attachment/{sys_id}/file is used. It's important to note that when retrieving the content, the content-type header plays a crucial role in ensuring the file is handled correctly by the receiving system. The comprehensive nature of the REST Attachment API, with its dedicated methods for both metadata and content management, provides the necessary tools for a reliable cross-instance attachment copying mechanism.
Method 1: Leveraging the REST Attachment API for Cross-Instance Copying
Copying attachments between ServiceNow instances using the REST Attachment API involves a series of well-defined steps to ensure a successful transfer.
Step 1: Authenticate with both instances.
Establishing secure communication is paramount. Both the source instance (from which the attachment is being copied) and the target instance (to which the attachment is being copied) will require authentication. A common method for this is Basic Authentication, which involves providing a username and password for a user with the necessary permissions on each instance. It is crucial to ensure that all API communication occurs over HTTPS to encrypt the transmitted data, including the authentication credentials. While Basic Authentication is relatively straightforward to implement, for more secure production environments, considering OAuth 2.0 is advisable as it uses tokens instead of directly transmitting credentials. The choice of authentication method directly influences the security posture and complexity of the cross-instance interaction.
Step 2: Retrieve the attachment metadata from the source instance.
Once authenticated with the source instance, the next step is to obtain the metadata of the attachment you wish to copy. This is achieved by making a GET request to the /now/attachment/{sys_id} endpoint, replacing {sys_id} with the sys_id of the attachment in the source instance. The response to this request will contain valuable information about the attachment, such as its file_name and content_type.
Step 3: Retrieve the attachment content from the source instance.
After obtaining the metadata, the actual file content needs to be retrieved. This is done by making another GET request, this time to the /now/attachment/{sys_id}/file endpoint on the source instance. When processing the response, it is essential to handle the binary data correctly and to pay close attention to the content-type header provided in the response. Incorrectly handling this header can lead to the file being corrupted or misinterpreted in the target instance.
Step 4: Upload the attachment to the target instance.
With the metadata and content retrieved from the source instance, the final step is to upload the attachment to the desired record in the target instance. This is accomplished using a POST request to the /now/attachment/file endpoint on the target instance. The request requires several query parameters: the table_name of the target record, the table_sys_id of the specific record where the attachment should be associated, and the file_name obtained from the source metadata. The binary content retrieved in Step 3 should be included as the request body. An alternative endpoint, /now/attachment/upload, could also be used for multipart uploads if that aligns better with the specific requirements. The necessity to explicitly specify the table_name and table_sys_id during the upload process underscores the fundamental requirement of ServiceNow to associate every attachment with a specific record within a defined table.
Below is an example of a ServiceNow server-side script that demonstrates this process:
function copyAttachment(sourceAttachmentSysId, targetTableName, targetSysId) {
var targetInstanceURL = "https://TARGET_INSTANCE.service-now.com/"; // Replace with your target instance URL
var targetUserID = "TARGET_USERNAME"; // Replace with your target instance username
var targetUserPassword = "TARGET_PASSWORD"; // Replace with your target instance password
var sourceInstanceURL = "https://SOURCE_INSTANCE.service-now.com/"; // Replace with your source instance URL
var sourceUserID = "SOURCE_USERNAME"; // Replace with your source instance username
var sourceUserPassword = "SOURCE_PASSWORD"; // Replace with your source instance password
// Step 1: Retrieve attachment metadata from the source instance
var attachmentMetaRequest = new sn_ws.RESTMessageV2();
attachmentMetaRequest.setHttpMethod("get");
attachmentMetaRequest.setBasicAuth(sourceUserID, sourceUserPassword);
attachmentMetaRequest.setEndpoint(sourceInstanceURL + "api/now/attachment/" + sourceAttachmentSysId);
attachmentMetaRequest.setRequestHeader("Accept", "application/json");
var metaResponse = attachmentMetaRequest.execute();
var metaResponseBody = metaResponse.getBody();
var metaHttpStatus = metaResponse.getStatusCode();
if (metaHttpStatus == 200) {
var parser = new JSONParser();
var parsedMeta = parser.parse(metaResponseBody);
var fileName = parsedMeta.result.file_name;
var contentType = parsedMeta.result.content_type;
// Step 2: Retrieve attachment content from the source instance
var attachmentContentRequest = new sn_ws.RESTMessageV2();
attachmentContentRequest.setHttpMethod("get");
attachmentContentRequest.setBasicAuth(sourceUserID, sourceUserPassword);
attachmentContentRequest.setEndpoint(sourceInstanceURL + "api/now/attachment/" + sourceAttachmentSysId + "/file");
attachmentContentRequest.setRequestHeader("Accept", "application/octet-stream"); // Assuming binary data
var contentResponse = attachmentContentRequest.execute();
var contentResponseBody = contentResponse.getBody();
var contentHttpStatus = contentResponse.getStatusCode();
if (contentHttpStatus == 200) {
// Step 3: Upload the attachment to the target instance
var uploadRequest = new sn_ws.RESTMessageV2();
uploadRequest.setHttpMethod("post");
uploadRequest.setBasicAuth(targetUserID, targetUserPassword);
uploadRequest.setEndpoint(targetInstanceURL + "api/now/attachment/file");
uploadRequest.setQueryParameter("table_name", targetTableName);
uploadRequest.setQueryParameter("table_sys_id", targetSysId);
uploadRequest.setQueryParameter("file_name", fileName);
uploadRequest.setRequestHeader("Content-Type", contentType);
uploadRequest.setRequestBody(contentResponseBody);
var uploadResponse = uploadRequest.execute();
var uploadHttpStatus = uploadResponse.getStatusCode();
if (uploadHttpStatus == 201) {
gs.info("Attachment successfully copied to target instance.");
} else {
gs.error("Error uploading attachment to target instance. Status code: " + uploadHttpStatus + ", Body: " + uploadResponse.getBody());
}
} else {
gs.error("Error retrieving attachment content from source instance. Status code: " + contentHttpStatus + ", Body: " + contentResponse.getBody());
}
} else {
gs.error("Error retrieving attachment metadata from source instance. Status code: " + metaHttpStatus + ", Body: " + metaResponse.getBody());
}
}
// Example usage:
// copyAttachment('SOURCE_ATTACHMENT_SYS_ID', 'TARGET_TABLE_NAME', 'TARGET_SYS_ID');
// Replace 'SOURCE_ATTACHMENT_SYS_ID', 'TARGET_TABLE_NAME', and 'TARGET_SYS_ID' with actual values.