How to Upload Files Directly from the Browser to Fastio
Open a Receive share, mint a guest token with POST /current/share/{share_id}/auth/guest/, then POST the file from the browser to https://api.fast.io/current/upload/ as multipart form data (name, size, chunk, action=create, instance_id, folder_id). Your application server stays off the byte path. Confirm the file with GET /current/events/search/ or GET /current/activity/poll/{entityId}, then let Ripley and MCP tools work from the same workspace.
Understanding the Bottleneck of Traditional Uploads
Traditional file upload architectures rely on the application server acting as an intermediary proxy. When a user uploads a document, video, or large dataset, the file travels from their browser to your backend server, which then streams that exact same file to your final cloud storage destination. While this approach is simple to understand and implement, it creates a big bottleneck as your application grows.
Proxying files consumes backend resources that should be reserved for business logic. Every active upload ties up server memory and holds open long-running network connections. It also burns through CPU cycles just to move bytes from network sockets. If multiple users attempt to upload large files concurrently, your server can easily exhaust its connection pool or run out of memory. This can cause the entire application to crash for every user on the system.
The financial cost is also an issue. This architecture forces you to pay for bandwidth twice. You pay once when the file data enters your infrastructure from the client, and you pay again when your server pushes the file to the storage layer. For data-heavy applications, these transfer costs add up fast. To solve this, keep the permanent Fastio API key on your server, hand the browser a guest credential for a Receive share, and let the browser POST the bytes straight to Fastio.
Helpful references: Fastio Workspaces, Fastio Collaboration, and Fastio AI.
What Is a Fastio Guest Token?
A Fastio guest token lets a browser upload into a Receive share without ever seeing your permanent API key. Your backend (or the branded Receive portal) calls POST https://api.fast.io/current/share/{share_id}/auth/guest/, then the browser uses that token as Authorization: Bearer on the normal upload route. The permanent API key never leaves your server.
Create the Receive share first with POST https://api.fast.io/current/workspace/{workspace_id}/create/share/. Send, Receive, and Exchange shares are the three branded portal modes: Receive is the upload-only door you want for client-side intake. Read the share back with GET https://api.fast.io/current/share/{share_id}/details/. After the guest token is issued, the browser follows the documented upload flow: POST https://api.fast.io/current/upload/ as multipart/form-data with name, size, chunk (the bytes), action=create, instance_id (the workspace ID), and folder_id=root. A small file returns HTTP 201 with result, id, and new_file_id.
This path matters for applications that deploy AI agents at scale. Fastio is an intelligent workspace, not a bare object bucket. Once the file lands, Ripley (the built-in RAG agent) and the MCP tools your agents already use can read it in place. Named mode includes upload, storage, find, ai, share, and event. Humans can also drop files into the same Receive portal in the browser, so agents and people share one intake path.
Evidence and Performance Benchmarks
According to the AWS Compute Blog, direct client uploads can reduce application server bandwidth costs by 100% for file operations. Because the file data bypasses your application server, your backend infrastructure only handles the lightweight requests required to open the Receive share, mint the guest token, and confirm the file later. It no longer has to process the megabytes or gigabytes of actual file data.
In practical terms, this means an application server that previously struggled to handle multiple large video uploads can easily manage many more concurrent uploads. The backend merely orchestrates the guest slot, while the heavy lifting of moving data is offloaded to Fastio. This architectural shift improves scalability and reduces latency for the end user. It also stabilizes your core application performance under heavy load.
The Direct Upload Architecture
Implementing direct client-side uploads requires coordinating state between your frontend application, your backend server, and the Fastio API. The process follows a strict multi-step sequence to maintain security while ensuring good performance.
- Request a guest slot from your backend: The client application signals its intent to upload a file by sending the file's metadata, such as its name and size, to your backend server. The backend authenticates the user and validates their permissions, then calls
POST https://api.fast.io/current/share/{share_id}/auth/guest/on a Receive share you already created withPOST https://api.fast.io/current/workspace/{workspace_id}/create/share/. - Upload the file from the frontend: The backend returns the guest credential to the client. The browser then executes a direct
POSTtohttps://api.fast.io/current/upload/with the six multipart fields. This transfers the file data straight to Fastio. - Confirm the file on the backend: Once the direct upload finishes, the client can pass
new_file_idback to your application. Your backend can also watch the audit log withGET https://api.fast.io/current/events/search/or long-pollGET https://api.fast.io/current/activity/poll/{entityId}?wait=95&lastactivity={timestamp}until the file appears.
Give Your AI Agents Persistent Storage
Open a Receive share, mint a guest token, and POST files to /current/upload/ without proxying bytes through your app. Built for fast api client side uploads in agent workspaces.
How to Request a Guest Token from Your Backend
The first phase begins when the user selects a file in their browser or application. Before any file data moves over the network, your frontend must ask your backend for permission. Your backend server is the only component that holds your Fastio API key (Authorization: Bearer {api_key} against https://api.fast.io/current/) and has the authority to decide which Receive share the browser may write into.
Your backend endpoint should accept the requested file name and size from the client. It must first verify that the current user is authenticated and authorized to perform this specific action. Once authorized, the backend mints a guest token on the Receive share. Get an API key in the UI under Settings > Devices & Agents > API Keys, or with POST /current/user/auth/key/. Workspace and share IDs are 19-digit numeric strings.
// Example Backend Implementation (Node.js)
app.post('/api/start-client-upload', async (req, res) => {
const { filename, size } = req.body;
// Verify user authentication here
// Validate file type and size restrictions
if (size > MAX_FILE_SIZE) {
return res.status(400).json({ error: 'File exceeds size limit' });
}
try {
const response = await fetch(
'https://api.fast.io/current/share/' + process.env.FASTIO_RECEIVE_SHARE_ID + '/auth/guest/',
{ method: 'POST' }
);
if (!response.ok) {
return res.status(502).json({ error: 'Failed to open a guest upload slot' });
}
// Forward the guest credential body as-is. Do not expose FASTIO_API_KEY.
res.status(response.status).type('application/json').send(await response.text());
} catch (error) {
res.status(500).json({ error: 'Failed to open a guest upload slot' });
}
});
Keep the guest token short-lived in your own session logic. If a credential is intercepted, it is scoped to that Receive share rather than to your permanent API key. Create a fresh guest token for the next upload instead of reusing one across users.
How to Upload Files Directly from the Frontend
Once the frontend receives the guest credential, it can begin the actual data transfer. The browser posts multipart/form-data to https://api.fast.io/current/upload/ and sends the guest token as Authorization: Bearer. Let the browser set the multipart Content-Type (including the boundary). Do not send a JSON body on this call. Most other Fastio POST bodies are application/x-www-form-urlencoded; uploads are the multipart exception.
Small files go in one request. Send name, size, chunk (the file bytes), action=create, instance_id (the workspace ID), and folder_id=root. HTTP 201 returns {"result":true,"id":"<upload_id>","new_file_id":"<node_id>"}. Keep new_file_id for later reads, shares, and Ripley queries. A same-name upload into the same folder overwrites in place and keeps the old content as a recoverable version. The node_id stays stable.
// Example Frontend Implementation (Browser)
async function uploadFileDirectly(file, token, workspaceId) {
const form = new FormData();
form.append('name', file.name);
form.append('size', String(file.size));
form.append('chunk', file);
form.append('action', 'create');
form.append('instance_id', workspaceId);
form.append('folder_id', 'root');
const uploadResponse = await fetch('https://api.fast.io/current/upload/', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token
},
body: form
});
if (!uploadResponse.ok) {
throw new Error('Direct upload failed');
}
const data = await uploadResponse.json();
return data.new_file_id;
}
For a larger file, use the chunked session on the same host. POST the same form without chunk to https://api.fast.io/current/upload/ and read {id}. Then POST /current/upload/{id}/chunk/?order=N&size=N with multipart field chunk (HTTP 202), POST /current/upload/{id}/complete/ (HTTP 202), and GET /current/upload/{id}/details/?wait=60 for {session:{status,new_file_id}}. Batch intake of many small files is POST /current/upload/batch/ (up to 200 files, each 4MB or smaller). A remote URL can skip the browser entirely with POST /current/web_upload/ (source_url, file_name, profile_id, profile_type set to workspace or share, and folder_id).
How to Notify the Backend and Trigger Workflows
The final step connects the isolated file upload to your application's core business logic. Because the file went directly to Fastio, your backend server does not automatically see the bytes land. You need a reliable way to learn the new node_id and continue your own processing.
The most straightforward approach is client-side confirmation. After the upload POST returns HTTP 201, the frontend makes a final call to your backend and passes new_file_id. Your backend can then update its database and link the file to the correct user profile.
Client-side confirmation can miss the event if the user closes the tab right after Fastio accepts the file. For production apps, also watch Fastio activity from the server. Search the audit log with GET https://api.fast.io/current/events/search/, or long-poll GET https://api.fast.io/current/activity/poll/{entityId}?wait=95&lastactivity={timestamp}. When a new node_id appears, start Ripley with POST /current/workspace/{workspace_id}/ai/agent/ and send a message with POST /current/workspace/{workspace_id}/ai/agent/{chat_id}/message/. Agents on MCP can do the same through the event tool and the ai tool (ask).
Handling Advanced Edge Cases: CORS and Custom Metadata
When implementing direct uploads, development teams frequently encounter challenges with browser security models and metadata tracking. The upload call is a cross-origin POST of multipart/form-data to https://api.fast.io/current/upload/. Send only the documented form fields and the Authorization: Bearer guest token. Extra JSON fields in the body are not part of the upload contract, and a hand-set Content-Type: application/json will not match the multipart form.
Structured metadata lives on the file after it exists, not on the upload POST. Create a template with POST /current/workspace/{workspace_id}/metadata/templates/, then extract fields from a stored file with POST /current/workspace/{workspace_id}/storage/{node_id}/metadata/extract/. You can also run POST /current/workspace/{workspace_id}/metadata/templates/{template_id}/extract-all/, list views with GET /current/workspace/{workspace_id}/metadata/views/, and export a view with POST /current/workspace/{workspace_id}/metadata/view/{template_id}/export/. Agents can ask Ripley about those files through MCP ai (ask) once the node_id is in the workspace.
Keep trailing slashes on every Fastio path. Profile IDs are 19-digit numeric strings. HTTP 429 with error code 1671 means rate limited; back off until the x-ve-limit-expires header. Other documented codes include 1605 Invalid Input, 1609 Not Found, 1650 Auth Invalid, 1680 Access Denied, 1685 Feature Limit, and 1654 Internal Error.
Fastio: The Ideal Intelligent Workspace for Developers
Implementing direct uploads solves the bandwidth problem, but where those files land matters even more. Fastio is designed as an intelligent workspace, not just basic commodity storage. It serves as the active coordination layer where agent output becomes real team output.
When the browser posts into a Receive share, you drop files into an environment that Ripley and your agents can query. Humans use the web interface and branded Send, Receive, or Exchange portals. Agents use the MCP server at https://mcp.fast.io/mcp (use https://mcp.fast.io/mcp/key with a Bearer header; legacy SSE is https://mcp.fast.io/sse). Named mode exposes 19 tools, including upload, storage, find, ai, share, fileshare, and event. Code mode for headless agents exposes 6 tools: auth, upload, search, execute, room, and how-to.
A tools/call looks like this:
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"upload","arguments":{"action":"web-import","url":"https://example.com/report.pdf",
"profile_type":"workspace","profile_id":"1234567890123456789"}}}
Verified upload actions include web-import, stream-upload, create-session, chunk, finalize, batch, limits, and blob-info. You can use OpenClaw by running clawhub install dbalve/fast-io in your terminal. Agents can create organizations, open workspaces, and hand finished files to people through a Send share or a durable fileshare (POST /current/workspace/{workspace_id}/create/fileshare/). That is the production path for client-side intake into an agent workspace.
Frequently Asked Questions
How do Fastio guest tokens work?
Your backend calls POST https://api.fast.io/current/share/{share_id}/auth/guest/ on a Receive share and hands the browser that guest token. The browser then POSTs multipart form data to https://api.fast.io/current/upload/ with Authorization: Bearer and fields name, size, chunk, action=create, instance_id, and folder_id.
How to upload large files directly from browser?
Mint a guest token on a Receive share, then open a chunked session: POST https://api.fast.io/current/upload/ without the chunk field to get {id}, POST /current/upload/{id}/chunk/?order=N&size=N with multipart chunk (HTTP 202), POST /current/upload/{id}/complete/ (HTTP 202), and GET /current/upload/{id}/details/?wait=60 for session.status and session.new_file_id.
Are guest tokens secure for public applications?
Yes, when the browser never sees your permanent API key. Keep the key on the server (Settings > Devices and Agents > API Keys, or POST /current/user/auth/key/). Issue a guest token scoped to a Receive share, and let the browser upload with that Bearer token only. Confirm the file later with GET /current/events/search/ or GET /current/activity/poll/{entityId}.
How do I handle CORS errors during a client-side upload?
POST multipart/form-data from the browser to https://api.fast.io/current/upload/. Send Authorization: Bearer with the guest token and the six documented form fields. Let the browser set the multipart Content-Type, including the boundary. A JSON content type on that request will not match the upload form.
What happens if an upload fails halfway through?
If a one-shot POST /current/upload/ is interrupted, send the same multipart fields again. For a large file, open a chunked session, POST each piece to /current/upload/{id}/chunk/?order=N&size=N, then POST /current/upload/{id}/complete/ and GET /current/upload/{id}/details/?wait=60. HTTP 429 with error code 1671 means back off until the x-ve-limit-expires header.
Can AI agents access the files immediately after upload?
Yes. After POST /current/upload/ returns new_file_id, Ripley can read the file through POST /current/workspace/{workspace_id}/ai/agent/ and POST /current/workspace/{workspace_id}/ai/agent/{chat_id}/message/. On MCP, use the ai tool (ask) with profile_type, or upload with the upload tool (stream-upload, web-import, create-session, chunk, finalize).
Related Resources
Give Your AI Agents Persistent Storage
Open a Receive share, mint a guest token, and POST files to /current/upload/ without proxying bytes through your app. Built for fast api client side uploads in agent workspaces.