<!-- BusyBot node reference — https://busybot.net/tools/upload-to-spaces/ -->

> Node: Upload to Spaces (`upload_to_spaces`) · Action (binary) · v1
> Category: Data & Storage · Credentials: DigitalOcean Spaces (`doSpaces`)
> Updated: 2026-08-16

# Upload to Spaces

> Upload to cloud storage

## Overview

Uploads a local file (e.g., produced by Write to File) or a binary property to DigitalOcean Spaces and stores the object URL + key on the item. Objects are uploaded with a private ACL by default; set ACL to public-read when the returned URL must be fetchable without credentials. Supports expressions like {{ $json.fileWrite.fullPath }}.

**Category:** Data & Storage  
**Tool Name:** `upload_to_spaces`  
**Version:** 1

**Appearance:** Icon: `uploadCloud` | Color: `#0ea5e9`

## Node Type

**Action (Binary)** — handles file/binary data operations

## Input / Output

| Direction | Port(s) |
|-----------|--------|
| Input | `Input` |
| Output | `Output`, `Error` |

## Credentials

This tool requires **DigitalOcean Spaces** credentials.
See the [Credentials Guide](https://busybot.net/credentials/do-spaces/) for setup instructions.

### Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Spaces Endpoint | `string` | Yes | `https://nyc3.digitaloceanspaces.com` | Spaces endpoint URL (region-specific). Example: https://nyc3.digitaloceanspaces.com |
| Spaces Region | `string` | Yes | `nyc3` | Spaces region (e.g., nyc3, sfo3, ams3). |
| Spaces Bucket | `string` | Yes | — | Bucket name to upload into. |
| CDN Base URL (Optional) | `string` | No | — | If provided, returned URLs will be built from this base URL instead of https://{bucket}.{region}.digitaloceanspaces.com |
| File Path | `string` | Yes | `{{ $json.fileWrite.fullPath }}` | Absolute path to the local file to upload. Typically from write_to_file: {{ $json.fileWrite.fullPath }}. |
| Key Prefix | `string` | No | `uploads` | Prefix/folder within the bucket (e.g., uploads, outputs, logs). |
| Key Template | `string` | Yes | `{{ $json.fileWrite.relativePath }}` | Object key path (relative) inside the bucket. Supports expressions. Example: {{ $json.fileWrite.relativePath }} |
| ACL | `options` | No | `private` | Object ACL. Defaults to "private" for safety; set to "public-read" to allow direct URL fetches. |
| | | | | Options: `private`, `public-read` |
| Content Type Mode | `options` | No | `auto` | How to set Content-Type on the upload. |
| | | | | Options: `auto` (detected from the file), `override` |
| Content Type Override | `string` | No | — | Used only when Content Type Mode = "Override". Example: text/markdown _(shown when Content Type Mode is `override`)_ |
| Delete Local File After Upload | `boolean` | No | `false` | If true, deletes the local file after a successful upload (useful for ephemeral job/pod storage). |
| Include Input Fields | `boolean` | No | `true` | If true, copies the input item JSON into the output item JSON. |
| Result Field Name | `string` | No | `spacesUpload` | Where to place upload result metadata on the output JSON. |
| Binary Property | `string` | No | — | If set, uploads from this binary property instead of file path. Names are case-sensitive — see the upstream node's Binary Data panel for the exact names to use. File Path is still resolved and sandbox-checked first, so leave it at a valid in-sandbox value. |
| Max Concurrency | `number` | No | `25` | Maximum number of items to upload concurrently. Range 1–100. |

## Output Data

One output item per input item. The upload result is written to the property named by Result Field Name (`spacesUpload` by default); the rest of the item JSON passes through when Include Input Fields is on, and binary data is forwarded.

```json
{
  "spacesUpload": {
    "ok": true,
    "bucket": "my-bucket",
    "key": "uploads/2026/report.pdf",
    "url": "https://my-bucket.nyc3.digitaloceanspaces.com/uploads/2026/report.pdf",
    "mimeType": "application/pdf",
    "sourceFilePath": "…/report.pdf",
    "sizeBytes": 84213,
    "acl": "private",
    "uploadedAt": 1765432100000
  }
}
```

| Field | Meaning |
|-------|---------|
| `ok` | `true` on a successful upload |
| `bucket` | The bucket the object was written to |
| `key` | The final object key — Key Prefix joined to the resolved Key Template |
| `url` | URL of the object, built from CDN Base URL when you set one, otherwise from bucket + region. Built from the key, not from the ACL — with the default `private` ACL this link exists but anonymous fetches of it are refused |
| `mimeType` | The Content-Type stored with the object |
| `sourceFilePath` | The file that was actually sent |
| `sizeBytes` | Size of that file, or `null` when it could no longer be measured (for example after Delete Local File After Upload removed it) |
| `acl` | The ACL applied to the object |
| `uploadedAt` | Upload time, in epoch milliseconds |

Set Binary Property to upload a file produced by an upstream node instead of one on disk: when the named property is present on the item its contents are uploaded, and otherwise the node falls back to reading File Path.

Reference the result downstream by expression, e.g. `{{ $json.spacesUpload.url }}`.

## Usage Examples

- upload the file to cloud storage
- save the image to Spaces
- push the report to S3
- store the backup in the cloud
- upload results to the bucket

## Example Configuration

Basic upload configuration:

```json
{
  "id": "upload_to_spaces",
  "name": "Upload to DigitalOcean Spaces",
  "type": "upload_to_spaces",
  "parameters": {
    "endpoint": "https://nyc3.digitaloceanspaces.com",
    "region": "nyc3",
    "bucket": "my-bucket",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyPrefix": "uploads",
    "keyTemplate": "{{ $json.fileWrite.relativePath }}",
    "acl": "public-read",
    "contentTypeMode": "auto"
  }
}
```

Upload with content type override:

```json
{
  "id": "upload_to_spaces",
  "name": "Upload PDF with Custom Content Type",
  "type": "upload_to_spaces",
  "parameters": {
    "endpoint": "https://sfo3.digitaloceanspaces.com",
    "region": "sfo3",
    "bucket": "documents-bucket",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyPrefix": "pdfs",
    "keyTemplate": "{{ $json.fileWrite.relativePath }}",
    "acl": "public-read",
    "contentTypeMode": "override",
    "contentTypeOverride": "application/pdf"
  }
}
```

Upload with CDN and cleanup:

```json
{
  "id": "upload_to_spaces",
  "name": "Upload with CDN and Local Cleanup",
  "type": "upload_to_spaces",
  "parameters": {
    "endpoint": "https://ams3.digitaloceanspaces.com",
    "region": "ams3",
    "bucket": "static-assets",
    "cdnUrl": "https://cdn.example.com",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyPrefix": "images",
    "keyTemplate": "{{ $now.format('YYYY/MM/DD') }}/{{ $json.fileWrite.filename }}",
    "acl": "public-read",
    "contentTypeMode": "auto",
    "deleteLocalAfterUpload": true,
    "resultFieldName": "uploadResult"
  }
}
```

Private upload configuration — private is the default, so ACL is spelled out here only for clarity:

```json
{
  "id": "upload_to_spaces",
  "name": "Private File Upload",
  "type": "upload_to_spaces",
  "parameters": {
    "endpoint": "https://nyc3.digitaloceanspaces.com",
    "region": "nyc3",
    "bucket": "private-documents",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyPrefix": "confidential",
    "keyTemplate": "{{ $json.fileWrite.relativePath }}",
    "acl": "private",
    "contentTypeMode": "auto",
    "includeInput": false,
    "maxConcurrency": 5
  }
}
```

After Write to File — a typical workflow where a file is generated and then uploaded:

```json
{
  "nodes": [
    {
      "id": "write_file",
      "name": "Generate Report",
      "type": "write_to_file",
      "parameters": {
        "fileName": "report.pdf",
        "content": "{{ $json.report_data }}"
      }
    },
    {
      "id": "upload_file",
      "name": "Upload to Spaces",
      "type": "upload_to_spaces",
      "parameters": {
        "bucket": "reports-bucket",
        "filePath": "{{ $json.fileWrite.fullPath }}",
        "keyTemplate": "{{ $json.fileWrite.relativePath }}",
        "deleteLocalAfterUpload": true
      }
    }
  ]
}
```

Organized storage with date prefixes:

```json
{
  "id": "upload_to_spaces",
  "name": "Organized Upload",
  "type": "upload_to_spaces",
  "parameters": {
    "bucket": "my-storage",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyPrefix": "{{ $now.format('YYYY/MM') }}",
    "keyTemplate": "{{ $workflow.id }}/{{ $json.fileWrite.filename }}",
    "contentTypeMode": "override",
    "contentTypeOverride": "text/markdown"
  }
}
```

High-volume upload with concurrency control:

```json
{
  "id": "upload_to_spaces",
  "name": "Batch Upload",
  "type": "upload_to_spaces",
  "parameters": {
    "bucket": "batch-processing",
    "filePath": "{{ $json.fileWrite.fullPath }}",
    "keyTemplate": "batch-{{ $now.unix() }}/{{ $json.fileWrite.filename }}",
    "maxConcurrency": 20,
    "deleteLocalAfterUpload": true,
    "resultFieldName": "batchUpload"
  }
}
```

Upload from a binary property — uploads the bytes held on the item's `data` property rather than a file on disk. Key Template supplies the object name because there is no `fileWrite` result to borrow one from:

```json
{
  "id": "upload_to_spaces",
  "name": "Upload Binary Attachment",
  "type": "upload_to_spaces",
  "parameters": {
    "endpoint": "https://nyc3.digitaloceanspaces.com",
    "region": "nyc3",
    "bucket": "my-bucket",
    "binaryProperty": "data",
    "keyPrefix": "attachments",
    "keyTemplate": "{{ $json.fileName }}",
    "acl": "public-read",
    "contentTypeMode": "auto"
  }
}
```

### Error Handling

| Mode | Behavior |
|------|----------|
| **stop** | Halts workflow on first error |
| **continue** | Skips failed items, passes successful ones through |
| **errorPort** | Routes failed items to Error output port |

## Tips

Uploads a file from disk, or the bytes on a binary property, to DigitalOcean Spaces (S3-compatible storage) and returns the object URL. Use when you need to persist generated files, images, or data exports to cloud storage. Produces items containing the upload URL, object key, and file metadata. Uploads are private by default — switch the ACL to public-read when the URL has to be fetchable without credentials.

### Key Rules and Gotchas

1. **ACL defaults to `private`.** The examples above that omit `acl` (After Write to File, Organized Storage, Batch Upload) upload private objects. The returned `url` is always built from the key, so it will look like a working link and return an access-denied error to anonymous fetchers. Set `"acl": "public-read"` whenever the URL is meant to be handed to a browser, an email, or a downstream node that fetches it without credentials.

2. **Max Concurrency defaults to `25`** (range 1–100), and the node's own global cap is 50 concurrent uploads across the whole engine.

3. **File Path is validated even when Binary Property is set.** It must resolve to a non-empty path under `/app/workflow-tmp/`, `/app/agent_files/` or `/app/outputs/`; anything else fails the item before the upload starts. Its default expression satisfies this, so leave it alone rather than blanking it out for binary uploads.

4. **Delete Local File After Upload only deletes inside the sandbox.** A source outside the allow-list is logged as a warning and left in place — the upload itself still succeeds.