S-Drive REST API
The S-Drive REST API lets external applications work with files stored in your S-Drive-enabled Salesforce org: search file records, create and update them, upload new files to your S3 bucket, and generate download links.
Getting started
Base URL
All S-Drive endpoints live under your Salesforce My Domain, with the cg namespace segment:
https://<your-domain>.my.salesforce.com/services/apexrest/cg/
The namespace segment cg is required. Requests that omit it will not resolve.
Authentication
The API uses standard Salesforce OAuth 2.0. Obtain an access token through any supported Salesforce OAuth flow, then send it as a bearer token:
Authorization: Bearer 00D5f000000abcd!AQEAQ...
Requests run as the authenticated user. Sharing rules, object permissions, and field-level security all apply — a user who cannot see a file record through the Salesforce UI cannot see it through the API either.
Required access
The calling user needs:
-
Read access to the S-Drive file objects being addressed (for example
cg__AccountFile__c) -
Access to the S-Drive File Objects and S-Drive Bucket List custom settings
-
Create, edit, or delete permission on the file object for the corresponding operations
You can verify a user's permission set assignment before starting work using Check user permission.
Content type
Send Content-Type: application/json on all requests with a body. Responses are JSON unless noted.
Conventions
Status codes
|
Code |
Meaning |
|---|---|
|
|
Success |
|
|
Record created |
|
|
Request valid, but no records matched |
|
|
Missing, malformed, or invalid parameters |
|
|
The user lacks permission for the requested object or field |
|
|
The referenced record or resource was not found |
|
|
Input could not be parsed |
|
|
Unexpected error; see |
|
|
Requested feature is not supported on this endpoint |
Error responses
Errors return a message field describing what went wrong:
{
"files": [],
"message": "Wrong input or missing input fields"
}
Partial success
Bulk operations can partially succeed. When they do, the response is still 200 and the outcome is described in message. Always check message in addition to the status code on bulk delete and bulk upload-completion calls — the Delete files and Complete an upload sections describe exactly what to look for.
Uploading a file
Uploading is a three-step process. Your application sends the file bytes directly to Amazon S3 — they never pass through Salesforce, which is what allows S-Drive to handle files of any size.
1. POST /SDrive/v2/upload/ Ask S-Drive to prepare the upload.
Returns a signed policy and an S3 endpoint.
2. PUT <s3Endpoint> Send the file bytes straight to S3 using
the credentials from step 1.
3. PUT /SDrive/v2/upload/ Tell S-Drive the upload finished.
The file record becomes visible in S-Drive.
Until step 3 completes, the file record exists but is flagged as work-in-progress and is excluded from searches and the S-Drive UI.
For large files, replace step 2 with the multipart upload sequence.
Worked example
Step 1 — initialize
curl -X POST \
"https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v2/upload/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"objectId": "0015f00000XyZaBAAV",
"fileType": "cg__AccountFile__c",
"fileProperties": [{
"fileName": "invoice.pdf",
"fileLocation": "",
"description": "October invoice",
"fileSizeInBytes": "184320",
"relationshipFieldName": "cg__Account__c"
}],
"policyMap": {}
}'
Response:
{
"accessKey": "AKIAIOSFODNN7EXAMPLE",
"bucketName": "acme-sdrive",
"s3EndPoint": "s3.us-east-1.amazonaws.com",
"uploadRequestInfo": [{
"wipFileId": "a0X5f000000abcdEAA",
"fileName": "invoice.pdf",
"fileLocation": "0015f00000XyZaBAAV/a0X5f000000abcdEAA/invoice.pdf",
"signature": "0dc2...",
"policy": "eyJleHBpcmF0aW9u...",
"timeStamp": "20260819T143022Z",
"awsRegion": "us-east-1",
"awsCredential": "AKIAIOSFODNN7EXAMPLE/20260819/us-east-1/s3/aws4_request",
"isSuccess": true
}],
"message": "success"
}
Step 2 — send bytes to S3
Use signature, policy, awsCredential, and timeStamp to construct a standard AWS Signature Version 4 upload against s3EndPoint and bucketName, targeting the key in fileLocation.
Step 3 — complete
curl -X PUT \
"https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v2/upload/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"files": [{"id": "a0X5f000000abcdEAA"}]}'
{ "files": [], "message": "success" }
The file is now visible in S-Drive.
Discovery
List parent objects
Returns every Salesforce object configured for S-Drive, together with its file objects and the relationship field name you need when uploading.
GET /SDrive/v2/parentObjects/
Query parameters
|
Name |
Required |
Description |
|---|---|---|
|
|
No |
API name of a single parent object. Omit to return all. |
Only one query parameter is accepted.
Example
curl "https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v2/parentObjects/?parentObjectName=Account" \
-H "Authorization: Bearer $TOKEN"
{
"objects": [{
"name": "Account",
"label": "Account",
"children": [{
"objectApiName": "cg__AccountFile__c",
"objectRelationshipName": "cg__Account__c"
}]
}],
"message": "success"
}
Use objectApiName as fileType and objectRelationshipName as relationshipFieldName when initializing an upload.
Check whether folders are empty
Returns the fill status of one or more folders. Useful for rendering folder icons without querying their contents.
GET /SDrive/v2/fileObjects/
Query parameters — all three are required.
|
Name |
Description |
|---|---|
|
|
File object API name, for example |
|
|
ID of the parent record |
|
|
Comma-separated folder record IDs |
Example
curl "https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v2/fileObjects/?fileObjectName=cg__AccountFile__c&parentObjectId=0015f00000XyZaBAAV&folderIds=a0X5f000000abcdEAA,a0X5f000000efghEAA" \
-H "Authorization: Bearer $TOKEN"
{
"Empty": ["a0X5f000000abcdEAA"],
"NonEmpty": ["a0X5f000000efghEAA"],
"Unknown": [],
"message": "success"
}
Folders appear under Unknown when their status could not be determined.
Files
Search files
Returns a paginated list of file records matching your criteria.
GET /SDrive/v2/files/?searchQuery=<url-encoded JSON>
Query parameters
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
A URL-encoded Search query JSON object. This is the only accepted parameter. |
Example search query (shown unencoded)
{
"fileObjectApiName": "cg__AccountFile__c",
"searchFields": [
{ "sequence": 1, "fieldApiName": "cg__File_Name__c", "value": "invoice", "operator": "contains" },
{ "sequence": 2, "fieldApiName": "cg__Account__c", "value": "0015f00000XyZaBAAV", "operator": "equals" }
],
"conditionalExpression": "{1} AND {2}",
"orderByFieldApiName": "cg__File_Name__c",
"limitValue": 25,
"offset": 0
}
Response
{
"files": [ { /* File object */ } ],
"totalPages": 4,
"page": 1,
"message": "success"
}
When no records match, the response is 204 with "message": "no records were found".
Pagination
limitValue is required and must be greater than zero. offset must be an exact multiple of limitValue — request page 3 of a 25-per-page result set with offset: 50, not offset: 51. Violating either rule returns 400.
totalPages reflects the full result set, not the current page.
Default filters
Searches automatically exclude records that are in progress, superseded, or deleted. Four conditions are applied unless you override them:
|
Field |
Default applied |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
To retrieve records the defaults would hide, include that field explicitly in searchFields. For example, to list every version of a file rather than just the latest:
{
"fileObjectApiName": "cg__AccountFile__c",
"searchFields": [
{ "sequence": 1, "fieldApiName": "cg__File_Name__c", "value": "invoice.pdf", "operator": "equals" },
{ "sequence": 2, "fieldApiName": "cg__Is_Latest_Version__c", "value": "true", "operator": "notequals" }
],
"conditionalExpression": "{1}",
"limitValue": 50
}
Naming a field in searchFields replaces its default condition entirely.
Create file records
Creates file or folder records in bulk. This creates Salesforce records only — to upload file content, use the upload flow.
The most common use is creating folders.
POST /SDrive/v2/files/
Request body
{
"files": [{
"fileObjectName": "cg__AccountFile__c",
"parentObjectName": "cg__Account__c",
"parentObjectId": "0015f00000XyZaBAAV",
"parentFolderId": "",
"fileName": "Q3 Reports",
"type": "Folder",
"fields": [
{ "fieldApiName": "cg__Description__c", "fieldValue": "Quarterly reports" }
]
}]
}
|
Field |
Required |
Description |
|---|---|---|
|
|
Yes |
File object API name |
|
|
Yes |
Relationship field API name on the file object |
|
|
Yes |
ID of the parent record |
|
|
Yes |
ID of the containing folder; empty string for the root |
|
|
Yes |
Name of the file or folder |
|
|
Yes |
Content type. Use the exact value |
|
|
No |
Additional field values to set on the new record |
Response 201
{ "files": ["a0X5f000000abcdEAA"], "message": "success" }
Returns 403 if the user cannot create records on the specified object.
Update file records
Updates fields on existing file records in bulk.
PUT /SDrive/v2/files/
Request body
{
"files": [{
"fileObjectName": "cg__AccountFile__c",
"fileObjectId": "a0X5f000000abcdEAA",
"fields": [
{ "fieldApiName": "cg__Description__c", "fieldValue": "Updated description" }
]
}]
}
Supply fieldValue using the JSON type that matches the Salesforce field: a string for text and picklist fields, true/false for checkboxes, a number for numeric fields.
Response 200
{ "files": ["a0X5f000000abcdEAA"], "message": "success" }
If no records changed, message is "no updates were made" and files is empty.
Delete files
Deletes file records and their S3 objects.
DELETE /SDrive/v2/files/?fileObjectName=<name>&fileObjectIds=<ids>
Query parameters — both required.
|
Name |
Description |
|---|---|
|
|
File object API name |
|
|
Comma-separated record IDs. Maximum 10,000 per request. |
Example
curl -X DELETE \
"https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v2/files/?fileObjectName=cg__AccountFile__c&fileObjectIds=a0X5f000000abcdEAA,a0X5f000000efghEAA" \
-H "Authorization: Bearer $TOKEN"
Response 200
files lists the IDs that were successfully deleted. Check message to determine the outcome:
|
|
Meaning |
|---|---|
|
|
Every file was deleted |
|
|
Some files were deleted; |
|
|
No files were deleted |
Requests exceeding 10,000 IDs return 400. Split larger deletions into batches.
Uploads
Initialize an upload
Creates a work-in-progress file record and returns the signed credentials your application needs to send the file to S3.
POST /SDrive/v2/upload/
Request body
{
"objectId": "0015f00000XyZaBAAV",
"fileType": "cg__AccountFile__c",
"fileProperties": [{
"fileName": "invoice.pdf",
"fileLocation": "a0X5f000000abcdEAA",
"description": "October invoice",
"fileSizeInBytes": "184320",
"relationshipFieldName": "cg__Account__c",
"versionId": ""
}],
"policyMap": {}
}
|
Field |
Required |
Description |
|---|---|---|
|
|
Yes |
ID of the parent record |
|
|
Yes |
File object API name |
|
|
Yes |
Upload policy overrides. Send |
|
|
Yes |
Name of the file being uploaded |
|
|
Yes |
ID of the destination folder; empty string for the root |
|
|
Yes |
File size in bytes, as a string |
|
|
Yes |
From List parent objects |
|
|
No |
Description to store on the record |
|
|
No |
Supply to upload a new version of an existing file |
All entries in a single request must share the same relationshipFieldName.
Response 201 — see the worked example. Each entry in uploadRequestInfo corresponds to one entry in fileProperties; see Upload request info for the full field list.
Complete an upload
Clears the work-in-progress flag after the file has reached S3, making it visible in S-Drive.
PUT /SDrive/v2/upload/
Request body
{
"files": [
{ "id": "a0X5f000000abcdEAA" }
]
}
|
Field |
Required |
Description |
|---|---|---|
|
|
Yes |
The |
|
|
No |
S3 version ID, for version-enabled buckets |
If you include versionId on any entry, include it on every entry in the request.
Response 200
Check message to determine the outcome:
|
|
Meaning |
|---|---|
|
|
Every upload was completed |
|
|
Some failed; |
|
|
No records were updated |
Note that on this endpoint files lists the records that failed, whereas on Delete files it lists the records that succeeded.
Get a signed payload
Returns a signed S3 payload for a given object key.
GET /SDrive/v2/upload/?fileKey=<key>
Query parameters
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
The S3 object key |
|
|
No |
Reserved for internal use |
Response 200
{ "payload": "AKIAIOSFODNN7EXAMPLE/20260819/...", "message": "" }
Multipart uploads
For large files, replace the single PUT to S3 with a multipart sequence. All four operations use the same URL and are distinguished by HTTP method.
PUT /SDrive/v2/upload/s3/ Start the multipart upload
GET /SDrive/v2/upload/s3/ Get authorization headers for one part
POST /SDrive/v2/upload/s3/ Finish the multipart upload
DELETE /SDrive/v2/upload/s3/ Cancel the multipart upload
The maxPartCount value returned when you initialize the upload tells you the maximum number of parts permitted.
Start a multipart upload
PUT /SDrive/v2/upload/s3/
{ "fileKey": "0015f00000XyZaBAAV/a0X5f000000abcdEAA/large-video.mp4" }
{ "uploadId": "2~pMEbxHW3iAG2SqBLxLM5r1vXNmVvC0v", "message": "" }
Use fileLocation from the initialize-upload response as fileKey.
Get authorization headers for a part
GET /SDrive/v2/upload/s3/?fileKey=<key>&uploadId=<id>&partNumber=<n>&contentLength=<bytes>
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
Same key used to start the upload |
|
|
Yes |
From the start response |
|
|
Yes |
Part number, starting at 1 |
|
|
Yes |
Size of this part in bytes |
{
"requestHeaders": {
"Authorization": "AWS4-HMAC-SHA256 Credential=...",
"x-amz-date": "20260819T143022Z",
"x-amz-content-sha256": "UNSIGNED-PAYLOAD"
},
"message": ""
}
Apply these headers to your own PUT request to S3 for that part, and keep the ETag S3 returns.
Finish a multipart upload
POST /SDrive/v2/upload/s3/
{
"fileKey": "0015f00000XyZaBAAV/a0X5f000000abcdEAA/large-video.mp4",
"uploadId": "2~pMEbxHW3iAG2SqBLxLM5r1vXNmVvC0v",
"eTagList": "\"9b2cf5...\",\"3d8a71...\",\"c04e29...\""
}
eTagList is a single comma-separated string, not a JSON array. List the ETags in part order.
{ "completeETag": "\"5f1a3c...-3\"", "message": "" }
After this succeeds, call Complete an upload to make the file visible in S-Drive.
Cancel a multipart upload
DELETE /SDrive/v2/upload/s3/?fileKey=<key>&uploadId=<id>
{ "message": "Abort multipart upload succeed." }
Cancelling releases the storage consumed by any parts already uploaded. Abandoned multipart uploads continue to incur S3 charges until aborted.
Downloads
Get a download URL
Returns a time-limited signed URL for downloading a file directly from S3.
GET /SDrive/files/?fileObjectId=<id>&parentId=<id>&timeValue=<seconds>
Query parameters
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
ID of the file record |
|
|
Yes |
ID of the parent record |
|
|
Yes |
Number of seconds the link stays valid |
Any additional query parameters are passed through to S3. Use this to control download behavior — for example, response-content-disposition=attachment forces a download rather than opening the file in the browser.
Example
curl "https://acme.my.salesforce.com/services/apexrest/cg/SDrive/files/?fileObjectId=a0X5f000000abcdEAA&parentId=0015f00000XyZaBAAV&timeValue=3600" \
-H "Authorization: Bearer $TOKEN"
{ "FileUrls": ["https://acme-sdrive.s3.amazonaws.com/0015f00000.../invoice.pdf?X-Amz-Algorithm=..."] }
The returned URL requires no authentication and is valid for timeValue seconds. Treat it as a secret.
Requesting several URLs at once
To generate links for multiple files in one call, send a JSON body with no query parameters:
{
"timeValue": 3600,
"fileObjectIds": ["a0X5f000000abcdEAA", "a0X5f000000efghEAA"],
"parentIds": ["0015f00000XyZaBAAV", "0015f00000XyZaBAAV"],
"requestParameters": { "response-content-disposition": "attachment" }
}
fileObjectIds and parentIds must be the same length and in matching order. URLs are returned in the same order.
Because some HTTP clients and proxies strip bodies from GET requests, use the query-parameter form where possible.
Get a ZIP archive URL
Retrieves the download URL for an archive produced by S-Drive's asynchronous zip-and-download feature.
GET /SDrive/v2/AsyncZipAndDownload/?zipName=<name>
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
Name of the generated archive |
The response body is the URL itself as plain text, not JSON:
https://acme-sdrive.s3.amazonaws.com/sdrivejob/Download-Job-Account-20260819143022.zip?X-Amz-Algorithm=...
The URL is valid for 7 days.
Check user permission
Confirms whether the authenticated user is assigned a given permission set. Use this at sign-in to verify a user can proceed before making further calls.
GET /SDrive/v1/permissionCheck/?permissionSetName=<name>
|
Name |
Required |
Description |
|---|---|---|
|
|
Yes |
Name of the permission set to check |
Example
curl "https://acme.my.salesforce.com/services/apexrest/cg/SDrive/v1/permissionCheck/?permissionSetName=SDriveUserPSet" \
-H "Authorization: Bearer $TOKEN"
{
"message": "User authorization is confirmed for this application. User ID: 0055f00000ABCDEAA3",
"hasPermission": true,
"statusCode": 200
}
This endpoint always returns HTTP 200. Read the hasPermission and statusCode fields in the response body to determine the result:
|
|
|
Meaning |
|---|---|---|
|
|
|
User is assigned and authorized |
|
|
|
|
|
|
|
User is not assigned the permission set |
|
|
|
No permission set with that name exists in the org |
|
|
|
An error occurred; see |
Checking ContentAuthoringPSet additionally requires that Desktop Experience be enabled in the org. If it is not, the result is 401 even when the user holds the permission set.
Object reference
Search query
|
Field |
Type |
Required |
Description |
|---|---|---|---|
|
|
String |
Yes |
File object to search |
|
|
Integer |
Yes |
Records per page; must be greater than zero |
|
|
Integer |
No |
Records to skip; must be a multiple of |
|
|
Array |
No |
Conditions to apply; see Search field |
|
|
String |
No |
How conditions combine. Defaults to all joined with |
|
|
String |
No |
Sort field. Defaults to |
conditionalExpression references search fields by their sequence using {n} placeholders, and supports grouping:
{1} AND {2}
{1} OR {2}
({1} OR {2}) AND {3}
Search field
|
Field |
Type |
Description |
|---|---|---|
|
|
Integer |
Number referenced by |
|
|
String |
Field to filter on; must exist on the file object |
|
|
String |
Value to compare against, always supplied as a string |
|
|
String |
One of the operators below |
Operators: equals, notequals, greaterthan, lessthan, contains, startswith, endswith
Supply value as a string even for checkbox, number, and date fields — for example "true" or "1024". S-Drive converts it to the correct type.
File
Returned by Search files.
|
Field |
Type |
Description |
|---|---|---|
|
|
String |
File record ID |
|
|
String |
File or folder name |
|
|
String |
File object the record belongs to |
|
|
String |
MIME type, or |
|
|
String |
Description |
|
|
String |
Tags |
|
|
String |
Human-readable size, for example |
|
|
Number |
Exact size in bytes |
|
|
String |
Containing folder ID |
|
|
String |
Containing folder name |
|
|
String |
ID of the creating user |
|
|
String |
Name of the creating user |
|
|
Datetime |
Creation timestamp |
|
|
String |
Signed download URL |
|
|
String |
Signed preview URL |
|
|
String |
Signed thumbnail URL |
|
|
String |
S3 object key |
|
|
String |
S3 version ID |
|
|
String |
Bucket setting ID |
|
|
String |
S3 bucket name |
|
|
Boolean |
Whether the bucket has versioning enabled |
|
|
Boolean |
Whether the upload is still in progress |
|
|
Boolean |
Whether the file is in the recycle bin |
|
|
String |
Target of a shortcut, if the record is one |
|
|
String |
Lock status |
|
|
String |
ID of the user who has the file checked out |
|
|
String |
Name of that user |
|
|
Boolean |
Whether access is restricted |
|
|
Object |
The complete underlying Salesforce record, including any custom fields |
The file field gives you every field on the record, so custom fields can be read without a separate Salesforce query.
Upload request info
Returned by Initialize an upload, one entry per file.
|
Field |
Type |
Description |
|---|---|---|
|
|
String |
Record ID to send when completing the upload |
|
|
String |
File name |
|
|
String |
S3 object key to upload to |
|
|
String |
File size |
|
|
String |
Content type |
|
|
String |
Signature for the S3 request |
|
|
String |
Base64-encoded upload policy |
|
|
String |
Timestamp used in signing |
|
|
String |
AWS region of the bucket |
|
|
String |
Credential scope string for signing |
|
|
String |
Destination bucket |
|
|
String |
S3 endpoint hostname |
|
|
String |
Server-side encryption to request |
|
|
Object |
Metadata headers to include on the upload |
|
|
Boolean |
Whether to use the S3 transfer acceleration endpoint |
|
|
Boolean |
Whether the bucket has versioning enabled |
|
|
Number |
Maximum parts allowed for a multipart upload |
|
|
String |
Storage backend in use |
|
|
Boolean |
Whether initialization succeeded for this file |
|
|
String |
Reason for failure when |
Always check isSuccess before uploading. When it is false, errorMessage explains why.
Legacy endpoints
These endpoints predate the v2 API and remain available for existing integrations. New applications should use the v2 equivalents.
|
Legacy endpoint |
Use instead |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
GET /SDrive/files/ is not deprecated — it remains the endpoint for download URLs.
List file objects
GET /SDrive/fileObjects/
Returns the API names of all configured file objects. Takes no parameters.
{ "fileObjects": ["cg__AccountFile__c", "cg__CaseFile__c"] }
The v2 equivalent additionally returns parent objects, labels, and relationship field names.
Initialize an upload (legacy)
POST /SDrive/files/
Accepts the same body as POST /SDrive/v2/upload/, minus description and versionId. Sending "multipartUpload": true returns 501 — use multipart uploads instead.
The response is not wrapped in a message envelope:
{
"accessKey": "...",
"bucketName": "...",
"s3EndPoint": "...",
"uploadRequestInfo": [ ... ]
}
Complete an upload (legacy)
PUT /SDrive/files/
{
"wipIds": ["a0X5f000000abcdEAA"],
"versionIds": ["3HL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY"]
}
versionIds is optional. The field names differ from v2, which uses files[].id and files[].versionId.
{
"resultObjects": [
{ "status": "success", "errorMessage": null, "wipFileId": "a0X5f000000abcdEAA" }
]
}
Delete files (legacy)
DELETE /SDrive/files/?objectId=<id>&wipId=<id>
Deletes a single file. For bulk deletion, send a body instead:
{
"objectId": "0015f00000XyZaBAAV",
"wipIds": ["a0X5f000000abcdEAA", "a0X5f000000efghEAA"]
}
{
"resultObjects": [
{ "status": "success", "errorMessage": null, "wipFileId": "a0X5f000000abcdEAA" }
]
}
Each entry reports the outcome for one file: status is success or fail, and errorMessage is populated only on failure.