Data Policy
Guide for creating ServiceNow Data Policies (sys_data_policy2) using the Fluent API. Data policies enforce field mandatory and read-only rules server-side across all interfaces — forms, imports, web services, and APIs — and cannot be bypassed, unlike client-side UI Policies.
When to Use
Use Data Policy when:
- User explicitly requests "data policy" or "data policies"
- Need ONLY mandatory or read-only enforcement across ALL interfaces (forms, imports, APIs, web services)
- Need server-side enforcement that cannot be bypassed
- Protecting critical fields from modification after certain conditions (e.g., closed records)
- Ensuring data integrity from external sources (imports, API calls, integrations)
- A field must be populated / required / non-empty when a condition is true — even if the word "validate" appears in the requirement
Natural language signals — Data Policy vs UI Policy vs Business Rule
The following phrases indicate mandatory/read-only enforcement. Default to Data Policy for server-side security unless the prompt explicitly mentions client-side context:
| Prompt says… | Means… | Tool |
|---|---|---|
| "must hold a value", "must carry a value", "must have a value", "is required when", "required if" | Field mandatory under a condition | DataPolicy mandatory: true |
| "locked", "read-only when", "cannot be edited when", "fields must be locked", "prevent modification" | Field read-only under a condition | DataPolicy readOnly: true |
| "cannot be modified once", "immutable after", "freeze fields after", "all fields locked" | Read-only after a state/condition | DataPolicy readOnly: true (+ Business Rule for transition guard if needed) |
| "locked against edits", "prevent changes if [field] was [value]" | Needs comparison to previous value | BusinessRule before — Data Policy cannot access previous |
Prefer table skill for unconditional enforcement: For fields that are always mandatory or always read-only, set mandatory: true or read_only: true directly on the column definition (see the table-guide topic). Use Data Policy when enforcement is conditional, OR when you need guaranteed cross-API/import enforcement as an additional security layer.
Instructions
- STOP if prompt mentions visibility or messages: Check if the prompt mentions ONLY visibility, messages, or value-setting — use UI Policy instead. If both mandatory/read-only AND visibility/value needs exist, create a Data Policy for the mandatory/read-only part and a UI Policy for the rest.
- Verify Data Policy is the right tool: Confirm the requirement is ONLY for mandatory or read-only enforcement. If the user needs visibility control, field values, scripts, or complex logic, use a different skill (see "When to Use" above).
- Use the
DataPolicyAPI from@servicenow/sdk/core. Every Data Policy must have$id,table, andshortDescription. Every individual rule insiderulesmust also have a$id. $idnaming convention: Use descriptive snake_case names inNow.ID[]— e.g.,Now.ID['scope_policy_purpose']. Good:Now.ID['hr_employee_mandatory_fields']. Bad:Now.ID['policy1'].- Default properties: See "Default Property Values" section below. Only specify properties when overriding defaults.
- Never set both
mandatory: trueandreadOnly: trueon the same field: These are mutually exclusive — a read-only field cannot be mandatory. This causes a build error. - Conditions use encoded query syntax — e.g.,
"priority=1^state!=6". Empty conditions apply to all records. Use^for AND,^ORfor OR. - Always use stored values in conditions: Choice fields use stored values, not display labels. Right-click field → Show Choice List → use the "Value" column.
- Omit
'ignore'rule properties for cleaner code: Since'ignore'is the default, only specifymandatoryorreadOnlywhen you need to change the field's state. - Use dot-walk notation for cross-table rules: To enforce rules on fields of a referenced table, use
'reference_field.target_field'as the rule key (e.g.,'caller_id.email'). Multiple levels are supported (e.g.,'employee.manager.department'). - Do not make fields mandatory for out-of-scope tables: The mandatory flag is silently disabled for tables outside the current scope. Read-only rules are not affected.
- Competing policies — most restrictive wins: When multiple policies target the same field on the same record, ServiceNow applies the most restrictive result. There is no evaluation order. To release a constraint, tighten the condition on the restrictive policy itself rather than relying on a second policy with
mandatory: false.
Key Concepts
Data Policy vs UI Policy
| Aspect | Data Policy | UI Policy |
|---|---|---|
| Execution | Server-side | Client-side (browser) |
| Can be bypassed via API? | No | Yes |
| Applies to imports | Yes (default) | No |
| Applies to REST/SOAP | Yes (default) | No |
| Visibility control | No | Yes |
| Field value actions | No | Yes |
| Scripting support | No | Yes |
| Field messages | No | Yes |
Use Data Policies for data integrity and security. Use UI Policies for UX enhancements.
Use UI Policy instead when prompt includes:
- "on the form only", "in the UI only", "to the user", "for the user"
- Visibility requirements alongside mandatory/read-only ("show section and make field required")
- Messages or warnings ("make mandatory and display a message")
- Client-side only context ("prevent users from editing on the form")
Use both Data Policy + UI Policy when:
- Prompt requires server-side enforcement (API/imports) AND client-side UX (messages/visibility)
- Example: "Make vendor mandatory for high-value contracts and show a warning message" → Data Policy (mandatory) + UI Policy (message)
For detailed UI Policy usage, see the ui-policy-guide topic.
Cross-policy conflict: If a UI Policy makes a field optional but a Data Policy makes it mandatory, the field is mandatory on save. Server-side always takes precedence.
Data Policy vs Business Rule
| Scenario | Data Policy | Business Rule |
|---|---|---|
| Field must be mandatory/read-only under a condition | ✅ | — |
| Field must be mandatory across all interfaces (imports, APIs) | ✅ | — |
| Need a custom error message | — | ✅ |
| Condition requires scripting or cross-field logic | — | ✅ |
| Auto-populate or calculate a field value | — | ✅ |
| Cascade updates to related records | — | ✅ |
Enforce on direct GlideRecord with setWorkflow(false) | — | ✅ |
| Enforcement on out-of-scope table fields (mandatory) | — | ✅ |
Use Business Rule instead when:
- Need to compare field's
previousvalue (e.g., "prevent changes if field was X") - Need custom error messages or validation logic
- Need to auto-populate or calculate field values (e.g., "auto-populate field from another field")
- Condition requires scripting or complex cross-field logic
- Need transition guards (e.g., additional validation when state changes)
Decision rule: If the requirement can be expressed as "when [condition] is true, field X must have a value / must be read-only" without mentioning forms, UI, or messages — that is always a Data Policy, not a Business Rule or UI Policy. Only reach for a Business Rule when the requirement needs previous, scripting, custom messaging, or value manipulation that Data Policy cannot express.
For detailed Business Rule usage, see the business-rule-guide topic.
Default Property Values
These properties default to true and should not be written in code unless overriding:
| Property | Default | Override to false when... |
|---|---|---|
active | true | Deliberately disabling the policy |
applyToImportSets | true | Policy should not apply to imports |
applyToSOAP | true | Policy should not apply to REST/SOAP |
useAsUiPolicyOnClient | true | Only server-side enforcement is wanted |
reverseIfFalse | true | Rules should remain even when conditions are false |
These default to false and should not be written in code unless overriding:
| Property | Default | Override to true when... |
|---|---|---|
inherit | false | Policy should apply to child tables in hierarchy |
When Policies Apply
- No conditions (omitted): Policy applies to ALL records on the table
- With conditions: Policy applies only when conditions evaluate to true
reverseIfFalse: true(default): Rules invert when conditions are false (mandatory → optional, read-only → editable)reverseIfFalse: false: No action when conditions are false — rules only apply when conditions match- Competing policies: When multiple policies match the same record and target the same field,
mandatory: truefrom any matching policy overridesmandatory: falsefrom another. To release a mandatory constraint for a specific case, tighten the condition on the mandatory policy itself.
Table Inheritance
inherit: false(default): Policy applies only to the specified tableinherit: true: Policy applies to all tables that extend the specified table (e.g.,task→incident,problem,change)
Use inheritance when child tables should follow the same rules as their parent without defining the policy separately on each.
Condition Syntax
AND operator (^) -- all conditions must be true:
conditions: "priority=1^state=2"
OR operator (^OR) -- at least one must be true:
conditions: "priority=1^ORpriority=2"
Common operators: =, !=, >, >=, <, <=, IN, LIKE, NOT LIKE, STARTSWITH, ANYTHING, EMPTYSTRING, SAMEAS, NSAMEAS, BETWEEN
Choice field patterns:
| Pattern | Condition Example |
|---|---|
| Specific value | category=hardware |
| Multiple values (OR) | categoryINhardware,software |
| Not a value | category!=hardware |
| Any value selected | category!=NULL |
| No value selected | category=NULL |
Rules Configuration
Basic rule syntax for field enforcement:
rules: {
field_name: {
$id: Now.ID['policy_name_field_name_rule'],
mandatory?: boolean | 'ignore', // Default: 'ignore'
readOnly?: boolean | 'ignore' // Default: 'ignore'
}
}
- $id (required): Unique identifier for the rule record
- mandatory (optional):
true= required,false= optional,'ignore'= no change - readOnly (optional):
true= locked,false= editable,'ignore'= no change
Important:
- Each field can have only one rule per policy
- Cannot set both
mandatory: trueandreadOnly: trueon the same field - Omit properties set to
'ignore'for cleaner code
Cross-Table Enforcement
Use dot-walk notation in rule keys to enforce rules on fields in referenced tables:
rules: {
'caller_id.email': { $id: Now.ID['policy_caller_email_rule'], mandatory: true },
'assigned_to.department': { $id: Now.ID['policy_assignee_dept_rule'], readOnly: true }
}
Technical note: While a table property exists in DataPolicyRuleConfig for specifying which table a rule applies to, dot-walk notation is the recommended approach for cross-table enforcement. The table property is primarily used by the plugin when transforming from ServiceNow XML and for internal scope validation. For new code, always use dot-walk notation in rule keys (e.g., 'employee.department') — it is clearer and more maintainable. Never use the table property inside rules for cross-table enforcement.
API Reference
See the datapolicy-api topic for the full property reference.
Examples
Conditional Mandatory -- Require Assignment for High Priority Records
Make fields mandatory for high-priority records. reverseIfFalse defaults to true — fields become optional automatically when conditions are not met.
import { DataPolicy } from '@servicenow/sdk/core'
export const highPriorityPolicy = DataPolicy({
$id: Now.ID['high_priority_incident_policy'],
table: 'incident',
shortDescription: 'Require assignment for high priority incidents',
conditions: 'priority=1^ORpriority=2',
rules: {
assigned_to: {
$id: Now.ID['high_priority_incident_policy_assigned_to_rule'],
mandatory: true,
},
assignment_group: {
$id: Now.ID['high_priority_incident_policy_assignment_group_rule'],
mandatory: true,
},
},
})
Conditional Read-Only -- Lock Fields After Record Closure
Lock resolution fields once an incident is resolved or closed.
import { DataPolicy } from '@servicenow/sdk/core'
export const closedIncidentProtection = DataPolicy({
$id: Now.ID['closed_incident_protection'],
table: 'incident',
shortDescription: 'Protect closed incident fields from modification',
conditions: 'state=6^ORstate=7',
rules: {
close_code: { $id: Now.ID['closed_incident_protection_close_code_rule'], readOnly: true },
close_notes: { $id: Now.ID['closed_incident_protection_close_notes_rule'], readOnly: true },
resolved_at: { $id: Now.ID['closed_incident_protection_resolved_at_rule'], readOnly: true },
resolved_by: { $id: Now.ID['closed_incident_protection_resolved_by_rule'], readOnly: true },
},
})
Unconditional Mandatory -- Validate All Records Including Imports and APIs
Ensure records arriving from external systems have required fields. Omitting conditions applies the policy to all records. applyToImportSets and applyToSOAP default to true — no extra config needed.
import { DataPolicy } from '@servicenow/sdk/core'
export const userImportValidation = DataPolicy({
$id: Now.ID['user_import_validation'],
table: 'sys_user',
shortDescription: 'Validate user imports and API calls',
rules: {
user_name: { $id: Now.ID['user_import_validation_user_name_rule'], mandatory: true },
email: { $id: Now.ID['user_import_validation_email_rule'], mandatory: true },
first_name: { $id: Now.ID['user_import_validation_first_name_rule'], mandatory: true },
last_name: { $id: Now.ID['user_import_validation_last_name_rule'], mandatory: true },
},
})
Mixed Rules -- Different Enforcement per Field in One Policy
Apply different rule types to different fields in one policy.
import { DataPolicy } from '@servicenow/sdk/core'
export const comprehensivePolicy = DataPolicy({
$id: Now.ID['comprehensive_policy'],
table: 'incident',
shortDescription: 'Comprehensive field enforcement for critical incidents',
conditions: 'priority=1^urgency=1',
rules: {
short_description: { $id: Now.ID['comprehensive_policy_short_description_rule'], mandatory: true },
state: { $id: Now.ID['comprehensive_policy_state_rule'], readOnly: true },
priority: { $id: Now.ID['comprehensive_policy_priority_rule'], mandatory: true, readOnly: false },
category: { $id: Now.ID['comprehensive_policy_category_rule'], readOnly: true },
subcategory: { $id: Now.ID['comprehensive_policy_subcategory_rule'], mandatory: true },
},
})
Inherited Policy -- Apply Once, Enforce Across a Table Hierarchy
Apply a policy to a parent table so all child tables automatically inherit it.
import { DataPolicy } from '@servicenow/sdk/core'
export const taskAssignmentPolicy = DataPolicy({
$id: Now.ID['task_assignment_policy'],
table: 'task',
shortDescription: 'Require assignment for in-progress tasks',
description: 'Applies to all tables extending task (incident, problem, change, etc.)',
inherit: true,
conditions: 'state=2',
rules: {
assigned_to: { $id: Now.ID['task_assignment_policy_assigned_to_rule'], mandatory: true },
assignment_group: { $id: Now.ID['task_assignment_policy_assignment_group_rule'], mandatory: true },
},
})
Cross-Table -- Enforce Rules on Referenced Fields via Dot-Walk
Enforce rules on fields in referenced tables using dot-walk notation as rule keys.
import { DataPolicy } from '@servicenow/sdk/core'
export const incidentReferenceValidation = DataPolicy({
$id: Now.ID['incident_reference_validation'],
table: 'incident',
shortDescription: 'Enforce caller contact information for high priority incidents',
conditions: 'priority=1^urgency=1',
rules: {
'caller_id.email': { $id: Now.ID['incident_reference_validation_caller_id_email_rule'], mandatory: true },
'caller_id.phone': { $id: Now.ID['incident_reference_validation_caller_id_phone_rule'], mandatory: true },
'assigned_to.department': { $id: Now.ID['incident_reference_validation_assigned_to_department_rule'], readOnly: true },
'assignment_group.manager': { $id: Now.ID['incident_reference_validation_assignment_group_manager_rule'], mandatory: true },
},
})
Multi-Condition -- Strict Validation Using IN and AND Operators
Combine multiple condition operators to enforce strict requirements for a specific record type and state. The IN operator matches multiple states, ^ joins conditions with AND.
import { DataPolicy } from '@servicenow/sdk/core'
export const emergencyChangeValidation = DataPolicy({
$id: Now.ID['emergency_change_validation'],
table: 'change_request',
shortDescription: 'Strict validation for emergency changes',
description: 'Emergency changes require CAB approval, detailed justification, and rollback plan',
conditions: 'type=emergency^stateIN1,2,3',
rules: {
cab_delegate: { $id: Now.ID['emergency_change_validation_cab_delegate_rule'], mandatory: true },
justification: { $id: Now.ID['emergency_change_validation_justification_rule'], mandatory: true },
backout_plan: { $id: Now.ID['emergency_change_validation_backout_plan_rule'], mandatory: true },
risk_impact_analysis: { $id: Now.ID['emergency_change_validation_risk_impact_analysis_rule'], mandatory: true },
emergency_contact: { $id: Now.ID['emergency_change_validation_emergency_contact_rule'], mandatory: true },
},
})
Layered Validation -- Data Policy for Security, UI Policy for UX
Use Data Policy for server-side security and UI Policy for client-side UX — together for complete coverage.
import { DataPolicy, UiPolicy } from '@servicenow/sdk/core'
// Data Policy: server-side enforcement (cannot be bypassed)
export const contractDataPolicy = DataPolicy({
$id: Now.ID['contract_approval_data_policy'],
table: 'ast_contract',
shortDescription: 'Enforce contract approval requirements',
conditions: 'value>100000',
rules: {
vendor: { $id: Now.ID['contract_approval_data_policy_vendor_rule'], mandatory: true },
approver: { $id: Now.ID['contract_approval_data_policy_approver_rule'], mandatory: true },
legal_review: { $id: Now.ID['contract_approval_data_policy_legal_review_rule'], mandatory: true },
},
})
// UI Policy: client-side visibility and messages
export const contractUiPolicy = UiPolicy({
$id: Now.ID['contract_approval_ui_policy'],
table: 'ast_contract',
shortDescription: 'Show approval section for high-value contracts',
onLoad: true,
conditions: 'value>100000',
actions: [
{ field: 'approval_section', visible: true },
{ field: 'approval_notes', visible: true, mandatory: true },
{ field: 'vendor', fieldMessage: 'Vendor approval required for contracts over $100,000', fieldMessageType: 'warning' },
],
})
Avoidance
- Never include default value properties in code — do NOT write
active: true,applyToImportSets: true,applyToSOAP: true,useAsUiPolicyOnClient: true,reverseIfFalse: true,inherit: false, orconditions: '' - Never set both
mandatory: trueandreadOnly: trueon the same field — mutually exclusive; causes a build error - Never use display labels in conditions — always use stored values for choice fields (right-click field → Show Choice List → "Value" column)
- Never make fields mandatory for out-of-scope tables — the mandatory flag is silently disabled; use a Business Rule as an alternative (see the
business-rule-guidetopic) - Never use a
tableproperty inside rules for cross-table enforcement — use dot-walk notation as the rule key ('employee.department') instead - Never create conflicting Data Policies — overlapping conditions with opposing rules cause unpredictable results; most restrictive wins
- Never use Data Policies for visibility control — use UI Policies instead (see the
ui-policy-guidetopic) - Never rely on UI Policies alone for critical validation — they can be bypassed via API, imports, or browser manipulation (see the
ui-policy-guidetopic) - Never create policies with empty conditions unintentionally — omitting
conditionsapplies the policy to ALL records on the table; be deliberate