> For the complete documentation index, see [llms.txt](https://docs.theseus.us/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.theseus.us/micro-vps/autopilot-integration/developer-api/map-download.md).

# Map Download

This document describes how to authenticate, list available maps, and download a map archive using raw HTTP requests against the Supabase backend.

All Supabase requests require the project URL and anon key, referred to as `SUPABASE_URL` and `SUPABASE_ANON_KEY` throughout this document.

{% stepper %}
{% step %}

### Authenticate

#### `POST /auth/v1/token?grant_type=password`

Exchange an email and password for a session containing an access token.

**Headers:**

| Header         | Value               |
| -------------- | ------------------- |
| `apikey`       | `SUPABASE_ANON_KEY` |
| `Content-Type` | `application/json`  |

**Body:**

```json
{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Example:**

```
POST <SUPABASE_URL>/auth/v1/token?grant_type=password
apikey: <SUPABASE_ANON_KEY>
Content-Type: application/json

{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Response 200:**

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "expires_at": 1711324800,
  "refresh_token": "v1.MjQ3...",
  "user": {
    "id": "d0d8c19e-1b2a-4c3d-8e4f-5a6b7c8d9e0f",
    "email": "pilot@example.com",
    "role": "authenticated"
  }
}
```

Save the `access_token` — every subsequent request uses it as a Bearer token.
{% endstep %}

{% step %}

### List Maps

#### `GET /rest/v1/map_requests`

Fetch all non-deleted map requests for the authenticated user, joined with their results. Row-Level Security (RLS) restricts rows to those owned by the caller or shared via an organization.

**Headers:**

| Header          | Value                   |
| --------------- | ----------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`     |
| `Authorization` | `Bearer <access_token>` |
| `Accept`        | `application/json`      |
| `Prefer`        | `count=exact`           |

**Query parameters:**

| Parameter    | Value              | Purpose                              |
| ------------ | ------------------ | ------------------------------------ |
| `select`     | `*,map_results(*)` | Join `map_results` onto each request |
| `deleted_at` | `is.null`          | Exclude soft-deleted maps            |
| `order`      | `created_at.desc`  | Newest first                         |
| `limit`      | `50`               | Page size (optional, default varies) |
| `offset`     | `0`                | Pagination offset (optional)         |

**Example:**

```
GET <SUPABASE_URL>/rest/v1/map_requests?select=*,map_results(*)&deleted_at=is.null&order=created_at.desc&limit=50&offset=0
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Accept: application/json
Prefer: count=exact
```

**Response 200:**

The response is a JSON array. Each element is a `map_request` row with a nested `map_results` object (or `null` if the map is still processing).

```json
[
  {
    "id": 42,
    "user_id": "d0d8c19e-1b2a-4c3d-8e4f-5a6b7c8d9e0f",
    "workflow_id": "wf-abc-123",
    "area_name": "Downtown Austin",
    "status": "completed",
    "percent_complete": 100,
    "status_message": null,
    "sw_lat": 30.26,
    "sw_lon": -97.75,
    "ne_lat": 30.28,
    "ne_lon": -97.73,
    "geometry_geojson": null,
    "home_position": { "lat": 30.27, "lon": -97.74, "alt": 0 },
    "created_at": "2026-03-20T14:30:00Z",
    "started_at": "2026-03-20T14:31:00Z",
    "completed_at": "2026-03-20T15:05:00Z",
    "failed_at": null,
    "deleted_at": null,
    "error_message": null,
    "area_km2": 1.25,
    "org_id": null,
    "map_results": {
      "id": 99,
      "request_id": 42,
      "unique_id": "map-1710942300000",
      "download_url": "https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz",
      "compressed_size_mb": 85.4,
      "compressed_file": "map-1710942300000_Downtown_Austin.tar.gz",
      "upload_path": "/maps/map-1710942300000_Downtown_Austin",
      "checksum": "e3b0c44298fc1c149afbf4c8996fb924...",
      "created_at": "2026-03-20T15:05:00Z"
    }
  },
  {
    "id": 41,
    "workflow_id": "wf-def-456",
    "area_name": "Lake Travis",
    "status": "processing",
    "percent_complete": 63,
    "map_results": null
  }
]
```

**Key fields for download:**

A map is downloadable when `map_results` is **not** `null` and `map_results.download_url` is present. Maps where `map_results` is `null` are still being generated.

| Field                            | Description                                              |
| -------------------------------- | -------------------------------------------------------- |
| `workflow_id`                    | Unique identifier for this map generation job            |
| `area_name`                      | Human-readable name of the mapped area                   |
| `status`                         | `queued`, `started`, `processing`, `completed`, `failed` |
| `map_results.download_url`       | Direct URL to the `.tar.gz` archive                      |
| `map_results.compressed_size_mb` | Archive size in megabytes                                |
| `map_results.checksum`           | SHA-256 hex digest for integrity verification            |
| `map_results.unique_id`          | Unique map identifier used in the archive filename       |

The `content-range` response header contains the total count (e.g., `0-49/73`) when `Prefer: count=exact` is set, useful for pagination.
{% endstep %}

{% step %}

### Get Download Link for a Specific Map

#### `GET /rest/v1/map_requests`

If you already know the `workflow_id`, you can fetch just the download-relevant fields.

**Query parameters:**

| Parameter     | Value                                                             |
| ------------- | ----------------------------------------------------------------- |
| `select`      | `area_name,map_results(download_url,compressed_size_mb,checksum)` |
| `workflow_id` | `eq.<workflow_id>`                                                |
| `deleted_at`  | `is.null`                                                         |

**Example:**

```
GET <SUPABASE_URL>/rest/v1/map_requests?select=area_name,map_results(download_url,compressed_size_mb,checksum)&workflow_id=eq.wf-abc-123&deleted_at=is.null
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Accept: application/vnd.pgrst.object+json
```

The `Accept: application/vnd.pgrst.object+json` header tells PostgREST to return a single object instead of an array (equivalent to `.single()` in the JS client).

**Response 200:**

```json
{
  "area_name": "Downtown Austin",
  "map_results": {
    "download_url": "https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz",
    "compressed_size_mb": 85.4,
    "checksum": "e3b0c44298fc1c149afbf4c8996fb924..."
  }
}
```

{% endstep %}

{% step %}

### Download the Map File

#### `GET <download_url>`

Download the `.tar.gz` archive directly from the URL returned in `map_results.download_url`. This request requires **no authentication** — the URL is publicly accessible.

**Headers:** None required.

**Example:**

```
GET https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz
```

**Response 200:**

Raw binary stream of the `.tar.gz` file. The `Content-Length` header indicates the total size in bytes.

**Post-download verification:**

If `map_results.checksum` was provided, verify the downloaded file's integrity by computing its SHA-256 hash and comparing:

```bash
sha256sum map-1710942300000_Downtown_Austin.tar.gz
# should match the checksum value from the API response
```

{% endstep %}
{% endstepper %}

## Complete Flow Summary

```mermaid
flowchart TD
    A["POST /auth/v1/token?grant_type=password
    Body: { email, password }
    ← access_token"] --> B

    B["GET /rest/v1/map_requests?select=\*,map_results(*)
    Headers: Authorization: Bearer access_token
    ← Array of map requests with nested results"] --> C

    C{"Find a completed map where
    map_results is not null"}
    C -- "map_results exists" --> D
    C -- "map_results is null" --> W["Map still processing — poll again"]
    W -.-> B

    D["Extract map_results.download_url
    and map_results.checksum"] --> E

    E["GET download_url
    No auth required
    ← .tar.gz binary stream"] --> G

    G["Verify: sha256(file) == map_results.checksum"]
    G -- Match --> H["Done ✓"]
    G -- Mismatch --> I["Re-download or abort"]
```

### Expected filename format

The archive filename follows the pattern:

```
<unique_id>_<area_name>.tar.gz
```

For example: `map-1710942300000_Downtown_Austin.tar.gz`
