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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.theseus.us/micro-vps/autopilot-integration/developer-api/map-download.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
