E-Signature (Sign Pad) in ServiceNow Catalog Item
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
Hi Everyone,
I recently worked on a ServiceNow project in the healthcare domain, where I came across a challenging requirement.
The client wanted users to acknowledge their requests by signing (E-Signature) while submitting a Catalog item.
Steps:
- Design the custom widget for the signature pad.
- HTML: used <canvas> tag to capture the user’s signature
- Client Script: handled signature drawing, clearing, and converting the image to Base64 format.
widget html :
<div class="text-center">
<canvas id="signature-pad" width="400" height="200"
style="border:1px solid #ccc; border-radius:8px; cursor:crosshair; touch-action:none;"></canvas>
<div class="mt-3">
<button class="btn btn-primary" ng-click="c.clearSignature()">Clear</button>
<button class="btn btn-success" ng-click="c.attachSignature()">Attach Signature</button>
</div>
</div>widget Client script:
api.controller = function($scope) {
var c = this;
var canvas, ctx;
var drawing = false;
var lastPos = { x: 0, y: 0 };
c.$onInit = function() {
setTimeout(function() {
canvas = document.getElementById('signature-pad');
if (!canvas) return;
ctx = canvas.getContext('2d');
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
canvas.addEventListener('mousedown', startDraw);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', endDraw);
canvas.addEventListener('touchstart', startDraw);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', endDraw);
}, 200);
};
function getPosition(event) {
var rect = canvas.getBoundingClientRect();
if (event.touches && event.touches[0]) {
return {
x: event.touches[0].clientX - rect.left,
y: event.touches[0].clientY - rect.top
};
}
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
function startDraw(e) {
drawing = true;
lastPos = getPosition(e);
}
function draw(e) {
if (!drawing) return;
e.preventDefault();
var pos = getPosition(e);
ctx.beginPath();
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
lastPos = pos;
}
function endDraw() {
drawing = false;
}
c.clearSignature = function() {
if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);
drawing = false;
$scope.page.g_form.setValue('signature_data', '');
};
c.attachSignature = function() {
if (!ctx) return alert("Canvas not initialized.");
var data = canvas.toDataURL('image/png');
$scope.page.g_form.setValue('signature_data', data);
alert("Signature captured successfully. It will be attached after submission.");
};
};
2) Created a custom variable in the Catalog item and called the custom widget inside it.
3) For the same Catalog item, created a Multi-line Text variable to store the signature image in Base64 format. This variable is just for storing the signature as base64
Backend Logic:
- Created a Flow for the Service Catalog item
- Added a Custom Action inside the flow
- Passed the RITM sys_id and the Base64 signature image to the action
- Attached the generated signature image to the RITM record so it appears under Attachments and Activity
Flow custom action:
custom action script:
(function execute(inputs, outputs) {
var signatureData = inputs.signature_data;
//gs.info("tejas_test"+signatureData);
if (!signatureData)
return;
var base64Data = signatureData.replace(/^data:image\/png;base64,/, "");
// var base64Data = signatureData.replace(/^data:image\/png;base64,/, "");
var decodedBytes = GlideStringUtil.base64DecodeAsBytes(base64Data);
var gr = new GlideRecord('sc_req_item');
gr.addQuery('sys_id', inputs.record_id);
gr.query();
if (gr.next()) {
new GlideSysAttachment().write(
gr,
"signature.png",
"image/png",
decodedBytes
);
}
})(inputs, outputs);
I have attached the update set : import this update set in your instance
Catalogue item name: User Acknowledgement Request
output: