QR code decoder
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
I have a requirement that when an inbound email is received, if a QR code is associated with the received email , it should be decoded and the extracted data should be stored in a custom ServiceNow table.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Solution - There are three main pieces to wire together:
1. Custom Table to store decoded QR data 2. Inbound Email Action to intercept emails with image attachments 3. QR Decode Logic — this is the tricky part, since ServiceNow has no native QR decoder
For the decoding, you have two practical paths:
Option A — Outbound REST to an external decoder API (most reliable, works in scoped apps) Option B — Java Packages.* with ZXing (global scope only, and ZXing isn't bundled by default, so you'd need a MID Server or custom JAR — generally not recommended)
I'd recommend Option A. Here's the full implementation:
Step 1 — Custom Table
Create a table, e.g. u_qr_code_data, with fields like:
| Field | Type | Purpose |
|---|---|---|
u_source_email |
String | Sender email address |
u_email_subject |
String | Subject of the inbound email |
u_decoded_data |
String (4000) | The extracted QR content |
u_attachment_name |
String | Original image filename |
u_processed_on |
Date/Time | When the QR was decoded |
u_sys_email |
Reference → sys_email |
Link back to the email record |
Step 2 — Outbound REST Message
Create a REST Message (e.g., QR_Decode_API) pointing to your chosen external decoder. A common free option is something like api.qrserver.com or a custom Azure Function / AWS Lambda you control.
Example using api.qrserver.com:
- Endpoint:
https://api.qrserver.com/v1/read-qr-code/ - Method: POST (multipart/form-data with the image file)
Or if you have your own endpoint, configure accordingly. Set up an HTTP Method called decode on the REST Message.
Step 3 — Script Include (QR Decoder Helper)
var QRCodeDecoder = Class.create();
QRCodeDecoder.prototype = {
initialize: function() {},
/**
* Decodes a QR code from an attachment sys_id
* @param {string} attachmentSysId - sys_id of the image attachment
* @returns {string|null} decoded text or null on failure
*/
decode: function(attachmentSysId) {
var gsa = new GlideSysAttachment();
var bytes = gsa.getBytes(attachmentSysId, '');
// Base64-encode the image bytes for the API call
var base64Image = GlideStringUtil.base64Encode(bytes);
// Use REST Message or direct GlideHTTPRequest
var request = new sn_ws.RESTMessageV2('QR_Decode_API', 'decode');
// If your API accepts base64 in the body:
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestBody(JSON.stringify({
image: base64Image
}));
var response = request.execute();
var httpStatus = response.getStatusCode();
var body = response.getBody();
if (httpStatus == 200) {
var result = JSON.parse(body);
// Structure depends on your API — adapt accordingly
// For api.qrserver.com, it returns: [{"symbol":[{"data":"...","error":null}]}]
if (result && result[0] && result[0].symbol && result[0].symbol[0].data) {
return result[0].symbol[0].data;
}
}
gs.error('QRCodeDecoder: Failed to decode. HTTP ' + httpStatus + ' | Body: ' + body);
return null;
},
type: 'QRCodeDecoder'
};
Note: If your API accepts
multipart/form-datainstead of base64 JSON, you'll need to adjust the request construction. Some APIs also accept a direct image URL — in that case you could generate a temporary URL to the attachment instead.
Step 4 — Inbound Email Action
Create an Inbound Email Action:
- Name:
QR Code Processor - Target table: Can be
sys_emailor any relevant table - Condition / When: Set a filter if needed (e.g., specific sender, subject pattern), or leave open to process all
Script:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event,
/*EmailWrapper*/ email, /*ScopedEmailLogger*/ logger) {
var emailSysId = current.sys_id + '';
var sender = email.from + '';
var subject = email.subject + '';
// Query attachments on this email record (images only)
var attGR = new GlideRecord('sys_attachment');
attGR.addQuery('table_name', 'sys_email');
attGR.addQuery('table_sys_id', emailSysId);
attGR.addQuery('content_type', 'STARTSWITH', 'image/');
attGR.query();
var decoder = new QRCodeDecoder();
while (attGR.next()) {
var attachmentSysId = attGR.sys_id + '';
var fileName = attGR.file_name + '';
gs.info('QR Processor: Attempting decode on attachment: ' + fileName);
var decodedData = decoder.decode(attachmentSysId);
if (decodedData) {
// Insert into custom table
var qrRecord = new GlideRecord('u_qr_code_data');
qrRecord.initialize();
qrRecord.u_source_email = sender;
qrRecord.u_email_subject = subject;
qrRecord.u_decoded_data = decodedData;
qrRecord.u_attachment_name = fileName;
qrRecord.u_processed_on = new GlideDateTime();
qrRecord.u_sys_email = emailSysId;
qrRecord.insert();
gs.info('QR Processor: Decoded and stored. Data: ' + decodedData);
} else {
gs.warn('QR Processor: No QR data found in attachment: ' + fileName);
}
}
})(current, event, email, logger);
Key Considerations
Security: If the decoded QR data could contain URLs or scripts, sanitize it before storing. Consider adding an u_data_type field (URL, text, vCard, etc.) and validating accordingly.
Performance: Outbound REST calls from an Inbound Email Action are synchronous. If you expect high volume, consider having the email action just flag the record and use a Scheduled Job or Flow Designer async flow to process the queue — this avoids blocking the email processing pipeline.
Scoped App: If this is in a scoped app, make sure the REST Message is in the same scope and that you have the sn_ws.RESTMessageV2 API available. The GlideSysAttachment.getBytes() approach works in scoped apps.
Error Handling: You may also want a u_status field (Pending / Success / Failed) and an u_error_message field on your custom table for traceability.
