---
title: "Orchestrate course import, segmentation, runner import, estimation, and optional PDF export"
method: POST
path: "/api/race-plans"
tags: ["RacePlans"]
---

# Orchestrate course import, segmentation, runner import, estimation, and optional PDF export

`POST /api/race-plans`

## Request

- Base URL: `https://courseprofiler.com`
- URL: `https://courseprofiler.com/api/race-plans`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer
  - none

Optional convenience workflow for AI agents and simpler API clients. Creates one top-level async job that composes the CRSProf import, segment generation, USRProf import, estimate, and race-plan PDF export capabilities. Atomic endpoints remain the canonical API surface.

Personalized race plans require runner input. CourseProfiler does not create meaningful pacing estimates from a course alone. Before calling this endpoint, make sure the user provides one runner input: a USRProf artifact ID, a public HTTPS URL to a USRProf file, local GPX/FIT/CRSProf evidence files uploaded through /api/artifact-uploads then imported with /api/usrprof/imports, public HTTPS URLs to runner evidence files, or a USRProf profile created through CourseProfiler's Strava import feature. Strava import requires browser-based user authentication and consent; direct the user to authenticate in the CourseProfiler web app, then use the resulting USRProf artifact/profile when available. If no runner profile or evidence is available, import/analyze the course only and ask the user for runner evidence before creating a race plan.

## Request body

- RacePlanCreateRequest
  - `course` union, required: Exactly one of source or crsprof_artifact_id should be supplied.
    - object
      - `source` union, required
        - object
          - `kind` 'artifact', required
          - `artifact_id` string, required
        - object
          - `kind` 'url', required
          - `url` string, uri, required
        - object
          - `kind` 'raw_json', required
          - `payload` object, required
        - object
          - `kind` 'import_id', required
          - `import_id` string, required
      - `crsprof_artifact_id` string
      - `import_options` CrsprofImportOptions: GPX/FIT analysis flags. Existing CRSProf is preserved with analysis_options_not_applied warnings when flags are supplied.
        - `include_segments` boolean
        - `include_routes` boolean
      - `waypoints` union: Optional waypoint/resource enrichment to apply before segment generation. Use this for official aid stations, water/food/crew resources, cutoffs, and course points.
        - StructuredWaypointEnrichment
          - `mode` 'structured'
          - `distance_unit` 'm' | 'km' | 'mi'
          - `items` WaypointItem[], required
            - `name` string, required
            - `distance` number, required
            - `distance_unit` 'm' | 'km' | 'mi'
            - `kind` 'start' | 'finish' | 'aid' | 'checkpoint' | 'water' | 'crew' | 'other'
            - `resources` string[]
            - `cutoff` string: Total elapsed cutoff in HH:MM. HH may be greater than 24.
            - `cutoff_kind` 'enter' | 'leave'
            - `split_separator` boolean: Reserved for explicit split separator behavior; current enrichment preserves existing CourseProfiler semantics.
            - `notes` string
            - `source_text` string
            - `source_ref` string
        - TableWaypointEnrichment
          - `mode` 'table', required
          - `text` string: Copied official aid-station/course-point table text. Provide either text or table_url.
          - `table_url` string, uri: Public HTTPS URL for an official aid-station/course-point table, HTML page, or PDF. Provide either text or table_url.
          - `source_ref` string: Optional source URL/page/table reference.
          - `start_time` string: Optional race start time used when parsing clock-based cutoffs.
      - `segments` RacePlanSegmentsInput
        - `mode` 'generate' | 'skip'
        - `options` CrsprofSegmentOptions
          - `algorithm` 'GREEDY' | 'SLOPE_BASED' | 'PACE_BASED'
          - `flat_grade` number: Flat grade threshold in percent.
          - `slope_change_threshold` number: Slope-change threshold in percent.
          - `flat_max_delta` number: Optional maximum elevation delta for flat segments, in meters.
          - `minimum_distance` number: Minimum segment distance in meters.
          - `flexible_minimum_distance` boolean
          - `flexible_flat_max_delta` boolean
          - `instant_mode` boolean
          - `include_routes` boolean
    - object
      - `source` union
        - object
          - `kind` 'artifact', required
          - `artifact_id` string, required
        - object
          - `kind` 'url', required
          - `url` string, uri, required
        - object
          - `kind` 'raw_json', required
          - `payload` object, required
        - object
          - `kind` 'import_id', required
          - `import_id` string, required
      - `crsprof_artifact_id` string, required
      - `import_options` CrsprofImportOptions: GPX/FIT analysis flags. Existing CRSProf is preserved with analysis_options_not_applied warnings when flags are supplied.
        - `include_segments` boolean
        - `include_routes` boolean
      - `waypoints` union: Optional waypoint/resource enrichment to apply before segment generation. Use this for official aid stations, water/food/crew resources, cutoffs, and course points.
        - StructuredWaypointEnrichment
          - `mode` 'structured'
          - `distance_unit` 'm' | 'km' | 'mi'
          - `items` WaypointItem[], required
            - `name` string, required
            - `distance` number, required
            - `distance_unit` 'm' | 'km' | 'mi'
            - `kind` 'start' | 'finish' | 'aid' | 'checkpoint' | 'water' | 'crew' | 'other'
            - `resources` string[]
            - `cutoff` string: Total elapsed cutoff in HH:MM. HH may be greater than 24.
            - `cutoff_kind` 'enter' | 'leave'
            - `split_separator` boolean: Reserved for explicit split separator behavior; current enrichment preserves existing CourseProfiler semantics.
            - `notes` string
            - `source_text` string
            - `source_ref` string
        - TableWaypointEnrichment
          - `mode` 'table', required
          - `text` string: Copied official aid-station/course-point table text. Provide either text or table_url.
          - `table_url` string, uri: Public HTTPS URL for an official aid-station/course-point table, HTML page, or PDF. Provide either text or table_url.
          - `source_ref` string: Optional source URL/page/table reference.
          - `start_time` string: Optional race start time used when parsing clock-based cutoffs.
      - `segments` RacePlanSegmentsInput
        - `mode` 'generate' | 'skip'
        - `options` CrsprofSegmentOptions
          - `algorithm` 'GREEDY' | 'SLOPE_BASED' | 'PACE_BASED'
          - `flat_grade` number: Flat grade threshold in percent.
          - `slope_change_threshold` number: Slope-change threshold in percent.
          - `flat_max_delta` number: Optional maximum elevation delta for flat segments, in meters.
          - `minimum_distance` number: Minimum segment distance in meters.
          - `flexible_minimum_distance` boolean
          - `flexible_flat_max_delta` boolean
          - `instant_mode` boolean
          - `include_routes` boolean
  - `runner` union, required: Exactly one of usrprof_artifact_id or sources should be supplied. Use usrprof_artifact_id for an existing USRProf artifact/profile. Use sources for public HTTPS runner evidence/profile URLs or source_file artifacts created via /api/artifact-uploads. Strava-based profiles must be created after browser-based user authentication in the CourseProfiler web app, then supplied as a USRProf artifact/profile.
    - object
      - `usrprof_artifact_id` string, required
      - `sources` union[]
        - union
          - ArtifactInputSource
            - `kind` 'artifact', required
            - `artifact_id` string, required
          - UrlInputSource
            - `kind` 'url', required
            - `url` string, uri, required
      - `import_options` UsrprofImportOptions
        - `file_name` string
    - object
      - `usrprof_artifact_id` string
      - `sources` union[], required
        - union
          - ArtifactInputSource
            - `kind` 'artifact', required
            - `artifact_id` string, required
          - UrlInputSource
            - `kind` 'url', required
            - `url` string, uri, required
      - `import_options` UsrprofImportOptions
        - `file_name` string
  - `estimate` RacePlanEstimateInput
    - `options` EstimateOptions
      - `strategy` 'segment_evidence'
      - `gap_algorithm` 'POLYNOMIAL' | 'CONSTANT_POWER' | 'MINETTI'
      - `flat_grade` number: Optional flat-grade threshold passed to segment-evidence locomotion planning.
    - `output` EstimateOutputOptions
      - `include_summary_artifact` boolean
  - `pdf` RacePlanPdfInput
    - `enabled` boolean
    - `options` RacePlanPdfOptions
      - `title` string
      - `subtitle` string
      - `unit_system` 'metric' | 'imperial'
      - `file_name` string

## Response `202`

Async job accepted

- Job
  - `request_id` string: Current HTTP request correlation ID, also X-Request-Id; not an idempotency key.
  - `job_persistence` 'not_persisted' | 'confirmed' | 'unknown': Latest job metadata write outcome, not a current retrievability guarantee. Do not blindly retry unknown writes.
  - `submission_request_id` string: Original allocation request ID; omitted in legacy job metadata.
  - `job_id` string, required
  - `status` 'queued' | 'running' | 'succeeded' | 'failed' | 'expired' | 'cancelled', required
  - `status_url` string, required
  - `created_at` string, date-time, required
  - `started_at` string, date-time
  - `completed_at` string, date-time
  - `expires_at` string, date-time, required
  - `result_artifacts` Artifact[], required
    - `request_id` string: Current HTTP request correlation ID, also X-Request-Id; not an idempotency key.
    - `artifact_id` string, required
    - `type` 'source_file' | 'crsprof' | 'usrprof' | 'pdf' | 'json' | 'diagnostics', required
    - `content_type` string, required
    - `file_name` string
    - `size_bytes` integer
    - `download_url` string, uri: Private pre-signed URL. May be omitted for expired artifacts.
    - `download_url_expires_at` string, date-time
    - `created_at` string, date-time, required
    - `expires_at` string, date-time, required
    - `metadata` object
    - `warnings` ApiProblem[]
      - `code` string, required
      - `message` string, required
      - `severity` 'info' | 'warning' | 'error', required
      - `field` string
      - `details` object
  - `warnings` ApiProblem[], required
    - `code` string, required
    - `message` string, required
    - `severity` 'info' | 'warning' | 'error', required
    - `field` string
    - `details` object
  - `errors` ApiProblem[], required
    - `code` string, required
    - `message` string, required
    - `severity` 'info' | 'warning' | 'error', required
    - `field` string
    - `details` object
  - `progress` object

## Other responses

- `400`: Invalid request
- `404`: Resource not found
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/courseprofiler/apis/courseprofiler-public-api.md) · [All operations](https://skmtc.dev/courseprofiler/apis/courseprofiler-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/courseprofiler/apis/courseprofiler-public-api/revisions/e07403d7dba9?raw)
