Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
A 3D rendering showing the Box.com Salesforce integration concept with cloud and folder icons.
Apex

Box.com Salesforce Integration: Apex Backend Guide

How to wire Salesforce up to Box.com in Apex: secure authentication, the file upload patterns that hold up, and error handling.

Key takeaways Use JWT authentication for server-side integration so the connection stays headless and secure. Build the multipart boundary carefully when you upload files through HttpRequest. Run callouts in Queueable Apex to respect governor limits and get asynchronous retries. Never hardcode credentials. Keep sensitive keys in Named Credentials and Custom Metadata or Encrypted Fields. Log the Box request IDs so troubleshooting is quicker when the API returns an error.

Introduction

Plenty of orgs want file storage to sit in Box while users keep working inside the CRM. Managed packages exist, but a custom Apex backend gives you finer control and the room for the custom logic and performance tuning that enterprise-scale work needs. Here is how I architect a custom integration on the Box REST API.

Establishing secure connectivity with OAuth 2.0

Authentication comes first, and Box uses OAuth 2.0. For server-to-server or app-user setups, JWT (JSON Web Token) authentication is the usual choice because it avoids manual user login prompts.

Start in the Box Developer Console: create a Custom App, configure the public/private key pair, and add the App Service Account to the Box folder you are targeting. On the Salesforce side, store the Private Key somewhere safe, either a Custom Metadata Type or an encrypted field.

To kick off the flow, have a service class build a JWT assertion, sign it with Crypto.sign(), and trade it for an access token:

public class BoxAuthService {
    private static final String BOX_TOKEN_URL = 'https://api.box.com/oauth2/token';

    public static String getAccessToken() {
        // Generate JWT assertion here...
        // Prepare request body for OAuth exchange
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BOX_TOKEN_URL);
        req.setMethod('POST');
        req.setBody('grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' + assertion + ...);
        
        Http http = new Http();
        HTTPResponse res = http.send(req);
        // Parse JSON response to extract access_token
        return token;
    }
}

Implementing file uploads via Apex REST

Once you are authorized, the job is usually pushing Salesforce files or attachments into Box. Box wants a multipart/form-data request for uploads, and building the boundary string is where most developers get stuck.

Build a helper method for the request body. Point your Named Credential at api.box.com so the endpoints stay tidy.

public static void uploadFileToBox(String fileName, Blob fileContent, String folderId) {
    String boundary = '--------------------------' + String.valueOf(Crypto.getRandomInteger());
    String body = '--' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="attributes"; filename="' + fileName + '"\r\n' +
        'Content-Type: application/json\r\n\r\n' +
        '{"name":"' + fileName + '", "parent":{"id":"' + folderId + '"}}\r\n' +
        '--' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="file"; filename="' + fileName + '"\r\n' +
        'Content-Type: application/octet-stream\r\n\r\n';
    
    // Append binary file content and closing boundary...
}

Handling asynchronous operations

Salesforce governor limits on heap size and callout counts make synchronous file processing risky once files get large. Push Box integrations into Queueable Apex. You get retries on failed requests, and the user interface stays responsive.

A pattern for an asynchronous file uploader:

public class BoxUploadQueueable implements Queueable, Database.AllowsCallouts {
    private Id contentVersionId;

    public BoxUploadQueueable(Id cvId) { this.contentVersionId = cvId; }

    public void execute(QueueableContext qc) {
        ContentVersion cv = [SELECT Title, VersionData FROM ContentVersion WHERE Id = :contentVersionId];
        // Perform Box API Callout
        // Handle 429 (Rate Limit) errors with a retry policy
    }
}

Monitoring and error handling

Integrations fail. A logging framework you can actually debug against matters here, because Box API errors are hard to chase otherwise. Capture the X-Box-Request-Id header from every response. When an upload fails, write the response body and the request ID to a custom Integration_Log__c object.

On 401 Unauthorized, refresh your JWT token and retry. On 429 Too Many Requests, add a Retry-After header check. On 404 Not Found, confirm the parent folderId exists before you proceed.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment