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

# Create a teardown

> POST a new teardown via the public API.

Create a new teardown owned by your organization. The teardown is
published immediately into the appropriate active state — there's no
draft path through the public API.

<Note>
  The public API supports `asset_type` of `aircraft` or `engine` only.
  `apu` and `landing_gear` teardowns are UI-only today — existing rows
  of those types stay readable on GET, but cannot be created via this
  endpoint and sending either returns `422`.
</Note>

## Headers

<ParamField header="Authorization" type="string" required>
  `Bearer tdao_live_…` — your API key.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  Your organization's UUID. Must match the org the API key was minted in.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

## Body

The request body shape depends on the `asset_type` you pick. Five
fields are required on every request regardless of type, and each
type then has its own set of required and optional fields on top.

Sending a request that's missing a required field returns `422` with
a single error message naming every missing field, so you can fix
all of them in one round trip.

### Universal required fields (every asset\_type)

These five are required on every request, no matter which type you
are creating.

<ParamField body="asset_type" type="string" required>
  One of `aircraft` or `engine`. Sending `apu` or `landing_gear`
  returns `422`.
</ParamField>

<ParamField body="status" type="string" required>
  One of `Starting`, `In process`, or `Completed`. Case-sensitive.
  The teardown is published into the matching `active_*` state. See
  [status lifecycle](/concepts/status-lifecycle). UI label: "Status \*".
</ParamField>

<ParamField body="start_date" type="string (date)" required>
  ISO date. Stored as `teardown_start_date` — the column the detail
  page reads to render "Start Date". UI label: "Start Date \*".
</ParamField>

<ParamField body="location" type="string" required>
  Free-text teardown location, e.g. `Tucson, AZ`. UI label:
  "Teardown Location \*".
</ParamField>

<ParamField body="country" type="string" required>
  ISO country code, e.g. `US`. UI label: "Country \*".
</ParamField>

***

### `asset_type = "aircraft"`

Mirrors the UI wizard's aircraft teardown form exactly.

#### Required for aircraft

<ParamField body="aircraft_type" type="string" required>
  Human-readable name from the aircraft-types vocabulary, e.g.
  `A320-200`. Case-insensitive. Unknown values return
  `400 unknown_vocabulary` with the full allowed list in the body.
  UI label: "Aircraft \*" (on the type-selection step).
</ParamField>

<ParamField body="traceability" type="string" required>
  The last operator's name, e.g. `Delta`. UI label: "Aircraft
  Traceability \*" on the wizard, "Last Operator" in the marketplace
  browse table. Shown only on the aircraft form — the engine form
  hides this field, and the API mirrors that.
</ParamField>

<ParamField body="incident_related" type="string" required>
  `Yes` or `No`. UI label: "Incident Related \*". Aircraft-only —
  engines don't carry an incident-related concept, so the API and the
  UI both omit it from the engine flow.
</ParamField>

#### Optional for aircraft

<ParamField body="tail_number" type="string">
  Aircraft tail / registration number, e.g. `VT-ABC`. UI label:
  "Tail Number". Maps to DB column `teardowns.registration`.
</ParamField>

<ParamField body="msn" type="string">
  Manufacturer serial number. UI label: "MSN".
</ParamField>

<ParamField body="end_date" type="string (date)">
  Optional teardown end date. Not on the create wizard step but
  surfaced on the edit page and accepted on create for partners that
  already know it.
</ParamField>

<ParamField body="description" type="string">
  Free-text description shown in the Description card on the detail
  page. UI label: "Description".
</ParamField>

***

### `asset_type = "engine"`

Mirrors the UI wizard's engine teardown form exactly. Notably, the
engine form does **not** ask for `traceability` or `incident_related`,
and the API follows the same rule.

#### Required for engine

<ParamField body="engine_model" type="string" required>
  Human-readable name from the engine-models vocabulary, e.g.
  `CFM56-5B`. Case-insensitive. Unknown values return
  `400 unknown_vocabulary` with the full allowed list in the body.
  UI label: "Engine \*" (on the type-selection step).
</ParamField>

#### Optional for engine

<ParamField body="msn" type="string">
  Engine serial number. UI label: "ESN".
</ParamField>

<ParamField body="end_date" type="string (date)">
  Optional teardown end date. Not on the create wizard step but
  surfaced on the edit page and accepted on create for partners that
  already know it.
</ParamField>

<ParamField body="description" type="string">
  Free-text description shown in the Description card on the detail
  page. UI label: "Description".
</ParamField>

***

<Note>
  **`audience` is intentionally NOT on the create body** — it's a
  document-visibility decision. Set it on the upload call that attaches
  your first document, or via PATCH later. See
  [audience](/concepts/audience).
</Note>

<Warning>
  Strict mode is on for this endpoint. Sending an unknown body key
  returns `422` that tells you which key was wrong. Common stale names
  to avoid: `registration` (use `tail_number`), `location_country` (use
  `country`), `estimated_teardown_date` (use `start_date`),
  `aircraft_type_id` (use the name, not the UUID). The fields
  `location_city`, `sub_status`, `non_incident_statement`, `apu_model`,
  and `landing_gear_type` are NOT accepted on the public surface —
  sending any of them returns `422`.
</Warning>

## Response

`201 Created`. The body is the full teardown in the same shape every
GET returns. Save the `teardown_id` — you'll need it for upload,
PATCH, or transition calls.

<ResponseField name="teardown_id" type="string (UUID)">
  The new teardown's identifier.
</ResponseField>

<ResponseField name="org_id" type="string (UUID)">
  Same as the `X-Organization-Id` you sent.
</ResponseField>

<ResponseField name="asset_type" type="string">
  Echoed back.
</ResponseField>

<ResponseField name="aircraft_type" type="object">
  Full vocabulary row for whichever asset\_type you chose:
  `{ id, name, manufacturer, category, is_active, created_at }`. The
  `engine_model` field is populated symmetrically when relevant.
  Legacy teardowns may surface `apu_model` / `landing_gear_type` on
  reads, but neither is creatable through the public API.
</ResponseField>

<ResponseField name="status" type="string">
  Internal lifecycle name: `active_starting` / `active_in_process` /
  `active_completed`.
</ResponseField>

<ResponseField name="teardown_status_label" type="string">
  Human label: `Starting` / `In Process` / `Completed`.
</ResponseField>

<ResponseField name="published_at" type="string (datetime)">
  Set to "now" at create time.
</ResponseField>

<ResponseField name="expires_at" type="string (datetime)">
  Set to 90 days from publish. Auto-archives when reached.
</ResponseField>

<ResponseField name="documents" type="array | null">
  Always `null` immediately after create — populated by upload calls.
</ResponseField>

<ResponseField name="images" type="array | null">
  Always `null` immediately after create.
</ResponseField>

<ResponseField name="audience" type="array | null">
  Always `null` immediately after create.
</ResponseField>

<RequestExample>
  ```bash aircraft theme={null}
  curl -X POST "$base_url/public/v1/teardowns" \
    -H "Authorization: Bearer $api_key" \
    -H "X-Organization-Id: $org_id" \
    -H "Content-Type: application/json" \
    -d '{
      "asset_type": "aircraft",
      "status": "Starting",
      "aircraft_type": "A320-200",
      "traceability": "Delta",
      "incident_related": "No",
      "start_date": "2026-08-01",
      "location": "Tucson, AZ",
      "country": "US",
      "tail_number": "VT-ABC",
      "msn": "12345",
      "description": "Posted from ERP"
    }'
  ```

  ```bash engine theme={null}
  curl -X POST "$base_url/public/v1/teardowns" \
    -H "Authorization: Bearer $api_key" \
    -H "X-Organization-Id: $org_id" \
    -H "Content-Type: application/json" \
    -d '{
      "asset_type": "engine",
      "status": "Starting",
      "engine_model": "CFM56-5B",
      "start_date": "2026-08-01",
      "location": "Phoenix, AZ",
      "country": "US",
      "msn": "ESN-9876",
      "description": "Engine teardown posted from ERP"
    }'
  ```

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

  resp = requests.post(
      f"{base_url}/public/v1/teardowns",
      headers={
          "Authorization": f"Bearer {api_key}",
          "X-Organization-Id": org_id,
      },
      json={
          "asset_type": "aircraft",
          "status": "Starting",
          "aircraft_type": "A320-200",
          "traceability": "Delta",
          "incident_related": "No",
          "start_date": "2026-08-01",
          "location": "Tucson, AZ",
          "country": "US",
          "tail_number": "VT-ABC",
          "msn": "12345",
          "description": "Posted from ERP",
      },
  )
  resp.raise_for_status()
  teardown_id = resp.json()["teardown_id"]
  ```

  ```typescript node theme={null}
  const resp = await fetch(`${baseUrl}/public/v1/teardowns`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "X-Organization-Id": orgId,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      asset_type: "aircraft",
      status: "Starting",
      aircraft_type: "A320-200",
      traceability: "Delta",
      incident_related: "No",
      start_date: "2026-08-01",
      location: "Tucson, AZ",
      country: "US",
      tail_number: "VT-ABC",
      msn: "12345",
      description: "Posted from ERP",
    }),
  });
  if (!resp.ok) throw new Error(`${resp.status}: ${await resp.text()}`);
  const { teardown_id } = await resp.json();
  ```
</RequestExample>

<ResponseExample>
  ```jsonc 201 Created theme={null}
  {
    "teardown_id": "3c707051-021d-4d04-a8e7-4eb254e80858",
    "org_id": "72f95f4e-65bb-41b3-8ef4-226c5a59cbc1",
    "created_by": "afdb4059-345b-4adb-b8f1-4035668f22d7",
    "asset_type": "aircraft",
    "aircraft_type_id": "c0000000-0000-0000-0000-000000000002",
    "aircraft_type": {
      "id": "c0000000-0000-0000-0000-000000000002",
      "name": "A320-200",
      "manufacturer": "Airbus",
      "category": "Narrowbody",
      "is_active": true
    },
    "tail_number": "VT-ABC",
    "msn": "12345",
    "traceability": "Delta",
    "start_date": "2026-08-01",
    "end_date": null,
    "status": "active_starting",
    "teardown_status_label": "Starting",
    "incident_related": "No",
    "description": "Posted from ERP",
    "location": "Tucson, AZ",
    "country": "US",
    "audience": null,
    "published_at": "2026-05-26T20:06:50.677262Z",
    "expires_at": "2026-08-24T20:06:50.677262Z",
    "created_at": "2026-05-26T20:06:48.825931Z",
    "updated_at": "2026-05-26T20:06:48.825931Z"
  }
  ```

  ```jsonc 422 Missing per-type required fields theme={null}
  {
    "detail": [
      {
        "type": "value_error",
        "loc": ["body"],
        "msg": "Value error, asset_type='aircraft' requires the following fields: traceability, incident_related",
        "input": { /* the request body you sent */ }
      }
    ]
  }
  ```

  ```jsonc 400 Unknown vocabulary theme={null}
  {
    "detail": {
      "error_code": "unknown_vocabulary",
      "field": "aircraft_type",
      "value": "A320-poo",
      "valid_values": [
        "737-700", "737-800", "747-400", "A320-200", "A321-200", ...
      ],
      "message": "Unknown aircraft_type: 'A320-poo'. See valid_values."
    }
  }
  ```

  ```jsonc 422 Invalid status theme={null}
  {
    "detail": {
      "error_code": "invalid_status",
      "message": "status must be one of: Starting, In process, Completed",
      "got": "draft"
    }
  }
  ```

  ```jsonc 422 Unknown body key (strict mode) theme={null}
  {
    "detail": [
      {
        "type": "extra_forbidden",
        "loc": ["body", "registration"],
        "msg": "Extra inputs are not permitted",
        "input": "VT-ABC"
      }
    ]
  }
  ```
</ResponseExample>

## Common gotchas

* **`status` is case-sensitive.** `starting` returns 422.
* **Only aircraft and engine are accepted.** Sending `asset_type=apu`
  or `asset_type=landing_gear` returns 422. Those flows aren't shipped
  in the UI yet.
* **One vocabulary field per asset\_type.** Sending `engine_model` while
  `asset_type=aircraft` is silently ignored — only the matching field
  is read.
* **`audience` here returns 422.** Set it on the upload call or via
  PATCH.
* **`teardown_status_label` is RESPONSE only.** The server computes it
  from your `status`; sending it on the request body returns 422
  (strict mode).
* **The teardown auto-archives in 90 days.** Run a transition or PATCH
  a fresh `expires_at` to extend.

## See also

* [Home](/) for the smallest end-to-end flow.
* [Status lifecycle](/concepts/status-lifecycle) for the state machine.
* [Vocabularies](/concepts/vocabularies) for the lookup behaviour.
* [Attach documents](/api-reference/uploads/teardown-documents) — the
  natural next call after create.
