fsl-territory-data-setup
Use this skill when bulk loading Service Territory data: boundary polygons, ServiceTerritoryMember assignments, OperatingHours, TimeSlots, and territory hierarchy setup. Trigger keywords: service territory bulk load, KML polygon import FSL, ServiceTerritoryMember migration, OperatingHours data setup, PolygonUtils Apex. NOT for Enterprise Territory Management (ETM/Account Territories), admin-level territory configuration UI, or scheduling policy setup.
Best use case
fsl-territory-data-setup is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Use this skill when bulk loading Service Territory data: boundary polygons, ServiceTerritoryMember assignments, OperatingHours, TimeSlots, and territory hierarchy setup. Trigger keywords: service territory bulk load, KML polygon import FSL, ServiceTerritoryMember migration, OperatingHours data setup, PolygonUtils Apex. NOT for Enterprise Territory Management (ETM/Account Territories), admin-level territory configuration UI, or scheduling policy setup.
Teams using fsl-territory-data-setup should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/fsl-territory-data-setup/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How fsl-territory-data-setup Compares
| Feature / Agent | fsl-territory-data-setup | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Use this skill when bulk loading Service Territory data: boundary polygons, ServiceTerritoryMember assignments, OperatingHours, TimeSlots, and territory hierarchy setup. Trigger keywords: service territory bulk load, KML polygon import FSL, ServiceTerritoryMember migration, OperatingHours data setup, PolygonUtils Apex. NOT for Enterprise Territory Management (ETM/Account Territories), admin-level territory configuration UI, or scheduling policy setup.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# FSL Territory Data Setup This skill activates when a data migration or org setup project requires bulk loading FSL Service Territory data: territory records, operating hours, time slots, boundary polygons, and ServiceTerritoryMember assignments. Territory data has strict insert-order dependencies and polygon boundaries are stored in managed package objects requiring special import handling. --- ## Before Starting Gather this context before working on anything in this domain: - Determine the territory hierarchy depth (parent territories, child territories). Parent territories must exist before child territories are inserted. - Confirm whether geographic polygon boundaries are in scope. Polygon boundaries are stored in FSMP managed package objects — the primary import mechanism is KML file upload in Setup, not Data Loader. - Verify all resource records (ServiceResource) exist before loading ServiceTerritoryMember records. ServiceTerritoryMember requires both ServiceTerritory and ServiceResource to exist. - Confirm TerritoryType for each member: Primary, Secondary, or Relocation. Omitting TerritoryType defaults to Primary, which misclassifies contractors and affects scheduling behavior. - Check territory limits: maximum 50 resources per territory and 1,000 service appointments per day per territory before optimization performance degrades. --- ## Core Concepts ### Insert Order for Territory Objects The correct sequence: 1. OperatingHours (no parent dependencies) 2. TimeSlot (child of OperatingHours) 3. ServiceTerritory — parent territories first, then child territories 4. ServiceTerritoryMember (references ServiceTerritory + ServiceResource) Each step must complete before the next. ServiceTerritory has a lookup to OperatingHours — the OperatingHours record must exist before the territory is created. ### Polygon Boundaries Service territory polygons define geographic boundaries used for appointment booking's "territory lookup by location." Polygon data is stored in FSMP (Field Service managed package) objects, not standard Salesforce objects. The supported bulk import mechanism is KML file upload in Setup > Field Service Settings > Service Territories > Import Polygon. For Apex-based polygon resolution (finding which territory a lat/lng falls in), use the `FSL.PolygonUtils` class: ```apex ServiceTerritory territory = FSL.PolygonUtils.getServiceTerritoryForLocation(lat, lng, Date.today()); ``` Bulk polygon creation via Apex uses queueable chaining because polygon processing is time-consuming and subject to governor limits per territory. ### ServiceTerritoryMember — Key Fields | Field | Notes | |---|---| | TerritoryType | Primary, Secondary, or Relocation. Omitting defaults to Primary | | EffectiveStartDate | Required. When the member starts serving this territory | | EffectiveEndDate | Optional. For relocation members moving between territories | | ServiceTerritoryId | Parent territory | | ServiceResourceId | The resource being assigned | **Relocation pattern:** When a resource permanently moves from Territory A to Territory B, create a ServiceTerritoryMember for Territory B with EffectiveStartDate = transfer date, and set EffectiveEndDate on the Territory A member. Do not delete the old STM record — it is required for historical appointment reporting. --- ## Common Patterns ### Bulk Territory Hierarchy Load **When to use:** Setting up a new FSL implementation with dozens or hundreds of territories in a regional hierarchy. **How it works:** 1. Load OperatingHours records with External IDs (one per schedule type — e.g., Monday-Friday 8am-5pm) 2. Load TimeSlot records referencing OperatingHours External IDs 3. Load parent ServiceTerritory records (IsActive = true, OperatingHoursId mapped) 4. Load child ServiceTerritory records with ParentTerritoryId mapped to parent External IDs 5. Load ServiceTerritoryMember records with TerritoryType, EffectiveStartDate, ServiceResourceId **Why not flat structure:** ServiceTerritory hierarchy is used for scheduling scope — optimization runs per territory, and child territories inherit scheduling scope boundaries from parents. Loading children before parents creates orphaned records. ### KML Polygon Import for Territory Boundaries **When to use:** Geographic territory boundaries are needed for the Book Appointment action's location-based territory lookup. **How it works:** 1. Prepare KML files — one per service territory. Each KML file contains one Polygon element. 2. In Setup > Field Service Settings > Service Territories, select the territory and click "Import Polygon" 3. Upload the KML file. The polygon is stored in the FSMP managed package FSL__Polygon__c object. 4. Verify: use PolygonUtils.getServiceTerritoryForLocation() with a known lat/lng to confirm territory lookup works. --- ## Decision Guidance | Situation | Recommended Approach | Reason | |---|---|---| | Bulk territory load (no polygons) | Data Loader upsert with External IDs in hierarchy order | Fastest path, re-runnable | | Geographic polygon boundaries needed | KML import via Setup per territory | Only supported bulk polygon import mechanism | | Large polygon creation via Apex | Queueable chaining, one territory per job | Governor limits on polygon processing | | Resource moving permanently to new territory | Create new STM with EffectiveStartDate, end-date old STM | Preserves historical data, correct for scheduling | | Contractor in multiple territories | Add as Secondary type member in each territory | Primary = home territory; Secondary = overflow territory | --- ## Recommended Workflow 1. **Define the territory hierarchy** — Identify parent/child relationships and territory types. Document operating hours schedules needed (one OperatingHours record per unique schedule pattern). 2. **Load OperatingHours and TimeSlots first** — These have no parent dependencies. Add External IDs for safe upsert. 3. **Load parent ServiceTerritory records** — Map OperatingHoursId via External ID. Verify all parent records before loading children. 4. **Load child ServiceTerritory records** — Map ParentTerritoryId via parent's External ID. 5. **Import polygon boundaries** (if in scope) — Use the KML import in Setup per territory. Verify with PolygonUtils after import. 6. **Load ServiceTerritoryMember records** — Set TerritoryType explicitly, EffectiveStartDate required, map ServiceResourceId. Confirm 50-resource-per-territory limit not exceeded. 7. **Validate with test scheduling** — Create a test ServiceAppointment and use the Book Appointment action to confirm territory lookup and slot retrieval work correctly. --- ## Review Checklist - [ ] OperatingHours and TimeSlots loaded before ServiceTerritory - [ ] Parent ServiceTerritory records loaded before child records - [ ] TerritoryType explicitly set on all ServiceTerritoryMember records - [ ] EffectiveStartDate set on all ServiceTerritoryMember records - [ ] Territory member count under 50 per territory - [ ] Polygon boundaries imported and verified with PolygonUtils (if in scope) - [ ] Test scheduling run confirms territory lookup and slot availability --- ## Salesforce-Specific Gotchas Non-obvious platform behaviors that cause real production problems: 1. **Omitting TerritoryType defaults to Primary and misclassifies contractors** — Primary designation affects which resource is the "home" resource for a territory and scheduling preference. Contractors sharing a territory as Secondary need explicit TerritoryType = Secondary. 2. **Polygon boundaries are in the FSL managed package, not standard objects** — You cannot Data Loader-import polygons directly. KML import via Setup is the only supported bulk mechanism. 3. **Relocation STM records require EffectiveStartDate/EndDate — not deletion** — Deleting the old STM breaks historical appointment reporting. Always set EffectiveEndDate on the departing STM and create a new one for the destination territory. 4. **Exceeding 50 resources per territory degrades optimization performance** — This is a soft limit but exceeding it causes Global Optimization jobs to consistently run over the 2-hour timeout. Design territory structure to respect this limit. 5. **Territory polygons that cross timezone boundaries silently break appointment booking** — If a polygon spans a timezone line, `Book Appointment` derives slots using the territory's OperatingHours timezone but the polygon boundary check uses geography — creating booking windows that don't correspond to local time. Keep polygons within single timezone boundaries. --- ## Output Artifacts | Artifact | Description | |---|---| | Territory data load sequence | Ordered loading plan with object dependencies and field mappings | | ServiceTerritoryMember load CSV | Template CSV with correct columns including TerritoryType and EffectiveStartDate | --- ## Related Skills - data/fsl-resource-and-skill-data — ServiceResource records that ServiceTerritoryMember references - architect/fsl-optimization-architecture — Territory sizing constraints and optimization engine considerations - architect/fsl-multi-region-architecture — Multi-region and multi-timezone territory design
Related Skills
shield-kms-byok-setup
Configure Shield Platform Encryption with customer-supplied (BYOK) or customer-held (Cache-Only Key Service) tenant secrets, rotate them, and recover. NOT for Classic Encryption or field masking.
sandbox-data-masking
Use this skill when configuring or reviewing Salesforce Data Mask to protect PII/PHI in partial or full copy sandboxes after a refresh. Trigger keywords: data mask, sandbox masking, PII in sandbox, GDPR sandbox, HIPAA non-production, mask contacts, obfuscate fields non-production. NOT for sandbox refresh mechanics (use sandbox-refresh-and-templates), NOT for production data anonymization, NOT for Shield Platform Encryption at rest.
gdpr-data-privacy
Use this skill when implementing GDPR or CCPA data privacy controls in Salesforce: Individual sObject linkage, consent tracking, Right to Be Forgotten (RTBF) requests, data subject request handling, and Privacy Center configuration. Trigger keywords: GDPR, data privacy, consent management, right to erasure, Individual object, ContactPointConsent, ShouldForget, data subject request, Privacy Center, data portability. NOT for general data quality cleanup, duplicate management, field-level encryption (see platform-encryption skill), or sandbox data masking (see sandbox-data-masking skill).
data-classification-labels
Classify Salesforce fields by data sensitivity and compliance category using the four built-in classification attributes (SecurityClassification, ComplianceGroup, BusinessOwnerId, BusinessStatus). Covers Metadata API deployment, Tooling API querying, and Einstein Data Detect recommendations. NOT for data masking, Shield Platform Encryption, or runtime access control enforcement.
customer-data-request-workflow
Implement GDPR/CCPA data subject rights (access, deletion, rectification) using Salesforce Privacy Center and/or custom workflow. NOT for general backup or org-level data retention policy.
omnistudio-deployment-datapacks
Use when exporting, importing, or version-controlling OmniStudio components using DataPacks via the OmniStudio DataPacks tool or vlocity CLI. Covers DataPack export/import, Git version control integration, CI/CD for OmniStudio. NOT for SFDX-based metadata deployment of non-OmniStudio components.
omnistudio-asynchronous-data-operations
Use Integration Procedures queues, DataRaptor Chain, and Remote Actions with async patterns for long-running OmniStudio flows. NOT for simple DataRaptor reads.
dataraptor-transform-optimization
Use when DataRaptor Transform operations are slow, hit governor limits, or use Apex where formula fields would suffice. Covers formula vs Apex expressions, bulk transform sizing, and chained transform composition. Triggers: 'dataraptor transform slow', 'dataraptor formula vs apex', 'dataraptor bulk transform', 'dr governor limit'. NOT for DataRaptor Extract or Load performance.
dataraptor-patterns
Use when designing or reviewing OmniStudio DataRaptors, especially Extract versus Turbo Extract versus Transform versus Load, field mapping strategy, performance tradeoffs, and when to move work into Integration Procedures or Apex. Triggers: 'DataRaptor Extract', 'Turbo Extract', 'DataRaptor Load', 'DataRaptor Transform', 'OmniStudio data mapping'. NOT for overall OmniScript journey design or Integration Procedure sequencing when the main question is not the DataRaptor shape itself.
lwc-datatable-advanced
Advanced lightning-datatable patterns — inline edit + draftValues, custom cell types via extending LightningDatatable, sortable columns, infinite scroll with onloadmore, row-level errors, and the cost of large data sets. NOT for read-only display of small lists (plain lightning-datatable suffices) or fully custom grids (use a third-party library).
lwc-data-table
Use when designing or reviewing `lightning-datatable` usage in Lightning Web Components, including column configuration, stable `key-field` values, inline editing, row actions, infinite loading, and custom cell types. Triggers: 'lightning datatable inline edit', 'row actions in lwc datatable', 'key field missing', 'infinite loading in datatable'. NOT for highly custom virtualized grids or broad page-performance work outside the datatable boundary.
lwc-custom-datatable-types
Use when you need to extend `lightning-datatable` with custom cell renderings: status pills, progress bars, image thumbnails, action cells, editable pickliststo, rich-text, or any column that `lightning-datatable` does not ship out of the box. Triggers: 'custom cell type lightning datatable', 'progress bar column', 'image column', 'inline edit picklist in datatable', 'rich text column'. NOT for basic datatable usage (see `lwc-data-table`) and NOT for tree-grid or large-dataset virtualization (see `virtualized-lists`).