SfSkills Domains Docs GitHub

Home / apex / fsl-scheduling-api

fsl-scheduling-api

Use this skill when implementing Apex code that calls the FSL scheduling Apex API: GetSlots, schedule(), GradeSlotsService, or OAAS optimization classes. Trigger keywords: AppointmentBookingService, ScheduleService.schedule, GetSlots, GradeSlotsService, OAAS, bulk scheduling, FSL optimization callout. NOT for scheduling policy config — use admin/fsl-scheduling-policies. NOT for optimization-mode and territory design — use architect/fsl-optimization-architecture.

Open the skill folder on GitHub


FSL Scheduling API

This skill activates when an Apex developer needs to programmatically call the FSL scheduling Apex namespace — specifically FSL.AppointmentBookingService.GetSlots(), FSL.ScheduleService.schedule(), FSL.GradeSlotsService, or FSL.OAAS — to book, rank, or optimize service appointments. It handles the transaction boundary constraints, bulk patterns, and add-on licensing requirements that the standard documentation buries or omits.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

AppointmentBookingService — Slot Retrieval

FSL.AppointmentBookingService.GetSlots(saId, policyId, schedulingPolicyId) returns a List<FSL.Scheduling.TimeSlot> representing available booking windows for a ServiceAppointment. Each TimeSlot includes start/end times and a list of candidate resources.

Critical constraint: GetSlots is a callout. After calling GetSlots, you cannot perform DML in the same transaction. The common pattern — call GetSlots, pick a slot, update ArrivalWindowStart/ArrivalWindowEnd on the SA, then call schedule() — requires two separate transactions:

Attempting to do all three in one transaction throws a System.CalloutException: You have uncommitted work pending.

ScheduleService — Hard Assignment

FSL.ScheduleService.schedule(saId, schedulingPolicyId) assigns a ServiceResource to the ServiceAppointment based on the scheduling policy. It returns an FSL.ScheduleResult containing the assigned resource, territory, and scheduled start/end time.

Unlike GetSlots, schedule() both calls out and commits scheduling data — it does not require a separate DML commit after it runs. However, it must be called after any preceding DML is committed (i.e., in a fresh transaction with no prior DML in scope).

GradeSlotsService — Slot Ranking

FSL.GradeSlotsService.GradeSlots(saId, slots, schedulingPolicyId) accepts a list of TimeSlots (from GetSlots) and returns them scored by the scheduling policy's optimization criteria. Use this when building a custom booking UI where customers or dispatchers choose from ranked options rather than having the system auto-assign.

OAAS — Optimization as a Service

FSL.OAAS exposes four optimization operations:

OAAS operations are asynchronous — they submit an optimization job and return immediately. ESO (Enhanced Scheduling and Optimization) is a newer Hyperforce-backed engine with higher throughput and work chain support. When ESO is enabled for a territory, that territory uses ESO; there is no automatic fallback to the legacy engine if ESO encounters an error.


Common Patterns

Two-Transaction Booking Flow

When to use: Any time you need to get slots, let a user or automated logic pick one, then hard-assign the resource.

How it works:

  1. In a first Apex transaction (e.g., an @AuraEnabled method or Queueable), call FSL.AppointmentBookingService.GetSlots(saId, ...). Return the slot list to the caller.
  2. Store the chosen slot data (start/end, resourceId, territoryId) in a temporary custom object or pass back to the next Apex call.
  3. In a second transaction, update ServiceAppointment.ArrivalWindowStart/ArrivalWindowEnd with DML, then call FSL.ScheduleService.schedule(saId, ...).

Why not one transaction: FSL scheduling calls are HTTP callouts to the optimization microservice. Uncommitted DML before a callout throws a CalloutException.

// Transaction 1 — slot retrieval (Queueable or @AuraEnabled)
List<FSL.Scheduling.TimeSlot> slots = 
    FSL.AppointmentBookingService.GetSlots(saId, null, schedulingPolicyId);
// Return to LWC or store in intermediate object

// Transaction 2 — assignment (separate @AuraEnabled or Queueable)
ServiceAppointment sa = [SELECT Id FROM ServiceAppointment WHERE Id = :saId];
sa.ArrivalWindowStart = chosenSlot.startTime;
sa.ArrivalWindowEnd = chosenSlot.endTime;
update sa; // DML first, no prior callout in this transaction
FSL.ScheduleService.schedule(saId, schedulingPolicyId);

Bulk Scheduling via Batch Apex

When to use: Scheduling more than one ServiceAppointment at a time from Apex (e.g., post-migration bulk assignment, nightly scheduling run).

How it works: Implement Database.Batchable<SObject> and set batch size to exactly 1. Each execute() call handles one SA and makes one FSL scheduling callout.

global class BulkFslScheduler implements Database.Batchable<SObject>, Database.AllowsCallouts {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id FROM ServiceAppointment WHERE Status = \'None\''
        );
    }
    global void execute(Database.BatchableContext bc, List<SObject> scope) {
        ServiceAppointment sa = (ServiceAppointment)scope[0];
        try {
            FSL.ScheduleService.schedule(sa.Id, SCHEDULING_POLICY_ID);
        } catch (Exception e) {
            // log and continue
        }
    }
    global void finish(Database.BatchableContext bc) {}
}
// Call with: Database.executeBatch(new BulkFslScheduler(), 1);

Why batch size 1: The Apex callout limit is 100 callouts per transaction. More importantly, FSL scheduling calls are long-running HTTP requests — running more than one per execute() risks timeouts and partial failures with no rollback.


Decision Guidance

SituationRecommended ApproachReason
User picks from a list of time windowsGetSlots → GradeSlots → display → schedule() in two transactionsGradeSlots ranks windows by policy score; user sees best options first
Fully automated assignment, one SAFSL.ScheduleService.schedule() in a Queueable with no prior DMLCleanest async path, avoids callout constraint
Fully automated assignment, many SAsBatch Apex, size 1, AllowsCalloutsOnly supported bulk pattern for FSL scheduling callouts
Background re-optimization of a territoryFSL.OAAS with Global or In-Day operationOAAS runs asynchronously and handles constraint-solving at scale
Custom booking UI with ranked slotsGetSlots → GradeSlots → return to LWCGradeSlots assigns scores; LWC displays ranked options
ESO licensed, territory enrolledUse standard schedule() or OAAS; ESO is transparent to API callerNo API change needed — ESO activates per-territory in Setup

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Confirm licensing and ESO status — Verify whether the org has the Field Service Scheduling optimization add-on and whether ESO is enabled per territory in Setup > Field Service > Enhanced Scheduling. Mismatched assumptions cause silent OAAS failures.
  2. Identify transaction context — Determine whether the call originates from a trigger (avoid — no callouts in triggers), a synchronous Apex controller (@AuraEnabled), a Queueable, or a Batch class. Plan transaction boundaries before writing any code.
  3. Split GetSlots and schedule() across two transactions — If the flow requires slot retrieval followed by user selection followed by assignment, implement the split explicitly. Pass slot data via a custom object or back through the LWC component.
  4. Implement bulk patterns with Batch Apex, size 1 — If scheduling more than one SA, use Database.executeBatch(new BulkFslScheduler(), 1). Never loop schedule() inside a single transaction or standard batch execute() with size > 1.
  5. Handle OAAS operations asynchronously — Call FSL.OAAS methods from a Queueable or scheduled job. Confirm the operation type (Global/In-Day/Resource/Reshuffle) matches the optimization window and scope.
  6. Add error handling for scheduling failures — schedule() can return null or throw if no resource is available. Always check the return value and log failures without rethrowing to avoid blocking the batch.
  7. Validate in a sandbox with real scheduling policy data — FSL scheduling calls depend on scheduling policies, resources, territories, and operating hours being correctly configured. Unit tests with mocked return values will pass but integration tests against real FSL config are mandatory.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. GetSlots → ArrivalWindow DML → schedule() must span two transactions — Attempting this in one transaction throws System.CalloutException: You have uncommitted work pending. This is the most common FSL scheduling Apex bug and the official docs do not call it out explicitly.
  2. Batch size must be exactly 1 for bulk scheduling — Using a batch size > 1 with FSL callouts risks System.LimitException: Too many callouts and partial failure with no automatic rollback. Always executeBatch(job, 1).
  3. OAAS has a 2-hour hard timeout for Global optimization — Jobs submitted past the 2-hour window are cancelled silently. Monitor FSL optimization job records to detect incomplete runs.
  4. ESO has no automatic fallback to legacy engine — If ESO is enabled for a territory and the ESO service is unavailable, optimization fails for that territory. There is no automatic downgrade to the classic scheduling engine.
  5. schedule() returns null when no resource is available — Do not assume a non-null return. Null means the SA was not scheduled. Log and handle gracefully.

Output Artifacts

ArtifactDescription
Two-transaction booking Apex patternApex classes implementing slot retrieval and hard assignment across two separate transactions
BulkFslScheduler batch classBatch Apex implementation for scheduling multiple SAs with callout isolation
OAAS operation invocation snippetQueueable-based OAAS call with correct operation type selection

Dependencies

Package files