document-management
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
More from groeimetai/snow-flow
knowledge-management
This skill should be used when the user asks to "create knowledge article", "KB article", "knowledge base", "knowledge workflow", "article template", "publish article", or any ServiceNow Knowledge Management development.
87reporting-dashboards
This skill should be used when the user asks to "create report", "dashboard", "chart", "visualization", "analytics", "scheduled report", "export data", or any ServiceNow reporting and dashboard development.
77predictive-intelligence
This skill should be used when the user asks to "predictive intelligence", "machine learning", "ML", "classification", "similarity", "clustering", "prediction", "AI", or any ServiceNow Predictive Intelligence development.
72vendor-management
This skill should be used when the user asks to "vendor", "supplier", "contract", "procurement", "SLA", "vendor risk", "vendor performance", or any ServiceNow Vendor Management development.
68mcp-tool-discovery
This skill should be used when the user asks about "available tools", "what tools", "how to find tools", "tool search", "MCP servers", "list tools", "discover tools", "which tools", or needs guidance on discovering and using Snow-Flow MCP tools.
68email-notifications
This skill should be used when the user asks to "create notification", "email notification", "send email", "notification script", "email template", "sysevent_email_action", "notify user", "alert", or any ServiceNow email and notification development.
67