Find your people. Pick a challenge. Ship something real. The CreatorCon Hackathon is coming to the Community Pavilion for one epic night. Every skill level, every role welcome. Join us on May 5th and learn more here.

Alka_Chaudhary
Mega Sage

Overview

In many business processes, approvals or acknowledgements require a user’s signature. While ServiceNow provides attachment capabilities, there is no out-of-the-box e-signature or sign pad feature for Service Catalog items.

In this article, I’ll walk through a practical approach to:

  • Capture a user’s signature using a sign pad (canvas) inside a catalog item

  • Store the signature as Base64 data

  • Convert it into an image attachment on the RITM after submission

This solution works entirely within ServiceNow using Catalog Variables, a Custom Widget, and Flow Designer.


Requirement

  • A catalog item should allow users to draw their signature

  • The captured signature should be attached to the RITM as an image

  • The signature should behave like an e-sign, captured at request time


High-Level Solution Design

  1. Create a Catalog Item

  2. Add variables:

    • A Custom Widget variable (Sign Pad)

    • A hidden variable to store Base64 signature data

1.png

 

  1. Build a Sign Pad Widget using HTML canvas

  2. Store the signature as Base64 in a hidden variable

  3. Use Flow Designer to:

    • Read the Base64 value

    • Convert it into bytes

    • Attach it to the RITM as a PNG image


Step 1: Catalog Item Variables

Create the following variables in your catalog item:

1. Custom Widget Variable

  • Type: Custom

  • Purpose: Render the Sign Pad UI

  • Widget: Custom Sign Pad Widget (explained below)

2.png

2. Signature Data Variable

  • Type: Multi-line Text

  • Name: signature_data

  • Visibility: Hidden

  • Purpose: Stores Base64 encoded signature image

This hidden variable acts as a temporary data store between the UI and backend processing.


Step 2: Sign Pad Widget – UI (HTML)

The widget uses an HTML <canvas> element where users can draw their signature.

<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>

UI Behavior

  • Users draw directly on the canvas

  • Clear button resets the canvas

  • Attach Signature converts the drawing into Base64


Step 3: Widget Client Script (Controller Logic)

This script handles:

  • Mouse and touch events

  • Drawing logic

  • Converting the canvas to Base64

  • Storing the Base64 value in the hidden catalog variable

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.");
    };
};

 

Key Point

The signature is stored as Base64 PNG data in the hidden variable signature_data.


Step 4: Flow Designer – Attachment Flow

After submission, the attachment is handled using Flow Designer.

Flow Overview

  • Trigger: RITM Created

  • Input: Catalog Variables


Step 4.1: Get Catalog Variable Data

  • Retrieve the value of signature_data

  • Retrieve the RITM Sys ID


Step 4.2: Custom Action – Attach Signature

Create a Custom Flow Action with inputs:

  • signature_data (String)

  • record_id (RITM Sys ID)

Action Script

(function execute(inputs, outputs) {

    var signatureData = inputs.signature_data;
    if (!signatureData)
        return;

    var base64Data = signatureData.replace(/^data&colon;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);

Final Result

  • User draws a signature in the catalog item

  • Signature is captured as Base64

  • On submission, Flow Designer:

    • Decodes the Base64 data

    • Creates a PNG file

    • Attaches it directly to the RITM

catalog item 1.pngRITM with attachment.png

This provides a clean and reusable e-signature experience without external plugins.


Key Benefits

  • No third-party integrations required

  • Works in Service Portal

  • Secure and auditable (stored as attachment)

  • Fully configurable using Flow Designer


Conclusion

This approach enables a lightweight yet powerful e-signature capability in ServiceNow Service Catalog. It can be extended further for:

  • Mandatory signature validation

  • Approval-based signing

  • Multiple signers

  • Timestamp and user stamping

If you’re looking to add real-world digital signing use cases to your catalog workflows, this pattern is a solid foundation.

Comments
lauri457
Tera Sage

This is cool, you could also use the E-signature scoped app for a more full-fledged feature. The signature pad widget is easy to get working as a variable as well if you don't want the tasks.

lauri457_0-1766016450606.png

 

SUBHAM_SHAW_SN
Tera Guru

This is so clean

Ankith Sharma
Tera Guru

Nice use of a custom widget to convert canvas data into a server-side attachment. Simple and effective.

Mani33
Tera Contributor

Hi Thanks for sharing.  Is this will work in mobile app as well ?

Ashwin Perumal1
Tera Contributor

I tried the same but when my esign is added as an attachment to RITM and when I download it shows 'We dont support this format". Even in RITM i am not able to see my esign. It just shows signature.png. Did anyone face this issue and can anyone help me on this.

Version history
Last update:
‎12-17-2025 12:52 AM
Updated by:
Contributors