Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
    • Task Scheduling
  • Platform
Try models
  • Timefold Solver SNAPSHOT
  • Deploying to the Timefold Platform
  • Platform model metadata
  • Edit this Page

Timefold Solver SNAPSHOT

    • Introduction
    • Getting started
      • Overview
      • Build as a service
      • Embed as a library
        • Hello World guide
        • Quarkus guide
        • Spring Boot guide
    • Domain modeling
      • Guide
      • Building blocks
      • Common patterns
    • Constraints and score
      • Overview
      • Score calculation
      • Understanding the score
      • Load balancing and fairness
      • Performance tips and tricks
    • Running the Solver
      • Overview
      • As a service
        • REST API
        • Model Enrichment
        • Constraint weights
        • Demo data
        • Exposing metrics
      • As a library
        • Configuring Timefold Solver
        • Constraint weights
        • Quarkus integration
        • Spring Boot integration
        • JPA/JAXB/JSON integration
    • Diagnosing the Solver
      • Benchmarking
      • Solver diagnostics
    • Deploying to the Timefold Platform
      • Overview
      • Getting started
      • Platform model metadata
      • Using metrics
    • Optimization algorithms
      • Overview
      • Construction heuristics
      • Local search
      • Exhaustive search
      • Custom moves
        • Neighborhoods API
        • Move Selector reference
    • Responding to change
      • Continuous planning
      • Real-time planning
      • Non-disruptive replanning
      • Assignment Recommendation API
    • Example use cases
      • Vehicle routing (guide)
      • More examples on GitHub
    • FAQ
    • New and noteworthy
    • Upgrading Timefold Solver
      • Upgrading Timefold Solver: Overview
      • Upgrade from Timefold Solver 1.x to 2.x
      • Upgrading from OptaPlanner
      • Backwards compatibility
      • Migration guides
        • Variable Listeners to Custom Shadow Variables
        • Chained planning variable to planning list variable
    • Commercial editions
      • Overview
      • Installation
      • Performance improvements
      • Score analysis
      • Recommendation API
      • Nearby selection
      • Multithreaded solving
      • Partitioned search
      • Constraint profiling
      • Multistage moves
      • Throttling best solution events
      • License management

Platform model metadata

Once your model is deployed to Timefold Platform, the platform UI surfaces several pieces of your model’s code and configuration directly.

This page documents that platform-specific metadata: which identity properties describe your model on the platform, how to give your constraints human-readable names and groups, and how to ship a default configuration profile.

See Deploying to the Timefold Platform for why you’d want to do this in the first place, and Platform concepts for how this metadata relates to your model’s identifier, registration key, and visibility on the platform.

Deploying custom models to the platform is in preview.

This is currently only available to a limited set of partners. If you’re interested in joining this preview program, get in touch with the Timefold team to discuss access.

1. Model identity properties

The following application.properties describe your model’s identity on the platform. timefold.application.name, timefold.application.version, and the timefold.application.contact.* properties are covered in Getting started: building a service, since they are required for every model, service or not.

Property Purpose

timefold.application.description

Description of the model, included in the generated OpenAPI specification and shown on the platform.

timefold.model.maturity-level

Maturity level of the model. Defaults to Experimental if not set.

Use timefold.model.maturity-level to tell consumers how stable a given model is, using the same maturity levels Timefold uses for its own models (Example, Experimental, Preview, Stable, Deprecated). This is especially useful once you’re running multiple versions of your model in parallel: consumers can tell at a glance which version is safe to build on and which is still being validated, instead of having to ask you.

1.1. Logo

To give your model a custom logo on the platform, add a square logo.png file to a model-images folder at the root of your project, next to pom.xml:

my-model/
├── model-images/
│   └── logo.png
├── src/
└── pom.xml

The build picks up any file in model-images automatically; logo.png specifically is used as the model’s logo, and any other file is added as an additional model image.

2. Constraint descriptions and groups

By default, a constraint is identified only by the string you pass to asConstraint(String id), which is also used as its display name. To give consumers a better score analysis and configuration profile experience, attach a ConstraintInfo to the constraint instead.

ConstraintInfo and ConstraintGroupInfo live in ai.timefold.solver.service.definition.api.description:

  • ConstraintInfo(id, name, description, constraintGroup): id is the stable identifier of the constraint, name is the human-readable name shown in the UI, description explains the constraint’s goal, and constraintGroup optionally assigns the constraint to a ConstraintGroupInfo.

  • ConstraintGroupInfo(id, name, description, icon, tags): groups related constraints under a shared category. icon accepts any icon name from Tabler Icons, and tags are optional labels you can use to classify or filter groups.

Extending the TimetableConstraintProvider from Adjusting constraint weights with names, descriptions, and a group:

The ConstraintProvider class, with ConstraintInfo attached.
  • Java

  • Kotlin

public class TimetableConstraintProvider implements ConstraintProvider {

    public static final String TEACHER_CONFLICT = "Teacher conflict";
    public static final String ROOM_CONFLICT = "Room conflict";

    private static final ConstraintGroupInfo CONFLICT_GROUP = new ConstraintGroupInfo(
            "conflicts",
            "Conflicts",
            "Constraints that prevent double-booking of teachers and rooms.",
            "alert-triangle",
            new String[] { "hard-constraints" });

    Constraint roomConflict(ConstraintFactory constraintFactory) {
        return constraintFactory
                // constraint implementation excluded
                .asConstraint(new ConstraintInfo(ROOM_CONFLICT, "Room conflict",
                        "A room can be used for at most one lesson at the same time.", CONFLICT_GROUP));
    }

    Constraint teacherConflict(ConstraintFactory constraintFactory) {
        return constraintFactory
                // constraint implementation excluded
                .asConstraint(new ConstraintInfo(TEACHER_CONFLICT, "Teacher conflict",
                        "A teacher can teach at most one lesson at the same time.", CONFLICT_GROUP));
    }

    // other constraints excluded
}
class TimetableConstraintProvider : ConstraintProvider {

    companion object {
        const val TEACHER_CONFLICT = "Teacher conflict"
        const val ROOM_CONFLICT = "Room conflict"

        private val CONFLICT_GROUP = ConstraintGroupInfo(
            "conflicts",
            "Conflicts",
            "Constraints that prevent double-booking of teachers and rooms.",
            "alert-triangle",
            arrayOf("hard-constraints")
        )
    }

    fun roomConflict(constraintFactory: ConstraintFactory): Constraint {
        return constraintFactory
                // constraint implementation excluded
                .asConstraint(ConstraintInfo(ROOM_CONFLICT, "Room conflict",
                        "A room can be used for at most one lesson at the same time.", CONFLICT_GROUP))
    }

    fun teacherConflict(constraintFactory: ConstraintFactory): Constraint {
        return constraintFactory
                // constraint implementation excluded
                .asConstraint(ConstraintInfo(TEACHER_CONFLICT, "Teacher conflict",
                        "A teacher can teach at most one lesson at the same time.", CONFLICT_GROUP))
    }

    // other constraints excluded
}

The id you pass into ConstraintInfo is still the identifier used by @ConstraintReference in your ModelConfigOverrides, as described in Adjusting constraint weights. The name, description, and constraintGroup are purely presentational: they are what the platform’s score analysis view and configuration profile editor use to show your constraints to consumers, instead of falling back to the raw id.

The UI groups constraints that don’t specify a constraintGroup under a default group.

3. Configuration profiles

Every model ships with a default configuration profile, defined through ai.timefold.model.default-config.* properties in application.properties. This profile is what consumers get out of the box, before they add any profile of their own.

Property Purpose

ai.timefold.model.default-config.name

Name of the default configuration profile.

ai.timefold.model.default-config.description

Description of the default configuration profile.

ai.timefold.model.default-config.max-thread-count

Default number of threads used for solving.

ai.timefold.model.default-config.map.provider

Default map provider, for models that require map data.

ai.timefold.model.default-config.map.location

Default map location, for models that require map data.

ai.timefold.model.default-config.map.max-distance-from-road

Default maximum distance from a road, for models that require map data.

ai.timefold.model.default-config.map.transport-type

Default transport type used when computing distances, for models that require map data.

ai.timefold.model.default-config.map.use-traffic

Whether the default profile takes live traffic into account. Defaults to false.

ai.timefold.model.default-config.termination.spent-limit

The default maximum solving time, in the ISO 8601 duration format. This property is required.

ai.timefold.model.default-config.termination.unimproved-spent-limit

The default unimproved time limit. If not set, Diminished Returns termination is used instead.

Once your model is deployed, tenant users can add additional configuration profiles on top of this default directly in the platform UI, without you making any code changes. A configuration profile is where per-request constraint weight overrides, thread count, memory, and termination limits are set for a specific use case. See Configuration parameters and profiles for how tenant users manage profiles from the platform side.

4. Visualization

There is currently no dedicated mechanism for adding a custom visualization UI for your model’s solution on the platform. Consumers see the raw solution data and the generic score analysis view. This is an open area of the platform, so expect it to evolve in future releases.

  • © 2026 Timefold BV
  • Timefold.ai
  • Documentation
  • Changelog
  • Send feedback
  • Privacy
  • Legal
    • Light mode
    • Dark mode
    • System default