mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Compare commits
16 Commits
dev
...
waleedlati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a4119d122 | ||
|
|
2da7a34e23 | ||
|
|
1e16fa975d | ||
|
|
17723bb44c | ||
|
|
758f076d48 | ||
|
|
2c0ed6bca8 | ||
|
|
faeb293ead | ||
|
|
ea948a692e | ||
|
|
d3bbb51f6c | ||
|
|
a9ce3ea0a9 | ||
|
|
3b6900236f | ||
|
|
68b2302d98 | ||
|
|
6e5cd21ab0 | ||
|
|
92565c088d | ||
|
|
0d11f9f61c | ||
|
|
c893ec7d89 |
@@ -1409,7 +1409,7 @@ export function AmplitudeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 49'>
|
||||
<path
|
||||
fill='#FFFFFF'
|
||||
fill='currentColor'
|
||||
d='M23.4,15.3c0.6,1.8,1.2,4.1,1.9,6.7c-2.6,0-5.3-0.1-7.8-0.1h-1.3c1.5-5.7,3.2-10.1,4.6-11.1 c0.1-0.1,0.2-0.1,0.4-0.1c0.2,0,0.3,0.1,0.5,0.3C21.9,11.5,22.5,12.7,23.4,15.3z M49,24.5C49,38,38,49,24.5,49S0,38,0,24.5 S11,0,24.5,0S49,11,49,24.5z M42.7,23.9c0-0.6-0.4-1.2-1-1.3l0,0l0,0l0,0c-0.1,0-0.1,0-0.2,0h-0.2c-4.1-0.3-8.4-0.4-12.4-0.5l0,0 C27,14.8,24.5,7.4,21.3,7.4c-3,0-5.8,4.9-8.2,14.5c-1.7,0-3.2,0-4.6-0.1c-0.1,0-0.2,0-0.2,0c-0.3,0-0.5,0-0.5,0 c-0.8,0.1-1.4,0.9-1.4,1.7c0,0.8,0.6,1.6,1.5,1.7l0,0h4.6c-0.4,1.9-0.8,3.8-1.1,5.6l-0.1,0.8l0,0c0,0.6,0.5,1.1,1.1,1.1 c0.4,0,0.8-0.2,1-0.5l0,0l2.2-7.1h10.7c0.8,3.1,1.7,6.3,2.8,9.3c0.6,1.6,2,5.4,4.4,5.4l0,0c3.6,0,5-5.8,5.9-9.6 c0.2-0.8,0.4-1.5,0.5-2.1l0.1-0.2l0,0c0-0.1,0-0.2,0-0.3c-0.1-0.2-0.2-0.3-0.4-0.4c-0.3-0.1-0.5,0.1-0.6,0.4l0,0l-0.1,0.2 c-0.3,0.8-0.6,1.6-0.8,2.3v0.1c-1.6,4.4-2.3,6.4-3.7,6.4l0,0l0,0l0,0c-1.8,0-3.5-7.3-4.1-10.1c-0.1-0.5-0.2-0.9-0.3-1.3h11.7 c0.2,0,0.4-0.1,0.6-0.1l0,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0l0,0c0,0,0.1,0,0.1-0.1l0,0C42.5,24.6,42.7,24.3,42.7,23.9z'
|
||||
/>
|
||||
</svg>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AsanaIcon,
|
||||
AshbyIcon,
|
||||
AttioIcon,
|
||||
BoxCompanyIcon,
|
||||
BrainIcon,
|
||||
BrandfetchIcon,
|
||||
BrowserUseIcon,
|
||||
@@ -185,6 +186,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
asana: AsanaIcon,
|
||||
ashby: AshbyIcon,
|
||||
attio: AttioIcon,
|
||||
box: BoxCompanyIcon,
|
||||
brandfetch: BrandfetchIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
calcom: CalComIcon,
|
||||
|
||||
440
apps/docs/content/docs/en/tools/box.mdx
Normal file
440
apps/docs/content/docs/en/tools/box.mdx
Normal file
@@ -0,0 +1,440 @@
|
||||
---
|
||||
title: Box
|
||||
description: Manage files, folders, and e-signatures with Box
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="box"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Box](https://www.box.com/) is a leading cloud content management and file sharing platform trusted by enterprises worldwide to securely store, manage, and collaborate on files. Box provides robust APIs for automating file operations and integrating with business workflows, including [Box Sign](https://www.box.com/esignature) for native e-signatures.
|
||||
|
||||
With the Box integration in Sim, you can:
|
||||
|
||||
- **Upload files**: Upload documents, images, and other files to any Box folder
|
||||
- **Download files**: Retrieve file content from Box for processing in your workflows
|
||||
- **Get file info**: Access detailed metadata including size, owner, timestamps, tags, and shared links
|
||||
- **List folder contents**: Browse files and folders with sorting and pagination support
|
||||
- **Create folders**: Organize your Box storage by creating new folders programmatically
|
||||
- **Delete files and folders**: Remove content with optional recursive deletion for folders
|
||||
- **Copy files**: Duplicate files across folders with optional renaming
|
||||
- **Search**: Find files and folders by name, content, extension, or location
|
||||
- **Update file metadata**: Rename, move, add descriptions, or tag files
|
||||
- **Create sign requests**: Send documents for e-signature with one or more signers
|
||||
- **Track signing status**: Monitor the progress of sign requests
|
||||
- **List sign requests**: View all sign requests with marker-based pagination
|
||||
- **Cancel sign requests**: Cancel pending sign requests that are no longer needed
|
||||
- **Resend sign reminders**: Send reminder notifications to signers who haven't completed signing
|
||||
|
||||
These capabilities allow your Sim agents to automate Box operations directly within your workflows — from organizing documents and distributing content to processing uploaded files, managing e-signature workflows for offer letters and contracts, and maintaining structured cloud storage as part of your business processes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Box into your workflow to manage files, folders, and e-signatures. Upload and download files, search content, create folders, send documents for e-signature, track signing status, and more.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `box_upload_file`
|
||||
|
||||
Upload a file to a Box folder
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `parentFolderId` | string | Yes | The ID of the folder to upload the file to \(use "0" for root\) |
|
||||
| `file` | file | No | The file to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Legacy: base64 encoded file content |
|
||||
| `fileName` | string | No | Optional filename override |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | File ID |
|
||||
| `name` | string | File name |
|
||||
| `size` | number | File size in bytes |
|
||||
| `sha1` | string | SHA1 hash of file content |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `modifiedAt` | string | Last modified timestamp |
|
||||
| `parentId` | string | Parent folder ID |
|
||||
| `parentName` | string | Parent folder name |
|
||||
|
||||
### `box_download_file`
|
||||
|
||||
Download a file from Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to download |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `file` | file | Downloaded file stored in execution files |
|
||||
| `content` | string | Base64 encoded file content |
|
||||
|
||||
### `box_get_file_info`
|
||||
|
||||
Get detailed information about a file in Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to get information about |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | File ID |
|
||||
| `name` | string | File name |
|
||||
| `description` | string | File description |
|
||||
| `size` | number | File size in bytes |
|
||||
| `sha1` | string | SHA1 hash of file content |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `modifiedAt` | string | Last modified timestamp |
|
||||
| `createdBy` | object | User who created the file |
|
||||
| `modifiedBy` | object | User who last modified the file |
|
||||
| `ownedBy` | object | User who owns the file |
|
||||
| `parentId` | string | Parent folder ID |
|
||||
| `parentName` | string | Parent folder name |
|
||||
| `sharedLink` | json | Shared link details |
|
||||
| `tags` | array | File tags |
|
||||
| `commentCount` | number | Number of comments |
|
||||
|
||||
### `box_list_folder_items`
|
||||
|
||||
List files and folders in a Box folder
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `folderId` | string | Yes | The ID of the folder to list items from \(use "0" for root\) |
|
||||
| `limit` | number | No | Maximum number of items to return per page |
|
||||
| `offset` | number | No | The offset for pagination |
|
||||
| `sort` | string | No | Sort field: id, name, date, or size |
|
||||
| `direction` | string | No | Sort direction: ASC or DESC |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `entries` | array | List of items in the folder |
|
||||
| ↳ `type` | string | Item type \(file, folder, web_link\) |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `size` | number | Item size in bytes |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `modifiedAt` | string | Last modified timestamp |
|
||||
| `totalCount` | number | Total number of items in the folder |
|
||||
| `offset` | number | Current pagination offset |
|
||||
| `limit` | number | Current pagination limit |
|
||||
|
||||
### `box_create_folder`
|
||||
|
||||
Create a new folder in Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Name for the new folder |
|
||||
| `parentFolderId` | string | Yes | The ID of the parent folder \(use "0" for root\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Folder ID |
|
||||
| `name` | string | Folder name |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `modifiedAt` | string | Last modified timestamp |
|
||||
| `parentId` | string | Parent folder ID |
|
||||
| `parentName` | string | Parent folder name |
|
||||
|
||||
### `box_delete_file`
|
||||
|
||||
Delete a file from Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the file was successfully deleted |
|
||||
| `message` | string | Success confirmation message |
|
||||
|
||||
### `box_delete_folder`
|
||||
|
||||
Delete a folder from Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `folderId` | string | Yes | The ID of the folder to delete |
|
||||
| `recursive` | boolean | No | Delete folder and all its contents recursively |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the folder was successfully deleted |
|
||||
| `message` | string | Success confirmation message |
|
||||
|
||||
### `box_copy_file`
|
||||
|
||||
Copy a file to another folder in Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to copy |
|
||||
| `parentFolderId` | string | Yes | The ID of the destination folder |
|
||||
| `name` | string | No | Optional new name for the copied file |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | File ID |
|
||||
| `name` | string | File name |
|
||||
| `size` | number | File size in bytes |
|
||||
| `sha1` | string | SHA1 hash of file content |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `modifiedAt` | string | Last modified timestamp |
|
||||
| `parentId` | string | Parent folder ID |
|
||||
| `parentName` | string | Parent folder name |
|
||||
|
||||
### `box_search`
|
||||
|
||||
Search for files and folders in Box
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | The search query string |
|
||||
| `limit` | number | No | Maximum number of results to return |
|
||||
| `offset` | number | No | The offset for pagination |
|
||||
| `ancestorFolderId` | string | No | Restrict search to a specific folder and its subfolders |
|
||||
| `fileExtensions` | string | No | Comma-separated file extensions to filter by \(e.g., pdf,docx\) |
|
||||
| `type` | string | No | Restrict to a specific content type: file, folder, or web_link |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Search results |
|
||||
| ↳ `type` | string | Item type \(file, folder, web_link\) |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `size` | number | Item size in bytes |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `modifiedAt` | string | Last modified timestamp |
|
||||
| ↳ `parentId` | string | Parent folder ID |
|
||||
| ↳ `parentName` | string | Parent folder name |
|
||||
| `totalCount` | number | Total number of matching results |
|
||||
|
||||
### `box_update_file`
|
||||
|
||||
Update file info in Box (rename, move, change description, add tags)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to update |
|
||||
| `name` | string | No | New name for the file |
|
||||
| `description` | string | No | New description for the file \(max 256 characters\) |
|
||||
| `parentFolderId` | string | No | Move the file to a different folder by specifying the folder ID |
|
||||
| `tags` | string | No | Comma-separated tags to set on the file |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | File ID |
|
||||
| `name` | string | File name |
|
||||
| `description` | string | File description |
|
||||
| `size` | number | File size in bytes |
|
||||
| `sha1` | string | SHA1 hash of file content |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `modifiedAt` | string | Last modified timestamp |
|
||||
| `createdBy` | object | User who created the file |
|
||||
| `modifiedBy` | object | User who last modified the file |
|
||||
| `ownedBy` | object | User who owns the file |
|
||||
| `parentId` | string | Parent folder ID |
|
||||
| `parentName` | string | Parent folder name |
|
||||
| `sharedLink` | json | Shared link details |
|
||||
| `tags` | array | File tags |
|
||||
| `commentCount` | number | Number of comments |
|
||||
|
||||
### `box_sign_create_request`
|
||||
|
||||
Create a new Box Sign request to send documents for e-signature
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `sourceFileIds` | string | Yes | Comma-separated Box file IDs to send for signing |
|
||||
| `signerEmail` | string | Yes | Primary signer email address |
|
||||
| `signerRole` | string | No | Primary signer role: signer, approver, or final_copy_reader \(default: signer\) |
|
||||
| `additionalSigners` | string | No | JSON array of additional signers, e.g. \[\{"email":"user@example.com","role":"signer"\}\] |
|
||||
| `parentFolderId` | string | No | Box folder ID where signed documents will be stored \(default: user root\) |
|
||||
| `emailSubject` | string | No | Custom subject line for the signing email |
|
||||
| `emailMessage` | string | No | Custom message in the signing email body |
|
||||
| `name` | string | No | Name for the sign request |
|
||||
| `daysValid` | number | No | Number of days before the request expires \(0-730\) |
|
||||
| `areRemindersEnabled` | boolean | No | Whether to send automatic signing reminders |
|
||||
| `areTextSignaturesEnabled` | boolean | No | Whether to allow typed \(text\) signatures |
|
||||
| `signatureColor` | string | No | Signature color: blue, black, or red |
|
||||
| `redirectUrl` | string | No | URL to redirect signers to after signing |
|
||||
| `declinedRedirectUrl` | string | No | URL to redirect signers to after declining |
|
||||
| `isDocumentPreparationNeeded` | boolean | No | Whether document preparation is needed before sending |
|
||||
| `externalId` | string | No | External system reference ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Sign request ID |
|
||||
| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) |
|
||||
| `name` | string | Sign request name |
|
||||
| `shortId` | string | Human-readable short ID |
|
||||
| `signers` | array | List of signers |
|
||||
| `sourceFiles` | array | Source files for signing |
|
||||
| `emailSubject` | string | Custom email subject line |
|
||||
| `emailMessage` | string | Custom email message body |
|
||||
| `daysValid` | number | Number of days the request is valid |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `autoExpireAt` | string | Auto-expiration timestamp |
|
||||
| `prepareUrl` | string | URL for document preparation \(if preparation is needed\) |
|
||||
| `senderEmail` | string | Email of the sender |
|
||||
|
||||
### `box_sign_get_request`
|
||||
|
||||
Get the details and status of a Box Sign request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `signRequestId` | string | Yes | The ID of the sign request to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Sign request ID |
|
||||
| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) |
|
||||
| `name` | string | Sign request name |
|
||||
| `shortId` | string | Human-readable short ID |
|
||||
| `signers` | array | List of signers |
|
||||
| `sourceFiles` | array | Source files for signing |
|
||||
| `emailSubject` | string | Custom email subject line |
|
||||
| `emailMessage` | string | Custom email message body |
|
||||
| `daysValid` | number | Number of days the request is valid |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `autoExpireAt` | string | Auto-expiration timestamp |
|
||||
| `prepareUrl` | string | URL for document preparation \(if preparation is needed\) |
|
||||
| `senderEmail` | string | Email of the sender |
|
||||
|
||||
### `box_sign_list_requests`
|
||||
|
||||
List all Box Sign requests
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | number | No | Maximum number of sign requests to return \(max 1000\) |
|
||||
| `marker` | string | No | Pagination marker from a previous response |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `signRequests` | array | List of sign requests |
|
||||
| ↳ `id` | string | Sign request ID |
|
||||
| ↳ `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) |
|
||||
| ↳ `name` | string | Sign request name |
|
||||
| ↳ `shortId` | string | Human-readable short ID |
|
||||
| ↳ `signers` | array | List of signers |
|
||||
| ↳ `sourceFiles` | array | Source files for signing |
|
||||
| ↳ `emailSubject` | string | Custom email subject line |
|
||||
| ↳ `emailMessage` | string | Custom email message body |
|
||||
| ↳ `daysValid` | number | Number of days the request is valid |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `autoExpireAt` | string | Auto-expiration timestamp |
|
||||
| ↳ `prepareUrl` | string | URL for document preparation \(if preparation is needed\) |
|
||||
| ↳ `senderEmail` | string | Email of the sender |
|
||||
| `count` | number | Number of sign requests returned in this page |
|
||||
| `nextMarker` | string | Marker for next page of results |
|
||||
|
||||
### `box_sign_cancel_request`
|
||||
|
||||
Cancel a pending Box Sign request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `signRequestId` | string | Yes | The ID of the sign request to cancel |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Sign request ID |
|
||||
| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) |
|
||||
| `name` | string | Sign request name |
|
||||
| `shortId` | string | Human-readable short ID |
|
||||
| `signers` | array | List of signers |
|
||||
| `sourceFiles` | array | Source files for signing |
|
||||
| `emailSubject` | string | Custom email subject line |
|
||||
| `emailMessage` | string | Custom email message body |
|
||||
| `daysValid` | number | Number of days the request is valid |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `autoExpireAt` | string | Auto-expiration timestamp |
|
||||
| `prepareUrl` | string | URL for document preparation \(if preparation is needed\) |
|
||||
| `senderEmail` | string | Email of the sender |
|
||||
|
||||
### `box_sign_resend_request`
|
||||
|
||||
Resend a Box Sign request to signers who have not yet signed
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `signRequestId` | string | Yes | The ID of the sign request to resend |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success confirmation message |
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ description: Send documents for e-signature via DocuSign
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
<BlockInfoCard
|
||||
type="docusign"
|
||||
color="#4C00FF"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
@@ -26,6 +26,7 @@ With the DocuSign integration in Sim, you can:
|
||||
In Sim, the DocuSign integration enables your agents to automate document workflows end-to-end. Agents can generate agreements, send them for signature, monitor completion, and retrieve signed copies—powering contract management, HR onboarding, sales closings, and compliance processes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Create and send envelopes for e-signature, use templates, check signing status, download signed documents, and manage recipients with DocuSign.
|
||||
@@ -36,194 +37,194 @@ Create and send envelopes for e-signature, use templates, check signing status,
|
||||
|
||||
### `docusign_send_envelope`
|
||||
|
||||
Create and send a DocuSign envelope with a document for e-signature
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `emailSubject` | string | Yes | Email subject for the envelope |
|
||||
| `emailBody` | string | No | Email body message |
|
||||
| `signerEmail` | string | Yes | Email address of the signer |
|
||||
| `signerName` | string | Yes | Full name of the signer |
|
||||
| `ccEmail` | string | No | Email address of carbon copy recipient |
|
||||
| `ccName` | string | No | Full name of carbon copy recipient |
|
||||
| `file` | file | No | Document file to send for signature |
|
||||
| `status` | string | No | Envelope status: "sent" to send immediately, "created" for draft \(default: "sent"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `envelopeId` | string | Created envelope ID |
|
||||
| `status` | string | Envelope status |
|
||||
| `statusDateTime` | string | Status change datetime |
|
||||
| `uri` | string | Envelope URI |
|
||||
|
||||
### `docusign_create_from_template`
|
||||
|
||||
Create and send a DocuSign envelope using a pre-built template
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `templateId` | string | Yes | DocuSign template ID to use |
|
||||
| `emailSubject` | string | No | Override email subject \(uses template default if not set\) |
|
||||
| `emailBody` | string | No | Override email body message |
|
||||
| `templateRoles` | string | Yes | JSON array of template roles, e.g. \[\{"roleName":"Signer","name":"John","email":"john@example.com"\}\] |
|
||||
| `status` | string | No | Envelope status: "sent" to send immediately, "created" for draft \(default: "sent"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `envelopeId` | string | Created envelope ID |
|
||||
| `status` | string | Envelope status |
|
||||
| `statusDateTime` | string | Status change datetime |
|
||||
| `uri` | string | Envelope URI |
|
||||
|
||||
### `docusign_get_envelope`
|
||||
|
||||
Get the details and status of a DocuSign envelope
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `envelopeId` | string | Yes | The envelope ID to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `status` | string | Envelope status \(created, sent, delivered, completed, declined, voided\) |
|
||||
| `emailSubject` | string | Email subject line |
|
||||
| `sentDateTime` | string | When the envelope was sent |
|
||||
| `completedDateTime` | string | When all recipients completed signing |
|
||||
| `createdDateTime` | string | When the envelope was created |
|
||||
| `statusChangedDateTime` | string | When the status last changed |
|
||||
| `voidedReason` | string | Reason the envelope was voided |
|
||||
| `signerCount` | number | Number of signers |
|
||||
| `documentCount` | number | Number of documents |
|
||||
|
||||
### `docusign_list_envelopes`
|
||||
|
||||
List envelopes from your DocuSign account with optional filters
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fromDate` | string | No | Start date filter \(ISO 8601\). Defaults to 30 days ago |
|
||||
| `toDate` | string | No | End date filter \(ISO 8601\) |
|
||||
| `envelopeStatus` | string | No | Filter by status: created, sent, delivered, completed, declined, voided |
|
||||
| `searchText` | string | No | Search text to filter envelopes |
|
||||
| `count` | string | No | Maximum number of envelopes to return \(default: 25\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `envelopes` | array | Array of DocuSign envelopes |
|
||||
| ↳ `envelopeId` | string | Unique envelope identifier |
|
||||
| ↳ `status` | string | Envelope status \(created, sent, delivered, completed, declined, voided\) |
|
||||
| ↳ `emailSubject` | string | Email subject line |
|
||||
| ↳ `sentDateTime` | string | ISO 8601 datetime when envelope was sent |
|
||||
| ↳ `completedDateTime` | string | ISO 8601 datetime when envelope was completed |
|
||||
| ↳ `createdDateTime` | string | ISO 8601 datetime when envelope was created |
|
||||
| ↳ `statusChangedDateTime` | string | ISO 8601 datetime of last status change |
|
||||
| `totalSetSize` | number | Total number of matching envelopes |
|
||||
| `resultSetSize` | number | Number of envelopes returned in this response |
|
||||
|
||||
### `docusign_void_envelope`
|
||||
|
||||
Void (cancel) a sent DocuSign envelope that has not yet been completed
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `envelopeId` | string | Yes | The envelope ID to void |
|
||||
| `voidedReason` | string | Yes | Reason for voiding the envelope |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `envelopeId` | string | Voided envelope ID |
|
||||
| `status` | string | Envelope status \(voided\) |
|
||||
|
||||
### `docusign_download_document`
|
||||
|
||||
Download a signed document from a completed DocuSign envelope
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `envelopeId` | string | Yes | The envelope ID containing the document |
|
||||
| `documentId` | string | No | Specific document ID to download, or "combined" for all documents merged \(default: "combined"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `mimeType` | string | MIME type of the document |
|
||||
| `fileName` | string | Original file name |
|
||||
|
||||
### `docusign_list_templates`
|
||||
|
||||
List available templates in your DocuSign account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `searchText` | string | No | Search text to filter templates by name |
|
||||
| `count` | string | No | Maximum number of templates to return |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `templates` | array | Array of DocuSign templates |
|
||||
| ↳ `templateId` | string | Template identifier |
|
||||
| ↳ `name` | string | Template name |
|
||||
| ↳ `description` | string | Template description |
|
||||
| ↳ `shared` | boolean | Whether template is shared |
|
||||
| ↳ `created` | string | ISO 8601 creation date |
|
||||
| ↳ `lastModified` | string | ISO 8601 last modified date |
|
||||
| `totalSetSize` | number | Total number of matching templates |
|
||||
| `resultSetSize` | number | Number of templates returned in this response |
|
||||
|
||||
### `docusign_list_recipients`
|
||||
|
||||
Get the recipient status details for a DocuSign envelope
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `envelopeId` | string | Yes | The envelope ID to get recipients for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `envelopeId` | string | Envelope ID |
|
||||
| `status` | string | Envelope or operation status |
|
||||
| `envelopes` | json | Array of envelopes |
|
||||
| `templates` | json | Array of templates |
|
||||
| `signers` | json | Array of signer recipients |
|
||||
| `carbonCopies` | json | Array of CC recipients |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `mimeType` | string | Document MIME type |
|
||||
| `fileName` | string | Document file name |
|
||||
| `emailSubject` | string | Envelope email subject |
|
||||
| `totalSetSize` | number | Total matching results |
|
||||
| `resultSetSize` | number | Results returned |
|
||||
| `signers` | array | Array of DocuSign recipients |
|
||||
| ↳ `recipientId` | string | Recipient identifier |
|
||||
| ↳ `name` | string | Recipient name |
|
||||
| ↳ `email` | string | Recipient email address |
|
||||
| ↳ `status` | string | Recipient signing status \(sent, delivered, completed, declined\) |
|
||||
| ↳ `signedDateTime` | string | ISO 8601 datetime when recipient signed |
|
||||
| ↳ `deliveredDateTime` | string | ISO 8601 datetime when delivered to recipient |
|
||||
| `carbonCopies` | array | Array of carbon copy recipients |
|
||||
| ↳ `recipientId` | string | Recipient ID |
|
||||
| ↳ `name` | string | Recipient name |
|
||||
| ↳ `email` | string | Recipient email |
|
||||
| ↳ `status` | string | Recipient status |
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"asana",
|
||||
"ashby",
|
||||
"attio",
|
||||
"box",
|
||||
"brandfetch",
|
||||
"browser_use",
|
||||
"calcom",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { z } from 'zod'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { syncWorkspaceOAuthCredentialsForUser } from '@/lib/credentials/oauth'
|
||||
import { getCanonicalScopesForProvider } from '@/lib/oauth/utils'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
@@ -38,7 +39,13 @@ function toCredentialResponse(
|
||||
scope: string | null
|
||||
) {
|
||||
const storedScope = scope?.trim()
|
||||
const scopes = storedScope ? storedScope.split(/[\s,]+/).filter(Boolean) : []
|
||||
// Some providers (e.g. Box) don't return scopes in their token response,
|
||||
// so the DB column stays empty. Fall back to the configured scopes for
|
||||
// the provider so the credential-selector doesn't show a false
|
||||
// "Additional permissions required" banner.
|
||||
const scopes = storedScope
|
||||
? storedScope.split(/[\s,]+/).filter(Boolean)
|
||||
: getCanonicalScopesForProvider(providerId)
|
||||
const [_, featureType = 'default'] = providerId.split('-')
|
||||
|
||||
return {
|
||||
|
||||
140
apps/sim/app/api/tools/box/upload/route.ts
Normal file
140
apps/sim/app/api/tools/box/upload/route.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { FileInputSchema } from '@/lib/uploads/utils/file-schemas'
|
||||
import { processFilesToUserFiles, type RawFileInput } from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('BoxUploadAPI')
|
||||
|
||||
const BoxUploadSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
parentFolderId: z.string().min(1, 'Parent folder ID is required'),
|
||||
file: FileInputSchema.optional().nullable(),
|
||||
fileContent: z.string().optional().nullable(),
|
||||
fileName: z.string().optional().nullable(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized Box upload attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: authResult.error || 'Authentication required' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Authenticated Box upload request via ${authResult.authType}`)
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = BoxUploadSchema.parse(body)
|
||||
|
||||
let fileBuffer: Buffer
|
||||
let fileName: string
|
||||
|
||||
if (validatedData.file) {
|
||||
const userFiles = processFilesToUserFiles(
|
||||
[validatedData.file as RawFileInput],
|
||||
requestId,
|
||||
logger
|
||||
)
|
||||
|
||||
if (userFiles.length === 0) {
|
||||
return NextResponse.json({ success: false, error: 'Invalid file input' }, { status: 400 })
|
||||
}
|
||||
|
||||
const userFile = userFiles[0]
|
||||
logger.info(`[${requestId}] Downloading file: ${userFile.name} (${userFile.size} bytes)`)
|
||||
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
fileName = validatedData.fileName || userFile.name
|
||||
} else if (validatedData.fileContent) {
|
||||
logger.info(`[${requestId}] Using legacy base64 content input`)
|
||||
fileBuffer = Buffer.from(validatedData.fileContent, 'base64')
|
||||
fileName = validatedData.fileName || 'file'
|
||||
} else {
|
||||
return NextResponse.json({ success: false, error: 'File is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Uploading to Box folder ${validatedData.parentFolderId}: ${fileName} (${fileBuffer.length} bytes)`
|
||||
)
|
||||
|
||||
const attributes = JSON.stringify({
|
||||
name: fileName,
|
||||
parent: { id: validatedData.parentFolderId },
|
||||
})
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('attributes', attributes)
|
||||
formData.append(
|
||||
'file',
|
||||
new Blob([new Uint8Array(fileBuffer)], { type: 'application/octet-stream' }),
|
||||
fileName
|
||||
)
|
||||
|
||||
const response = await fetch('https://upload.box.com/api/2.0/files/content', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.message || 'Failed to upload file'
|
||||
logger.error(`[${requestId}] Box API error:`, { status: response.status, data })
|
||||
return NextResponse.json({ success: false, error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const file = data.entries?.[0]
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'No file returned in upload response' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] File uploaded successfully: ${file.name} (ID: ${file.id})`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
id: file.id ?? '',
|
||||
name: file.name ?? '',
|
||||
size: file.size ?? 0,
|
||||
sha1: file.sha1 ?? null,
|
||||
createdAt: file.created_at ?? null,
|
||||
modifiedAt: file.modified_at ?? null,
|
||||
parentId: file.parent?.id ?? null,
|
||||
parentName: file.parent?.name ?? null,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Validation error:`, error.errors)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: error.errors[0]?.message || 'Validation failed' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] Unexpected error:`, error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
599
apps/sim/blocks/blocks/box.ts
Normal file
599
apps/sim/blocks/blocks/box.ts
Normal file
@@ -0,0 +1,599 @@
|
||||
import { BoxCompanyIcon } from '@/components/icons'
|
||||
import { getScopesForService } from '@/lib/oauth/utils'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
|
||||
export const BoxBlock: BlockConfig = {
|
||||
type: 'box',
|
||||
name: 'Box',
|
||||
description: 'Manage files, folders, and e-signatures with Box',
|
||||
longDescription:
|
||||
'Integrate Box into your workflow to manage files, folders, and e-signatures. Upload and download files, search content, create folders, send documents for e-signature, track signing status, and more.',
|
||||
docsLink: 'https://docs.sim.ai/tools/box',
|
||||
category: 'tools',
|
||||
bgColor: '#FFFFFF',
|
||||
icon: BoxCompanyIcon,
|
||||
authMode: AuthMode.OAuth,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Upload File', id: 'upload_file' },
|
||||
{ label: 'Download File', id: 'download_file' },
|
||||
{ label: 'Get File Info', id: 'get_file_info' },
|
||||
{ label: 'List Folder Items', id: 'list_folder_items' },
|
||||
{ label: 'Create Folder', id: 'create_folder' },
|
||||
{ label: 'Delete File', id: 'delete_file' },
|
||||
{ label: 'Delete Folder', id: 'delete_folder' },
|
||||
{ label: 'Copy File', id: 'copy_file' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Update File', id: 'update_file' },
|
||||
{ label: 'Create Sign Request', id: 'sign_create_request' },
|
||||
{ label: 'Get Sign Request', id: 'sign_get_request' },
|
||||
{ label: 'List Sign Requests', id: 'sign_list_requests' },
|
||||
{ label: 'Cancel Sign Request', id: 'sign_cancel_request' },
|
||||
{ label: 'Resend Sign Request', id: 'sign_resend_request' },
|
||||
],
|
||||
value: () => 'upload_file',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Box Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'box',
|
||||
requiredScopes: getScopesForService('box'),
|
||||
placeholder: 'Select Box account',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Upload File fields
|
||||
{
|
||||
id: 'uploadFile',
|
||||
title: 'File',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Upload file to send to Box',
|
||||
mode: 'basic',
|
||||
multiple: false,
|
||||
required: { field: 'operation', value: 'upload_file' },
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'fileRef',
|
||||
title: 'File',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Reference file from previous blocks',
|
||||
mode: 'advanced',
|
||||
required: { field: 'operation', value: 'upload_file' },
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'parentFolderId',
|
||||
title: 'Parent Folder ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Folder ID (use "0" for root)',
|
||||
required: { field: 'operation', value: ['upload_file', 'create_folder', 'copy_file'] },
|
||||
condition: { field: 'operation', value: ['upload_file', 'create_folder', 'copy_file'] },
|
||||
},
|
||||
{
|
||||
id: 'uploadFileName',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional filename override',
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// File ID field (shared by download, get info, delete, copy, update)
|
||||
{
|
||||
id: 'fileId',
|
||||
title: 'File ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Box file ID',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['download_file', 'get_file_info', 'delete_file', 'copy_file', 'update_file'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['download_file', 'get_file_info', 'delete_file', 'copy_file', 'update_file'],
|
||||
},
|
||||
},
|
||||
|
||||
// Folder ID field (shared by list, delete folder)
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Folder ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Box folder ID (use "0" for root)',
|
||||
required: { field: 'operation', value: ['list_folder_items', 'delete_folder'] },
|
||||
condition: { field: 'operation', value: ['list_folder_items', 'delete_folder'] },
|
||||
},
|
||||
|
||||
// Create Folder fields
|
||||
{
|
||||
id: 'folderName',
|
||||
title: 'Folder Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Name for the new folder',
|
||||
required: { field: 'operation', value: 'create_folder' },
|
||||
condition: { field: 'operation', value: 'create_folder' },
|
||||
},
|
||||
|
||||
// Copy File fields
|
||||
{
|
||||
id: 'copyName',
|
||||
title: 'New Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional name for the copy',
|
||||
condition: { field: 'operation', value: 'copy_file' },
|
||||
},
|
||||
|
||||
// Search fields
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search query string',
|
||||
required: { field: 'operation', value: 'search' },
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'ancestorFolderId',
|
||||
title: 'Ancestor Folder ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Restrict search to a folder',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'fileExtensions',
|
||||
title: 'File Extensions',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., pdf,docx,xlsx',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Files', id: 'file' },
|
||||
{ label: 'Folders', id: 'folder' },
|
||||
{ label: 'Web Links', id: 'web_link' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Update File fields
|
||||
{
|
||||
id: 'newName',
|
||||
title: 'New Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Rename the file',
|
||||
condition: { field: 'operation', value: 'update_file' },
|
||||
},
|
||||
{
|
||||
id: 'description',
|
||||
title: 'Description',
|
||||
type: 'short-input',
|
||||
placeholder: 'File description (max 256 chars)',
|
||||
condition: { field: 'operation', value: 'update_file' },
|
||||
},
|
||||
{
|
||||
id: 'moveToFolderId',
|
||||
title: 'Move to Folder ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Move file to this folder',
|
||||
condition: { field: 'operation', value: 'update_file' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags',
|
||||
condition: { field: 'operation', value: 'update_file' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Delete Folder options
|
||||
{
|
||||
id: 'recursive',
|
||||
title: 'Delete Recursively',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'delete_folder' },
|
||||
},
|
||||
|
||||
// Shared pagination fields (file operations)
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Max results per page',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['list_folder_items', 'search', 'sign_list_requests'],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination offset',
|
||||
condition: { field: 'operation', value: ['list_folder_items', 'search'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// List Folder sort options
|
||||
{
|
||||
id: 'sort',
|
||||
title: 'Sort By',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'ID', id: 'id' },
|
||||
{ label: 'Name', id: 'name' },
|
||||
{ label: 'Date', id: 'date' },
|
||||
{ label: 'Size', id: 'size' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_folder_items' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'direction',
|
||||
title: 'Sort Direction',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Ascending', id: 'ASC' },
|
||||
{ label: 'Descending', id: 'DESC' },
|
||||
],
|
||||
value: () => 'ASC',
|
||||
condition: { field: 'operation', value: 'list_folder_items' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Sign Request fields
|
||||
{
|
||||
id: 'sourceFileIds',
|
||||
title: 'Source File IDs',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated Box file IDs (e.g., 12345,67890)',
|
||||
required: { field: 'operation', value: 'sign_create_request' },
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'signerEmail',
|
||||
title: 'Signer Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'Primary signer email address',
|
||||
required: { field: 'operation', value: 'sign_create_request' },
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'signerRole',
|
||||
title: 'Signer Role',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Signer', id: 'signer' },
|
||||
{ label: 'Approver', id: 'approver' },
|
||||
{ label: 'Final Copy Reader', id: 'final_copy_reader' },
|
||||
],
|
||||
value: () => 'signer',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'emailSubject',
|
||||
title: 'Email Subject',
|
||||
type: 'short-input',
|
||||
placeholder: 'Custom email subject line',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'emailMessage',
|
||||
title: 'Email Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Custom message in the signing email',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'signRequestName',
|
||||
title: 'Request Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Name for this sign request',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
},
|
||||
{
|
||||
id: 'additionalSigners',
|
||||
title: 'Additional Signers',
|
||||
type: 'long-input',
|
||||
placeholder: '[{"email":"user@example.com","role":"signer"}]',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'signParentFolderId',
|
||||
title: 'Destination Folder ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Box folder ID for signed documents',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'daysValid',
|
||||
title: 'Days Valid',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of days before expiry (0-730)',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'areRemindersEnabled',
|
||||
title: 'Enable Reminders',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'areTextSignaturesEnabled',
|
||||
title: 'Allow Text Signatures',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'signatureColor',
|
||||
title: 'Signature Color',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Blue', id: 'blue' },
|
||||
{ label: 'Black', id: 'black' },
|
||||
{ label: 'Red', id: 'red' },
|
||||
],
|
||||
value: () => 'blue',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'redirectUrl',
|
||||
title: 'Redirect URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'URL to redirect after signing',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'declinedRedirectUrl',
|
||||
title: 'Declined Redirect URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'URL to redirect after declining',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'isDocumentPreparationNeeded',
|
||||
title: 'Document Preparation Needed',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'externalId',
|
||||
title: 'External ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'External system reference ID',
|
||||
condition: { field: 'operation', value: 'sign_create_request' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Sign Request ID (shared by get, cancel, resend)
|
||||
{
|
||||
id: 'signRequestId',
|
||||
title: 'Sign Request ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Box Sign request ID',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['sign_get_request', 'sign_cancel_request', 'sign_resend_request'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['sign_get_request', 'sign_cancel_request', 'sign_resend_request'],
|
||||
},
|
||||
},
|
||||
|
||||
// Sign list pagination marker
|
||||
{
|
||||
id: 'marker',
|
||||
title: 'Pagination Marker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Marker from previous response',
|
||||
condition: { field: 'operation', value: 'sign_list_requests' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'box_upload_file',
|
||||
'box_download_file',
|
||||
'box_get_file_info',
|
||||
'box_list_folder_items',
|
||||
'box_create_folder',
|
||||
'box_delete_file',
|
||||
'box_delete_folder',
|
||||
'box_copy_file',
|
||||
'box_search',
|
||||
'box_update_file',
|
||||
'box_sign_create_request',
|
||||
'box_sign_get_request',
|
||||
'box_sign_list_requests',
|
||||
'box_sign_cancel_request',
|
||||
'box_sign_resend_request',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
const op = params.operation as string
|
||||
if (op.startsWith('sign_')) {
|
||||
return `box_${op}`
|
||||
}
|
||||
return `box_${op}`
|
||||
},
|
||||
params: (params) => {
|
||||
const normalizedFile = normalizeFileInput(params.file, { single: true })
|
||||
if (normalizedFile) {
|
||||
params.file = normalizedFile
|
||||
}
|
||||
const { credential, operation, ...rest } = params
|
||||
|
||||
const baseParams: Record<string, unknown> = {
|
||||
accessToken: credential,
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case 'upload_file':
|
||||
baseParams.parentFolderId = rest.parentFolderId
|
||||
baseParams.file = rest.file
|
||||
if (rest.uploadFileName) baseParams.fileName = rest.uploadFileName
|
||||
break
|
||||
case 'download_file':
|
||||
case 'get_file_info':
|
||||
case 'delete_file':
|
||||
baseParams.fileId = rest.fileId
|
||||
break
|
||||
case 'list_folder_items':
|
||||
baseParams.folderId = rest.folderId
|
||||
if (rest.limit) baseParams.limit = Number(rest.limit)
|
||||
if (rest.offset) baseParams.offset = Number(rest.offset)
|
||||
if (rest.sort) baseParams.sort = rest.sort
|
||||
if (rest.direction) baseParams.direction = rest.direction
|
||||
break
|
||||
case 'create_folder':
|
||||
baseParams.name = rest.folderName
|
||||
baseParams.parentFolderId = rest.parentFolderId
|
||||
break
|
||||
case 'delete_folder':
|
||||
baseParams.folderId = rest.folderId
|
||||
if (rest.recursive !== undefined) baseParams.recursive = rest.recursive
|
||||
break
|
||||
case 'copy_file':
|
||||
baseParams.fileId = rest.fileId
|
||||
baseParams.parentFolderId = rest.parentFolderId
|
||||
if (rest.copyName) baseParams.name = rest.copyName
|
||||
break
|
||||
case 'search':
|
||||
baseParams.query = rest.query
|
||||
if (rest.limit) baseParams.limit = Number(rest.limit)
|
||||
if (rest.offset) baseParams.offset = Number(rest.offset)
|
||||
if (rest.ancestorFolderId) baseParams.ancestorFolderId = rest.ancestorFolderId
|
||||
if (rest.fileExtensions) baseParams.fileExtensions = rest.fileExtensions
|
||||
if (rest.contentType) baseParams.type = rest.contentType
|
||||
break
|
||||
case 'update_file':
|
||||
baseParams.fileId = rest.fileId
|
||||
if (rest.newName) baseParams.name = rest.newName
|
||||
if (rest.description !== undefined) baseParams.description = rest.description
|
||||
if (rest.moveToFolderId) baseParams.parentFolderId = rest.moveToFolderId
|
||||
if (rest.tags) baseParams.tags = rest.tags
|
||||
break
|
||||
case 'sign_create_request':
|
||||
baseParams.sourceFileIds = rest.sourceFileIds
|
||||
baseParams.signerEmail = rest.signerEmail
|
||||
if (rest.signerRole) baseParams.signerRole = rest.signerRole
|
||||
if (rest.additionalSigners) baseParams.additionalSigners = rest.additionalSigners
|
||||
if (rest.signParentFolderId) baseParams.parentFolderId = rest.signParentFolderId
|
||||
if (rest.emailSubject) baseParams.emailSubject = rest.emailSubject
|
||||
if (rest.emailMessage) baseParams.emailMessage = rest.emailMessage
|
||||
if (rest.signRequestName) baseParams.name = rest.signRequestName
|
||||
if (rest.daysValid) baseParams.daysValid = Number(rest.daysValid)
|
||||
if (rest.areRemindersEnabled !== undefined)
|
||||
baseParams.areRemindersEnabled = rest.areRemindersEnabled
|
||||
if (rest.areTextSignaturesEnabled !== undefined)
|
||||
baseParams.areTextSignaturesEnabled = rest.areTextSignaturesEnabled
|
||||
if (rest.signatureColor) baseParams.signatureColor = rest.signatureColor
|
||||
if (rest.redirectUrl) baseParams.redirectUrl = rest.redirectUrl
|
||||
if (rest.declinedRedirectUrl) baseParams.declinedRedirectUrl = rest.declinedRedirectUrl
|
||||
if (rest.isDocumentPreparationNeeded !== undefined)
|
||||
baseParams.isDocumentPreparationNeeded = rest.isDocumentPreparationNeeded
|
||||
if (rest.externalId) baseParams.externalId = rest.externalId
|
||||
break
|
||||
case 'sign_get_request':
|
||||
case 'sign_cancel_request':
|
||||
case 'sign_resend_request':
|
||||
baseParams.signRequestId = rest.signRequestId
|
||||
break
|
||||
case 'sign_list_requests':
|
||||
if (rest.limit) baseParams.limit = Number(rest.limit)
|
||||
if (rest.marker) baseParams.marker = rest.marker
|
||||
break
|
||||
}
|
||||
|
||||
return baseParams
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Box OAuth credential' },
|
||||
file: { type: 'json', description: 'File to upload (canonical param)' },
|
||||
fileId: { type: 'string', description: 'Box file ID' },
|
||||
folderId: { type: 'string', description: 'Box folder ID' },
|
||||
parentFolderId: { type: 'string', description: 'Parent folder ID' },
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
sourceFileIds: { type: 'string', description: 'Comma-separated Box file IDs' },
|
||||
signerEmail: { type: 'string', description: 'Primary signer email address' },
|
||||
signRequestId: { type: 'string', description: 'Sign request ID' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: 'string',
|
||||
name: 'string',
|
||||
description: 'string',
|
||||
size: 'number',
|
||||
sha1: 'string',
|
||||
createdAt: 'string',
|
||||
modifiedAt: 'string',
|
||||
createdBy: 'json',
|
||||
modifiedBy: 'json',
|
||||
ownedBy: 'json',
|
||||
parentId: 'string',
|
||||
parentName: 'string',
|
||||
sharedLink: 'json',
|
||||
tags: 'json',
|
||||
commentCount: 'number',
|
||||
file: 'file',
|
||||
content: 'string',
|
||||
entries: 'json',
|
||||
totalCount: 'number',
|
||||
offset: 'number',
|
||||
limit: 'number',
|
||||
results: 'json',
|
||||
deleted: 'boolean',
|
||||
message: 'string',
|
||||
status: 'string',
|
||||
shortId: 'string',
|
||||
signers: 'json',
|
||||
sourceFiles: 'json',
|
||||
emailSubject: 'string',
|
||||
emailMessage: 'string',
|
||||
daysValid: 'number',
|
||||
autoExpireAt: 'string',
|
||||
prepareUrl: 'string',
|
||||
senderEmail: 'string',
|
||||
signRequests: 'json',
|
||||
count: 'number',
|
||||
nextMarker: 'string',
|
||||
},
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export const DocuSignBlock: BlockConfig<DocuSignResponse> = {
|
||||
'Create and send envelopes for e-signature, use templates, check signing status, download signed documents, and manage recipients with DocuSign.',
|
||||
docsLink: 'https://docs.sim.ai/tools/docusign',
|
||||
category: 'tools',
|
||||
bgColor: '#4C00FF',
|
||||
bgColor: '#FFFFFF',
|
||||
icon: DocuSignIcon,
|
||||
authMode: AuthMode.OAuth,
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ArxivBlock } from '@/blocks/blocks/arxiv'
|
||||
import { AsanaBlock } from '@/blocks/blocks/asana'
|
||||
import { AshbyBlock } from '@/blocks/blocks/ashby'
|
||||
import { AttioBlock } from '@/blocks/blocks/attio'
|
||||
import { BoxBlock } from '@/blocks/blocks/box'
|
||||
import { BrandfetchBlock } from '@/blocks/blocks/brandfetch'
|
||||
import { BrowserUseBlock } from '@/blocks/blocks/browser_use'
|
||||
import { CalComBlock } from '@/blocks/blocks/calcom'
|
||||
@@ -216,6 +217,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
ashby: AshbyBlock,
|
||||
attio: AttioBlock,
|
||||
brandfetch: BrandfetchBlock,
|
||||
box: BoxBlock,
|
||||
browser_use: BrowserUseBlock,
|
||||
calcom: CalComBlock,
|
||||
calendly: CalendlyBlock,
|
||||
|
||||
@@ -211,6 +211,16 @@ export const auth = betterAuth({
|
||||
modifiedAccount.refreshTokenExpiresAt = getMicrosoftRefreshTokenExpiry()
|
||||
}
|
||||
|
||||
// Box token response does not include a scope field, so Better Auth
|
||||
// stores nothing. Populate it from the requested scopes so the
|
||||
// credential-selector can verify permissions.
|
||||
if (account.providerId === 'box' && !account.scope) {
|
||||
const requestedScopes = getCanonicalScopesForProvider('box')
|
||||
if (requestedScopes.length > 0) {
|
||||
modifiedAccount.scope = requestedScopes.join(' ')
|
||||
}
|
||||
}
|
||||
|
||||
return { data: modifiedAccount }
|
||||
},
|
||||
after: async (account) => {
|
||||
@@ -478,6 +488,7 @@ export const auth = betterAuth({
|
||||
'sharepoint',
|
||||
'jira',
|
||||
'airtable',
|
||||
'box',
|
||||
'dropbox',
|
||||
'salesforce',
|
||||
'wealthbox',
|
||||
@@ -2177,6 +2188,51 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'box',
|
||||
clientId: env.BOX_CLIENT_ID as string,
|
||||
clientSecret: env.BOX_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://account.box.com/api/oauth2/authorize',
|
||||
tokenUrl: 'https://api.box.com/oauth2/token',
|
||||
scopes: getCanonicalScopesForProvider('box'),
|
||||
responseType: 'code',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/box`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
const response = await fetch('https://api.box.com/2.0/users/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
logger.error('Box API error:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: errorText,
|
||||
})
|
||||
throw new Error(`Box API error: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
id: `${data.id}-${crypto.randomUUID()}`,
|
||||
email: data.login,
|
||||
name: data.name || data.login,
|
||||
emailVerified: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
image: data.avatar_url || undefined,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in Box getUserInfo:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'dropbox',
|
||||
clientId: env.DROPBOX_CLIENT_ID as string,
|
||||
|
||||
@@ -277,6 +277,8 @@ export const env = createEnv({
|
||||
PIPEDRIVE_CLIENT_SECRET: z.string().optional(), // Pipedrive OAuth client secret
|
||||
LINEAR_CLIENT_ID: z.string().optional(), // Linear OAuth client ID
|
||||
LINEAR_CLIENT_SECRET: z.string().optional(), // Linear OAuth client secret
|
||||
BOX_CLIENT_ID: z.string().optional(), // Box OAuth client ID
|
||||
BOX_CLIENT_SECRET: z.string().optional(), // Box OAuth client secret
|
||||
DROPBOX_CLIENT_ID: z.string().optional(), // Dropbox OAuth client ID
|
||||
DROPBOX_CLIENT_SECRET: z.string().optional(), // Dropbox OAuth client secret
|
||||
SLACK_CLIENT_ID: z.string().optional(), // Slack OAuth client ID
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
AirtableIcon,
|
||||
AsanaIcon,
|
||||
AttioIcon,
|
||||
BoxCompanyIcon,
|
||||
CalComIcon,
|
||||
ConfluenceIcon,
|
||||
DocuSignIcon,
|
||||
@@ -572,6 +573,21 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
},
|
||||
defaultService: 'linear',
|
||||
},
|
||||
box: {
|
||||
name: 'Box',
|
||||
icon: BoxCompanyIcon,
|
||||
services: {
|
||||
box: {
|
||||
name: 'Box',
|
||||
description: 'Manage files, folders, and e-signatures with Box.',
|
||||
providerId: 'box',
|
||||
icon: BoxCompanyIcon,
|
||||
baseProviderIcon: BoxCompanyIcon,
|
||||
scopes: ['root_readwrite', 'sign_requests.readwrite'],
|
||||
},
|
||||
},
|
||||
defaultService: 'box',
|
||||
},
|
||||
dropbox: {
|
||||
name: 'Dropbox',
|
||||
icon: DropboxIcon,
|
||||
@@ -1125,6 +1141,15 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
|
||||
useBasicAuth: false,
|
||||
}
|
||||
}
|
||||
case 'box': {
|
||||
const { clientId, clientSecret } = getCredentials(env.BOX_CLIENT_ID, env.BOX_CLIENT_SECRET)
|
||||
return {
|
||||
tokenEndpoint: 'https://api.box.com/oauth2/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
}
|
||||
}
|
||||
case 'docusign': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.DOCUSIGN_CLIENT_ID,
|
||||
|
||||
@@ -21,6 +21,7 @@ export type OAuthProvider =
|
||||
| 'airtable'
|
||||
| 'notion'
|
||||
| 'jira'
|
||||
| 'box'
|
||||
| 'dropbox'
|
||||
| 'microsoft'
|
||||
| 'microsoft-dataverse'
|
||||
@@ -70,6 +71,7 @@ export type OAuthService =
|
||||
| 'airtable'
|
||||
| 'notion'
|
||||
| 'jira'
|
||||
| 'box'
|
||||
| 'dropbox'
|
||||
| 'microsoft-dataverse'
|
||||
| 'microsoft-excel'
|
||||
|
||||
@@ -342,6 +342,7 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
// Box scopes
|
||||
root_readwrite: 'Read and write all files and folders in Box account',
|
||||
root_readonly: 'Read all files and folders in Box account',
|
||||
'sign_requests.readwrite': 'Create and manage Box Sign e-signature requests',
|
||||
|
||||
// Shopify scopes
|
||||
write_products: 'Read and manage Shopify products',
|
||||
|
||||
82
apps/sim/tools/box/copy_file.ts
Normal file
82
apps/sim/tools/box/copy_file.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxCopyFileParams, BoxUploadFileResponse } from './types'
|
||||
import { UPLOAD_FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxCopyFileTool: ToolConfig<BoxCopyFileParams, BoxUploadFileResponse> = {
|
||||
id: 'box_copy_file',
|
||||
name: 'Box Copy File',
|
||||
description: 'Copy a file to another folder in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to copy',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the destination folder',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional new name for the copied file',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}/copy`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
parent: { id: params.parentFolderId.trim() },
|
||||
}
|
||||
if (params.name) body.name = params.name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: UPLOAD_FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
71
apps/sim/tools/box/create_folder.ts
Normal file
71
apps/sim/tools/box/create_folder.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxCreateFolderParams, BoxFolderResponse } from './types'
|
||||
import { FOLDER_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxCreateFolderTool: ToolConfig<BoxCreateFolderParams, BoxFolderResponse> = {
|
||||
id: 'box_create_folder',
|
||||
name: 'Box Create Folder',
|
||||
description: 'Create a new folder in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the new folder',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the parent folder (use "0" for root)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.box.com/2.0/folders',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
name: params.name,
|
||||
parent: { id: params.parentFolderId.trim() },
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FOLDER_OUTPUT_PROPERTIES,
|
||||
}
|
||||
57
apps/sim/tools/box/delete_file.ts
Normal file
57
apps/sim/tools/box/delete_file.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { BoxDeleteFileParams } from './types'
|
||||
|
||||
export const boxDeleteFileTool: ToolConfig<BoxDeleteFileParams, ToolResponse> = {
|
||||
id: 'box_delete_file',
|
||||
name: 'Box Delete File',
|
||||
description: 'Delete a file from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 204) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
message: 'File deleted successfully',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the file was successfully deleted' },
|
||||
message: { type: 'string', description: 'Success confirmation message' },
|
||||
},
|
||||
}
|
||||
68
apps/sim/tools/box/delete_folder.ts
Normal file
68
apps/sim/tools/box/delete_folder.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { BoxDeleteFolderParams } from './types'
|
||||
|
||||
export const boxDeleteFolderTool: ToolConfig<BoxDeleteFolderParams, ToolResponse> = {
|
||||
id: 'box_delete_folder',
|
||||
name: 'Box Delete Folder',
|
||||
description: 'Delete a folder from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to delete',
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete folder and all its contents recursively',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.recursive) queryParams.set('recursive', 'true')
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.box.com/2.0/folders/${params.folderId.trim()}${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 204) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
message: 'Folder deleted successfully',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the folder was successfully deleted' },
|
||||
message: { type: 'string', description: 'Success confirmation message' },
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/box/download_file.ts
Normal file
87
apps/sim/tools/box/download_file.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxDownloadFileParams, BoxDownloadFileResponse } from './types'
|
||||
|
||||
export const boxDownloadFileTool: ToolConfig<BoxDownloadFileParams, BoxDownloadFileResponse> = {
|
||||
id: 'box_download_file',
|
||||
name: 'Box Download File',
|
||||
description: 'Download a file from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to download',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}/content`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 202) {
|
||||
const retryAfter = response.headers.get('retry-after') || 'a few'
|
||||
throw new Error(`File is not yet ready for download. Retry after ${retryAfter} seconds.`)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(errorText || `Failed to download file: ${response.status}`)
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type') || 'application/octet-stream'
|
||||
const contentDisposition = response.headers.get('content-disposition')
|
||||
let fileName = 'download'
|
||||
|
||||
if (contentDisposition) {
|
||||
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)
|
||||
if (match?.[1]) {
|
||||
fileName = match[1].replace(/['"]/g, '')
|
||||
}
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
name: fileName,
|
||||
mimeType: contentType,
|
||||
data: buffer.toString('base64'),
|
||||
size: buffer.length,
|
||||
},
|
||||
content: buffer.toString('base64'),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'file',
|
||||
description: 'Downloaded file stored in execution files',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Base64 encoded file content',
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/box/get_file_info.ts
Normal file
87
apps/sim/tools/box/get_file_info.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFileInfoResponse, BoxGetFileInfoParams } from './types'
|
||||
import { FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxGetFileInfoTool: ToolConfig<BoxGetFileInfoParams, BoxFileInfoResponse> = {
|
||||
id: 'box_get_file_info',
|
||||
name: 'Box Get File Info',
|
||||
description: 'Get detailed information about a file in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to get information about',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
createdBy: data.created_by
|
||||
? {
|
||||
id: data.created_by.id,
|
||||
name: data.created_by.name,
|
||||
login: data.created_by.login,
|
||||
}
|
||||
: null,
|
||||
modifiedBy: data.modified_by
|
||||
? {
|
||||
id: data.modified_by.id,
|
||||
name: data.modified_by.name,
|
||||
login: data.modified_by.login,
|
||||
}
|
||||
: null,
|
||||
ownedBy: data.owned_by
|
||||
? {
|
||||
id: data.owned_by.id,
|
||||
name: data.owned_by.name,
|
||||
login: data.owned_by.login,
|
||||
}
|
||||
: null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
sharedLink: data.shared_link ?? null,
|
||||
tags: data.tags ?? [],
|
||||
commentCount: data.comment_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
10
apps/sim/tools/box/index.ts
Normal file
10
apps/sim/tools/box/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export { boxCopyFileTool } from '@/tools/box/copy_file'
|
||||
export { boxCreateFolderTool } from '@/tools/box/create_folder'
|
||||
export { boxDeleteFileTool } from '@/tools/box/delete_file'
|
||||
export { boxDeleteFolderTool } from '@/tools/box/delete_folder'
|
||||
export { boxDownloadFileTool } from '@/tools/box/download_file'
|
||||
export { boxGetFileInfoTool } from '@/tools/box/get_file_info'
|
||||
export { boxListFolderItemsTool } from '@/tools/box/list_folder_items'
|
||||
export { boxSearchTool } from '@/tools/box/search'
|
||||
export { boxUpdateFileTool } from '@/tools/box/update_file'
|
||||
export { boxUploadFileTool } from '@/tools/box/upload_file'
|
||||
98
apps/sim/tools/box/list_folder_items.ts
Normal file
98
apps/sim/tools/box/list_folder_items.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFolderItemsResponse, BoxListFolderItemsParams } from './types'
|
||||
import { FOLDER_ITEMS_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxListFolderItemsTool: ToolConfig<BoxListFolderItemsParams, BoxFolderItemsResponse> =
|
||||
{
|
||||
id: 'box_list_folder_items',
|
||||
name: 'Box List Folder Items',
|
||||
description: 'List files and folders in a Box folder',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to list items from (use "0" for root)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of items to return per page',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The offset for pagination',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort field: id, name, date, or size',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction: ASC or DESC',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.limit !== undefined) queryParams.set('limit', String(params.limit))
|
||||
if (params.offset !== undefined) queryParams.set('offset', String(params.offset))
|
||||
if (params.sort) queryParams.set('sort', params.sort)
|
||||
if (params.direction) queryParams.set('direction', params.direction)
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.box.com/2.0/folders/${params.folderId.trim()}/items${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
entries: (data.entries ?? []).map((item: Record<string, unknown>) => ({
|
||||
type: item.type ?? '',
|
||||
id: item.id ?? '',
|
||||
name: item.name ?? '',
|
||||
size: item.size ?? null,
|
||||
createdAt: item.created_at ?? null,
|
||||
modifiedAt: item.modified_at ?? null,
|
||||
})),
|
||||
totalCount: data.total_count ?? 0,
|
||||
offset: data.offset ?? 0,
|
||||
limit: data.limit ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FOLDER_ITEMS_OUTPUT_PROPERTIES,
|
||||
}
|
||||
105
apps/sim/tools/box/search.ts
Normal file
105
apps/sim/tools/box/search.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSearchParams, BoxSearchResponse } from './types'
|
||||
import { SEARCH_RESULT_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSearchTool: ToolConfig<BoxSearchParams, BoxSearchResponse> = {
|
||||
id: 'box_search',
|
||||
name: 'Box Search',
|
||||
description: 'Search for files and folders in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The search query string',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The offset for pagination',
|
||||
},
|
||||
ancestorFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Restrict search to a specific folder and its subfolders',
|
||||
},
|
||||
fileExtensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated file extensions to filter by (e.g., pdf,docx)',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Restrict to a specific content type: file, folder, or web_link',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.set('query', params.query)
|
||||
if (params.limit !== undefined) queryParams.set('limit', String(params.limit))
|
||||
if (params.offset !== undefined) queryParams.set('offset', String(params.offset))
|
||||
if (params.ancestorFolderId)
|
||||
queryParams.set('ancestor_folder_ids', params.ancestorFolderId.trim())
|
||||
if (params.fileExtensions) queryParams.set('file_extensions', params.fileExtensions)
|
||||
if (params.type) queryParams.set('type', params.type)
|
||||
return `https://api.box.com/2.0/search?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results: (data.entries ?? []).map((item: Record<string, unknown>) => ({
|
||||
type: item.type ?? '',
|
||||
id: item.id ?? '',
|
||||
name: item.name ?? '',
|
||||
size: item.size ?? null,
|
||||
createdAt: item.created_at ?? null,
|
||||
modifiedAt: item.modified_at ?? null,
|
||||
parentId: (item.parent as Record<string, unknown> | undefined)?.id ?? null,
|
||||
parentName: (item.parent as Record<string, unknown> | undefined)?.name ?? null,
|
||||
})),
|
||||
totalCount: data.total_count ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SEARCH_RESULT_OUTPUT_PROPERTIES,
|
||||
}
|
||||
249
apps/sim/tools/box/types.ts
Normal file
249
apps/sim/tools/box/types.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface BoxUploadFileParams {
|
||||
accessToken: string
|
||||
parentFolderId: string
|
||||
file?: unknown
|
||||
fileContent?: string
|
||||
fileName?: string
|
||||
}
|
||||
|
||||
export interface BoxDownloadFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxGetFileInfoParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxListFolderItemsParams {
|
||||
accessToken: string
|
||||
folderId: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
sort?: string
|
||||
direction?: string
|
||||
}
|
||||
|
||||
export interface BoxCreateFolderParams {
|
||||
accessToken: string
|
||||
name: string
|
||||
parentFolderId: string
|
||||
}
|
||||
|
||||
export interface BoxDeleteFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxDeleteFolderParams {
|
||||
accessToken: string
|
||||
folderId: string
|
||||
recursive?: boolean
|
||||
}
|
||||
|
||||
export interface BoxCopyFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
parentFolderId: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface BoxSearchParams {
|
||||
accessToken: string
|
||||
query: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
ancestorFolderId?: string
|
||||
fileExtensions?: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface BoxUpdateFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
name?: string
|
||||
description?: string
|
||||
parentFolderId?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface BoxUploadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
size: number
|
||||
sha1: string | null
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxDownloadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
file: {
|
||||
name: string
|
||||
mimeType: string
|
||||
data: string
|
||||
size: number
|
||||
}
|
||||
content: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFileInfoResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
size: number
|
||||
sha1: string | null
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
createdBy: { id: string; name: string; login: string } | null
|
||||
modifiedBy: { id: string; name: string; login: string } | null
|
||||
ownedBy: { id: string; name: string; login: string } | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
sharedLink: Record<string, unknown> | null
|
||||
tags: string[]
|
||||
commentCount: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFolderItemsResponse extends ToolResponse {
|
||||
output: {
|
||||
entries: Array<Record<string, unknown>>
|
||||
totalCount: number
|
||||
offset: number
|
||||
limit: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Array<Record<string, unknown>>
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
const USER_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
login: { type: 'string', description: 'User email/login' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FILE_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'File ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
description: { type: 'string', description: 'File description', optional: true },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
sha1: { type: 'string', description: 'SHA1 hash of file content', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
createdBy: {
|
||||
type: 'object',
|
||||
description: 'User who created the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
modifiedBy: {
|
||||
type: 'object',
|
||||
description: 'User who last modified the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
ownedBy: {
|
||||
type: 'object',
|
||||
description: 'User who owns the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
sharedLink: { type: 'json', description: 'Shared link details', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'File tags',
|
||||
items: { type: 'string' },
|
||||
optional: true,
|
||||
},
|
||||
commentCount: { type: 'number', description: 'Number of comments', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FOLDER_ITEMS_OUTPUT_PROPERTIES = {
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'List of items in the folder',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Item type (file, folder, web_link)' },
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
name: { type: 'string', description: 'Item name' },
|
||||
size: { type: 'number', description: 'Item size in bytes', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: { type: 'number', description: 'Total number of items in the folder' },
|
||||
offset: { type: 'number', description: 'Current pagination offset' },
|
||||
limit: { type: 'number', description: 'Current pagination limit' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FOLDER_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Folder ID' },
|
||||
name: { type: 'string', description: 'Folder name' },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SEARCH_RESULT_OUTPUT_PROPERTIES = {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Search results',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Item type (file, folder, web_link)' },
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
name: { type: 'string', description: 'Item name' },
|
||||
size: { type: 'number', description: 'Item size in bytes', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: { type: 'number', description: 'Total number of matching results' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const UPLOAD_FILE_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'File ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
sha1: { type: 'string', description: 'SHA1 hash of file content', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
124
apps/sim/tools/box/update_file.ts
Normal file
124
apps/sim/tools/box/update_file.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFileInfoResponse, BoxUpdateFileParams } from './types'
|
||||
import { FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxUpdateFileTool: ToolConfig<BoxUpdateFileParams, BoxFileInfoResponse> = {
|
||||
id: 'box_update_file',
|
||||
name: 'Box Update File',
|
||||
description: 'Update file info in Box (rename, move, change description, add tags)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to update',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the file',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the file (max 256 characters)',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Move the file to a different folder by specifying the folder ID',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated tags to set on the file',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.name) body.name = params.name
|
||||
if (params.description !== undefined) body.description = params.description
|
||||
if (params.parentFolderId) body.parent = { id: params.parentFolderId.trim() }
|
||||
if (params.tags)
|
||||
body.tags = params.tags
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean)
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
createdBy: data.created_by
|
||||
? {
|
||||
id: data.created_by.id,
|
||||
name: data.created_by.name,
|
||||
login: data.created_by.login,
|
||||
}
|
||||
: null,
|
||||
modifiedBy: data.modified_by
|
||||
? {
|
||||
id: data.modified_by.id,
|
||||
name: data.modified_by.name,
|
||||
login: data.modified_by.login,
|
||||
}
|
||||
: null,
|
||||
ownedBy: data.owned_by
|
||||
? {
|
||||
id: data.owned_by.id,
|
||||
name: data.owned_by.name,
|
||||
login: data.owned_by.login,
|
||||
}
|
||||
: null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
sharedLink: data.shared_link ?? null,
|
||||
tags: data.tags ?? [],
|
||||
commentCount: data.comment_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
78
apps/sim/tools/box/upload_file.ts
Normal file
78
apps/sim/tools/box/upload_file.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxUploadFileParams, BoxUploadFileResponse } from './types'
|
||||
import { UPLOAD_FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxUploadFileTool: ToolConfig<BoxUploadFileParams, BoxUploadFileResponse> = {
|
||||
id: 'box_upload_file',
|
||||
name: 'Box Upload File',
|
||||
description: 'Upload a file to a Box folder',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to upload the file to (use "0" for root)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The file to upload (UserFile object)',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Legacy: base64 encoded file content',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional filename override',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/box/upload',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
parentFolderId: params.parentFolderId,
|
||||
file: params.file,
|
||||
fileContent: params.fileContent,
|
||||
fileName: params.fileName,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: UPLOAD_FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
80
apps/sim/tools/box_sign/cancel_request.ts
Normal file
80
apps/sim/tools/box_sign/cancel_request.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSignCancelRequestParams, BoxSignResponse } from './types'
|
||||
import { SIGN_REQUEST_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSignCancelRequestTool: ToolConfig<BoxSignCancelRequestParams, BoxSignResponse> = {
|
||||
id: 'box_sign_cancel_request',
|
||||
name: 'Box Sign Cancel Request',
|
||||
description: 'Cancel a pending Box Sign request',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
signRequestId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the sign request to cancel',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/sign_requests/${params.signRequestId}/cancel`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: () => ({}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box Sign API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
status: data.status ?? '',
|
||||
name: data.name ?? null,
|
||||
shortId: data.short_id ?? null,
|
||||
signers: (data.signers ?? []).map((s: Record<string, unknown>) => ({
|
||||
email: s.email ?? null,
|
||||
role: s.role ?? null,
|
||||
hasViewedDocument: s.has_viewed_document ?? null,
|
||||
signerDecision: s.signer_decision ?? null,
|
||||
embedUrl: s.embed_url ?? null,
|
||||
order: s.order ?? null,
|
||||
})),
|
||||
sourceFiles: (data.source_files ?? []).map((f: Record<string, unknown>) => ({
|
||||
id: f.id ?? null,
|
||||
type: f.type ?? null,
|
||||
name: f.name ?? null,
|
||||
})),
|
||||
emailSubject: data.email_subject ?? null,
|
||||
emailMessage: data.email_message ?? null,
|
||||
daysValid: data.days_valid ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
autoExpireAt: data.auto_expire_at ?? null,
|
||||
prepareUrl: data.prepare_url ?? null,
|
||||
senderEmail: data.sender_email ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SIGN_REQUEST_OUTPUT_PROPERTIES,
|
||||
}
|
||||
225
apps/sim/tools/box_sign/create_request.ts
Normal file
225
apps/sim/tools/box_sign/create_request.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSignCreateRequestParams, BoxSignResponse } from './types'
|
||||
import { SIGN_REQUEST_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSignCreateRequestTool: ToolConfig<BoxSignCreateRequestParams, BoxSignResponse> = {
|
||||
id: 'box_sign_create_request',
|
||||
name: 'Box Sign Create Request',
|
||||
description: 'Create a new Box Sign request to send documents for e-signature',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
sourceFileIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated Box file IDs to send for signing',
|
||||
},
|
||||
signerEmail: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Primary signer email address',
|
||||
},
|
||||
signerRole: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Primary signer role: signer, approver, or final_copy_reader (default: signer)',
|
||||
},
|
||||
additionalSigners: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of additional signers, e.g. [{"email":"user@example.com","role":"signer"}]',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Box folder ID where signed documents will be stored (default: user root)',
|
||||
},
|
||||
emailSubject: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom subject line for the signing email',
|
||||
},
|
||||
emailMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom message in the signing email body',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the sign request',
|
||||
},
|
||||
daysValid: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of days before the request expires (0-730)',
|
||||
},
|
||||
areRemindersEnabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to send automatic signing reminders',
|
||||
},
|
||||
areTextSignaturesEnabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to allow typed (text) signatures',
|
||||
},
|
||||
signatureColor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Signature color: blue, black, or red',
|
||||
},
|
||||
redirectUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL to redirect signers to after signing',
|
||||
},
|
||||
declinedRedirectUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL to redirect signers to after declining',
|
||||
},
|
||||
isDocumentPreparationNeeded: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether document preparation is needed before sending',
|
||||
},
|
||||
externalId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'External system reference ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.box.com/2.0/sign_requests',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const fileIds = params.sourceFileIds
|
||||
.split(',')
|
||||
.map((id: string) => id.trim())
|
||||
.filter(Boolean)
|
||||
const sourceFiles = fileIds.map((id: string) => ({ type: 'file', id }))
|
||||
|
||||
const signers: Array<Record<string, unknown>> = [
|
||||
{
|
||||
email: params.signerEmail,
|
||||
role: params.signerRole || 'signer',
|
||||
},
|
||||
]
|
||||
|
||||
if (params.additionalSigners) {
|
||||
try {
|
||||
const additional =
|
||||
typeof params.additionalSigners === 'string'
|
||||
? JSON.parse(params.additionalSigners)
|
||||
: params.additionalSigners
|
||||
if (Array.isArray(additional)) {
|
||||
signers.push(...additional)
|
||||
}
|
||||
} catch {
|
||||
throw new Error(
|
||||
'Invalid JSON in additionalSigners. Expected a JSON array of signer objects.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
source_files: sourceFiles,
|
||||
signers,
|
||||
}
|
||||
|
||||
if (params.parentFolderId) {
|
||||
body.parent_folder = { type: 'folder', id: params.parentFolderId }
|
||||
}
|
||||
if (params.emailSubject) body.email_subject = params.emailSubject
|
||||
if (params.emailMessage) body.email_message = params.emailMessage
|
||||
if (params.name) body.name = params.name
|
||||
if (params.daysValid !== undefined) body.days_valid = params.daysValid
|
||||
if (params.areRemindersEnabled !== undefined)
|
||||
body.are_reminders_enabled = params.areRemindersEnabled
|
||||
if (params.areTextSignaturesEnabled !== undefined)
|
||||
body.are_text_signatures_enabled = params.areTextSignaturesEnabled
|
||||
if (params.signatureColor) body.signature_color = params.signatureColor
|
||||
if (params.redirectUrl) body.redirect_url = params.redirectUrl
|
||||
if (params.declinedRedirectUrl) body.declined_redirect_url = params.declinedRedirectUrl
|
||||
if (params.isDocumentPreparationNeeded !== undefined)
|
||||
body.is_document_preparation_needed = params.isDocumentPreparationNeeded
|
||||
if (params.externalId) body.external_id = params.externalId
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box Sign API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
status: data.status ?? '',
|
||||
name: data.name ?? null,
|
||||
shortId: data.short_id ?? null,
|
||||
signers: (data.signers ?? []).map((s: Record<string, unknown>) => ({
|
||||
email: s.email ?? null,
|
||||
role: s.role ?? null,
|
||||
hasViewedDocument: s.has_viewed_document ?? null,
|
||||
signerDecision: s.signer_decision ?? null,
|
||||
embedUrl: s.embed_url ?? null,
|
||||
order: s.order ?? null,
|
||||
})),
|
||||
sourceFiles: (data.source_files ?? []).map((f: Record<string, unknown>) => ({
|
||||
id: f.id ?? null,
|
||||
type: f.type ?? null,
|
||||
name: f.name ?? null,
|
||||
})),
|
||||
emailSubject: data.email_subject ?? null,
|
||||
emailMessage: data.email_message ?? null,
|
||||
daysValid: data.days_valid ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
autoExpireAt: data.auto_expire_at ?? null,
|
||||
prepareUrl: data.prepare_url ?? null,
|
||||
senderEmail: data.sender_email ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SIGN_REQUEST_OUTPUT_PROPERTIES,
|
||||
}
|
||||
78
apps/sim/tools/box_sign/get_request.ts
Normal file
78
apps/sim/tools/box_sign/get_request.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSignGetRequestParams, BoxSignResponse } from './types'
|
||||
import { SIGN_REQUEST_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSignGetRequestTool: ToolConfig<BoxSignGetRequestParams, BoxSignResponse> = {
|
||||
id: 'box_sign_get_request',
|
||||
name: 'Box Sign Get Request',
|
||||
description: 'Get the details and status of a Box Sign request',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
signRequestId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the sign request to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/sign_requests/${params.signRequestId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box Sign API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
status: data.status ?? '',
|
||||
name: data.name ?? null,
|
||||
shortId: data.short_id ?? null,
|
||||
signers: (data.signers ?? []).map((s: Record<string, unknown>) => ({
|
||||
email: s.email ?? null,
|
||||
role: s.role ?? null,
|
||||
hasViewedDocument: s.has_viewed_document ?? null,
|
||||
signerDecision: s.signer_decision ?? null,
|
||||
embedUrl: s.embed_url ?? null,
|
||||
order: s.order ?? null,
|
||||
})),
|
||||
sourceFiles: (data.source_files ?? []).map((f: Record<string, unknown>) => ({
|
||||
id: f.id ?? null,
|
||||
type: f.type ?? null,
|
||||
name: f.name ?? null,
|
||||
})),
|
||||
emailSubject: data.email_subject ?? null,
|
||||
emailMessage: data.email_message ?? null,
|
||||
daysValid: data.days_valid ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
autoExpireAt: data.auto_expire_at ?? null,
|
||||
prepareUrl: data.prepare_url ?? null,
|
||||
senderEmail: data.sender_email ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SIGN_REQUEST_OUTPUT_PROPERTIES,
|
||||
}
|
||||
5
apps/sim/tools/box_sign/index.ts
Normal file
5
apps/sim/tools/box_sign/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { boxSignCancelRequestTool } from '@/tools/box_sign/cancel_request'
|
||||
export { boxSignCreateRequestTool } from '@/tools/box_sign/create_request'
|
||||
export { boxSignGetRequestTool } from '@/tools/box_sign/get_request'
|
||||
export { boxSignListRequestsTool } from '@/tools/box_sign/list_requests'
|
||||
export { boxSignResendRequestTool } from '@/tools/box_sign/resend_request'
|
||||
100
apps/sim/tools/box_sign/list_requests.ts
Normal file
100
apps/sim/tools/box_sign/list_requests.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSignListRequestsParams, BoxSignListResponse } from './types'
|
||||
import { SIGN_REQUEST_LIST_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSignListRequestsTool: ToolConfig<BoxSignListRequestsParams, BoxSignListResponse> = {
|
||||
id: 'box_sign_list_requests',
|
||||
name: 'Box Sign List Requests',
|
||||
description: 'List all Box Sign requests',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of sign requests to return (max 1000)',
|
||||
},
|
||||
marker: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination marker from a previous response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.limit !== undefined) queryParams.set('limit', String(params.limit))
|
||||
if (params.marker) queryParams.set('marker', params.marker)
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.box.com/2.0/sign_requests${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box Sign API error: ${response.status}`)
|
||||
}
|
||||
|
||||
const entries = data.entries ?? []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
signRequests: entries.map((req: Record<string, unknown>) => ({
|
||||
id: req.id ?? '',
|
||||
status: req.status ?? '',
|
||||
name: req.name ?? null,
|
||||
shortId: req.short_id ?? null,
|
||||
signers: ((req.signers as Array<Record<string, unknown>> | undefined) ?? []).map(
|
||||
(s: Record<string, unknown>) => ({
|
||||
email: s.email ?? null,
|
||||
role: s.role ?? null,
|
||||
hasViewedDocument: s.has_viewed_document ?? null,
|
||||
signerDecision: s.signer_decision ?? null,
|
||||
embedUrl: s.embed_url ?? null,
|
||||
order: s.order ?? null,
|
||||
})
|
||||
),
|
||||
sourceFiles: ((req.source_files as Array<Record<string, unknown>> | undefined) ?? []).map(
|
||||
(f: Record<string, unknown>) => ({
|
||||
id: f.id ?? null,
|
||||
type: f.type ?? null,
|
||||
name: f.name ?? null,
|
||||
})
|
||||
),
|
||||
emailSubject: req.email_subject ?? null,
|
||||
emailMessage: req.email_message ?? null,
|
||||
daysValid: req.days_valid ?? null,
|
||||
createdAt: req.created_at ?? null,
|
||||
autoExpireAt: req.auto_expire_at ?? null,
|
||||
prepareUrl: req.prepare_url ?? null,
|
||||
senderEmail: req.sender_email ?? null,
|
||||
})),
|
||||
count: entries.length,
|
||||
nextMarker: data.next_marker ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SIGN_REQUEST_LIST_OUTPUT_PROPERTIES,
|
||||
}
|
||||
55
apps/sim/tools/box_sign/resend_request.ts
Normal file
55
apps/sim/tools/box_sign/resend_request.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { BoxSignResendRequestParams } from './types'
|
||||
|
||||
export const boxSignResendRequestTool: ToolConfig<BoxSignResendRequestParams, ToolResponse> = {
|
||||
id: 'box_sign_resend_request',
|
||||
name: 'Box Sign Resend Request',
|
||||
description: 'Resend a Box Sign request to signers who have not yet signed',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
signRequestId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the sign request to resend',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/sign_requests/${params.signRequestId}/resend`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (!response.ok) {
|
||||
const data = await response.json()
|
||||
throw new Error(data.message || `Box Sign API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: 'Sign request resent successfully',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: { type: 'string', description: 'Success confirmation message' },
|
||||
},
|
||||
}
|
||||
162
apps/sim/tools/box_sign/types.ts
Normal file
162
apps/sim/tools/box_sign/types.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface BoxSignCreateRequestParams {
|
||||
accessToken: string
|
||||
sourceFileIds: string
|
||||
signerEmail: string
|
||||
signerRole?: string
|
||||
additionalSigners?: string
|
||||
parentFolderId?: string
|
||||
emailSubject?: string
|
||||
emailMessage?: string
|
||||
name?: string
|
||||
daysValid?: number
|
||||
areRemindersEnabled?: boolean
|
||||
areTextSignaturesEnabled?: boolean
|
||||
signatureColor?: string
|
||||
redirectUrl?: string
|
||||
declinedRedirectUrl?: string
|
||||
isDocumentPreparationNeeded?: boolean
|
||||
externalId?: string
|
||||
}
|
||||
|
||||
export interface BoxSignGetRequestParams {
|
||||
accessToken: string
|
||||
signRequestId: string
|
||||
}
|
||||
|
||||
export interface BoxSignListRequestsParams {
|
||||
accessToken: string
|
||||
limit?: number
|
||||
marker?: string
|
||||
}
|
||||
|
||||
export interface BoxSignCancelRequestParams {
|
||||
accessToken: string
|
||||
signRequestId: string
|
||||
}
|
||||
|
||||
export interface BoxSignResendRequestParams {
|
||||
accessToken: string
|
||||
signRequestId: string
|
||||
}
|
||||
|
||||
export interface BoxSignResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
status: string
|
||||
name: string | null
|
||||
shortId: string | null
|
||||
signers: Array<Record<string, unknown>>
|
||||
sourceFiles: Array<Record<string, unknown>>
|
||||
emailSubject: string | null
|
||||
emailMessage: string | null
|
||||
daysValid: number | null
|
||||
createdAt: string | null
|
||||
autoExpireAt: string | null
|
||||
prepareUrl: string | null
|
||||
senderEmail: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxSignListResponse extends ToolResponse {
|
||||
output: {
|
||||
signRequests: Array<Record<string, unknown>>
|
||||
count: number
|
||||
nextMarker: string | null
|
||||
}
|
||||
}
|
||||
|
||||
const SIGNER_OUTPUT_PROPERTIES = {
|
||||
email: { type: 'string', description: 'Signer email address' },
|
||||
role: { type: 'string', description: 'Signer role (signer, approver, final_copy_reader)' },
|
||||
hasViewedDocument: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the signer has viewed the document',
|
||||
optional: true,
|
||||
},
|
||||
signerDecision: {
|
||||
type: 'json',
|
||||
description: 'Signer decision details (type, finalized_at, additional_info)',
|
||||
optional: true,
|
||||
},
|
||||
embedUrl: {
|
||||
type: 'string',
|
||||
description: 'URL for embedded signing experience',
|
||||
optional: true,
|
||||
},
|
||||
order: { type: 'number', description: 'Order in signing sequence', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
const SOURCE_FILE_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'File ID' },
|
||||
type: { type: 'string', description: 'File type' },
|
||||
name: { type: 'string', description: 'File name', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SIGN_REQUEST_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Sign request ID' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Request status (converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing)',
|
||||
},
|
||||
name: { type: 'string', description: 'Sign request name', optional: true },
|
||||
shortId: { type: 'string', description: 'Human-readable short ID', optional: true },
|
||||
signers: {
|
||||
type: 'array',
|
||||
description: 'List of signers',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SIGNER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
sourceFiles: {
|
||||
type: 'array',
|
||||
description: 'Source files for signing',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SOURCE_FILE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
emailSubject: {
|
||||
type: 'string',
|
||||
description: 'Custom email subject line',
|
||||
optional: true,
|
||||
},
|
||||
emailMessage: {
|
||||
type: 'string',
|
||||
description: 'Custom email message body',
|
||||
optional: true,
|
||||
},
|
||||
daysValid: {
|
||||
type: 'number',
|
||||
description: 'Number of days the request is valid',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
autoExpireAt: { type: 'string', description: 'Auto-expiration timestamp', optional: true },
|
||||
prepareUrl: {
|
||||
type: 'string',
|
||||
description: 'URL for document preparation (if preparation is needed)',
|
||||
optional: true,
|
||||
},
|
||||
senderEmail: { type: 'string', description: 'Email of the sender', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SIGN_REQUEST_LIST_OUTPUT_PROPERTIES = {
|
||||
signRequests: {
|
||||
type: 'array',
|
||||
description: 'List of sign requests',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SIGN_REQUEST_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of sign requests returned in this page' },
|
||||
nextMarker: {
|
||||
type: 'string',
|
||||
description: 'Marker for next page of results',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
@@ -153,6 +153,25 @@ import {
|
||||
attioUpdateTaskTool,
|
||||
attioUpdateWebhookTool,
|
||||
} from '@/tools/attio'
|
||||
import {
|
||||
boxCopyFileTool,
|
||||
boxCreateFolderTool,
|
||||
boxDeleteFileTool,
|
||||
boxDeleteFolderTool,
|
||||
boxDownloadFileTool,
|
||||
boxGetFileInfoTool,
|
||||
boxListFolderItemsTool,
|
||||
boxSearchTool,
|
||||
boxUpdateFileTool,
|
||||
boxUploadFileTool,
|
||||
} from '@/tools/box'
|
||||
import {
|
||||
boxSignCancelRequestTool,
|
||||
boxSignCreateRequestTool,
|
||||
boxSignGetRequestTool,
|
||||
boxSignListRequestsTool,
|
||||
boxSignResendRequestTool,
|
||||
} from '@/tools/box_sign'
|
||||
import { brandfetchGetBrandTool, brandfetchSearchTool } from '@/tools/brandfetch'
|
||||
import { browserUseRunTaskTool } from '@/tools/browser_use'
|
||||
import {
|
||||
@@ -2409,6 +2428,21 @@ export const tools: Record<string, ToolConfig> = {
|
||||
ashby_update_candidate: ashbyUpdateCandidateTool,
|
||||
brandfetch_get_brand: brandfetchGetBrandTool,
|
||||
brandfetch_search: brandfetchSearchTool,
|
||||
box_copy_file: boxCopyFileTool,
|
||||
box_create_folder: boxCreateFolderTool,
|
||||
box_delete_file: boxDeleteFileTool,
|
||||
box_delete_folder: boxDeleteFolderTool,
|
||||
box_download_file: boxDownloadFileTool,
|
||||
box_get_file_info: boxGetFileInfoTool,
|
||||
box_list_folder_items: boxListFolderItemsTool,
|
||||
box_search: boxSearchTool,
|
||||
box_update_file: boxUpdateFileTool,
|
||||
box_upload_file: boxUploadFileTool,
|
||||
box_sign_create_request: boxSignCreateRequestTool,
|
||||
box_sign_get_request: boxSignGetRequestTool,
|
||||
box_sign_list_requests: boxSignListRequestsTool,
|
||||
box_sign_cancel_request: boxSignCancelRequestTool,
|
||||
box_sign_resend_request: boxSignResendRequestTool,
|
||||
browser_use_run_task: browserUseRunTaskTool,
|
||||
openai_embeddings: openAIEmbeddingsTool,
|
||||
http_request: httpRequestTool,
|
||||
|
||||
Reference in New Issue
Block a user