> ## 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.

# Images

> Upload and remove images on a teardown.

Image-specific endpoints for a teardown your org owns. Images are not
covered by the audience field they're visible to anyone who can see
the teardown.

## Upload

```http theme={null}
POST /public/v1/teardown/images/{teardown_id}
```

Attach an image. URL is appended to the teardown's `images[]` array.

### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer tdao_live_…`
</ParamField>

<ParamField header="X-Organization-Id" type="string" required />

<ParamField header="Content-Type" type="string" required>
  `multipart/form-data`
</ParamField>

### Form fields

<ParamField body="file" type="file" required>
  The image file.
</ParamField>

<Note>
  The `audience` form field is **not** accepted on image endpoints
  audience controls document visibility, not images. Sending it is
  silently ignored.
</Note>

### File rules

* **Max size:** 50 MB.
* **Allowed types:** JPEG, PNG, WEBP, GIF.
* **One file per call.**

### Storage path

```
{SUPABASE_URL}/storage/v1/object/public/teardowns-media/teardowns/<teardown_id>/images/<8-hex>_<filename>
```

### Response

`200 OK`:

```jsonc theme={null}
{
  "url": "https://.../teardowns-media/teardowns/<id>/images/abcd1234_photo.jpg",
  "filename": "photo.jpg",
  "size": 234567,
  "content_type": "image/jpeg"
}
```

The URL is also appended to `teardown.images[]` automatically.

<RequestExample>
  ```bash curl theme={null}
  curl -X POST "$base_url/public/v1/teardown/images/$teardown_id" \
    -H "Authorization: Bearer $api_key" \
    -H "X-Organization-Id: $org_id" \
    -F "file=@/absolute/path/to/photo.jpg"
  ```

  ```python python theme={null}
  import requests
  with open("photo.jpg", "rb") as f:
      resp = requests.post(
          f"{base_url}/public/v1/teardown/images/{teardown_id}",
          headers={"Authorization": f"Bearer {api_key}", "X-Organization-Id": org_id},
          files={"file": f},
      )
  resp.raise_for_status()
  ```
</RequestExample>

## Delete by URL

```http theme={null}
DELETE /public/v1/teardown/images/{teardown_id}/by-url?url=<encoded-url>
```

Removes a single image URL from `images[]`. Best-effort deletes the
underlying storage file.

### Query parameters

<ParamField query="url" type="string" required>
  The full image URL (URL-encoded).
</ParamField>

### Response

`200 OK`:

```jsonc theme={null}
{ "status": "ok" }
```

Idempotent.

<RequestExample>
  ```bash curl theme={null}
  encoded_url=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$IMG_URL")

  curl -X DELETE "$base_url/public/v1/teardown/images/$teardown_id/by-url?url=$encoded_url" \
    -H "Authorization: Bearer $api_key" \
    -H "X-Organization-Id: $org_id"
  ```
</RequestExample>

## Common errors

| Status | error\_code           | Why                                        |
| ------ | --------------------- | ------------------------------------------ |
| 400    | (no code)             | File empty, or content type isn't an image |
| 400    | `file_too_large`      | Over 50 MB                                 |
| 404    | (no code)             | Teardown isn't yours or doesn't exist      |
| 502    | `storage_unavailable` | Supabase Storage upstream issue retry      |

## See also

* [Teardown documents](/api-reference/uploads/teardown-documents) for
  PDFs / spreadsheets / etc.
* [Get a teardown](/api-reference/teardowns/get) to read back the
  `images[]` array.
