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

# Downloading Files

> Download files from gs:// URLs returned in your flow execution outputs.

When SuperAI Flows processes documents, task outputs often include file references as `gs://` URLs. These point to files stored in Google Cloud Storage, but you don't need to understand GCS internals — the File API handles everything.

Example `gs://` URL in a task output:

```json theme={null}
{
  "document_url": "gs://superai-file-upload-prod-eu/flows/255a17d0-5c21-47d9-8e0b-a08b48436f0a/documents/f8230da2-4136-4ffb-acfe-db9318970ea2"
}
```

## Prerequisites

* **curl** — for making HTTP requests
* **jq** — for parsing JSON in shell scripts (optional, but recommended)
* **A SuperAI Flows API key** — get one from the dashboard under **Settings → API Keys**

## Quick start

Download a file in one command:

```bash theme={null}
curl -f -L -H "X-API-Key: saf_your_api_key" \
  "https://flows.super.ai/api/files/download?uri=gs://superai-file-upload-prod-eu/flows/255a17d0-5c21-47d9-8e0b-a08b48436f0a/documents/f8230da2-4136-4ffb-acfe-db9318970ea2" \
  -o downloaded_file.pdf
```

* The `-L` flag tells curl to follow the redirect to the actual download URL.
* The `-f` flag makes curl fail with an error instead of saving error responses as your file.

## Authentication

Both file endpoints require authentication. You can use either method:

| Method                    | Header          | Example                             |
| ------------------------- | --------------- | ----------------------------------- |
| **API Key** (recommended) | `X-API-Key`     | `X-API-Key: saf_abc123...`          |
| **JWT Token**             | `Authorization` | `Authorization: Bearer eyJhbGci...` |

About API keys:

* API keys start with the `saf_` prefix (e.g., `saf_org_550e8400_abc123...`).
* Get your API key from the dashboard under **Settings → API Keys**.
* Store keys in environment variables — never hardcode them in scripts.

About JWT tokens: obtained by authenticating with your email and password. See the [API Quickstart](/quickstart#authentication). Tokens expire after 1 hour and must be refreshed.

## Two ways to download

### Option 1: Direct download (GET)

Use `GET /api/files/download` for the simplest approach. This endpoint redirects you to a temporary signed URL.

```bash theme={null}
curl -f -L -H "X-API-Key: $SAF_API_KEY" \
  "https://flows.super.ai/api/files/download?uri=gs://bucket/path/to/file" \
  -o output_file.pdf
```

<Note>
  If your URI contains special characters, URL-encode it first:

  ```bash theme={null}
  ENCODED_URI=$(printf '%s' "$GS_URL" | jq -sRr @uri)
  curl -f -L -H "X-API-Key: $SAF_API_KEY" \
    "https://flows.super.ai/api/files/download?uri=$ENCODED_URI" -o file.pdf
  ```
</Note>

| Parameter  | Required | Description                                                        |
| ---------- | -------- | ------------------------------------------------------------------ |
| `uri`      | Yes      | The `gs://` URL from your task output (URL-encoded)                |
| `redirect` | No       | Set to `false` to get JSON instead of a redirect (default: `true`) |

**Response:** 302 redirect to a pre-signed download URL (valid for 1 hour).

### Option 2: Resolve URL first (POST)

Use `POST /api/files/resolve` when you need the download URL for further processing or want to keep URIs out of server logs.

```bash theme={null}
curl -X POST "https://flows.super.ai/api/files/resolve" \
  -H "X-API-Key: $SAF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"uri": "gs://bucket/path/to/file"}'
```

```json theme={null}
{
  "download_url": "https://storage.googleapis.com/bucket/path/to/file?X-Goog-Algorithm=...",
  "expires_at": "2025-01-15T15:00:00+00:00",
  "expires_in_seconds": 3600
}
```

Then download using the returned URL:

```bash theme={null}
curl -o output_file.pdf "https://storage.googleapis.com/bucket/path/to/file?X-Goog-Algorithm=..."
```

## Complete example: download after flow completion

This script executes a flow, waits for completion, and downloads any output files:

```bash theme={null}
#!/bin/bash
set -e

# Configuration — use environment variables for API key
API_KEY="${SAF_API_KEY:-saf_your_api_key}"
FLOW_ID="your-flow-id"
BASE_URL="https://flows.super.ai"
MAX_ATTEMPTS=60  # 5 minutes timeout (60 * 5 seconds)

# 1. Create flow execution
echo "Starting flow execution..."
EXEC_RESPONSE=$(curl -s -X POST "$BASE_URL/api/flow-executions" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"flow_id\": \"$FLOW_ID\",
    \"input\": {
      \"filename\": \"invoice.pdf\",
      \"mime_type\": \"application/pdf\",
      \"document_url\": \"https://cdn.super.ai/invoice-example.pdf\"
    }
  }")

EXEC_ID=$(echo "$EXEC_RESPONSE" | jq -r .id)
if [ "$EXEC_ID" = "null" ] || [ -z "$EXEC_ID" ]; then
  echo "Failed to create execution. Response: $EXEC_RESPONSE"
  exit 1
fi
echo "Execution started: $EXEC_ID"

# 2. Wait for completion
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
  STATUS=$(curl -s "$BASE_URL/api/flow-executions/$EXEC_ID" \
    -H "X-API-Key: $API_KEY" | jq -r .status)
  echo "Status: $STATUS (attempt $((ATTEMPT + 1))/$MAX_ATTEMPTS)"

  if [ "$STATUS" = "completed" ]; then break
  elif [ "$STATUS" = "failed" ]; then echo "Execution failed!"; exit 1; fi

  ATTEMPT=$((ATTEMPT + 1))
  sleep 5
done

# 3. Get task outputs
RESULTS=$(curl -s "$BASE_URL/api/task-executions?flow_execution_id=$EXEC_ID" \
  -H "X-API-Key: $API_KEY")

# 4. Download any gs:// URLs from the output (requires jq)
echo "$RESULTS" | jq -r '.. | select(type == "string" and startswith("gs://"))' 2>/dev/null | while read -r GS_URL; do
  if [ -n "$GS_URL" ]; then
    FILENAME=$(basename "$GS_URL")
    ENCODED_URI=$(printf '%s' "$GS_URL" | jq -sRr @uri)
    if curl -f -L -H "X-API-Key: $API_KEY" \
      "$BASE_URL/api/files/download?uri=$ENCODED_URI" -o "$FILENAME"; then
      echo "Downloaded: $FILENAME"
    else
      echo "Failed to download: $FILENAME"
    fi
  fi
done

echo "Done!"
```

## Error handling

| Status | Description  | Common causes                                                             |
| ------ | ------------ | ------------------------------------------------------------------------- |
| 400    | Bad Request  | URI doesn't start with `gs://`, malformed URI, path traversal attempt     |
| 401    | Unauthorized | Missing or invalid API key / token                                        |
| 403    | Forbidden    | File belongs to a different organization, invalid bucket                  |
| 404    | Not Found    | Flow doesn't exist, you lack access, or the file was deleted from storage |
| 500    | Server Error | Internal error generating download URL                                    |

Example error response:

```json theme={null}
{
  "error": {
    "message": "Access denied: you do not have access to this file",
    "code": "forbidden"
  },
  "request_id": "01K8KABR6S16YETA2SZPVBS9SP"
}
```

Include the `request_id` when contacting support for faster resolution.

<Tip>
  Always use `-f -L` with curl on the GET endpoint: `-L` follows the redirect to the actual download URL, and `-f` makes curl return a non-zero exit code on HTTP 4xx/5xx errors instead of silently saving an error response as your output file.
</Tip>

## Security

* **Organization-scoped access** — You can only download files from flows your organization owns.
* **Temporary URLs** — Pre-signed download URLs expire after 1 hour.
* **Path validation** — The API rejects path traversal attempts.
* **Bucket validation** — Only files from the authorized storage bucket can be accessed.

For sensitive files, prefer `POST /api/files/resolve` to keep URIs out of server access logs.

## FAQ

<AccordionGroup>
  <Accordion title="What URI formats are supported?">
    Currently only `gs://` URIs (Google Cloud Storage) are supported.
  </Accordion>

  <Accordion title="How long are download URLs valid?">
    Pre-signed URLs are valid for **1 hour**. Request a new URL if your download fails after this time.
  </Accordion>

  <Accordion title="Can I download files from any flow?">
    No. You can only download files from flows that belong to your organization.
  </Accordion>

  <Accordion title="Do I need to parse the gs:// URL?">
    No — pass the full URI exactly as received from the task output.
  </Accordion>

  <Accordion title="Which endpoint should I use?">
    Use `GET /api/files/download` for simple downloads with curl. Use `POST /api/files/resolve` when you need the URL for programmatic use or want to keep URIs out of logs.
  </Accordion>
</AccordionGroup>

Need help? Email [support@super.ai](mailto:support@super.ai) and include your `request_id` and flow ID.
