Document Management for ServiceNow
Document Management handles attachments, templates, and document generation.
Document Architecture
Record ├── Attachments (sys_attachment) │ └── Attachment Data (sys_attachment_doc) ├── Generated Documents └── Document Templates
Key Tables
Table Purpose
sys_attachment
Attachment metadata
sys_attachment_doc
Attachment content
sys_report_template
Report templates
dms_document
Document records
Attachments (ES5)
Upload Attachment
// Attach file to record (ES5 ONLY!) function attachFile(tableName, recordSysId, fileName, contentType, content) { var attachment = new GlideSysAttachment()
// Content can be base64 encoded string var attachmentSysId = attachment.write(tableName, recordSysId, fileName, contentType, content)
return attachmentSysId }
// Example var base64Content = "SGVsbG8gV29ybGQh" // Base64 encoded attachFile("incident", incidentSysId, "notes.txt", "text/plain", base64Content)
Read Attachment
// Read attachment content (ES5 ONLY!) function getAttachmentContent(attachmentSysId) { var attachment = new GlideSysAttachment() var content = attachment.getContent(attachmentSysId) return content }
// Get attachment as base64 function getAttachmentBase64(attachmentSysId) { var attachment = new GlideSysAttachment() var bytes = attachment.getBytes(attachmentSysId)
// Convert to base64 var base64 = GlideBase64.encode(bytes) return base64 }
List Attachments
// Get all attachments for record (ES5 ONLY!) function getRecordAttachments(tableName, recordSysId) { var attachments = []
var gr = new GlideRecord("sys_attachment") gr.addQuery("table_name", tableName) gr.addQuery("table_sys_id", recordSysId) gr.query()
while (gr.next()) { attachments.push({ sys_id: gr.getUniqueValue(), file_name: gr.getValue("file_name"), content_type: gr.getValue("content_type"), size_bytes: gr.getValue("size_bytes"), created_on: gr.getValue("sys_created_on"), created_by: gr.sys_created_by.getDisplayValue(), }) }
return attachments }
Copy Attachments
// Copy attachments between records (ES5 ONLY!) function copyAttachments(sourceTable, sourceSysId, targetTable, targetSysId) { var attachment = new GlideSysAttachment() attachment.copy(sourceTable, sourceSysId, targetTable, targetSysId) }
// Copy specific attachment function copyOneAttachment(attachmentSysId, targetTable, targetSysId) { var source = new GlideRecord("sys_attachment") if (!source.get(attachmentSysId)) { return null }
var attachment = new GlideSysAttachment() var content = attachment.getBytes(attachmentSysId)
return attachment.write( targetTable, targetSysId, source.getValue("file_name"), source.getValue("content_type"), content, ) }
Document Templates (ES5)
Create Document Template
// Create document template (ES5 ONLY!) var template = new GlideRecord("sys_report_template") template.initialize()
template.setValue("name", "Incident Summary Report") template.setValue("table", "incident") template.setValue("description", "Summary report for incident resolution")
// Template content with placeholders template.setValue( "template", "<html>\n" + "<head><title>Incident Summary</title></head>\n" + "<body>\n" + "<h1>Incident Summary Report</h1>\n" + "<h2>${number}</h2>\n" + "<p><strong>Description:</strong> ${short_description}</p>\n" + "<p><strong>Caller:</strong> ${caller_id.name}</p>\n" + "<p><strong>Priority:</strong> ${priority}</p>\n" + "<p><strong>State:</strong> ${state}</p>\n" + "<p><strong>Resolution:</strong> ${close_notes}</p>\n" + "<p><strong>Resolved By:</strong> ${resolved_by.name}</p>\n" + "<p><strong>Resolved On:</strong> ${resolved_at}</p>\n" + "</body>\n" + "</html>", )
template.insert()
Generate Document from Template
// Generate document from template (ES5 ONLY!) function generateDocumentFromTemplate(templateName, recordSysId) { // Get template var template = new GlideRecord("sys_report_template") if (!template.get("name", templateName)) { return null }
// Get record var tableName = template.getValue("table") var gr = new GlideRecord(tableName) if (!gr.get(recordSysId)) { return null }
// Process template var content = template.getValue("template") content = processTemplateVariables(content, gr)
return content }
function processTemplateVariables(template, gr) { // Replace ${field} and ${field.property} patterns var pattern = /${([^}]+)}/g var match
while ((match = pattern.exec(template)) !== null) { var fieldPath = match[1] var value = getFieldValue(gr, fieldPath) template = template.replace(match[0], value) }
return template }
function getFieldValue(gr, fieldPath) { var parts = fieldPath.split(".") var value = gr
for (var i = 0; i < parts.length; i++) { if (!value) return ""
if (i < parts.length - 1) {
// Reference field - get referenced record
value = value[parts[i]].getRefRecord()
} else {
// Final field
if (value.isValidField && value.isValidField(parts[i])) {
value = value[parts[i]].getDisplayValue() || value.getValue(parts[i])
} else {
value = ""
}
}
}
return value || "" }
PDF Generation (ES5)
Generate PDF
// Generate PDF from HTML (ES5 ONLY!) function generatePDF(htmlContent, fileName) { try { var pdfCreator = new sn_pdfgeneratorutils.PDFGenerationAPI() var pdfBytes = pdfCreator.convertToPDF(htmlContent)
// Return as base64
return {
success: true,
content: GlideBase64.encode(pdfBytes),
fileName: fileName || "document.pdf",
}
} catch (e) { gs.error("PDF generation failed: " + e.message) return { success: false, error: e.message, } } }
// Generate and attach PDF to record function generateAndAttachPDF(templateName, recordSysId, fileName) { // Generate HTML from template var html = generateDocumentFromTemplate(templateName, recordSysId) if (!html) { return { success: false, error: "Template generation failed" } }
// Convert to PDF var pdf = generatePDF(html, fileName) if (!pdf.success) { return pdf }
// Get table name from template var template = new GlideRecord("sys_report_template") template.get("name", templateName) var tableName = template.getValue("table")
// Attach PDF var attachmentSysId = attachFile(tableName, recordSysId, fileName, "application/pdf", pdf.content)
return { success: true, attachment_sys_id: attachmentSysId, } }
Document Workflow (ES5)
Document Approval
// Create document for approval (ES5 ONLY!) function submitDocumentForApproval(documentSysId, approvers) { var doc = new GlideRecord("dms_document") if (!doc.get(documentSysId)) { return { success: false, message: "Document not found" } }
// Update state doc.setValue("state", "pending_approval") doc.update()
// Create approval records for (var i = 0; i < approvers.length; i++) { var approval = new GlideRecord("sysapproval_approver") approval.initialize() approval.setValue("sysapproval", documentSysId) approval.setValue("approver", approvers[i]) approval.setValue("state", "requested") approval.insert() }
// Notify approvers gs.eventQueue("document.approval.requested", doc, approvers.join(","), "")
return { success: true } }
Version Control
// Create new document version (ES5 ONLY!) function createDocumentVersion(documentSysId, newContent, changeNotes) { var doc = new GlideRecord("dms_document") if (!doc.get(documentSysId)) { return null }
// Get current version var currentVersion = parseInt(doc.getValue("version"), 10) || 1 var newVersion = currentVersion + 1
// Archive current version var archive = new GlideRecord("dms_document_version") archive.initialize() archive.setValue("document", documentSysId) archive.setValue("version", currentVersion) archive.setValue("content", doc.getValue("content")) archive.setValue("created_by", gs.getUserID()) archive.insert()
// Update document with new version doc.setValue("version", newVersion) doc.setValue("content", newContent) doc.work_notes = "Version " + newVersion + ": " + changeNotes doc.update()
return { document_sys_id: documentSysId, version: newVersion, } }
// Get document version history function getDocumentVersions(documentSysId) { var versions = []
var version = new GlideRecord("dms_document_version") version.addQuery("document", documentSysId) version.orderByDesc("version") version.query()
while (version.next()) { versions.push({ version: version.getValue("version"), created_on: version.getValue("sys_created_on"), created_by: version.sys_created_by.getDisplayValue(), }) }
return versions }
Attachment Security (ES5)
Check Attachment Access
// Check if user can access attachment (ES5 ONLY!) function canAccessAttachment(attachmentSysId) { var attachment = new GlideRecord("sys_attachment") if (!attachment.get(attachmentSysId)) { return false }
// Check access to parent record var parentTable = attachment.getValue("table_name") var parentSysId = attachment.getValue("table_sys_id")
var parent = new GlideRecord(parentTable) return parent.get(parentSysId) // Returns false if no read access }
MCP Tool Integration
Available Tools
Tool Purpose
snow_query_table
Query attachments
snow_execute_script_with_output
Test document scripts
snow_find_artifact
Find templates
Example Workflow
// 1. Get record attachments await snow_query_table({ table: "sys_attachment", query: "table_name=incident^table_sys_id=inc_sys_id", fields: "file_name,content_type,size_bytes,sys_created_on", })
// 2. Generate document
await snow_execute_script_with_output({
script: var html = generateDocumentFromTemplate('Incident Summary', 'inc_sys_id'); gs.info('Generated: ' + (html ? 'success' : 'failed')); ,
})
// 3. Find document templates await snow_query_table({ table: "sys_report_template", query: "table=incident", fields: "name,description,table", })
Best Practices
-
File Types - Restrict allowed types
-
Size Limits - Set max file sizes
-
Virus Scanning - Scan uploads
-
Access Control - Inherit from parent
-
Version Control - Track changes
-
Cleanup - Remove orphaned files
-
Templates - Use for consistency
-
ES5 Only - No modern JavaScript syntax