> ## 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 sales / lease / exchange listing.

Image endpoints for a listing your org owns. Images aren't covered by
the audience field they're visible to anyone who can see the
listing.

## Upload

```http theme={null}
POST /public/v1/sales-lease-exchange/images/{listing_id}
```

### 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` field is **not** accepted on listing images audience
  applies to documents only.
</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/listings/<listing_id>/images/<8-hex>_<filename>
```

### Response

`200 OK`:

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

The URL is appended to `listing.images[]` automatically.

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

  ```python python theme={null}
  import requests
  with open("engine.jpg", "rb") as f:
      resp = requests.post(
          f"{base_url}/public/v1/sales-lease-exchange/images/{listing_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/sales-lease-exchange/images/{listing_id}/by-url?url=<encoded-url>
```

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

### Query parameters

<ParamField query="url" type="string" required>
  URL-encoded.
</ParamField>

### Response

`200 OK`:

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

<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/sales-lease-exchange/images/$listing_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)             | Listing isn't yours or doesn't exist       |
| 502    | `storage_unavailable` | Supabase Storage upstream issue retry      |

## See also

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