Need to see if your shared folder is taking up space on your dropbox 👨‍💻? Find out how to check here.

Forum Discussion

barryplum's avatar
barryplum
Explorer | Level 3
4 months ago

Dropbox App created with one account, but seems to be identifying as a different account

I've created an app with a dedicated account that our administrator set up for me.  The app works great.  I've set up Salesforce to connect to the app using an Auth. Provider and Named Credentials.  None of this has my name on it.  

The admin created a Team Folder that I want to be able to create sub folders under and then file requests for those sub folders.  Whenever I create the sub folders, they were getting created under my account, not the system account and not the Team Folder.  

At first I was just using the /create/folder_v2 endpoint, but after going through a bunch of articles on it, I realized I probably needed to use the /sharing/share_folder endpoint.  It still wasn't working quite right.  

Finally, I found this resource: https://developers.dropbox.com/dbx-team-files-guide Which suggested that I set the Dropbox-API-Path-Root in the header of the callout.  So I got the root id from the api explorer from my service account and tried to use it, but I'm getting errors.  And the errors HAVE MY NAME in it.  Somehow it still is trying to create the folders under my named account instead of the account that was created by the app.

Error creating folder: {"error":{".tag":"invalid_root","invalid_root":{".tag":"team","home_namespace_id":"xxxxxxxx","home_path":"/Barry ...","root_namespace_id":"xxxxxxxx"}},"error_summary":"invalid_root/"}
Skipped file request due to folder creation error.

9 Replies

  • DB-Des's avatar
    DB-Des
    Icon for Dropbox Community Moderator rankDropbox Community Moderator
    4 months ago

    Hi barryplum​ 

    An 'invalid_root' error message indicates that the call failed because the namespace ID specified as "root" for the call in the "Dropbox-API-Path-Root" header is invalid for the specified account.
     
    In order to operate on a particular account while specifying the "Dropbox-API-Path-Root" header, you need to supply a valid value for that particular account. To get the correct root information for any particular account, you can first call the /2/users/get_current_account endpoint, without the "Dropbox-API-Path-Root" header. 
     
    Note that the root namespace for an account can change, e.g., due to team changes, so apps should be written to catch and handle such errors. You can find more information in the Path Root Header Modes documentation.
     

  • barryplum's avatar
    barryplum
    Explorer | Level 3
    4 months ago

    Thanks for the quick reply, DB-Des​ !  I did that get current account call in the api explorer as the same account as the app and got the root_namespace_id and provided that in the header.  I don't know if the id is something that needs to be obfuscated on here, so I just replaced it with xxx out of caution.

    'Dropbox-API-Path-Root','{".tag": "root", "root": "xxxxxx"}'

    Does the callout need to do the endpoint callout before it tries to call the folder?  I know the value could change, but we're not making any changes to the team, so it should stay the same.

  • DB-Des's avatar
    DB-Des
    Icon for Dropbox Community Moderator rankDropbox Community Moderator
    4 months ago

    barryplum​ 

    Could you share relevant code snippet(s), but don't include any access or refresh token(s), in order to attempt to reproduce the issue?

  • barryplum's avatar
    barryplum
    Explorer | Level 3
    4 months ago

    Here is the response to get_current_account for the user that the app is built under:

    { "account_id": "dbid:AAA...", "name": { "given_name": "iSpot", "surname": "TV", "familiar_name": "iSpot", "display_name": "iSpot TV", "abbreviated_name": "IT" }, "email": "XXX", "email_verified": true, "disabled": false, "country": "US", "locale": "en", "referral_link": "https://www.dropbox.com/referrals/AACYm_TyrF5o2xgAwv-HbCNzPr3B0h3ua-g?src=app9-806211", "team": { "id": "dbtid:AAA...", "name": "IspotTV", "sharing_policies": { "shared_folder_member_policy": { ".tag": "anyone" }, "shared_folder_join_policy": { ".tag": "from_anyone" }, "shared_link_create_policy": { ".tag": "default_public" }, "group_creation_policy": { ".tag": "admins_and_members" }, "shared_folder_link_restriction_policy": { ".tag": "anyone" }, "enforce_link_password_policy": { ".tag": "optional" }, "default_link_expiration_days_policy": { ".tag": "none" }, "shared_link_default_permissions_policy": { ".tag": "default" } }, "office_addin_policy": { ".tag": "enabled" }, "top_level_content_policy": { ".tag": "admin_only" } }, "team_member_id": "dbmid:AAA...", "is_paired": false, "account_type": { ".tag": "business" }, "root_info": { ".tag": "user", "root_namespace_id": "1276...", "home_namespace_id": "1276...", "home_path": "/iSpot TV" } }

    Here is the class that I'm using to connect to the app: 

    public with sharing class DropboxIntegration {    // Constants for Dropbox API endpoints    private static final String API_CREATE_FOLDER = 'callout:Dropbox_prod/sharing/share_folder';    private static final String API_CREATE_SHARED_LINK = 'callout:Dropbox_prod/sharing/create_shared_link_with_settings';    private static final String API_CREATE_FILE_REQUEST = 'callout:Dropbox_prod/file_requests/create';    // Inner class for invocable method inputs    public class DropboxActionInput {        @InvocableVariable(label='Base Folder Path' description='The base path for the new Dropbox folder (e.g., /Apps/Salesforce).')        public String baseFolderPath;        @InvocableVariable(label='Folder Name' description='The name of the new folder to be created in Dropbox.')        public String folderName;        @InvocableVariable(label='Create Folder' description='Set to true to create a new Dropbox folder.')        public Boolean createFolder;        @InvocableVariable(label='Create File Request' description='Set to true to create a Dropbox file request link.')        public Boolean createFileRequest;    }    // Inner class for invocable method outputs    public class DropboxActionResult {        @InvocableVariable(label='Operation Result' description='The combined result message of the Dropbox operations.')        public String resultMessage;    }    @InvocableMethod(label='Automate Dropbox Folder & File Request' description='Creates a Dropbox folder and/or a file request link.')    public static List<DropboxActionResult> automateDropbox(List<DropboxActionInput> inputs) {        List<DropboxActionResult> results = new List<DropboxActionResult>();        for (DropboxActionInput input : inputs) {            String folderName = input.folderName;            String baseFolder = String.isNotBlank(input.baseFolderPath) ? input.baseFolderPath : 'Pre Test Creatives';            Boolean doCreateFolder = input.createFolder != null && input.createFolder;            Boolean doCreateFileRequest = input.createFileRequest != null && input.createFileRequest;                        // Clean the folder name to remove characters that might be invalid in a path            String sanitizedFolderName = folderName.replaceAll('[^a-zA-Z0-9_ -]', '');            String folderPath = '/' + baseFolder + '/' + sanitizedFolderName;                        String folderCreationResult = '';            boolean folderOperationSuccess = false;            // --- Create Folder ---            if (doCreateFolder) {                String requestBody = '{"path": "' + folderPath + '"}';                HttpResponse res = makeDropboxApiCall(API_CREATE_FOLDER, 'POST', requestBody);                if (res.getStatusCode() == 200) {                    folderCreationResult = 'Success: Folder "' + folderPath + '" created.';                    folderOperationSuccess = true;                } else {                    folderCreationResult = 'Error creating folder: ' + res.getBody();                }            }            // --- Create Shared Link ---            String sharedLinkResult = '';            if (folderOperationSuccess) {                String requestBody = '{"path": "' + folderPath + '"}';                HttpResponse res = makeDropboxApiCall(API_CREATE_SHARED_LINK, 'POST', requestBody);                if (res.getStatusCode() == 200) {                    Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());                    String shareUrl = (String) responseMap.get('url');                    sharedLinkResult = 'Success: Link to share: <sofs>' + shareUrl + '<eofs>';                } else {                    sharedLinkResult = 'Error creating share: ' + res.getBody();                }            }                        // --- Create File Request ---            String fileRequestResult = '';            if (doCreateFileRequest && (folderOperationSuccess || !doCreateFolder)) {                String title = 'Files for ' + folderName;                String requestBody = '{"destination": "' + folderPath + '","title": "' + title + '"}';                HttpResponse res = makeDropboxApiCall(API_CREATE_FILE_REQUEST, 'POST', requestBody);                                if (res.getStatusCode() == 200) {                    Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());                    String url = (String) responseMap.get('url');                    fileRequestResult = 'Success: File request link created: <sofr>' + url + '<eofr>';                } else {                    fileRequestResult = 'Error creating file request: ' + res.getBody();                }            } else if (doCreateFileRequest && !folderOperationSuccess) {                fileRequestResult = 'Skipped file request due to folder creation error.';            }            // --- Consolidate Results ---            List<String> finalMessages = new List<String>();            if (String.isNotBlank(folderCreationResult)) finalMessages.add(folderCreationResult);            if (String.isNotBlank(sharedLinkResult)) finalMessages.add(sharedLinkResult);            if (String.isNotBlank(fileRequestResult)) finalMessages.add(fileRequestResult);                        DropboxActionResult actionResult = new DropboxActionResult();            actionResult.resultMessage = String.join(finalMessages, '\n');            results.add(actionResult);        }        return results;    }    /**     * @description Helper method to make a callout to the Dropbox API.     * @param endpoint The API endpoint to call.     * @param method The HTTP method (e.g., 'POST').     * @param body The request body.     * @return The HttpResponse from the callout.     */    private static HttpResponse makeDropboxApiCall(String endpoint, String method, String body) {        HttpRequest req = new HttpRequest();        req.setEndpoint(endpoint);        req.setMethod(method);        req.setHeader('Content-Type', 'application/json');        req.setHeader('Dropbox-API-Path-Root','{".tag": "namespace_id", "namespace_id": "12762239491"}');        req.setBody(body);        Http http = new Http();        try {            return http.send(req);        } catch (Exception e) {            HttpResponse errorResponse = new HttpResponse();            errorResponse.setStatusCode(500);            errorResponse.setBody('Exception during callout: ' + e.getMessage());            return errorResponse;        }    } }

    I tried updating the header to use the namespace_id and I get the no_permission error.

    Here is the full response I was getting back when I was trying to use root in the header: 

    Error creating folder: {"error":{".tag":"invalid_root","invalid_root":{".tag":"team","home_namespace_id":"1229...","home_path":"/Barry ...","root_namespace_id":"1229..."}},"error_summary":"invalid_root/"}
    Skipped file request due to folder creation error.

    Which seems to indicate that it thinks I'm authenticated as Barry ..., not iSpot TV.  But I don't have any credentials that are connected to my user account.  

  • barryplum's avatar
    barryplum
    Explorer | Level 3
    4 months ago

    DB-Des​ 

    Also, if I comment out the root path, it works perfectly, except the folder is under my account and not the team folder that we want it in.  And, for what it's worth, my user account is a member of the group that has edit access to the team folder.  

    Barry

  • barryplum's avatar
    barryplum
    Explorer | Level 3
    4 months ago

    DB-Des​ 

    So, playing with the api explorer, it looks like there are two, distinct issues.  One is that I'm not able to use the team folder as the root for creating my folders via my app. 

    The second issue is that there are two separate accounts, let's call one IT and the other BP.  I created the app under IT. Used that app key and secret to create my connection to salesforce and am calling out to the app with those credentials, and yet it still seems to be using the BP account as the root for the app.

    I'm not sure the second one matters if we can get the callout to actually sett the root to the Team Folder and get the sharing to work appropriately.  It's just bugging me that it should have nothing to do with the BP account other than the BP account has access to the Team Folder.

    Thanks

  • DB-Des's avatar
    DB-Des
    Icon for Dropbox Community Moderator rankDropbox Community Moderator
    4 months ago

    barryplum​ 

    Based on the error, it does appear as though the access token being used is for your user (Barry) . We'd recommend re-authenticating, while logged in as the iSpot TV user, and using the resulting access token.

  • barryplum's avatar
    barryplum
    Explorer | Level 3
    4 months ago

    DB-Des​ 

    OK, so I figured out the user issue.  When I authenticated my named credentials, I must not have realized that I was logged into dropbox as my user account instead of system account.  It is now creating folders under the system account.  

    It's still not using the team folder as the parent for the folders that I'm creating.  I've tried specifying the path, but it just creates a folder of the same name under the root of the system account.

    I think we're 99% there, but even though I've created a folder share, the share can see the folder, but not the contents of the folder.

    The end goal of the project is to create a folder based on a record in salesforce.  Then, create a file request link so that a customer can upload the file into that folder.  Finally provide a link to an internal user to be able to go to that folder and download whatever the customer uploads.  

  • DB-Des's avatar
    DB-Des
    Icon for Dropbox Community Moderator rankDropbox Community Moderator
    4 months ago

    barryplum​ 

    It's still not using the team folder as the parent for the folders that I'm creating.  I've tried specifying the path, but it just creates a folder of the same name under the root of the system account.

    A couple of things to keep in mind:

    • If you are trying to create a team folder in the team space, you will need to use the /2/team/team_folder/create endpoint.
    • If you are trying to create a folder inside an existing team folder (in the team space), you can use /2/files/create_folder_v2, but you will need to include both the 'Dropbox-API-Path-Root' (with the root namespace ID) and 'Dropbox-API-Select-User' headers.

     

    I think we're 99% there, but even though I've created a folder share, the share can see the folder, but not the contents of the folder.

    You can update the access level for folder members, by sending a request to the /2/sharing/update_folder_member endpoint.

About Dropbox API Support & Feedback

Node avatar for Dropbox API Support & Feedback
Find help with the Dropbox API from other developers.

The Dropbox Community team is active from Monday to Friday. We try to respond to you as soon as we can, usually within 2 hours.

If you need more help you can view your support options (expected response time for an email or ticket is 24 hours), or contact us on X, Facebook or Instagram.

For more info on available support options for your Dropbox plan, see this article.

If you found the answer to your question in this Community thread, please 'like' the post to say thanks and to let us know it was useful!