Timefold Solve

Timefold Solve

Certified

Submit an optimization problem to the Timefold Platform

Submits a modelInput dataset to a Timefold Platform model (Field Service Routing or Employee Scheduling).

When wait is false (the default) the task performs a single POST and immediately returns the jobId. Use the job id in a subsequent GetDataset task to poll for status or retrieve the solution.

When wait is true the task submits the dataset and polls the platform until solving completes (or requestTimeout elapses), then returns the jobId, solverStatus, score, and the full modelOutput. See the Timefold API documentation.

yaml
type: io.kestra.plugin.timefold.Solve

Submit a Field Service Routing problem and capture the job ID for downstream tasks.

yaml
id: timefold_route
namespace: company.team

tasks:
  - id: solve
    type: io.kestra.plugin.timefold.Solve
    apiKey: "{{ secret('TIMEFOLD_API_KEY') }}"
    model: FIELD_SERVICE_ROUTING
    solveDuration: PT30S
    modelInput:
      vehicles:
        - id: Ann
          shifts:
            - id: Ann-2027-02-01
              startLocation: [33.68786, -84.18487]
              minStartTime: "2027-02-01T09:00:00Z"
      visits:
        - id: Visit A
          location: [33.77301, -84.43838]
          serviceDuration: PT1H30M
  - id: log_job_id
    type: io.kestra.plugin.core.log.Log
    message: "Submitted job: {{ outputs.solve.jobId }}"

Build an Employee Scheduling dataset from CSV inputs and solve.

yaml
id: timefold_schedule
namespace: company.team

# employees.csv  (name becomes the employee id):
#   name,skills
#   Alice,nursing|doctor
#   Bob,nursing
#
# shifts.csv  (required_skill becomes a requiredSkills object array):
#   id,start,end,required_skill
#   SHIFT-001,2027-02-01T08:00:00Z,2027-02-01T16:00:00Z,nursing
#   SHIFT-002,2027-02-01T16:00:00Z,2027-02-02T00:00:00Z,nursing

inputs:
  - id: employees_csv
    type: FILE
  - id: shifts_csv
    type: FILE

tasks:
  - id: build_dataset
    type: io.kestra.plugin.scripts.python.Script
    inputFiles:
      employees.csv: "{{ inputs.employees_csv }}"
      shifts.csv: "{{ inputs.shifts_csv }}"
    script: |
      import csv
      from kestra import Kestra

      employees = []
      with open("employees.csv") as f:
          for row in csv.DictReader(f):
              employees.append({
                  "id": row["name"],
                  "skills": [{"id": s} for s in row["skills"].split("|")],
              })

      shifts = []
      with open("shifts.csv") as f:
          for row in csv.DictReader(f):
              shifts.append({
                  "id": row["id"],
                  "start": row["start"],
                  "end": row["end"],
                  "requiredSkills": [row["required_skill"]],
              })

      Kestra.outputs({"modelInput": {"employees": employees, "shifts": shifts}})

  - id: solve
    type: io.kestra.plugin.timefold.Solve
    apiKey: "{{ secret('TIMEFOLD_API_KEY') }}"
    model: EMPLOYEE_SCHEDULING
    solveDuration: PT1M
    modelInput: "{{ outputs.build_dataset.vars.modelInput }}"

Submit a Field Service Routing problem and wait for the optimized solution.

yaml
id: timefold_route_wait
namespace: company.team

tasks:
  - id: solve
    type: io.kestra.plugin.timefold.Solve
    apiKey: "{{ secret('TIMEFOLD_API_KEY') }}"
    model: FIELD_SERVICE_ROUTING
    solveDuration: PT30S
    wait: true
    modelInput:
      vehicles:
        - id: Ann
          shifts:
            - id: Ann-2027-02-01
              startLocation: [33.68786, -84.18487]
              minStartTime: "2027-02-01T09:00:00Z"
      visits:
        - id: Visit A
          location: [33.77301, -84.43838]
          serviceDuration: PT1H30M
  - id: log_result
    type: io.kestra.plugin.core.log.Log
    message: "Solved {{ outputs.solve.jobId }} — status: {{ outputs.solve.solverStatus }}, score: {{ outputs.solve.score }}"
Properties

The Timefold Platform API key

Sent as the X-API-KEY header on every request. Provide it via a secret, for example apiKey: "{{ secret('TIMEFOLD_API_KEY') }}". The key must have access to the selected model.

Possible Values
FIELD_SERVICE_ROUTINGEMPLOYEE_SCHEDULINGPICKUP_DELIVERY_ROUTING

The Timefold model to solve with

Determines the REST resource the dataset is submitted to. FIELD_SERVICE_ROUTING uses the route-plans endpoint and EMPLOYEE_SCHEDULING uses the schedules endpoint.

The optimization input dataset (modelInput)

The data to be optimized, following the selected model's schema. For Field Service Routing this contains vehicles and visits; for Employee Scheduling it contains employees, shifts, etc. Accepts a map, or a JSON string / expression that resolves to the modelInput object. The value is wrapped into the { "modelInput": ... } request body sent to Timefold.

Defaulthttps://app.timefold.ai

Base URL of the Timefold Platform API

Defaults to the Timefold managed cloud. Override this when running a self-hosted Timefold deployment. The model and resource path segments are appended automatically, so provide only the host, e.g. https://app.timefold.ai.

DefaultSTORE
Possible Values
STOREFETCHFETCH_ONENONE

How to return the modelOutput

Only applies when wait is true.

  • STORE (default): writes modelOutput as a JSON file to Kestra's internal storage and returns its URI in uri. Recommended for large solutions.
  • FETCH / FETCH_ONE: returns modelOutput inline in the modelOutput output field.
  • NONE: discards modelOutput entirely (useful when only solverStatus and score are needed).

Reference (ref) of the pluginDefaults to apply to this task.

DefaultPT2S

How often to poll the Timefold Platform for the solver status

Only applies when wait is true. Minimum is PT0.5S (500 ms). Defaults to PT2S (every 2 seconds).

DefaultPT10M

Overall timeout for the whole operation, including queueing on the platform

Only applies when wait is true. If the solver has not completed within this duration the task fails. Should comfortably exceed solveDuration. Defaults to PT10M (10 minutes).

Optional run name attached to the submitted dataset

Stored as config.run.name and shown in the Timefold Platform UI.

Maximum time Timefold should spend solving

Passed as the config.run.termination.spentLimit of the submitted dataset. Controls how long the platform solver runs; it does not affect when this task returns. When omitted, the Timefold Platform uses its built-in diminishing-returns termination to decide how long to run based on solution quality improvements over time.

Defaultfalse

Whether to poll for the solution before returning

When true the task polls the platform until solving completes (or requestTimeout elapses) and returns the full modelOutput, solverStatus, and score. When false (the default) the task submits the dataset and returns the jobId immediately.

The identifier of the solving job on the Timefold Platform

Pass this to a subsequent task to poll for status or retrieve the solution via the Timefold API.

The optimized solution (modelOutput) returned inline (populated when fetchType is FETCH or FETCH_ONE)

Populated only when wait is true and fetchType is FETCH or FETCH_ONE. The modelOutput object returned by the Timefold Platform containing the optimized assignments (routes, schedules, etc.).

The score of the returned solution, e.g. 0hard/0medium/-3603soft

Populated only when wait is true.

The final solver status, e.g. SOLVING_COMPLETED or TERMINATED

Populated only when wait is true.

Formaturi

URI to the stored modelOutput (populated when fetchType is STORE)

Populated only when wait is true and fetchType is STORE (the default). Points to the modelOutput JSON file written to Kestra's internal storage.