Integrations / N8N Send File To Storage
Document metadata
Category
Integrations
Endpoint
POST https://valora.spotahome.com/api/v1/storage/file
Sandbox
POST https://valora-testing.laravel.cloud/api/sandbox/v1/storage/file
Authentication
Bearer token (long-lived API token, auth:api guard)
Token class
employee
Pagination
Not supported
Filters
Not supported
Version
1
Stability
stable
Deprecated
No
Last updated
2026-05-03

n8n Workflow: Send File to Valora Storage

This guide explains how to configure an n8n workflow to send a file to the Valora storage API endpoint. This is useful when you've already generated a file in n8n (e.g., XML, PDF, CSV) and need to store it in Valora's document system.


Prerequisites

  • n8n workflow with a file already created/saved
  • Valora Employee API Token
  • Access to Valora API (production or sandbox)
  • Your Valora API base URL (e.g., https://valora.spotahome.com)

Quick Reference: API Endpoints

Environment URL
Production https://valora.spotahome.com/api/v1/storage/file
Sandbox https://valora-testing.laravel.cloud/api/sandbox/v1/storage/file

Step by Step: Using n8n UI

This section provides detailed instructions for configuring the upload using n8n's visual interface. No coding required.

Step 1: Add HTTP Request Node

  1. In your n8n workflow, click the + button to add a new node
  2. Search for "HTTP Request" and select it
  3. Connect it after the node that creates/saves your file
  4. Rename the node to something descriptive like "Upload to Valora Storage"

Step 2: Configure Basic Settings

  1. Click on the HTTP Request node to open its settings
  2. In the Method dropdown, select POST
  3. In the URL field, enter:
    • Production: https://valora.spotahome.com/api/v1/storage/file
    • Sandbox: https://valora-testing.laravel.cloud/api/sandbox/v1/storage/file

Step 3: Set Up Authentication

  1. Scroll down to the Authentication section
  2. Click the dropdown and select "Header Auth"
  3. In the Name field, enter: Authorization
  4. In the Value field, enter: Bearer YOUR_API_TOKEN
    • Replace YOUR_API_TOKEN with your actual Valora Employee API token

Tip: For better security, consider using n8n credentials instead of hardcoding the token. Go to CredentialsCreate NewHeader Auth and save your token there.

Step 4: Add Request Headers

  1. Expand the Options section (click "Show more options" if needed)
  2. Find the Headers section
  3. Click "Add Header"
  4. Set:
    • Name: Accept
    • Value: application/json

Important: Do NOT add a Content-Type header. n8n will automatically set this to multipart/form-data when you configure the body.

Step 5: Configure Request Body

  1. Scroll to the Body section
  2. In the Body Content Type dropdown, select "Form-Data" or "Multipart-Form-Data"
  3. You'll see a table to add parameters. Add the following fields:

Required Fields

Name Type Value
file File Click the expression icon (fx) and enter: {{ $binary.data }}
file_name String Enter your desired filename, e.g., dac7-report-2025.xml
store_in String Enter one of: documents, invoicing, reporting, or reports

Optional Fields

Name Type Value
category String e.g., dac7-reporting, invoice, report
description String Description of the document
visibility String public or private

Step 6: Configure File Field (Important!)

The file field needs special attention:

  1. Click on the Value field for the file parameter

  2. Click the expression icon (fx) to enable expressions

  3. Enter one of these based on your setup:

    If your previous node outputs binary data:

    {{ $binary.data }}
    

    If you have a file path from previous node:

    {{ $json.filePath }}
    

    If you need to read a file from disk:

    • First, add a "Read Binary File" node before the HTTP Request
    • Then use: {{ $binary.data }}

Step 7: Test the Configuration

  1. Click "Execute Node" to test
  2. Check the output:
    • Success: You'll see a response with "success": true and a document_id
    • Error: Check the error message and refer to the troubleshooting section

Step 8: Add Error Handling (Recommended)

  1. Add an IF node after the HTTP Request node
  2. Configure the condition:
    • Value 1: {{ $json.success }}
    • Operation: Equal
    • Value 2: true
  3. Connect success path to continue your workflow
  4. Connect error path to a notification node (e.g., email, Slack)

Step by Step: For Advanced Developers

This section provides code-based examples and advanced configurations for developers comfortable with n8n expressions and JavaScript.

Method 1: Using Code Node to Prepare Request

If you need to dynamically prepare the file and metadata before uploading:

// Code Node: Prepare Upload Data
const fileName = `dac7-report-${$now.toFormat('yyyy-MM-dd')}.xml`;
const storeIn = $json.reportType === 'dac7' ? 'reporting' : 'documents';

return [{
  json: {
    fileName: fileName,
    storeIn: storeIn,
    category: 'dac7-reporting',
    description: `DAC7 report generated on ${$now.toFormat('yyyy-MM-dd HH:mm:ss')}`,
    visibility: 'private'
  },
  binary: {
    data: {
      data: Buffer.from($json.xmlContent, 'utf8'),
      mimeType: 'application/xml',
      fileName: fileName
    }
  }
}];

Then in HTTP Request node, use expressions:

  • file: {{ $binary.data }}
  • file_name: {{ $json.fileName }}
  • store_in: {{ $json.storeIn }}
  • category: {{ $json.category }}

Method 2: Direct HTTP Request with Expressions

Configure the HTTP Request node with dynamic expressions:

URL:

{{ ($env.ENVIRONMENT === 'sandbox' ? 'https://valora-testing.laravel.cloud/api/sandbox' : 'https://valora.spotahome.com/api') + '/v1/storage/file' }}

Body Parameters:

Name Expression
file {{ $binary.data }}
file_name `{{ $json.fileName
store_in `{{ $json.storeIn
category `{{ $json.category
description `{{ $json.description
visibility `{{ $json.visibility

Method 3: Error Handling with Code Node

Add a Code node after HTTP Request to handle errors gracefully:

// Code Node: Handle Upload Response
const response = $input.item.json;

if (response.success) {
  return [{
    json: {
      success: true,
      documentId: response.data.document_id,
      fileName: response.data.file_name,
      sizeBytes: response.data.size_bytes,
      storedIn: response.data.stored_in,
      message: 'File uploaded successfully'
    }
  }];
} else {
  // Log error and throw for error workflow path
  console.error('Upload failed:', response);
  throw new Error(response.message || 'Upload failed');
}

Method 4: Complete Workflow with Validation

// Code Node 1: Validate and Prepare
const fileSize = $binary.data.data.length;
const maxSize = 10 * 1024 * 1024; // 10 MB

if (fileSize > maxSize) {
  throw new Error(`File size (${fileSize} bytes) exceeds maximum (${maxSize} bytes)`);
}

const fileName = $json.fileName || `upload-${$now.toFormat('yyyy-MM-dd-HHmmss')}.${$json.fileExtension || 'xml'}`;
const storeIn = ['documents', 'invoicing', 'reporting', 'reports'].includes($json.storeIn) 
  ? $json.storeIn 
  : 'documents';

return [{
  json: {
    fileName,
    storeIn,
    category: $json.category || 'api-upload',
    description: $json.description || null,
    visibility: ['public', 'private'].includes($json.visibility) ? $json.visibility : 'private',
    fileSize
  },
  binary: {
    data: $binary.data
  }
}];

Method 5: Batch Upload Multiple Files

If you need to upload multiple files:

// Code Node: Process Multiple Files
const files = $input.all();

return files.map(item => ({
  json: {
    fileName: item.json.fileName,
    storeIn: item.json.storeIn || 'documents',
    category: item.json.category || 'batch-upload',
    index: item.json.index
  },
  binary: {
    data: item.binary.data
  }
}));

Then use Split In Batches node before HTTP Request to process one at a time, or use HTTP Request with "Process all items" enabled.


Storage Destinations Reference

store_in Value Description Default Visibility Use Case
documents General document storage private Any document type
invoicing Invoice documents public Invoices, billing documents
reporting Reporting documents (DAC7, etc.) private XML reports, compliance documents
reports General reports (CSV, ZIP, etc.) private Data exports, analytics

Response Format

Success Response (201 Created)

{
  "success": true,
  "message": "File uploaded successfully.",
  "data": {
    "document_id": "39f6b0c8-8997-46f1-9c9d-e9f765f89447",
    "file_name": "dac7-report-2025.xml",
    "size_bytes": 120431,
    "stored_in": "reporting"
  }
}

Error Responses

422 Validation Error:

{
  "success": false,
  "message": "Validation failed.",
  "errors": {
    "store_in": ["The selected store in is invalid."]
  }
}

500 Server Error:

{
  "success": false,
  "message": "An unexpected error occurred while uploading the file.",
  "error": "Detailed error message (only in debug mode)"
}

401 Unauthorized:

{
  "message": "Unauthenticated."
}

Common Issues & Solutions

Issue: "No file was uploaded"

Symptoms: API returns error about missing file

Solutions:

  • Verify the file field uses {{ $binary.data }} expression
  • Check that the previous node outputs binary data
  • If using file path, add a Read Binary File node first
  • Ensure the file parameter type is set to File (not String)

Issue: "Validation failed"

Symptoms: 422 error with validation details

Solutions:

  • Check that file_name is provided and is a string
  • Verify store_in is exactly one of: documents, invoicing, reporting, reports
  • Ensure file size is under 10 MB (10240 KB)
  • Check that all required fields are present

Issue: "Unauthenticated"

Symptoms: 401 error

Solutions:

  • Verify your API token is valid and not expired
  • Check that the token has employee permissions (not customer token)
  • Ensure Authorization header format is: Bearer YOUR_TOKEN (with space after Bearer)
  • Test the token with a simple API call first

Issue: File Not Found or Binary Data Missing

Symptoms: Error about missing binary data

Solutions:

  • If previous node outputs JSON with file path, add Read Binary File node
  • Verify the binary data exists: check {{ $binary }} in previous node output
  • Ensure file path is absolute or relative to n8n's working directory
  • For Code nodes, make sure you're returning binary data correctly

Issue: Wrong Content-Type

Symptoms: API doesn't recognize the file

Solutions:

  • Don't manually set Content-Type header - let n8n handle it
  • Use Form-Data or Multipart-Form-Data body type
  • Ensure file field is set as File type, not String

n8n Expression Examples

Dynamic File Name with Timestamp

{{ 'dac7-report-' + $now.toFormat('yyyy-MM-dd') + '.xml' }}

Conditional Storage Destination

{{ $json.reportType === 'dac7' ? 'reporting' : 'documents' }}

Extract Document ID from Response

{{ $json.data.document_id }}

File Size Check Before Upload

{{ $binary.data.data.length < 10485760 ? 'OK' : 'TOO_LARGE' }}

Generate Filename from Multiple Sources

{{ ($json.prefix || 'document') + '-' + ($json.id || $now.toFormat('yyyyMMdd')) + '.' + ($json.extension || 'xml') }}

Best Practices

  1. Error Handling: Always add error handling nodes after the HTTP Request

    • Use IF node to check {{ $json.success }}
    • Log errors for debugging
    • Send notifications on failure
  2. Logging: Log the document_id for future reference

    • Store in a database
    • Include in notifications
    • Save to workflow execution logs
  3. File Size Validation: Check file size before uploading

    • Max size: 10 MB (10240 KB)
    • Add validation in Code node if needed
  4. Naming Convention: Use descriptive, timestamped filenames

    • Include date/time: report-2025-01-15-143022.xml
    • Include source: dac7-2025-Q1.xml
    • Avoid special characters
  5. Metadata: Include category and description for better organization

    • Helps with document retrieval
    • Improves audit trail
    • Aids in compliance
  6. Testing: Test with sandbox environment first

    • Use sandbox endpoint for development
    • Verify file uploads correctly
    • Check document appears in system
  7. Security: Use n8n credentials for API tokens

    • Don't hardcode tokens in workflows
    • Use credential management
    • Rotate tokens regularly
  8. Retry Logic: Consider adding retry for transient failures

    • Use n8n's built-in retry on error
    • Or implement custom retry logic in Code node

Complete Example Workflow

Scenario: Generate DAC7 XML and Upload

Workflow Structure:

  1. Code Node - Generate XML content
  2. Code Node - Convert to binary
  3. HTTP Request - Upload to Valora
  4. IF Node - Check success
  5. Code Node - Log result (success path)
  6. Code Node - Handle error (error path)

Node 1: Generate XML

const xmlContent = `<?xml version="1.0"?>
<report>
  <docRefId>ES2025-001</docRefId>
  <generatedAt>${$now.toISO()}</generatedAt>
  <!-- Your XML content -->
</report>`;

return [{
  json: {
    xmlContent,
    fileName: `dac7-${$now.toFormat('yyyy-MM-dd')}.xml`
  }
}];

Node 2: Convert to Binary

return [{
  json: $input.item.json,
  binary: {
    data: {
      data: Buffer.from($input.item.json.xmlContent, 'utf8'),
      mimeType: 'application/xml',
      fileName: $input.item.json.fileName
    }
  }
}];

Node 3: HTTP Request (configure as shown in UI steps)

Node 4: IF Node

  • Condition: {{ $json.success === true }}

Node 5: Log Success

return [{
  json: {
    message: 'Upload successful',
    documentId: $input.item.json.data.document_id,
    timestamp: $now.toISO()
  }
}];

Testing Checklist

Before deploying your workflow to production:

  • [ ] Test with a small file (< 1 MB)
  • [ ] Test with maximum size file (10 MB)
  • [ ] Test with different file types (XML, PDF, CSV)
  • [ ] Test all storage destinations (documents, invoicing, reporting, reports)
  • [ ] Test error scenarios (invalid token, missing fields, oversized file)
  • [ ] Verify document appears in Valora system
  • [ ] Check document metadata is correct
  • [ ] Test in sandbox environment first
  • [ ] Verify error handling works correctly
  • [ ] Test with actual production data (if safe)

© Valora n8n Integration Guide