> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rotastellar.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Plan

> Create a constraint-aware execution plan for a satellite and workload

<Note>
  **Base URL:** `https://rotastellar-cae.subhadip-mitra.workers.dev`
  — No API key required.
</Note>

## Request

<ParamField body="satellite_id" type="string" required>
  NORAD catalog ID (e.g., `"25544"` for ISS).
</ParamField>

<ParamField body="preset_id" type="string">
  Preset workload ID. One of: `onboard-ml-inference`, `split-learning`, `earth-observation-qa`, `federated-learning`, `resilient-store-forward`. Mutually exclusive with `custom_job`.
</ParamField>

<ParamField body="custom_job" type="object">
  Custom workload definition with arbitrary step DAG. Mutually exclusive with `preset_id`. See [Custom Workloads](/cae/custom-workloads) for the full schema.

  * `name` (string) — Workload name
  * `steps` (array, required) — Step definitions with dependencies
  * `security` (object) — Security policy overrides
  * `policy` (object) — Execution policy overrides
</ParamField>

<ParamField body="security" type="object">
  Override security settings from the preset or custom job:

  * `encryption` — `none`, `aes128`, or `aes256`
  * `data_classification` — `open`, `restricted`, or `confidential`
  * `require_authenticated_uplink` — boolean
  * `key_rotation_orbits` — number
</ParamField>

<ParamField body="options" type="object">
  Planning options:

  * `prediction_hours` (number, 1–48, default: 12) — Orbital prediction window
  * `min_elevation_deg` (number, 0–90, default: 5) — Minimum ground station elevation
</ParamField>

<Info>
  Either `preset_id` or `custom_job` is required, but not both.
</Info>

<RequestExample>
  ```bash Preset theme={null}
  curl -X POST https://rotastellar-cae.subhadip-mitra.workers.dev/v1/plan \
    -H "Content-Type: application/json" \
    -H "Origin: https://rotastellar.com" \
    -d '{
      "satellite_id": "25544",
      "preset_id": "onboard-ml-inference"
    }'
  ```

  ```bash Custom Job theme={null}
  curl -X POST https://rotastellar-cae.subhadip-mitra.workers.dev/v1/plan \
    -H "Content-Type: application/json" \
    -H "Origin: https://rotastellar.com" \
    -d '{
      "satellite_id": "25544",
      "custom_job": {
        "name": "Capture and Process",
        "steps": [
          {
            "id": "capture",
            "name": "Sensor Capture",
            "location": "onboard",
            "duration_s": 30,
            "depends_on": [],
            "requires": {"power_w": 40, "compute": 0.3, "thermal_w": 15, "memory_mb": 256, "storage_mb": 1024},
            "input_data_mb": 0,
            "output_data_mb": 500
          },
          {
            "id": "process",
            "name": "Ground Processing",
            "location": "ground",
            "duration_s": 60,
            "depends_on": ["capture"],
            "requires": {"power_w": 100, "compute": 1.0, "thermal_w": 50, "memory_mb": 2048, "storage_mb": 2048},
            "input_data_mb": 500,
            "output_data_mb": 50,
            "data_reduction_ratio": 0.1
          }
        ]
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://rotastellar-cae.subhadip-mitra.workers.dev/v1/plan",
      headers={
          "Content-Type": "application/json",
          "Origin": "https://rotastellar.com"
      },
      json={
          "satellite_id": "25544",
          "preset_id": "onboard-ml-inference"
      }
  )

  plan = response.json()
  print(f"Plan {plan['id']}: {plan['plan']['windows_used']} windows, "
        f"confidence {plan['error_budget']['delivery_confidence']}")
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "a92f33e8-a349-494c-8b4e-f87d859b9ec5",
    "created_at": "2026-03-05T06:55:37.811Z",
    "version": "1.0.0",
    "satellite": {
      "id": "25544",
      "name": "ISS (ZARYA)",
      "norad_id": 25544,
      "altitude_km": 417,
      "inclination_deg": 51.6,
      "period_min": 93,
      "tle_epoch": "26063.86671769"
    },
    "preset": {
      "id": "onboard-ml-inference",
      "name": "On-Board ML Inference",
      "category": "ml-inference",
      "steps": 4
    },
    "orbital_environment": {
      "prediction_start": "2026-03-05T06:55:37.811Z",
      "prediction_hours": 12,
      "eclipse_fraction": 0.348,
      "bus": { "..." : "..." },
      "windows": [ "..." ],
      "ground_passes": [ "..." ],
      "summary": {
        "total_windows": 14,
        "comms_windows": 6,
        "sunlit_windows": 9,
        "eclipse_windows": 5,
        "total_pass_time_s": 2847,
        "ground_stations_visible": 8
      }
    },
    "placement_decisions": [
      { "step_id": "capture", "location": "onboard", "reason": "preset_defined" },
      { "step_id": "preprocess", "location": "onboard", "reason": "preset_defined" },
      { "step_id": "inference", "location": "onboard", "reason": "preset_defined" },
      { "step_id": "encrypt_results", "location": "onboard", "reason": "preset_defined" }
    ],
    "transfer_schedule": {
      "transfers": [ "..." ],
      "total_transfers": 1,
      "total_downlink_mb": 11.03,
      "total_uplink_mb": 0,
      "passes_used": 1,
      "total_transfer_time_s": 12
    },
    "error_budget": {
      "worst_case_ber": 0.00001,
      "total_fec_overhead_mb": 0.53,
      "total_retransmission_reserve_mb": 0.11,
      "delivery_confidence": 0.997
    },
    "security_summary": {
      "encryption": "aes256",
      "total_encryption_overhead_mb": 0.53,
      "total_key_exchanges": 1,
      "data_classification": "restricted"
    },
    "plan": {
      "segments": [ "..." ],
      "total_duration_s": 1382,
      "total_compute_s": 170,
      "total_transfer_s": 12,
      "total_ground_s": 0,
      "windows_used": 2,
      "policy": {
        "objective": "min_latency",
        "deadline_orbits": 3
      }
    },
    "events": [ "..." ]
  }
  ```

  ```json 400 Validation Error theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "satellite_id is required"
    }
  }
  ```

  ```json 422 Planning Failed theme={null}
  {
    "error": {
      "code": "planning_failed",
      "message": "No feasible window for step 'inference': thermal constraint exceeded"
    }
  }
  ```
</ResponseExample>

## Errors

| Status | Code                 | Description                             |
| ------ | -------------------- | --------------------------------------- |
| 400    | `validation_error`   | Invalid request body or parameters      |
| 400    | `invalid_body`       | Request body is not valid JSON          |
| 403    | `origin_not_allowed` | Request from disallowed origin          |
| 422    | `planning_failed`    | Planner cannot find a feasible schedule |
| 500    | `internal_error`     | Unexpected server error                 |
