Attachments in ServiceNow are normally attached to the records they belong to, making it easy to identify and share those attachments over an integration. Embedded attachments require a bit more effort to integrate since they can belong to other records, or in some cases, no record at all. They are typically found inside journal fields.
The use case being looked at in this example is embedding attachments in the Additional Comments field using HTML inside [code]
tags so lengthy comments such as operational checks can be shared with third parties directly from the instance.
To recreate this scenario:
Open a record with a journal field, i.e. an Incident.
From the More options selector, choose Email. The Compose Email window will open.
Copy/paste or drag an image into the email composer.
(In Windows, you can use Windows Key + Shift + S
to take a screenshot and then Ctrl + V
to paste it into the formatter).
Click the Source code button to view the HTML source code.
Select all the source code (Ctrl + A
) and copy it (Ctrl + C
).
Back in your Incident, go to a journal field, e.g. Additional Comments and paste in the HTML.
Wrap the comment in [code]...[/code]
tags to tell ServiceNow to treat this comment as code.
If the record and field is integrated, the field will be shared over the integration, but the other system will not have the attachment so the image will not show.
Note: [code]
tags will only be rendered if the glide.ui.security.allow_codetag
system property is enabled.
We can allow the integration to share embedded attachments with some modification to the outbound mapping scripts.
In this example, we are sharing a journal field so will modify the Field Map for Journal fields. We want the attachment data to be automatically embedded as base64 instead of sending the sys_attachment reference.
Open Integration Designer and navigate to the Field Map you want to modify. Find the Source to Stage
mapping script in theOutbound
panel.
Copy the function below into your Source to Stage
mapping script. This function accepts some text and updates all<img>
tags that reference the sys_attachment
table.
The final step is to update the same Field Map to use the new embedImageAttachments()
function that you've just added.
Add the code to the bottom of the same Source to Stage
mapping script and modify as required.
This code is designed for the out of box Journal field map.
Build your integration to push the Field Map changes to all the messages. You can now test.
Embedding attachments in this way will mean the maximum string length imposed by ServiceNow of 5mb will be easy to reach. Since base64 is a 4/3 compression, you have a realistic limit of around 3mb of images which can be transferred this way in any single request.
This approach is not considered secure since it is possible for malicious Javascript to be rendered in the [code] tags.
The glide.ui.security.allow_codetag
system property is responsible for rendering [code] tags. This is typically disabled as part of Instance Security Hardening.
If the property is disabled then [code] tags will not be rendered. Both systems must have this property enabled for this solution to work.
Some alternative approaches to embedding attachments in this way are:
Create a document containing the images and upload it as an attachment on the record. If you are streaming attachments, you will be able to share a file up to 50mb in size.
Upload the images separately as attachments and reference them in your comments. If you are streaming attachments, each image could be up to 50mb in size.
Sometimes attachments details can be provided within another message which require additional API calls to fetch them. We recommend breaking this out using a Poller which will asynchronously fetch the attachments and then push them into Unifi for full tracking. This has a lot of benefits and follows Unifi best practice.
The basic flow is:
Receive a message containing a list of attachment details to fetch from the other system.
Process the message as normal and store the attachment details in the Stage just like any other field.
In the Stage to Target script, loop through the attachments and execute an On-demand asynchronous Poller for each one.
The Poller fetches the attachment and pushes it into Unifi just like any other inbound message. We use a Poller because it has it's own queue management and provides visibility for the transactions.
Unifi processes the message and moves the attachment to the target record. The attachment is also recorded against the bond and the full transaction stack is created.
This example is based on receiving a message which contains a list of attachments that need to be fetched. You may need to adjust to your specific environment and requirements.
Create a new Poll Processor (we recommend <Integration name> - Get Attachment
). It will fetch a single attachment as required and push it into your integration attachment message.
Name: <Integration name> - Get Attachment
Integration: <Integration name>
Active: true
Run parallel: true
Create a new Poller (we recommend <Integration name> - Get Attachment
) and set it use the Poll Processor we've just created. For scheduling, set it to run On Demand.
Name: <Integration name> - Get Attachment
Poll processor: <Integration name> - Get Attachment
Integration: <Integration name>
Active: true
Run: On Demand
You'll need a Message to handle the incoming attachment. You can either use an existing one you already have or create a new one. We recommend calling it AddAttachment
.
Since Unifi will automatically recognise the attachment provided by the Poll Processor, the only mapping you need to configure here is the internal and external reference mapping.
We need to tell the integration which Poller to use for processing the attachments. We do this with the Poller record sys_id. To prevent hard-coding it, we specify it in a connection variable. You'll need to make sure this variable is present on all connections you use.
Description: The sys_id of the Poller record that we execute to retrieve an attachment.
Key: get_attachment_poller_id
Value: <The Poller sys_id>
This section describes what the message that receives the attachment URLs needs to do. You may consider doing this in a new Field and Field Map, or just update the relevant Message Scripts.
When the attachments details are received, they are treated like any other field. Store them in the stage for visibility and later processing.
In the Stage to Target script (either Message Script or Field Map), loop through the attachments and execute a Poller.
Now you've configured your inbound message, Poll Processor, Poller and AddAttachment message, it's time to test!
Attachments can be extracted from payloads and saved before the payload is saved. This is highly recommended as it saves from storing the attachment itself in the payload on the HTTP Request record.
Attachment data should be extracted, saved as an attachment, and replaced with the attachment sys_id in the format <x-attachment-data sys_id="...">
. Unifi will automatically collect the attachment ids, create Bonded Attachments for each of them, and finally move the attachments to the target record.
We strongly recommend streaming attachments when possible. Attachments can be streamed into Unifi which bypasses the need for extraction, supports binary file sharing, and also allows for sizes up to 50MB (providing your instance and integration configuration supports this).
This is available for all new integrations from Unifi 3.0.
For more information on configuring inbound streaming, please see our guide.
Variable | Type | Description |
---|
| String,Stream | The raw inbound payload object to process. Attachment data should be removed and replaced with the attachment sys_id in the format "<x-attachment-data sys_id=> |
| Object | An object containing the request headers. |
| Object | A key-value pair object of URL parameters. |
| GlideRecord | The record of the |
| Object | An object containing specific properties for processing. |
| Boolean | True if the inbound payload is a stream. |
| Object | Object containing several functions that can be used for logging. |
| String | The error message to return from the script. Alternatively you can simply throw a |