When to Use
- Report archiving: Store generated PDF or CSV reports to S3 after a workflow produces them, using a date-partitioned key for organized retrieval.
- Document intake: Accept user-submitted documents from a form workflow and persist them to S3 before initiating a review process.
- Data export storage: Write processed data exports to S3 at the end of an ETL pipeline for downstream consumers or compliance archiving.
- Database backup offload: Upload compressed database dump files to S3 with
GLACIER storage class for cost-efficient long-term retention.
- Static asset publishing: Upload versioned HTML, CSS, or JS assets to an S3 bucket configured as a static website or CloudFront origin.
Configuration
Connection
| Field | Required | Description |
accessKeyId | Required | AWS Access Key ID. Store in BizFirst Credentials Manager. |
secretKey | Required | AWS Secret Access Key. Never log or expose. |
sessionToken | Optional | STS session token for temporary credentials. |
region | Required | AWS region of the target bucket. |
Operation Fields
| Field | Required | Description |
bucketName | Required | Target S3 bucket name. |
objectKey | Required | Full key path including any prefix (e.g. uploads/2026/05/report.pdf). Forward slashes create logical folder hierarchy. |
dataMode | Optional | Content encoding mode. base64 (default) for binary files; text for plain text or JSON content. |
contentType | Optional | MIME type of the object. Default: application/octet-stream. Use application/pdf, text/csv, image/png etc. for better browser handling. |
storageClass | Optional | S3 storage class. Default: STANDARD. Options: INTELLIGENT_TIERING, STANDARD_IA, ONE_ZONE_IA, GLACIER, DEEP_ARCHIVE. |
serverSideEncryption | Optional | Encryption algorithm. Default: AES256. Options: aws:kms, none. |
kmsKeyId | Optional | KMS key ARN. Required when serverSideEncryption is aws:kms. |
cannedAcl | Optional | Object-level ACL. Default: private. Options: public-read. |
cacheControl | Optional | Cache-Control header value for CDN/browser caching (e.g. max-age=86400). |
metadata | Optional | JSON object of custom user-defined metadata key-value pairs attached to the object. |
tagging | Optional | JSON key-value object of S3 tags applied to the object (e.g. {"Environment":"prod","Owner":"billing"}). |
Sample Configuration
{
"connection": {
"accessKeyId": "{{ $credentials.s3_prod.accessKeyId }}",
"secretKey": "{{ $credentials.s3_prod.secretKey }}",
"region": "us-east-1"
},
"operation": {
"resource": "object",
"action": "upload",
"bucketName": "acme-prod-reports",
"objectKey": "monthly/{{ $now | date: 'YYYY/MM' }}/billing-summary-{{ $now | date: 'YYYY-MM' }}.pdf",
"dataMode": "base64",
"contentType": "application/pdf",
"storageClass": "STANDARD",
"serverSideEncryption": "AES256",
"cannedAcl": "private",
"metadata": {
"generatedBy": "billing-workflow",
"tenantId": "{{ $node.BillingJob.output.tenantId }}"
},
"tagging": {
"Environment": "production",
"DataClass": "confidential"
}
}
}
Validation Errors
| Error Code | Cause & Resolution |
ObjectTooLarge | The object exceeds the 5 GB single-part upload limit. Use multipart upload orchestration for large files. |
AccessDenied | The IAM identity lacks s3:PutObject permission on the target bucket/key. Update the IAM policy. |
InvalidArgument | The metadata or tagging JSON is malformed, or a metadata key/value contains invalid characters. Validate JSON structure before passing. |
NoSuchBucket | The target bucket does not exist. Create it first using bucket/create or verify the bucket name. |
KMSDisabledException | The specified KMS key is disabled. Enable the key in AWS KMS before uploading. |
Output
Success Port
| Field | Type | Description |
status | string | success on successful upload. |
errorCode | string | Empty string on success. |
bucketName | string | Target bucket name. |
objectKey | string | Full key of the uploaded object. |
eTag | string | MD5 ETag of the uploaded object — use for integrity verification. |
size | long | Size of the uploaded object in bytes. |
uploadedAt | string | ISO 8601 timestamp of the upload. |
storageClass | string | Storage class applied to the object. |
Error Port
On failure, activates with errorCode, errorMessage, and httpStatusCode. Route to a retry or alerting node. For ObjectTooLarge, route to a multipart upload sub-workflow.
Sample Output
{
"status": "success",
"errorCode": "",
"bucketName": "acme-prod-reports",
"objectKey": "monthly/2026/05/billing-summary-2026-05.pdf",
"eTag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
"size": 204800,
"uploadedAt": "2026-05-26T11:30:15Z",
"storageClass": "STANDARD"
}
Expression Reference
| Expression | Result |
{{ $node.UploadReport.output.objectKey }} | Full S3 key — use to construct a presigned URL or pass to an email attachment node. |
{{ $node.UploadReport.output.eTag }} | ETag value — log for data integrity audit trail. |
{{ $node.UploadReport.output.size }} | Object size in bytes — include in upload confirmation logs. |
{{ $node.UploadReport.output.uploadedAt }} | Upload timestamp — store in database record alongside the object key. |
{{ $node.UploadReport.output.storageClass }} | Confirm the applied storage class for cost accounting. |
Node Policies & GuardRails
| Policy Area | Recommendation |
| Credential Storage | Store credentials in BizFirst Credentials Manager. Never hardcode. |
| IAM Permissions | Grant s3:PutObject scoped to the specific bucket ARN and prefix (e.g. arn:aws:s3:::my-bucket/uploads/*). |
| Object Key Expression | Always use deterministic, expression-generated keys. Never write to a key derived from user input without sanitization — prevent path traversal patterns. |
| Default ACL | Always default to private. Never use public-read for objects containing PII or business-sensitive data. |
| Encryption | Set serverSideEncryption: AES256 minimum on all uploads. Use aws:kms for regulated data. |
| Content Type | Always set the correct contentType MIME type — prevents browser security issues when serving objects via presigned URLs. |
| Tagging for Cost Allocation | Apply resource tags (Environment, CostCenter, DataClass) to enable accurate S3 cost allocation reports per team or tenant. |
| ETag Audit Logging | Log eTag and objectKey to the workflow execution audit trail for every upload to enable future integrity verification. |
Examples
Example 1: Monthly Billing Report Archival
After generating a billing PDF, a workflow uploads it to S3 under a date-partitioned prefix and records the S3 key in the database.
// Node: GenerateReport (Code Execute → produces base64 PDF)
// Node: UploadToBilling (S3 — object/upload)
{
"bucketName": "acme-billing-archive",
"objectKey": "invoices/{{ $node.BillingJob.output.tenantId }}/{{ $now | date: 'YYYY-MM' }}.pdf",
"dataMode": "base64",
"contentType": "application/pdf",
"storageClass": "STANDARD_IA",
"serverSideEncryption": "AES256",
"metadata": { "invoiceId": "{{ $node.BillingJob.output.invoiceId }}" }
}
// Next: UpdateInvoiceRecord → set s3Key = objectKey
Example 2: Form Submission Document Storage
A form workflow accepts user-uploaded documents and stores them in S3 under the submitter's tenant folder.
// Node: StoreDocument (S3 — object/upload)
{
"bucketName": "acme-tenant-uploads",
"objectKey": "{{ $node.FormData.output.tenantId }}/{{ $node.FormData.output.submissionId }}/{{ $node.FormData.output.fileName }}",
"dataMode": "base64",
"contentType": "{{ $node.FormData.output.mimeType }}",
"serverSideEncryption": "AES256",
"tagging": {
"TenantId": "{{ $node.FormData.output.tenantId }}",
"SubmissionId": "{{ $node.FormData.output.submissionId }}"
}
}
// Next: ApprovalWorkflow with objectKey reference
Example 3: Static Site Asset Deployment
A CI/CD workflow uploads versioned static assets to S3 with public-read ACL and Cache-Control headers for CDN delivery.
// Node: UploadAssets (S3 — object/upload, inside Loop)
{
"bucketName": "acme-static-assets",
"objectKey": "v{{ $node.BuildInfo.output.version }}/{{ $loop.currentItem.filename }}",
"dataMode": "base64",
"contentType": "{{ $loop.currentItem.mimeType }}",
"storageClass": "STANDARD",
"cannedAcl": "public-read",
"cacheControl": "max-age=31536000, immutable",
"serverSideEncryption": "none"
}