Predict.aiDocs
Goals

Tournament & champion

How candidate models compete, how to read the leaderboard, and how to pick or cancel a champion.

After discovery, the goal fits a model. Instead of betting on one architecture, the platform runs a tournament: a lineup of candidate models — classical learners, foundation models, and any of your own models you selected — all train on the same discovered drivers and are scored on the same purged walk-forward folds, in the goal type's own metric. The best scorer becomes the champion and trains on full history for serving.

Tournaments start automatically after a discovery run when auto-fit is on, or on demand with POST /v1/goals/{goal_id}/train.

Read the leaderboard

curl "$API_BASE/v1/goals/$GOAL_ID/tournament" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
{
  "data": {
    "tournament": {
      "tournament_id": "e7a1…",
      "goal_id": "b7e9c2d4-…",
      "status": "completed",
      "metric": { "name": "smape", "direction": "lower" },
      "n_folds": 5,
      "entries": [
        {
          "preset_id": "gbt_belief",
          "label": "HistGradientBoosting",
          "display_name": "Driver-weighted trees",
          "engine": "classical",
          "status": "completed",
          "score": 8.41,
          "rank": 1,
          "fold_scores": [9.1, 8.3, 8.2, 8.5, 8.0],
          "duration_s": 74.2,
          "training_id": "t_5b2e…",
          "model_id": "9f2c41d8-…",
          "error": null
        },
        {
          "preset_id": "seasonal_naive",
          "display_name": "Steady baseline",
          "status": "completed",
          "score": 12.90,
          "rank": 2,
          "…": "…"
        }
      ],
      "excluded": [
        { "preset_id": "trend_classifier", "reason": "Applies to trend goals only" }
      ],
      "champion": {
        "preset_id": "gbt_belief",
        "model_id": "9f2c41d8-…",
        "training_id": "t_5b2e…",
        "score": 8.41,
        "training_status": "completed",
        "executed": true,
        "accuracy_score": 91.4,
        "progress": null,
        "deployment": {
          "promotion_id": "p_88c1…",
          "status": "deployed",
          "ready": true
        }
      },
      "auto_deploy": "review",
      "created_at": "2026-07-15T09:40:00+00:00",
      "finished_at": "2026-07-15T09:52:31+00:00"
    }
  }
}

Response fields

FieldMeaning
entriesOne row per candidate, with per-fold scores, the final score, and rank. Statuses move pendingrunningcompleted (or failed / cancelled); failed entries carry an error and are skipped, so one bad candidate never blocks the board.
excludedCandidates that didn't make the lineup, each with a human-readable reason.
championThe winner, enriched with its live serving-training state (training_status, progress) and its deployment state — the full arc from "won the board" to "serving".
auto_deployThe goal's deploy policy: off, review, or auto. On auto, the champion ships itself; on review, it waits for you.
metric / n_foldsThe scoring metric (with its direction) and how many purged walk-forward folds every candidate was scored on.

Returns 404 ("Goal not found") for unknown goals; tournament is null if none has run yet.

Select a champion manually

Deploy any scored entry instead of the automatic best — for example, a simpler model whose score is close enough:

curl -X POST "$API_BASE/v1/goals/$GOAL_ID/tournament/champion" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{ "preset_id": "seasonal_naive" }'
{
  "data": {
    "tournament_id": "e7a1…",
    "preset_id": "seasonal_naive",
    "model_id": "3ac9…",
    "training_id": "t_91d0…",
    "status": "training",
    "unchanged": false
  }
}
FieldMeaning
preset_id requiredThe scored entry to make champion — from the leaderboard's entries.

The chosen entry materializes through the same path as an automatic champion — full-history training, then the standard promotion gate. Picking the entry that's already champion returns unchanged: true.

StatusWhyExample message
400Missing body field"preset_id is required"
404No goal, or no tournament yet"No tournament for this goal"
409The entry can't be selected (not scored, or failed)"Could not select that model"

Cancel

Three scopes, all cooperative — workers stop at their next checkpoint:

The whole tournament. Aborts fitting; no champion is selected.

curl -X POST "$API_BASE/v1/goals/$GOAL_ID/tournament/cancel" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
{ "data": { "tournament_id": "e7a1…", "status": "cancelling", "cancelled": true } }

If the tournament already finished, you get 200 with cancelled: false and "Tournament is not running." — nothing to stop.

One entry. Drop a single stuck candidate; the rest keep fitting and a champion is still selected from whatever scored:

curl -X POST "$API_BASE/v1/goals/$GOAL_ID/tournament/entries/fm_adapter/cancel" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
{ "data": { "tournament_id": "e7a1…", "preset_id": "fm_adapter", "status": "cancelling" } }

status is cancelled if the entry was still pending, cancelling if it was mid-flight. Returns 409 if the tournament isn't running or the entry already finished.

The champion's serving training. The full-history training that runs after the board completes can also be stopped:

curl -X POST "$API_BASE/v1/goals/$GOAL_ID/tournament/training/cancel" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
{
  "data": {
    "training_id": "t_5b2e…",
    "pipeline_id": "9f2c41d8-…",
    "status": "cancelling",
    "cancelled": true
  }
}

Returns 404 ("No champion training to cancel") when there's nothing in flight, or 200 with "Training already finished." when it's done.

The model lineup

See the candidate catalog — every model the tournament can field — before or after creating a goal. Pass goal_type to flag which apply to that intent:

curl "$API_BASE/v1/goals/model-lineup?goal_type=forecast_value" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
{
  "data": {
    "models": [
      {
        "id": "gbt_belief",
        "label": "HistGradientBoosting",
        "display_name": "Driver-weighted trees",
        "engine": "Classical",
        "description": "Gradient-boosted trees weighted by discovered driver beliefs.",
        "always_on": false,
        "relevant": true,
        "cost_weight": 1.0,
        "cost_tier": "Standard",
        "metered": false
      }
    ]
  }
}

Use these id values in the goal's options.model_selection (include / exclude) to shape the lineup — the tournament charge scales with it, so a lighter selection costs proportionally less. metered models additionally charge their standard training meters while fitting.

On this page