Downloading Files
Learn how to download files from gs:// URLs returned in your flow execution outputs.
Prerequisites
Before you begin, ensure you have:
- curl: For making HTTP requests
- jq: For parsing JSON in shell scripts (optional, but recommended)
- A Super.AI Flows API key: Get one from the dashboard under Settings > API Keys
Overview
When Super.AI 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—our 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"
}
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
-Lflag tells curl to follow the redirect to the actual download URL - The
-fflag 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 Super.AI Flows dashboard under Settings > API Keys
- Store keys in environment variables, never hardcode them in scripts
About JWT Tokens:
- JWT tokens are obtained by authenticating with your email and password
- See the API Quickstart for authentication details
- 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
Note: 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
Parameters:
| Parameter | Required | Description |
|---|---|---|
uri |
Yes | The gs:// URL from your task output (URL-encoded) |
redirect |
No | Set to false to get JSON instead of 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"}'
Response:
{
"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 Examples
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 "{
\"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 (polling is used here for simplicity)
echo "Waiting 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
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
echo "Timeout waiting for execution to complete"
exit 1
fi
# 3. Get task outputs
echo "Fetching results..."
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
# Note: jq is required for this step
if ! command -v jq &> /dev/null; then
echo "Warning: jq is not installed. Cannot automatically extract gs:// URLs."
echo "Results: $RESULTS"
exit 0
fi
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")
echo "Downloading: $FILENAME"
# URL-encode the URI for safe transmission
ENCODED_URI=$(printf '%s' "$GS_URL" | jq -sRr @uri)
# Use -f to fail on HTTP errors instead of saving error responses
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!"
Resolve Then Download (Two-Step)
When you need the URL for logging or passing to another service:
#!/bin/bash
API_KEY="${SAF_API_KEY:-saf_your_api_key}"
GS_URL="gs://superai-file-upload-prod-eu/flows/255a17d0-5c21-47d9-8e0b-a08b48436f0a/documents/f8230da2-4136-4ffb-acfe-db9318970ea2"
# Step 1: Resolve the URI to get a download URL
RESPONSE=$(curl -s -X POST "https://flows.super.ai/api/files/resolve" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"uri\": \"$GS_URL\"}")
DOWNLOAD_URL=$(echo "$RESPONSE" | jq -r .download_url)
EXPIRES_AT=$(echo "$RESPONSE" | jq -r .expires_at)
if [ "$DOWNLOAD_URL" = "null" ]; then
echo "Failed to resolve URI. Response: $RESPONSE"
exit 1
fi
echo "Download URL obtained (expires: $EXPIRES_AT)"
# Step 2: Download the file
curl -o "document.pdf" "$DOWNLOAD_URL"
echo "Downloaded: document.pdf"
Download Without Redirect
Get the download URL as JSON instead of following a redirect:
curl -H "X-API-Key: $SAF_API_KEY" \
"https://flows.super.ai/api/files/download?uri=gs://bucket/path/file.pdf&redirect=false"
Response:
{
"download_url": "https://storage.googleapis.com/...",
"expires_at": "2025-01-15T15:00:00+00:00",
"expires_in_seconds": 3600
}
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 responses:
{
"error": {
"message": "Access denied: you do not have access to this file",
"code": "forbidden"
},
"request_id": "01K8KABR6S16YETA2SZPVBS9SP"
}
{
"error": {
"message": "File not found in storage. The file may have been deleted or moved.",
"code": "not_found"
},
"request_id": "01K8KACP7D2XFGHJ9KLM4NPQR8"
}
Include the request_id when contacting support for faster resolution.
Using curl with Error Detection
To make curl fail on HTTP errors (recommended for scripts):
# Use -f to make curl exit with error code on HTTP failures
curl -f -L -H "X-API-Key: $SAF_API_KEY" \
"https://flows.super.ai/api/files/download?uri=gs://..." \
-o file.pdf
# Check exit code
if [ $? -ne 0 ]; then
echo "Download failed!"
fi
The -f flag makes curl return a non-zero exit code for HTTP 4xx/5xx errors, preventing silent failures where an error response would be saved 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
Best practice: Use POST /api/files/resolve for sensitive files to keep URIs out of server access logs.
Best Practices
Use environment variables for API keys: Never hardcode keys in scripts
export SAF_API_KEY="saf_your_actual_key"Handle URL expiration: Request a new URL if downloads fail after an hour
Always use
-f -Lwith curl when using the GET endpoint:-Lfollows redirects to the actual download URL-ffails on HTTP errors instead of silently saving error responses
URL-encode the URI parameter for GET requests to handle special characters:
ENCODED_URI=$(printf '%s' "$GS_URL" | jq -sRr @uri)Add timeouts to polling loops to prevent scripts from running forever
Check exit codes in scripts to detect download failures:
if ! curl -f -L -H "X-API-Key: $KEY" "$URL" -o file.pdf; then echo "Download failed!" exit 1 fi
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/downloadfor simple downloads with curl - Use
POST /api/files/resolvewhen you need the URL for programmatic use or want to keep URIs out of logs
What if jq is not installed?
You can parse JSON manually or use alternative tools like Python:
DOWNLOAD_URL=$(python3 -c "import sys,json; print(json.load(sys.stdin)['download_url'])" <<< "$RESPONSE")
Need Help?
- Support: support@super.ai
- Include your
request_idand flow ID when reporting issues
What made this section unhelpful for you?
On this page
- Downloading Files