Retool S3 Uploader & File Storage
Retool File Uploads to S3-Style Storage Without the IAM Setup
Store and manage files from your Retool internal tools.
Retool S3 uploader & file storage
Retool file upload and storage without setting up S3: add one REST resource, run one query, and get a permanent CDN URL back. Retool does ship a built-in file store — Retool Storage — but it's capped at roughly 5 GB per organization and 40 MB per file, and the bytes stay locked inside Retool with no permanent global-CDN URL you can embed in an email, a public page, or another app.
So the moment you outgrow those caps or need a real shareable link, the textbook workaround is a Retool S3 uploader: stand up an S3 bucket, write an IAM policy, generate access keys, configure CORS for the Retool domain, and build signed-URL generation in a backend, which is easily a half-day of plumbing for what should be a one-line upload. cdn22.net sits between those two options — when you outgrow Retool Storage's 5 GB/40 MB caps or need a permanent global-CDN URL, it's a drop-in S3 alternative for Retool internal tools, with no AWS console anywhere in the loop.
The architecture is four hops and no infrastructure: Retool File Input → POST /v1/files/{folderId} on cdn22.net, which returns a presigned PUT URL and a file id → PUT the bytes straight to storage → POST /v1/files/confirm-upload → a permanent CDN URL you write to your database or bind to an Image, PDF, Video, or Download component. Set it up by adding a REST API resource in Retool with base URL https://api.cdn22.net/v1 and your API key in the Authorization header (the raw key, no Bearer prefix).
Because the returned URL is permanent and edge-served, dashboards with dozens of embedded files stay fast and don't depend on regenerating short-lived links on every render.
Here's the entire Retool file upload as a single JS query — bind it to your File Input's upload button and it returns the permanent CDN URL:
// Retool JS query: upload the file selected in fileInput1 to cdn22.net and
// return its permanent CDN URL.
const API = 'https://api.cdn22.net/v1';
const API_KEY = '<YOUR_API_KEY>'; // store this in a Retool config variable, don't hardcode it
const FOLDER_ID = '<YOUR_FOLDER_ID>';
// Retool's File Input exposes parallel arrays: .files (metadata) and
// .value (base64-encoded contents). Take the first selected file.
const meta = fileInput1.files[0]; // { name, size, type }
const base64 = fileInput1.value[0]; // base64 string of the file contents
// 1. Ask cdn22.net for a presigned PUT URL and a file id
const { urls } = await (
await fetch(`${API}/files/${FOLDER_ID}`, {
method: 'POST',
headers: { Authorization: API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ filesMetadata: [{ name: meta.name, size: meta.size, type: meta.type }] }),
})
).json();
const { url, id, key } = urls[0];
// 2. PUT the bytes straight to storage (no auth header — the signature is in the URL)
await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': meta.type || 'application/octet-stream' },
body: Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)),
});
// 3. Confirm the upload so the file goes live
await fetch(`${API}/files/confirm-upload`, {
method: 'POST',
headers: { Authorization: API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: [id] }),
});
// Permanent CDN URL — write it to your DB or bind it to an Image / PDF / Download component
return `https://cdn.cdn22.net/${key}`;Prefer not to write JavaScript? The same three hops work as three plain REST queries wired together with {{ }} bindings and a success event handler:
Resource: REST API Base URL: https://api.cdn22.net/v1
Headers: Authorization: <YOUR_API_KEY> (raw key, no "Bearer " prefix)
Query 1 createUpload POST /files/<YOUR_FOLDER_ID>
body: { "filesMetadata": [{ "name": "{{ fileInput1.files[0].name }}",
"size": {{ fileInput1.files[0].size }},
"type": "{{ fileInput1.files[0].type }}" }] }
Query 2 putBytes PUT {{ createUpload.data.urls[0].url }}
no Authorization header — the presigned signature is already in the URL
Query 3 confirmUpload POST /files/confirm-upload
body: { "ids": ["{{ createUpload.data.urls[0].id }}"] }
on success → SQL query writes the permanent URL to your row:
https://cdn.cdn22.net/{{ createUpload.data.urls[0].key }}Keeping the API key private matters more in Retool than in a normal backend, because a JS query runs in the browser. The safest shape is a thin server-side proxy — a Retool Workflow step or an endpoint on your own backend — that holds the key and hands the Retool app nothing but a presigned URL:
// Optional proxy: the cdn22.net API key never reaches the Retool client.
// Deploy as a Retool Workflow step or on your own backend; the Retool query
// calls THIS endpoint instead of api.cdn22.net.
export default async function handler(req, res) {
const { name, size, type } = req.body; // sent by the Retool query
const r = await fetch(`https://api.cdn22.net/v1/files/${process.env.CDN22_FOLDER_ID}`, {
method: 'POST',
headers: { Authorization: process.env.CDN22_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ filesMetadata: [{ name, size, type }] }),
});
const { urls } = await r.json();
// Return only the short-lived presigned URL and the id — never the key.
res.json({ url: urls[0].url, id: urls[0].id, key: urls[0].key });
}Once the URL is saved, displaying it is a binding: point an Image component's src, a PDF viewer's URL, or a Download button's file URL at the stored column, and a Table cell can render it as a link. This fits the internal-tools use cases Retool is built for: an ops team uploading vendor invoices, a support tool attaching screenshots to tickets, an admin panel managing product imagery, or a review queue where staff open user-submitted documents.
For anything sensitive, upload private and mint a signed URL on demand (GET /v1/files/signed/{id}, 10-minute default) instead of exposing a public path — useful when an internal tool surfaces customer documents that shouldn't be world-readable.
The contrast with native Retool + S3 is mostly setup and ongoing maintenance: no bucket policy to get wrong, no IAM key to rotate, no CORS error to debug at 5pm. Billing is prepaid credits with no subscription — you pay only for the storage and bandwidth you actually use, and there's no separate egress charge, so a tool that suddenly serves a lot of files doesn't generate a surprise bandwidth invoice. cdn22.net is purely the storage backend; your Retool queries, resources, and app logic stay exactly as they are.
For the long-form walkthrough with download, preview, and table-cell patterns, read the Retool file upload guide; the upload a file guide covers the identical three calls from curl, a browser, or Node.js, and the cloud file storage API, file storage with CDN, file to link, and Next.js file storage pages cover the wider surface. Create an API key and point your first Retool query at it.
Retool upload architecture
Retool File Input → cdn22.net API → CDN URL
Five steps take a file a user drops into your internal tool and turn it into a permanent CDN URL stored on the record. No bucket, IAM policy, or CORS rule anywhere in the path — the Retool file upload guide walks the same flow in full, and the upload a file guide shows the identical three calls outside Retool.
Add the REST API resource
Base URL https://api.cdn22.net/v1, with the Authorization header set to your raw API key — no Bearer prefix. That single resource replaces the bucket, IAM user, access keys, and CORS rule a Retool S3 uploader would need. See API-key auth for key handling and rotation.
Collect the file in Retool
Drop in a File Input or File Button component. It exposes parallel arrays: fileInput1.files[0] holds { name, size, type } and fileInput1.value[0] holds the base64-encoded contents you decode before the PUT.
Request a presigned upload
POST /v1/files/{folderId} with the file metadata returns { url, id, key }. Because the PUT is presigned, the bytes go straight from the browser to storage instead of streaming through your Retool query or a backend you maintain.
PUT the bytes, then confirm
PUT the file to the presigned URL with no Authorization header — sending your API key there is the most common way this flow breaks — then POST /v1/files/confirm-upload with { ids: [id] } so the file goes live.
Store and display the URL
Write https://cdn.cdn22.net/{key} to your database with a SQL query, then bind it to an Image, PDF, Video, or Download component. Private files skip the public URL entirely: mint GET /v1/files/signed/{id} on demand instead. Delivery details live on the file storage with CDN page.
Security and cost notes
- Keep the API key server-side where you can — a Retool Workflow step or your own endpoint returns only the presigned URL.
- Never hardcode a key in a JS query; use a Retool config variable and rotate it from your dashboard.
- Files that shouldn't be world-readable upload as private, then get a short-lived signed URL (10-minute default).
- The presigned PUT carries no Authorization header — the signature in the URL is the authorization.
- No subscription and no per-seat plan — prepaid credits keep spend predictable.
- Pay only for the storage and bandwidth you use; there's no separate egress bill when a dashboard serves a lot of files.
Benefits With No Complexity
Global CDN delivery
Edge-cached worldwide
Signed-URL security
What You Get
Unlimited files
Unlimited storage
Public + Private storage
CDN ready links
Prepaid credits
More coming soon
How cdn22.net Works
1. Upload
2. Copy
3. Use Anywhere
Why Developers Choose cdn22.net
A Better Way to Store & Deliver Files
| Retool Storage | Native Retool + S3 | DIY bucket + CDN | Temporary file links | cdn22.net | |
|---|---|---|---|---|---|
| Drop-in REST resource (no SDK) | |||||
| Permanent CDN URLs returned | |||||
| Global CDN delivery | |||||
| No 5 GB org / 40 MB file cap | |||||
| No IAM / CORS / bucket policy | |||||
| Signed URLs for private files | |||||
| Files stay available after days pass | |||||
| Pay-as-you-go (prepaid credits) |
Calculate Your Needs
Storage
Egress
CDN Bandwidth
Start Retool Storage
- Global CDN delivery
- Edge-cached worldwide
- Signed-URL security
- Unlimited files
- Unlimited storage
- Public + Private storage
- No subscription — prepaid credits keep spend predictable