# Documentation Index

Fetch the complete documentation index at: [/llms.txt](https://docs.flows.super.ai/llms.txt)

Use this file to discover all available pages before exploring further.

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:

```
{
  "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:

```
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](https://docs.flows.super.ai/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.

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

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

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

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

```
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"}'
```

```
{
  "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:

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

```
#!/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 "{\n    \"flow_id\": \"$FLOW_ID\",\n    \"input\": {\n      \"filename\": \"invoice.pdf\",\n      \"mime_type\": \"application/pdf\",\n      \"document_url\": \"https://cdn.super.ai/invoice-example.pdf\"\n    }\n  }")

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:

```
{
  "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.

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.

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

### What URI formats are supported?

Currently only `gs://` URIs (Google Cloud Storage) are supported.

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

### Can I download files from any flow?

No. You can only download files from flows that belong to your organization.

### Do I need to parse the gs:// URL?

No — pass the full URI exactly as received from the task output.

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

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

[API Quickstart](https://docs.flows.super.ai/quickstart)
