# Using R2

Cloudflare R2 provides S3-compatible object storage for file uploads in the Quickback Stack.

## Upload Flow

The generated API provides a two-step upload process:

1. **Request a presigned URL** — Client calls the upload endpoint to get a time-limited URL
2. **Upload directly to R2** — Client uploads the file directly to R2 (bypasses your Worker)
3. **Store the reference** — The API records the file metadata in D1

```bash
# 1. Request presigned upload URL
curl -X POST https://api.example.com/api/v1/files/upload \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"filename": "report.pdf", "contentType": "application/pdf"}'

# Response:
# { "uploadUrl": "https://bucket.r2.cloudflarestorage.com/...", "fileId": "file_abc123" }

# 2. Upload directly to R2 using the presigned URL
curl -X PUT "<uploadUrl>" \
  -H "Content-Type: application/pdf" \
  --data-binary @report.pdf
```

## Download Flow

Files are served through your Worker with security checks applied:

```bash
# Download a file (auth + firewall enforced)
curl https://api.example.com/api/v1/files/file_abc123/download \
  -H "Authorization: Bearer <token>"
```

The download endpoint:
1. Validates the user's session
2. Checks firewall — the file must belong to the user's organization
3. Checks access — the user must have the required role
4. Streams the file from R2

## Role-Based Access

File access respects the same security layers as your API:

- **Firewall** — Users can only access files belonging to their organization
- **Access** — Role-based download permissions

## See Also

- [R2 Setup](/stack/storage/r2) — Bucket creation, wrangler bindings, and configuration
- [Avatars](/stack/account-ui/features/avatars) — Avatar upload UI integration
