Dropbox breaks the standard REST pattern — every operation is a POST request with a JSON body, the root folder path is an empty string (not a slash), and you need two separate API Connector entries because file uploads go to content.dropboxapi.com while metadata calls go to api.dropboxapi.com. Mark your Bearer token Private in both connectors and you have server-side auth without any proxy. Requires a paid Bubble plan for Backend Workflow automation.
| Fact | Value |
|---|---|
| Tool | Dropbox |
| Category | Storage & Files |
| Method | Bubble API Connector |
| Difficulty | Intermediate |
| Time required | 1–2 hours |
| Last updated | July 2026 |
Bubble + Dropbox: Two Hosts, POST-Only, and the Empty-String Root
Most REST APIs follow a familiar pattern: GET requests with query strings to read data, POST or PUT requests with JSON bodies to write. Dropbox v2 does not follow that pattern. Every single Dropbox API call — including listing folder contents — is a POST request with a JSON body. This trips up founders who build a list_folder call as a GET and wonder why it returns 405 Method Not Allowed.
The second structural difference is the two-hostname architecture. Dropbox separates its API surface into two domains: api.dropboxapi.com handles all RPC-style operations (list_folder, create_shared_link, move, search, get_metadata), while content.dropboxapi.com handles all content operations (file uploads via upload endpoint, file downloads via download endpoint). Each hostname requires its own API Connector entry in Bubble. If you call /files/upload on the api. host instead of the content. host, Dropbox returns a 404 with no clear explanation — one of the most confusing errors new integrators encounter.
The third gotcha is the root folder path. In Dropbox, the root of a user's Dropbox is represented as an empty string `""` — not `"/"`. Passing a forward slash as the path in a list_folder call returns a 409 path/not_found error. Subfolders use their lowercase path (e.g., `"/documents/project-a"`).
For Bubble specifically: the API Connector's Private checkbox on the shared Authorization header means your Bearer token never leaves Bubble's servers. CORS is not an issue because the API Connector operates server-side. You do not need any external proxy function for secret management — Bubble's architecture handles this natively. Backend Workflows can automate file operations server-side, but require a paid Bubble plan (Starter or above).
Integration method
Two separate Bubble API Connector entries — 'Dropbox RPC' (api.dropboxapi.com/2) for metadata operations and 'Dropbox Content' (content.dropboxapi.com/2) for file uploads — each with a Bearer token marked Private.
Prerequisites
- A Dropbox account (any plan — the API is available on free Dropbox accounts for personal use)
- A Dropbox app created at dropbox.com/developers/apps (you will generate the Bearer token here)
- Files.content.read, files.content.write, and sharing.write permissions enabled on your Dropbox app
- A Bubble app on a paid plan (Starter or above) if you plan to use Backend Workflows for automated file operations — the free plan can only trigger API Connector calls from client-side workflows
- The Bubble API Connector plugin installed (Plugins tab → Add plugins → search 'API Connector' by Bubble)
Step-by-step guide
Create a Dropbox app and generate a Bearer token
Go to dropbox.com/developers/apps and click 'Create app'. Choose 'Scoped access' as the API type and 'Full Dropbox' as the access type (or 'App folder' if you want to restrict the integration to a single Dropbox folder — App folder is simpler and safer for production apps where you control the Dropbox account). Give your app a name like 'Bubble Integration'. After creating the app, go to the Permissions tab. Enable these permission scopes: - `files.content.read` — list folder contents and download files - `files.content.write` — upload and move files - `sharing.write` — create shared links Click Submit on the Permissions page, then go to the Settings tab. Scroll down to the 'OAuth 2' section and click 'Generate' under 'Generated access token'. Copy the token — this is your long-lived Bearer token. Important: tokens generated in the Developer Console are tied to your personal Dropbox account. For multi-user apps where each user connects their own Dropbox, you need a full OAuth 2.0 flow. For single-account apps (the typical founder use case — 'my Bubble app accesses my Dropbox'), this long-lived token is sufficient and does not expire by default unless you revoke it. Store this token somewhere safe locally before you paste it into Bubble. You will add it to two separate API Connector entries in the next steps.
Pro tip: If you need to access users' personal Dropbox accounts (not just your own), you need the full OAuth 2.0 authorization code flow. A Bubble Backend Workflow handles the code exchange: POST to https://api.dropboxapi.com/oauth2/token with the authorization code, client_id, client_secret (Private header), and redirect_uri. This is an advanced pattern — start with the long-lived token for your own account first.
Expected result: You have a Dropbox app with files.content.read, files.content.write, and sharing.write permissions, and a long-lived Bearer token copied and ready to paste.
Add the 'Dropbox RPC' API Connector (metadata operations)
In your Bubble app, go to the Plugins tab → Add plugins → search 'API Connector' and install the plugin by Bubble if not already installed. Click 'API Connector' in the left panel. Click 'Add another API'. Name it exactly 'Dropbox RPC'. In the Shared headers section, add: - Header name: `Authorization` - Value: `Bearer YOUR_ACCESS_TOKEN_HERE` (replace with your actual token) - Check the **Private** checkbox — this is critical. The Private checkbox tells Bubble to inject this header server-side and never expose it in browser network requests. Set the base URL for this API group to: `https://api.dropboxapi.com/2` Now add your first call. Click 'Add another call' and configure the list_folder call: - Name: `List Folder` - Method: POST - URL: `/files/list_folder` - Body type: JSON - Body fields: `path` (text, value `""`) The call configuration looks like: ```json { "method": "POST", "url": "https://api.dropboxapi.com/2/files/list_folder", "headers": { "Authorization": "Bearer <private>", "Content-Type": "application/json" }, "body": { "path": "" } } ``` Scroll to 'Use as' and select **Data** (this call returns a list of files to display). Click **Initialize call**. In the path field, enter an empty string `""` — this lists the root of the Dropbox. Click Run. Dropbox will return a JSON object with an `entries` array. Bubble will detect the fields. Click Save. Critical: the `path` value for the root folder is literally an empty string — not `"/"`, not `"root"`, not `"."`. Passing any other value for root returns a 409 path/not_found error.
1{2 "method": "POST",3 "url": "https://api.dropboxapi.com/2/files/list_folder",4 "headers": {5 "Authorization": "Bearer <private>",6 "Content-Type": "application/json"7 },8 "body": {9 "path": ""10 }11}Pro tip: Dropbox list_folder can return paginated results. If the folder has more than 2,000 files, the response includes `has_more: true` and a `cursor` value. To get subsequent pages, call POST /files/list_folder/continue with body `{"cursor": "<value>"}`. For most founders, 2,000 entries is more than sufficient for a first implementation.
Expected result: The 'Dropbox RPC' API Connector group is configured with a Private Authorization header and the 'List Folder' call is initialized and returning file entries from the Bubble editor.
Add more RPC calls: create_shared_link and move
In the 'Dropbox RPC' API Connector group, add two more calls that you will commonly need. **Call 2: Create Shared Link** - Name: `Create Shared Link` - Method: POST - URL: `/sharing/create_shared_link_with_settings` - Body type: JSON - Body fields: `path` (text, dynamic — will be the file path) Call configuration: ```json { "method": "POST", "url": "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings", "headers": { "Authorization": "Bearer <private>", "Content-Type": "application/json" }, "body": { "path": "<dynamic>" } } ``` Set 'Use as' to **Action** (triggered by user clicking 'Share'). For Initialize call, enter a real file path from your Dropbox (e.g., `/test.txt`) and run. The response includes a `url` field — this is the shareable link. **Critical gotcha**: If a shared link already exists for that file, Dropbox returns a 409 error with error code `shared_link_already_exists` and the existing URL in the error body. To handle this gracefully in Bubble, add a conditional in the workflow: if the API Connector action returns a 409 error, call `/sharing/list_shared_links` (another POST call) with the same path to retrieve the existing URL. Alternatively, always call list_shared_links first and only call create if no link exists. **Call 3: Move File** - Name: `Move File` - Method: POST - URL: `/files/move_v2` - Body type: JSON - Body fields: `from_path` (text, dynamic), `to_path` (text, dynamic) Initialize call with real test paths from your Dropbox. Set 'Use as' to Action. This call is used in Backend Workflows for automated organization (e.g., archiving completed projects).
1{2 "method": "POST",3 "url": "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings",4 "headers": {5 "Authorization": "Bearer <private>",6 "Content-Type": "application/json"7 },8 "body": {9 "path": "<dynamic>"10 }11}Pro tip: The 409 shared_link_already_exists error is not really an error — it means a link already exists and you can use it. Build your workflow to treat a 409 on this endpoint as a signal to call /sharing/list_shared_links instead of treating it as a failure. This makes the 'get or create shared link' pattern robust.
Expected result: The 'Dropbox RPC' group now contains three calls: List Folder (Data), Create Shared Link (Action), and Move File (Action). All three are initialized and returning valid responses.
Add the 'Dropbox Content' API Connector for file uploads
File uploads in Dropbox go to a completely different hostname: content.dropboxapi.com. You cannot add the upload endpoint to the 'Dropbox RPC' group — Bubble API Connector groups are tied to a single base URL. You must create a second, separate API Connector group. Click 'Add another API' again. Name it 'Dropbox Content'. Add the same Authorization shared header: - Header name: `Authorization` - Value: `Bearer YOUR_ACCESS_TOKEN_HERE` - Check the **Private** checkbox Set the base URL to: `https://content.dropboxapi.com/2` Add a call named 'Upload File': - Method: POST - URL: `/files/upload` - Body type: **Binary** (not JSON — file upload sends raw bytes) - Add a CUSTOM header (not a body field): `Dropbox-API-Arg` The `Dropbox-API-Arg` header is unique to Dropbox's content API. Instead of JSON in the request body, upload parameters go in this header as a JSON string. Here is what the full call configuration looks like: ```json { "method": "POST", "url": "https://content.dropboxapi.com/2/files/upload", "headers": { "Authorization": "Bearer <private>", "Content-Type": "application/octet-stream", "Dropbox-API-Arg": "{\"path\": \"/uploads/<dynamic-filename>\", \"mode\": \"add\", \"autorename\": true}" } } ``` In practice, you will build the Dropbox-API-Arg value dynamically in a Bubble workflow expression, constructing the JSON string with the correct path. Set 'Use as' to Action. For Initialize call: Bubble needs a real binary response to detect response fields. Run the initialization with a small test file. The response includes `path_lower`, `id`, `name`, and `size` — save these to your Bubble database when an upload succeeds. Note: this upload method works well for files up to approximately 150 MB. For larger files, Dropbox requires a chunked upload session (upload_session/start, upload_session/append, upload_session/finish) — an advanced pattern requiring multiple sequential Backend Workflow steps.
1{2 "method": "POST",3 "url": "https://content.dropboxapi.com/2/files/upload",4 "headers": {5 "Authorization": "Bearer <private>",6 "Content-Type": "application/octet-stream",7 "Dropbox-API-Arg": "{\"path\": \"/uploads/<dynamic-filename>\", \"mode\": \"add\", \"autorename\": true}"8 }9}Pro tip: The Dropbox-API-Arg header must be valid JSON encoded as a string in the header value. The most common mistake is putting the upload parameters in the request body instead of the header. If you see a 400 Bad Request with 'Error in call to API function', the Dropbox-API-Arg header is either missing or malformed.
Expected result: The 'Dropbox Content' API Connector group is configured with a Private Authorization header and the 'Upload File' call is initialized. You now have two separate connector groups: one for RPC/metadata and one for content/uploads.
Build the file browser UI and wire upload workflow
Now connect your API Connector calls to Bubble UI elements. **File browser repeating group:** 1. Add a Repeating Group element to your page 2. Set Type of content to 'Dropbox RPC - List Folder' (the API call type Bubble detected) 3. Set Data source to 'Get data from external API' → select 'Dropbox RPC - List Folder' → set path to `""` (empty string for root) 4. Inside the repeating group, add a Text element bound to 'Current cell's entry's name' 5. Add a second Text element for 'Current cell's entry's size' (the file size in bytes) 6. Add a Button element labeled 'Get Share Link' **Share button workflow:** 1. Click the Share Link button → Add workflow 2. Action: Plugins → Dropbox RPC → Create Shared Link 3. Set path to: Current cell's entry's path_lower (the file path returned by list_folder) 4. After the action, set an App State variable 'shared_url' to: Result of Step 1's url field 5. Add a next action: 'Copy to clipboard' → value: App State's shared_url **Upload button workflow:** Add an Upload element and a 'Upload to Dropbox' button: 1. File Uploader element (Bubble's built-in) on the page 2. Button click workflow → Plugins → Dropbox Content → Upload File 3. Set the file body to: FileUploader A's value (the uploaded file) 4. Set the Dropbox-API-Arg dynamically: `{"path": "/uploads/` + FileUploader A's value's name + `", "mode": "add", "autorename": true}` 5. After upload succeeds, create a DropboxFile Thing in Bubble's database with path_lower and name from the result Finally, go to Data tab → Data types → DropboxFile → Privacy. Add a rule: only the file's creator can read and search records. Without privacy rules, all logged-in users can read each other's DropboxFile records.
Pro tip: If you want to navigate into subfolders, update the repeating group's data source path to the subfolder path returned from list_folder (the path_lower field on folder entries). Store the current path in a Custom State on the page so the Back button can restore the previous path. This pattern creates a full folder navigation UI without any additional API calls.
Expected result: Your Bubble page displays a repeating group of Dropbox files and folders, a Share Link button that copies a Dropbox shared link, and an upload button that sends files to Dropbox and saves references in Bubble's database.
Test the integration and verify Bubble privacy rules
Run your Bubble app in preview mode (HTTPS by default, which satisfies Dropbox's API requirements). **Test list_folder**: The repeating group should populate with your Dropbox root's contents within 1-2 seconds. If it stays empty, check the Bubble Logs tab → API Connector logs for the raw Dropbox response. **Test Create Shared Link**: Click the Share button on a file. The shared link URL should appear in the App State. If you get a 409 error, this means the file already has a shared link — this is expected behavior. Update your workflow to handle the 409 by calling list_shared_links for that path. **Test file upload**: Select a small test file (under 1 MB) and click Upload. Check the Bubble Logs tab to confirm the Content API call succeeded. Then open your Dropbox account's web UI — the file should appear in the `/uploads/` folder. **Verify WU consumption**: Bubble charges Workload Units for each API call. In your app's dashboard, check Settings → Workload → Recent logs after running a few operations. List Folder calls against large Dropbox accounts return substantial JSON payloads, which cost more WUs to process. If WU costs are a concern, add `limit` to the list_folder call body to reduce response size, and consider caching folder listings in Bubble's database. **Reminder about paid plan requirement**: Backend Workflows that run automated file operations (scheduled moves, archive triggers) require a Bubble paid plan. Client-side API Connector calls (like list_folder bound to a repeating group) work on the free plan but expose a limitation: any user with DevTools open can see the API response. For production apps, run Dropbox operations through Backend Workflows. RapidDev's team has built Bubble apps with file management integrations like this — if you need help with multi-user OAuth flows or complex folder automation, book a free scoping call at rapidevelopers.com/contact.
Pro tip: After deployment, rotate your Dropbox Bearer token periodically from the Dropbox Developer Console → Settings → Generated access token → Revoke and Regenerate. Update both API Connector groups (Dropbox RPC and Dropbox Content) with the new token. If you forget to update both groups, one will silently fail with 401 errors.
Expected result: File listing, shared link creation, and file upload all work end-to-end in Bubble preview. Bubble privacy rules are configured on any Dropbox-related data types. WU consumption is visible in the dashboard and within acceptable range.
Common use cases
Client document portal
Build a Bubble app where clients can browse folders in a connected Dropbox account, preview file names and sizes in a repeating group, and download files via generated shared links — without needing a Dropbox account themselves.
Create a Bubble page with a repeating group bound to the Dropbox list_folder API call. Each row shows the file name, last modified date, and a 'Share' button that triggers the create_shared_link_with_settings call and copies the returned URL to the clipboard.
Copy this prompt to try it in Bubble
Automated file archiving workflow
Trigger a Bubble Backend Workflow when a project is marked complete — the workflow calls Dropbox's /files/move endpoint to move all project files from an active folder to an archive folder, keeping Dropbox organized without manual intervention.
When the Project status is changed to 'Archived' in Bubble, run a Backend Workflow that calls Dropbox /files/move to move the folder at path '/projects/{project-id}/active' to '/projects/{project-id}/archived', then update the Project record's archived_at field.
Copy this prompt to try it in Bubble
User file upload to shared Dropbox
Let users upload files from your Bubble app directly into a designated Dropbox folder using the Content API's upload endpoint, with the file path constructed from the user's account ID to keep uploads organized by user.
Add a file upload button to the Bubble user profile page. On upload, trigger a Backend Workflow that calls the Dropbox Content API /files/upload endpoint with the Dropbox-API-Arg header setting the path to '/user-uploads/{userId}/{filename}' and the uploaded file bytes in the request body.
Copy this prompt to try it in Bubble
Troubleshooting
List Folder call returns 409 path/not_found even though the path looks correct
Cause: The path for the Dropbox root folder must be an empty string `""` — not `"/"`, not `"root"`, and not any other string. Passing a forward slash is the most common mistake and returns a 409.
Solution: Open the 'List Folder' call in the Dropbox RPC API Connector. Check the path body field and confirm the value is an empty string with nothing between the quotes. Delete any slashes or other characters. Re-initialize the call with path set to empty string and click Run.
Create Shared Link returns a 409 error with 'shared_link_already_exists'
Cause: Dropbox returns a 409 when a shared link already exists for the file you are trying to share. This is not a configuration error — it is Dropbox's design to prevent duplicate link creation.
Solution: Update your workflow to handle the 409 gracefully. Add a parallel call to POST /sharing/list_shared_links with body `{"path": "<file-path>"}` — this returns any existing links for that file. Either call list_shared_links first and create only if no link exists, or catch the 409 and call list_shared_links as a fallback to retrieve the existing URL. The existing URL is also embedded in the 409 error response body under the `error_summary` field.
File upload to the Content API returns 400 Bad Request with 'Error in call to API function'
Cause: The Dropbox-API-Arg header is malformed, missing, or the JSON string inside it is invalid. This is the most common issue with the upload endpoint because the upload parameters go in the header, not the body.
Solution: Open the 'Dropbox Content' API Connector → 'Upload File' call. Verify the Dropbox-API-Arg header exists and its value is valid JSON formatted as a string: `{"path": "/folder/filename.ext", "mode": "add", "autorename": true}`. Test with a hardcoded path in the Initialize call before making it dynamic. Check for unescaped quotes inside the JSON string.
API Connector returns 401 Unauthorized on all calls
Cause: The Bearer token in the Authorization header is incorrect, expired (if a short-lived OAuth token was used), or the Private checkbox is not checked — causing the header to be sent incorrectly.
Solution: Go to both API Connector groups (Dropbox RPC and Dropbox Content) and verify: (1) the Authorization header value starts with 'Bearer ' followed by your token, (2) the Private checkbox is checked on the header row, (3) the token has not been revoked in the Dropbox Developer Console. Regenerate a fresh token from the Dropbox app's Settings tab if needed.
Initialize call shows 'There was an issue setting up your call' when testing in the API Connector
Cause: The Initialize call requires a real successful response from Dropbox to detect field types. If the token is invalid, the path is wrong, or network issues prevent the request from completing, Bubble cannot set up the call schema.
Solution: First test the API call directly from the Dropbox API Explorer (dropbox.github.io/dropbox-api-v2-explorer/) to confirm your token and endpoint work. For list_folder, ensure path is empty string. For create_shared_link, provide a real path to an existing file in your Dropbox. Once the direct test succeeds, retry the Initialize call in Bubble with identical parameters.
Best practices
- Create TWO separate API Connector groups — 'Dropbox RPC' for api.dropboxapi.com and 'Dropbox Content' for content.dropboxapi.com. Using the wrong host for an operation returns a confusing 404 with no explanation.
- Always use an empty string for the root folder path in list_folder. Document this clearly in your Bubble notes so future collaborators don't accidentally change it to a slash.
- Mark the Authorization Bearer token as Private in BOTH API Connector groups. This keeps the token server-side in all Bubble plans, without any proxy function required.
- Handle the 409 shared_link_already_exists error as expected behavior, not as a failure. Build a 'get or create' pattern: try to create the link, and if you get 409, call list_shared_links to retrieve the existing URL.
- Set Bubble privacy rules on any data type that stores Dropbox file metadata (paths, names, shared URLs) so records are only accessible to the creating user. Without privacy rules, any authenticated user can query all file records.
- Avoid polling Dropbox repeatedly in repeating groups with auto-refresh — each list_folder call consumes Bubble WUs. Cache folder contents in a Dropbox Folder data type and refresh only when the user explicitly requests a reload.
- For multi-user apps where each user connects their own Dropbox (not just your admin account), implement the OAuth 2.0 authorization code flow via a Backend Workflow — the code exchange requires your App Secret in a Private header and must run server-side.
- Test with small files (under 1 MB) first to confirm the upload pipeline works before testing with large files. The standard upload endpoint works up to approximately 150 MB; larger files require the chunked upload session API.
Alternatives
Box is enterprise content management with JWT server-to-server auth and Admin Console authorization — better suited for regulated industries (healthcare, finance, legal). Dropbox is simpler to set up (Bearer token vs RSA JWT signing) and better for consumer and small-team use cases. Box requires an external JWT signing proxy for production; Dropbox works with Bubble's Private headers alone.
OneDrive via Microsoft Graph gives access to the entire Microsoft 365 ecosystem (SharePoint, Teams files) from a single API endpoint. If your users are already on Microsoft 365, OneDrive is the natural choice. Dropbox is simpler to set up and doesn't require Azure AD app registration or admin consent, making it faster to prototype.
Amazon S3 gives you full control over raw object storage — folder structures, bucket policies, CDN integration, lifecycle rules. But S3 requires a server-side proxy to compute AWS SigV4 signatures, making it more complex to set up than Dropbox. Use S3 when you need app-controlled storage with custom policies; use Dropbox when users need to access files from their own Dropbox accounts.
Frequently asked questions
Why does Dropbox API v2 use POST for everything, even read operations?
Dropbox designed v2 as an RPC-style API where every operation is a POST with a JSON body, rather than a REST API with GET/POST/PUT/DELETE verbs. This was a deliberate design choice to make the API more predictable and avoid URL encoding issues with file paths. In Bubble, this means every Dropbox call — including list_folder — must be configured as a POST with Body type set to JSON, never as a GET with query parameters.
Do I need to worry about token expiry for the long-lived token?
Long-lived tokens generated in the Dropbox Developer Console do not expire automatically — they remain valid until you revoke them manually. Short-lived tokens (used in the newer Dropbox OAuth 2.0 PKCE flow) expire in 4 hours. If you used the Developer Console 'Generate' button, your token is long-lived. If you implemented OAuth 2.0, check whether your app uses short-lived or long-lived token mode in your Dropbox app settings.
Can this integration work on Bubble's free plan?
Partially. API Connector calls triggered by client-side workflows (like clicking a button to list folder contents) work on the free plan. However, Backend Workflows — needed for server-side automation, scheduled file operations, and the OAuth code exchange for multi-user apps — require a paid Bubble plan (Starter or above). For a production app with automated file management, a paid plan is necessary.
How do I handle a multi-user app where each user has their own Dropbox account?
You need the OAuth 2.0 authorization code flow. Direct users to the Dropbox authorization URL (https://www.dropbox.com/oauth2/authorize) with your app's client_id and a redirect_uri pointing back to a Bubble page or Backend Workflow endpoint. When Dropbox redirects back with a code, a Backend Workflow exchanges the code for an access token by POSTing to /oauth2/token with your client_secret in a Private header. Store the returned access_token and refresh_token in the user's Bubble record. This is an advanced pattern — start with a single admin token first.
Why do I need two separate API Connector groups for Dropbox?
Dropbox routes different types of operations to different hostnames: api.dropboxapi.com handles all metadata and RPC operations (list, move, search, share), while content.dropboxapi.com handles all content transfers (file uploads and downloads). Bubble's API Connector groups are tied to a single base URL, so you need one group per hostname. Using the wrong hostname for an operation returns a 404 with no explanation.
What is the file size limit for uploads through Bubble?
Dropbox's standard upload endpoint (used in Step 4) supports files up to approximately 150 MB. Bubble itself may also impose limits on file handling in workflows. For files larger than 150 MB, Dropbox requires a chunked upload session using three sequential calls: /files/upload_session/start, /files/upload_session/append_v2, and /files/upload_session/finish. This requires multiple Backend Workflow steps and is an advanced pattern.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation