Batch operations and SharePoint access
Upload or delete many files in one request, and open Office files for viewing or editing in the browser on SharePoint buckets. Choose an interface below. REST examples use a server-side API key; keep it out of browser code.
Configure client with the JavaScript SDK setup or
the synchronous Python client setup. For
Refine, register the named storage provider
and initialize these hooks inside a React component or custom hook under
<Refine>. Browser examples use the signed-in user session; keep API keys in server code. Replace BUCKET_SLUG with your bucket slug.
Batch upload#
Upload up to 100 files in a single multipart request. Total request size is capped at 800 MB.
- JavaScript SDK
- Python SDK
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageUploadBatchResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG').upload({
files, paths: ['users/1/avatar.png', 'users/2/avatar.png'], metadatas: [{}, {}],
}).execute<StorageUploadBatchResponse>();
files is an array of two browser File objects, in the same order as
paths and metadatas. Inspect the response’s data.successful and
data.failed lists before retrying any file.
client.storage.from_("BUCKET_SLUG").upload(
files=[("avatar.png", first), ("avatar.png", second)],
paths=["users/1/avatar.png", "users/2/avatar.png"],
)
first and second are open binary streams for the two files. Inspect the
returned dictionary’s successful and failed lists before retrying any file.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/batch-upload/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Multipart form
filesFile: users/1/avatar.pngfilesFile: users/2/avatar.pngpaths- ["users/1/avatar.png", "users/2/avatar.png"]
View cURL
curl --silent --show-error -X POST "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/batch-upload/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--form 'files=@users/1/avatar.png' \
--form 'files=@users/2/avatar.png' \
--form-string 'paths=["users/1/avatar.png","users/2/avatar.png"]'
200200 when all items succeed; 207 for partial or per-item failures.
Abbreviated REST response (object details omitted):
{
"status": "success",
"message": "Successfully uploaded 2 files",
"data": {
"uploaded_count": 2,
"failed_count": 0,
"total": 2,
"successful": [
{"index": 0, "path": "users/1/avatar.png"},
{"index": 1, "path": "users/2/avatar.png"}
],
"failed": []
},
"success": true
}
JavaScript exposes the counts and per-file results in the response’s data
field; Python returns them directly in a dictionary.
Status codes: 200 OK when every file uploaded, 207 Multi-Status when some files failed. Per-file errors in failed[] include "Permission denied", size or MIME rejections, and provider errors.
Validation rejections — return 400 Bad Request and refuse the whole batch:
- Total size exceeds 800 MB.
- Duplicate paths in the request.
- Any file is empty.
pathsarray length differs fromfilesarray length.metadataarray (when present) length differs fromfiles.
Each file is checked against the bucket's access policy. Files the caller can't upload appear in failed[] with "Permission denied".
Batch delete#
Delete up to 100 objects in a single request. Deletion is permanent; review object deletion and access requirements and confirm every path before sending the batch.
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageDeleteBatchResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.delete(['users/1/avatar.png', 'users/2/avatar.png'])
.execute<StorageDeleteBatchResponse>();
client.storage.from_("BUCKET_SLUG").delete([
"users/1/avatar.png", "users/2/avatar.png",
])
import {useDeleteMany} from '@refinedev/core';
const {mutate: remove} = useDeleteMany();
// Call from an event handler.
remove({
dataProviderName: 'storage',
resource: 'BUCKET_SLUG',
ids: ['users/1/avatar.png', 'users/2/avatar.png'],
});
Confirm every target path before calling remove. Pass object paths in
ids. Refine rejects the mutation if any path fails.
Successful deletes remain applied, so refresh the list before retrying.
All-denied failures produce ForbiddenError, all-missing failures produce
NotFoundError, and mixed failures produce TaruviError.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/batch-delete/Headers
AuthorizationApi-Key $TARUVI_API_KEYContent-Typeapplication/json
Request body
{
"paths": [
"users/1/avatar.png",
"users/2/avatar.png"
]
}
View cURL
curl --silent --show-error -X POST "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/batch-delete/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"paths": [
"users/1/avatar.png",
"users/2/avatar.png"
]
}
JSON
200200 when all items succeed; 207 for partial or per-item failures.
Check the response’s data.failed in JavaScript or the returned dictionary’s
failed list in Python.
deleted_count reports how many objects were deleted.
REST response:
{
"status": "success",
"message": "Deleted 2 of 3 objects",
"data": {
"deleted_count": 2,
"failed": [{"path": "users/3/missing.png", "error": "Object not found"}]
},
"success": true
}
Status codes: 200 OK when every path was deleted, 207 Multi-Status when some entries failed. Per-object errors include "Object not found" (safe to ignore for idempotent workflows), "Permission denied" (the policy doesn't allow deleting that file), and provider errors.
Duplicate paths are deduplicated automatically. The request rejects with 400 when the paths array is empty, has more than 100 entries, or contains empty strings.
SharePoint view and edit#
These endpoints work only on SharePoint buckets. They give the caller access to the file in SharePoint and return either a link or a redirect to a loading page.
Get a JSON view link#
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageAccessLinkResponse, TaruviResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.viewAccess('reports/quarterly.docx')
.execute<TaruviResponse<StorageAccessLinkResponse>>();
The response’s data.url contains the SharePoint link.
client.storage.from_("BUCKET_SLUG").view_access("reports/quarterly.docx")
The returned dictionary’s url contains the SharePoint link.
import {useCustom} from '@refinedev/core';
import type {StorageAccessLinkResponse} from '@taruvi/sdk';
useCustom<StorageAccessLinkResponse>({
dataProviderName: 'storage',
url: 'BUCKET_SLUG',
method: 'get',
meta: {kind: 'viewAccess', filePath: 'reports/quarterly.docx'},
});
url is the bucket slug; meta.filePath is the object path. After the
request succeeds, the hook’s result.data.url contains the SharePoint link
and result.data.mode identifies the access mode.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/view/Headers
AuthorizationApi-Key $TARUVI_API_KEYAcceptapplication/json
View cURL
curl --silent --show-error "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/view/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Accept: application/json"
200
Response:
{"status": "success", "message": "View access granted", "data": {"url": "https://<tenant>.sharepoint.com/…", "mode": "view"}}
The URL is user-specific and time-bounded by the provider; do not share it across users or embed it as a public asset.
Get an edit link#
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageAccessLinkResponse, TaruviResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.editAccess('reports/quarterly.docx')
.execute<TaruviResponse<StorageAccessLinkResponse>>();
The response’s data.url contains the SharePoint link.
client.storage.from_("BUCKET_SLUG").edit_access("reports/quarterly.docx")
The returned dictionary’s url contains the SharePoint link.
import {useCustom} from '@refinedev/core';
import type {StorageAccessLinkResponse} from '@taruvi/sdk';
useCustom<StorageAccessLinkResponse>({
dataProviderName: 'storage',
url: 'BUCKET_SLUG',
method: 'get',
meta: {kind: 'editAccess', filePath: 'reports/quarterly.docx'},
});
url is the bucket slug; meta.filePath is the object path. After the
request succeeds, the hook’s result.data.url contains the SharePoint link
and result.data.mode identifies the access mode.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/edit/Headers
AuthorizationApi-Key $TARUVI_API_KEYAcceptapplication/json
View cURL
curl --silent --show-error "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/edit/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Accept: application/json"
200
Edit access requires authentication regardless of visibility. Non-Office MIME types are refused with provider_error_code: "non_office_edit_rejected". The editable Office MIME set is fixed — see Providers for the list.
Replace reports/quarterly.docx with an existing Office file in your
SharePoint bucket. The SDK and Refine examples request JSON links.
Redirect flow (browser)#
When the Accept header does not include application/json, the endpoint returns an HTTP redirect to the SharePoint loader page:
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/view/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Save response
File/dev/null
Response headers are printed to the terminal.
View cURL
curl --silent --show-error "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/view/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--output '/dev/null' \
--dump-header -
302Redirects to the browser loader; this command prints the Location header.
Response: 302 Found with Location: /sites/{site}/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/open/view/. The loader page provisions the grant and reveals a link once SharePoint propagation completes.
Server-Sent Events grant stream#
The loader page uses a Server-Sent Events endpoint to stream grant progress:
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/open/view/grant/Headers
AuthorizationApi-Key $TARUVI_API_KEYAccepttext/event-stream
Stream events as they arrive.
View cURL
curl --silent --show-error "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/OBJECT_PATH/open/view/grant/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Accept: text/event-stream" \
--no-buffer
200
Events describe each step: inviting the user to SharePoint, granting access, and waiting for access to take effect. TaruviBase waits up to 8 seconds; if it times out, try again.
Related pages#
- Providers for the Office file types that support editing.
- Security and limits for who can view and edit files.
- Troubleshooting for SharePoint-specific error codes.