askill
csm-patterns

csm-patternsSafety 75Repository

This skill should be used when the user asks to "customer service", "CSM", "case", "account", "contact", "customer portal", "entitlement", "service contract", or any ServiceNow Customer Service Management development.

56 stars
1.2k downloads
Updated 2/23/2026

Package Files

Loading files...
SKILL.md

Customer Service Management for ServiceNow

CSM enables organizations to deliver exceptional customer service through cases, accounts, and self-service.

CSM Architecture

Account (customer_account)
    ├── Contacts (customer_contact)
    ├── Contracts (ast_contract)
    │   └── Entitlements (service_entitlement)
    ├── Assets (alm_asset)
    └── Cases (sn_customerservice_case)
        ├── Case Tasks
        └── Communications

Key Tables

TablePurpose
customer_accountCustomer accounts
customer_contactAccount contacts
sn_customerservice_caseCustomer cases
service_entitlementService entitlements
ast_contractService contracts

Customer Accounts (ES5)

Create Customer Account

// Create customer account (ES5 ONLY!)
var account = new GlideRecord('customer_account');
account.initialize();

// Basic info
account.setValue('name', 'Acme Corporation');
account.setValue('account_code', 'ACME-001');
account.setValue('industry', 'Technology');

// Contact info
account.setValue('phone', '+1-555-123-4567');
account.setValue('email', 'info@acme.com');
account.setValue('website', 'https://www.acme.com');

// Address
account.setValue('street', '123 Main Street');
account.setValue('city', 'San Francisco');
account.setValue('state', 'CA');
account.setValue('zip', '94105');
account.setValue('country', 'US');

// Account details
account.setValue('account_type', 'customer');  // customer, partner, vendor
account.setValue('tier', 'gold');  // bronze, silver, gold, platinum

// Assignment
account.setValue('account_manager', accountManagerSysId);

account.insert();

Create Contact

// Create contact for account (ES5 ONLY!)
var contact = new GlideRecord('customer_contact');
contact.initialize();

// Link to account
contact.setValue('account', accountSysId);

// Contact info
contact.setValue('name', 'John Smith');
contact.setValue('email', 'john.smith@acme.com');
contact.setValue('phone', '+1-555-123-4568');
contact.setValue('title', 'IT Manager');

// Contact type
contact.setValue('type', 'primary');  // primary, billing, technical
contact.setValue('active', true);

// Create user for portal access
var user = createUserFromContact(contact);
contact.setValue('user', user);

contact.insert();

Customer Cases (ES5)

Create Customer Case

// Create customer case (ES5 ONLY!)
var caseRecord = new GlideRecord('sn_customerservice_case');
caseRecord.initialize();

// Case info
caseRecord.setValue('short_description', 'Unable to access product features');
caseRecord.setValue('description', 'Customer reports error when trying to use premium features');

// Classification
caseRecord.setValue('category', 'product_issue');
caseRecord.setValue('subcategory', 'access_problem');
caseRecord.setValue('priority', 2);

// Customer
caseRecord.setValue('account', accountSysId);
caseRecord.setValue('contact', contactSysId);

// Product/Asset
caseRecord.setValue('product', productSysId);
caseRecord.setValue('asset', assetSysId);

// Assignment
caseRecord.setValue('assignment_group', getGroupSysId('Customer Support'));

// Channel
caseRecord.setValue('channel', 'email');  // email, phone, chat, web

caseRecord.insert();

Case Routing

// Route case based on account and product (ES5 ONLY!)
// Business Rule: before, insert, sn_customerservice_case

(function executeRule(current, previous) {
    if (current.assignment_group) {
        return;  // Already assigned
    }

    var group = determineAssignmentGroup(current);
    if (group) {
        current.assignment_group = group;
    }
})(current, previous);

function determineAssignmentGroup(caseRecord) {
    // Check for premium support entitlement
    if (hasPremiumSupport(caseRecord.getValue('account'))) {
        return getGroupSysId('Premium Support');
    }

    // Route by product
    var product = caseRecord.product.getRefRecord();
    if (product.isValidRecord()) {
        var supportGroup = product.getValue('support_group');
        if (supportGroup) {
            return supportGroup;
        }
    }

    // Default
    return getGroupSysId('General Support');
}

function hasPremiumSupport(accountSysId) {
    var entitlement = new GlideRecord('service_entitlement');
    entitlement.addQuery('account', accountSysId);
    entitlement.addQuery('type', 'premium_support');
    entitlement.addQuery('start_date', '<=', new GlideDateTime());
    entitlement.addQuery('end_date', '>=', new GlideDateTime());
    entitlement.query();
    return entitlement.hasNext();
}

Entitlements (ES5)

Create Service Entitlement

// Create entitlement (ES5 ONLY!)
var entitlement = new GlideRecord('service_entitlement');
entitlement.initialize();

entitlement.setValue('name', 'Premium Support - Acme Corp');
entitlement.setValue('account', accountSysId);
entitlement.setValue('contract', contractSysId);

// Entitlement type
entitlement.setValue('type', 'premium_support');

// Dates
entitlement.setValue('start_date', '2024-01-01');
entitlement.setValue('end_date', '2024-12-31');

// Limits
entitlement.setValue('total_cases', 100);
entitlement.setValue('used_cases', 0);
entitlement.setValue('remaining_cases', 100);

// SLA
entitlement.setValue('response_sla', '4 hours');
entitlement.setValue('resolution_sla', '24 hours');

entitlement.insert();

Check Entitlement

// Check if customer is entitled to service (ES5 ONLY!)
function checkEntitlement(accountSysId, entitlementType) {
    var now = new GlideDateTime();

    var entitlement = new GlideRecord('service_entitlement');
    entitlement.addQuery('account', accountSysId);
    entitlement.addQuery('type', entitlementType);
    entitlement.addQuery('start_date', '<=', now);
    entitlement.addQuery('end_date', '>=', now);
    entitlement.query();

    if (entitlement.next()) {
        var remaining = parseInt(entitlement.getValue('remaining_cases'), 10);

        return {
            entitled: true,
            remaining: remaining,
            unlimited: remaining < 0,  // -1 = unlimited
            expiration: entitlement.getValue('end_date'),
            sla: {
                response: entitlement.getValue('response_sla'),
                resolution: entitlement.getValue('resolution_sla')
            }
        };
    }

    return {
        entitled: false,
        message: 'No active entitlement found'
    };
}

Decrement Entitlement

// Use entitlement when case created (ES5 ONLY!)
// Business Rule: after, insert, sn_customerservice_case

(function executeRule(current, previous) {
    var accountSysId = current.getValue('account');
    if (!accountSysId) return;

    var entitlement = new GlideRecord('service_entitlement');
    entitlement.addQuery('account', accountSysId);
    entitlement.addQuery('type', 'support');
    entitlement.addQuery('start_date', '<=', new GlideDateTime());
    entitlement.addQuery('end_date', '>=', new GlideDateTime());
    entitlement.addQuery('remaining_cases', '>', 0);
    entitlement.orderBy('end_date');  // Use earliest expiring first
    entitlement.setLimit(1);
    entitlement.query();

    if (entitlement.next()) {
        var used = parseInt(entitlement.getValue('used_cases'), 10);
        var remaining = parseInt(entitlement.getValue('remaining_cases'), 10);

        entitlement.setValue('used_cases', used + 1);
        entitlement.setValue('remaining_cases', remaining - 1);
        entitlement.update();

        // Link case to entitlement
        current.u_entitlement = entitlement.getUniqueValue();
        current.update();

        // Alert if running low
        if (remaining - 1 <= 5) {
            gs.eventQueue('entitlement.low', entitlement, accountSysId, (remaining - 1).toString());
        }
    }
})(current, previous);

Customer Portal (ES5)

Portal Case Submission

// Widget Server Script for case submission (ES5 ONLY!)
(function() {
    // Handle case creation
    if (input && input.action === 'createCase') {
        var contactId = getContactForUser(gs.getUserID());
        if (!contactId) {
            data.error = 'No contact record found';
            return;
        }

        var contact = new GlideRecord('customer_contact');
        contact.get(contactId);

        // Create case
        var caseRecord = new GlideRecord('sn_customerservice_case');
        caseRecord.initialize();
        caseRecord.setValue('short_description', input.subject);
        caseRecord.setValue('description', input.description);
        caseRecord.setValue('contact', contactId);
        caseRecord.setValue('account', contact.getValue('account'));
        caseRecord.setValue('priority', input.priority || 3);
        caseRecord.setValue('channel', 'web');

        var caseSysId = caseRecord.insert();

        data.success = true;
        data.case_number = caseRecord.getValue('number');
        data.case_sys_id = caseSysId;
    }

    // Get user's cases
    if (!input || input.action === 'getCases') {
        var contactId = getContactForUser(gs.getUserID());
        data.cases = [];

        if (contactId) {
            var gr = new GlideRecord('sn_customerservice_case');
            gr.addQuery('contact', contactId);
            gr.orderByDesc('sys_created_on');
            gr.setLimit(20);
            gr.query();

            while (gr.next()) {
                data.cases.push({
                    sys_id: gr.getUniqueValue(),
                    number: gr.getValue('number'),
                    short_description: gr.getValue('short_description'),
                    state: gr.state.getDisplayValue(),
                    priority: gr.priority.getDisplayValue(),
                    opened_at: gr.getValue('opened_at')
                });
            }
        }
    }

    function getContactForUser(userId) {
        var contact = new GlideRecord('customer_contact');
        contact.addQuery('user', userId);
        contact.query();
        if (contact.next()) {
            return contact.getUniqueValue();
        }
        return null;
    }
})();

MCP Tool Integration

Available Tools

ToolPurpose
snow_query_tableQuery CSM tables
snow_find_artifactFind CSM configurations
snow_execute_script_with_outputTest CSM scripts
snow_deployDeploy CSM widgets

Example Workflow

// 1. Query customer cases
await snow_query_table({
    table: 'sn_customerservice_case',
    query: 'active=true^priority<=2',
    fields: 'number,short_description,account,contact,state'
});

// 2. Check entitlements
await snow_execute_script_with_output({
    script: `
        var result = checkEntitlement('account_sys_id', 'premium_support');
        gs.info(JSON.stringify(result));
    `
});

// 3. Find accounts with expiring contracts
await snow_query_table({
    table: 'ast_contract',
    query: 'endsBETWEENjavascript:gs.beginningOfToday()@javascript:gs.daysAgoEnd(-30)',
    fields: 'number,vendor,ends,account'
});

Best Practices

  1. Account Hierarchy - Parent/child accounts
  2. Contact Roles - Clear contact types
  3. Entitlements - Track usage limits
  4. SLA Mapping - Account tier to SLA
  5. Portal Access - Secure customer data
  6. Case Routing - Smart assignment
  7. Communication - Audit trail
  8. ES5 Only - No modern JavaScript syntax

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

65/100Analyzed 2/25/2026

This is a moderately comprehensive ServiceNow CSM patterns skill with good code examples for accounts, contacts, cases, entitlements, and portal integration. It has clear structure, architecture diagrams, and MCP tool integration. However, it abruptly ends with an incomplete sentence, has mismatched tags, and is tightly coupled to the Snow-Code tool with proprietary MCP tools. The skill provides practical, actionable code but lacks completeness around contracts and testing patterns.

75
75
65
60
75

Metadata

Licenseunknown
Version-
Updated2/23/2026
Publishergroeimetai

Tags

ci-cdgithub-actionstesting