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

# Maneuver API

> Create, read, edit, delete, import, and export stored spacecraft maneuvers with the 2.0.0 contract.

> For the complete documentation index, see [llms.txt](/llms.txt).

The Maneuver API manages stored spacecraft maneuver records at **`https://api.valar.space`**.
Each record belongs to one spacecraft and contains a name, optional metadata, and burn segments.
Creating a record stores data in VALAR; it does not uplink a command to a spacecraft.

## Maneuver contract version 2.0.0

Download the [standalone OpenAPI contract](/api-reference/contracts/maneuvers-v1.openapi.json)
to generate a client or pin an integration. The filename remains `maneuvers-v1.openapi.json`
to preserve the artifact URL; **contract metadata, not the filename, determines the version**.
The standalone document's `info.version` is `2.0.0`. The complete live customer API at
[`/api-docs/mintlify`](https://api.valar.space/api-docs/mintlify) identifies the maneuver revision
with `x-valar-maneuver-api-version`; each maneuver operation carries `x-valar-contract-version`.

Versioning is in the contract rather than a URL version segment. The seven public operations are:

| Method | Path                                 | Purpose                                        |
| ------ | ------------------------------------ | ---------------------------------------------- |
| GET    | `/operations/maneuvers`              | List records by spacecraft and time window     |
| POST   | `/operations/maneuvers`              | Create a batch of records atomically           |
| GET    | `/operations/maneuvers/{maneuverId}` | Read complete editable details                 |
| PUT    | `/operations/maneuvers/{maneuverId}` | Replace editable fields of one record          |
| DELETE | `/operations/maneuvers/{maneuverId}` | Delete one record                              |
| POST   | `/operations/maneuvers/import`       | Add records from OCM files                     |
| GET    | `/operations/maneuvers/export`       | Export selected IDs or a spacecraft/time range |

The separate [maneuver planner](/features/planner), approval, and orbit-raise workflows remain
available in the application and are outside this published API.

## Authenticate a mission-control integration

Use an [API key](/features/api-keys) issued for your organization and granted the **Operations**
environment. Send `Authorization: Bearer $VALAR_API_KEY` on every request. The key inherits its
owner's resource permissions, and requests are recorded in the API-key usage audit log.
Organization and environment are derived from authentication and the route.

| Operation                 | Required permission       |
| ------------------------- | ------------------------- |
| Read or export            | `read:plannedmaneuvers`   |
| Create, update, or import | `write:plannedmaneuvers`  |
| Delete                    | `delete:plannedmaneuvers` |

These permission identifiers retain their existing spelling; they do not classify records.
Look up the **VALAR spacecraft ID** before making maneuver requests:

```bash theme={null}
curl --fail-with-body 'https://api.valar.space/operations/spacecraft' \
  --header "Authorization: Bearer $VALAR_API_KEY"
```

Use that returned ID as `SPACECRAFT_ID` below. Names, NORAD IDs, COSPAR IDs, and external keys
are lookup filters, not substitutes for the VALAR spacecraft ID on CRUD requests.

## Create a maneuver

`POST /operations/maneuvers` accepts a `maneuvers` array. The initial `name` establishes the
stable `maneuverId` within its spacecraft and the editable name. Creation names have a maximum
length of 63 characters. Each record requires at least one burn segment with an ISO-8601
`referenceEpoch`, positive finite `duration`, nonzero finite thrust, and `thrISP` between 50
and 10,000 seconds. Record and segment `attitudeMode` values are required reference frames;
supported examples include `TNW`, `RTN`, `QSW`, and `RSW_ROTATING`. Supply physically consistent
values for your burn definition.

```bash theme={null}
curl --fail-with-body 'https://api.valar.space/operations/maneuvers' \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --header 'Content-Type: application/json' \
  --data @- <<EOF
{
  "maneuvers": [{
    "spacecraftId": "$SPACECRAFT_ID",
    "name": "BURN-001",
    "purpose": "[ORBIT]",
    "attitudeMode": "RSW_ROTATING",
    "composition": [{
      "referenceEpoch": "2026-10-01T12:00:00Z",
      "duration": 60,
      "thrX": 0, "thrY": 0.1, "thrZ": 0,
      "thrISP": 1500,
      "attitudeMode": "RSW_ROTATING"
    }]
  }]
}
EOF
```

The example uses seconds for duration and ISP and newtons for thrust. Acceleration, delta-V,
and delta mass are read-only projections in detail responses (`null` when unavailable).
Supply every thrust component explicitly, including zero components; missing physical inputs are not interpreted as zero.
Those fields are ignored on writes; edit the intrinsic burn definition instead. Consult the
generated schema for every field.
Success is **201** with `createdCount` and `message`. The JSON batch is atomic: if any record
fails validation or conflicts, none of that batch is created. An existing identity is a
conflict; creation never acts as an update.

## Find and read records

List records using comma-separated or repeated `spacecraftIds` (1–50 IDs) and ISO-8601 instants.
`endDate` must be after `startDate`:

```bash theme={null}
curl --fail-with-body --get 'https://api.valar.space/operations/maneuvers' \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --data-urlencode "spacecraftIds=$SPACECRAFT_ID" \
  --data-urlencode 'startDate=2026-10-01T00:00:00Z' \
  --data-urlencode 'endDate=2026-10-02T00:00:00Z'
```

The **200** response contains `items` and `totalCount`, including an empty list when there are
no matching records. List items contain `maneuverId`, `displayId`, editable `name`, the actual
`spacecraftId`, derived projection fields, and `conflictingManeuverIds`.

Read a record's complete intrinsic burn segments before editing it:

```bash theme={null}
curl --fail-with-body --get 'https://api.valar.space/operations/maneuvers/BURN-001' \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --data-urlencode "spacecraftId=$SPACECRAFT_ID" \
  --output maneuver.json
```

The **200** detail response contains `maneuverId`, `displayId`, `spacecraftId`, `name`,
`purpose`, `attitudeMode`, full `composition`, and `conflictingManeuverIds`. `displayId` is a
separate display identifier. Always address a record by **`maneuverId` plus `spacecraftId`**;
URL-encode path values when necessary.

## Update the same record

`PUT /operations/maneuvers/{maneuverId}` replaces **all editable fields**, not just fields that
changed. Send `name`, `purpose`, `attitudeMode`, and the complete `composition`. Include metadata
you want to retain; omitted optional metadata is cleared. The name can be changed to a nonblank
label of up to 255 characters. Identity, display ID, and spacecraft association remain fixed.

For example, use `jq` to build an update from the detail response, preserving all segments and
metadata while changing the name:

```bash theme={null}
jq '{name: "North-south correction", purpose, attitudeMode, composition}' \
  maneuver.json > maneuver-update.json

curl --fail-with-body --request PUT \
  "https://api.valar.space/operations/maneuvers/BURN-001?spacecraftId=$SPACECRAFT_ID" \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --header 'Content-Type: application/json' \
  --data @maneuver-update.json
```

Success is **200** with the complete updated record. Its `maneuverId` is still `BURN-001`.
To change timing or physical values, edit the corresponding composition fields in the update
body. Start and end derive from the segments; provide the intended complete burn definition.

## Resolve conflicts

Creates, imports, and updates must not overlap another stored maneuver on the same spacecraft.
The check uses each record's enclosing start-to-end interval, including gaps between segments.
Adjoining intervals may touch: a record ending at 12:01 may be followed by one starting at 12:01.
Records on different spacecraft do not conflict with each other.

Duplicate identities and overlaps with stored records return **409 `MANEUVER_CONFLICT`**.
Read the conflicting records and choose an ordinary edit or delete to resolve them. No other
record is automatically merged, shortened, replaced, or deleted. A conflict may be outside
your current list window.

Pre-existing overlapping records are preserved and marked with `conflictingManeuverIds` in
list and detail responses. They remain readable, exportable, and deletable. Every update,
including a name-only edit, must leave the updated record non-overlapping. Previously computed
results retain their captured maneuver data; later computations use current records under
their existing selection rules.

## Delete a record

```bash theme={null}
curl --fail-with-body --request DELETE \
  "https://api.valar.space/operations/maneuvers/BURN-001?spacecraftId=$SPACECRAFT_ID" \
  --header "Authorization: Bearer $VALAR_API_KEY"
```

Success is **204** with no response body. Deletion removes the live record and cannot be undone.

## Import and export OCM files

Import adds records through one generic CCSDS OCM flow:

```bash theme={null}
curl --fail-with-body \
  "https://api.valar.space/operations/maneuvers/import?spacecraftId=$SPACECRAFT_ID" \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --form 'files=@burns.ocm'
```

Repeat the multipart **`files`** part for several files. `spacecraftId` is optional when file
metadata resolves the target unambiguously. Files require thrust, ISP, and physical wet mass;
see [OCM import requirements](/file-formats/ocm#valar-import-requirements). `MAN_BASIS` is optional
format metadata and has no effect on stored-record behavior. Exports omit it. OCM `MAN_ID`
remains the stable identity, even after the editable name changes.

Each file commits atomically and independently. **200** means every file succeeded; **207**
contains per-file successes and failures, including ambiguous targets. Inspect `successCount`,
`failureCount`, and `results`. An all-ambiguous batch also returns 207. If every file fails
without ambiguity, the request returns an error, such as 400 for invalid input or 409 for a
stored-record conflict. A failed file adds none of its records; successful files remain saved.
Reimporting an existing `MAN_ID` fails: use PUT to edit an existing record.

Optional `X-Content-Hash` and `X-Resolution-Fingerprint` headers bind an import to a previously
inspected file. A stale preview returns **409 `IMPORT_STALE_PREVIEW`**. Direct callers may omit
these headers. Submit preview-bound files individually rather than binding a multi-file batch
to one preview.

All exports use `GET /operations/maneuvers/export`. Export one record by supplying one
`spacecraftIds` value and one `maneuverIds` value, for example
`/operations/maneuvers/export?spacecraftIds=SC-1&maneuverIds=BURN-001`.
For selected IDs, use the collection GET with exactly one `spacecraftIds` value and 1–100
unique `maneuverIds`. Repeat the `maneuverIds` parameter for each identity; commas inside a
value are literal name characters, not separators. One ID produces `text/plain` OCM;
multiple IDs produce `application/zip`. No request body or `format` parameter is needed.

```bash theme={null}
curl --fail-with-body --get 'https://api.valar.space/operations/maneuvers/export' \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --data-urlencode "spacecraftIds=$SPACECRAFT_ID" \
  --data-urlencode 'maneuverIds=BURN-001' \
  --data-urlencode 'maneuverIds=BURN-002' \
  --output maneuvers.zip
```

Export by range using exact instants (no expansion to calendar-day boundaries):

```bash theme={null}
curl --fail-with-body --get 'https://api.valar.space/operations/maneuvers/export' \
  --header "Authorization: Bearer $VALAR_API_KEY" \
  --data-urlencode "spacecraftIds=$SPACECRAFT_ID" \
  --data-urlencode 'startDate=2026-10-01T00:00:00Z' \
  --data-urlencode 'endDate=2026-10-02T00:00:00Z' \
  --output maneuvers.ocm
```

Range export accepts 1–50 spacecraft IDs (repeated or comma-separated) and requires both time
bounds, with `endDate` after `startDate`. One spacecraft produces OCM text; multiple spacecraft
produce a ZIP of OCM files. ID and time-range selection are mutually exclusive: do not send
time bounds with `maneuverIds`. Missing, empty, or mixed selections return 400. URI size limits
still apply; divide a large selection into smaller GET requests if its encoded URL is too long.
Integration software owns downstream submission and uplink procedures.

## Handle errors and retries

Resource, validation, and permission errors use VALAR's shared error envelope, including
`timestamp`, `status`, `error`, `message`, and `path`, with structured details where available.
Use the machine-readable error token and HTTP status instead of parsing prose messages.

| Status | Integration response                                                      |
| ------ | ------------------------------------------------------------------------- |
| 400    | Correct invalid input, file syntax, or physical values.                   |
| 401    | Check for a missing, invalid, expired, revoked key, or disabled owner.    |
| 403    | Check owner permissions, environment grant, and IP allowlist.             |
| 404    | Check record IDs and their owning spacecraft/organization.                |
| 409    | Resolve `MANEUVER_CONFLICT`, or inspect again for `IMPORT_STALE_PREVIEW`. |
| 413    | Reduce file or total multipart request size.                              |
| 422    | Correct the unmet precondition reported in the error details.             |
| 5xx    | Preserve request details and any correlation ID for diagnosis.            |

GET requests can be retried with bounded backoff. Creation and import have no idempotency-key
contract: if a connection drops after submission, read existing records before repeating the
write. Do not retry successful files in a 207 batch. After an uncertain PUT or DELETE result,
read the addressed record to establish its current state before issuing another write.

## Migration to 2.0.0

This is a coordinated clean replacement of the 1.0.0 stored-maneuver contract, without a
temporary compatibility period. Update integration clients together with the API deployment:

1. Regenerate or update clients using contract **2.0.0**. All stored-record requests use
   `/operations/maneuvers`; the former `/api/v1/...` and `/v1/...` stored-maneuver routes are retired.
2. Remove the JSON `basis` field and classification-dependent logic. Lists expose editable
   `name` and `conflictingManeuverIds`; `spacecraftId` is the actual VALAR ID. Persist the stable
   `maneuverId` separately from the editable name.
3. Read complete records with **GET detail**, then send all editable fields with **PUT detail**.
   The canonical `report-file` and `/{maneuverId}/report-epoch` routes are retired. Timing changes
   are explicit segment edits, with no inferred trimming or lifecycle transition.
4. Use the single `/import` route only to add records. Remove `overwrite` and merge-strategy
   options. Duplicate identities and overlaps are rejected; an existing record requires an
   explicit PUT. Handle both whole-request conflicts and per-file failures.
5. Treat incoming `MAN_BASIS` only as optional file-format metadata. No uniform value is
   required across a file, and exported records omit it.
6. Review preserved historical conflicts through list/detail `conflictingManeuverIds`. Existing
   identities, display IDs, and burn contents are retained. Resolve conflicts by explicit edits
   or deletion before saving an affected record; a metadata-only edit must also satisfy the
   non-overlap rule. Historical computation results keep their original captured meaning.
7. Remove calls to `/operations/maneuvers/projections/refresh`. Projection maintenance remains
   internal application functionality and has no public or private HTTP endpoint.
8. Replace `POST /operations/maneuvers/export` with the collection GET shown above. Move the
   body `ids` to repeated `maneuverIds` parameters, use plural `spacecraftIds`, and omit the JSON
   body and `format`. The removed POST returns 405. Regenerated SDK clients use
   `exportOperationsManeuvers` for both selection modes.
9. Replace the single-record `/{maneuverId}/export?spacecraftId=...` shortcut with
   `/export?spacecraftIds=...&maneuverIds=...`. The shortcut is removed and returns 404;
   the same collection endpoint now handles one record, selected records, and time ranges.

Authentication and permission identifiers remain the same. The standalone snapshot keeps its
existing filename and URL; verify its `info.version` when updating a pinned client.
