File Storage

Files an app keeps at runtime (uploads, attachments, generated PDFs and exports) do not survive on Vercel or Cloud Run: the disk is temporary. Telbase hosts file storage for them: one private, S3-compatible bucket per app.

How It Works

Your agent creates the app's bucket with telbase storage create <app>. The answer names the bucket and its four values:

VariablePurpose
R2_ENDPOINTS3-compatible endpoint URL
R2_ACCESS_KEY_IDAccess key for authentication
R2_SECRET_ACCESS_KEYSecret key for authentication
R2_BUCKET_NAMEName of the storage bucket

The agent sets each value on the provider from a command, never through a file, under the names your code reads. The region is auto. The bucket is S3-compatible, so standard AWS SDK patterns work.

Setting the values on the app

bash
telbase storage create my-app

# Vercel: one value at a time, the same for the other three
telbase storage credentials my-app --get R2_SECRET_ACCESS_KEY | telbase vercel env add R2_SECRET_ACCESS_KEY production

# Cloud Run: on the deploy command
--update-env-vars "R2_ENDPOINT=$(telbase storage credentials my-app --get R2_ENDPOINT),..."

Seeds and scripts that write to the bucket run with the values set and nothing written to disk: telbase storage run my-app -- npm run seed. The key reaches this bucket's files only; it cannot create or delete buckets.

Using Storage in Your App

Use the standard AWS S3 SDK. The storage is S3-compatible, so any library that works with S3 works with Telbase storage.

Install the AWS SDK:

bash
npm install @aws-sdk/client-s3

Create a storage client:

typescript
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({
  region: 'auto',
  endpoint: process.env.R2_ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
  },
});

// Upload a file
export async function uploadFile(key: string, body: Buffer, contentType: string) {
  await s3.send(new PutObjectCommand({
    Bucket: process.env.R2_BUCKET_NAME,
    Key: key,
    Body: body,
    ContentType: contentType,
  }));
  return key;
}

// Download a file
export async function getFile(key: string) {
  const response = await s3.send(new GetObjectCommand({
    Bucket: process.env.R2_BUCKET_NAME,
    Key: key,
  }));
  return response.Body;
}

Example: File Upload Endpoint

A complete Express endpoint for handling file uploads with multer:

typescript
import express from 'express';
import multer from 'multer';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const app = express();
const upload = multer({ storage: multer.memoryStorage() });

const s3 = new S3Client({
  region: 'auto',
  endpoint: process.env.R2_ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
  },
});

app.post('/upload', upload.single('file'), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: 'No file' });

  const key = `uploads/${Date.now()}-${req.file.originalname}`;

  await s3.send(new PutObjectCommand({
    Bucket: process.env.R2_BUCKET_NAME,
    Key: key,
    Body: req.file.buffer,
    ContentType: req.file.mimetype,
  }));

  res.json({ key, size: req.file.size });
});

Local Development

Run a local command with the bucket's values set:

bash
telbase storage run my-app -- npm run dev

Your local code connects to the same bucket as production. This means files uploaded locally are visible in production and vice versa.

Separate dev bucket
For isolated development, register a second app (e.g., my-app-dev) with its own storage bucket.

Managing Credentials

bash
# View credentials (masked)
telbase storage credentials my-app

# Rotate: the old key stops at once
telbase storage credentials my-app --rotate --yes
Credential rotation
Rotating credentials invalidates the old keys immediately. Set the two new key values on the app and redeploy right away.

CORS Configuration

If the browser uploads straight to the bucket (presigned PUT URLs, needed for files over 4.5 MB on Vercel), allow the page's origin: scheme and host exactly, no trailing slash.

bash
telbase storage cors add my-app https://my-app.telbase.ai

Storage Tiers

TierBucketsStorageCost
Free——$0
Starter11 GB$5/mo
Builder35 GB$19/mo
Pro1025 GB$79/mo

Storage uses Cloudflare R2 with $0 egress fees. Upgrade at Settings → Billing.

Next Steps