Why the error occurs:
The error “This doesn’t appear to reference a file in your HubSpot account”
happens because the Forms API only accepts plain text strings for standard
custom fields [1.1.2]. If you attempt to send a raw binary file stream, a local
file path, or an unverified temporary file ID directly in your form submission
payload, HubSpot’s automated email personalization tokens will fail to resolve
the link [1.1.2].
- Direct Answers to Your Clarifications
Q1: Is a backend service mandatory to upload files via the Files API?
Yes. The HubSpot Files API requires your Private App Access Token (Bearer Token)
for authentication. You must never expose this private token in frontend
client-side JavaScript (like BigCommerce stencil files), as doing so would allow
any visitor to inspect your code, steal your token, and access/delete your
entire portal’s data. A secure backend service (such as a serverless function,
Node.js, or PHP backend) is mandatory to securely proxy the file upload [1.1.3].
Q2: Is there an alternative way to support custom form attachments without a backend?
If you absolutely cannot run a backend proxy, the only secure alternative is to
embed a native HubSpot Form on your BigCommerce page rather than using a fully
custom HTML form [1.1.2]. A native HubSpot form has a built-in File Upload field
that securely handles the frontend upload directly to HubSpot’s secure storage
automatically [1.1.2].
Q3: How do we ensure the attachment link is correctly included in emails?
Once your backend uploads the file to the Files API, the API returns a JSON
response containing the public URL of the file [1.1.2]. You must pass this
returned URL string into your custom contact/ticket property (e.g.,
attach_a_file) in your Forms API payload [1.1.2]. In your HubSpot Email or
Workflow notification, you simply insert this custom property as a
Personalization Token, which dynamically renders as a clickable download link
for both the owner and the user [1.1.2].
- Step-by-Step Technical Implementation Roadmap
Step 1: Upload the file to the HubSpot Files API (Server-Side)
Send a multipart/form-data POST request to the Files API v3 endpoint [1.1.2]:
- Endpoint: POST https://api.hubapi.com/files/v3/files [1.1.2]
- Authorization: Bearer YOUR_PRIVATE_APP_ACCESS_TOKEN [1.1.3]
- Form Parameters:
- file: (The binary file from your BigCommerce form)
- folderPath: (e.g., /form_attachments)
- options: { “access”: “PUBLIC_NOT_INDEXABLE” } (This ensures the client
can download it via the email, but search engine bots won’t index their
private files on Google) [1.1.2].
Step 2: Extract the File URL
The Files API will return a JSON response containing the file metadata. Extract
the url property:
{
“id”: “12345678”,
“url”: “https://yourportal.hs-sites.com/hubfs/form_attachments/user_file.pdf”,
“name”: “user_file.pdf”
}
Step 3: Submit the Form Payload (Forms API v3)
Submit your contact fields to the Submit Form Submission endpoint, passing the
extracted file URL into your custom text property [1.1.2]:
{
“fields”: [
{
“name”: “email”,
“value”: “user@example.com”
},
{
“name”: “firstname”,
“value”: “John”
},
{
“name”: “attach_a_file”,
“value”: “https://yourportal.hs-sites.com/hubfs/form_attachments/user_file.pdf”
}
]
}
- Sample Node.js Backend Implementation (Express)
Here is a clean, modern Node.js controller example demonstrating how to combine
both API requests in a single, secure backend workflow [1.3.1]:
const express = require(‘express’);
const multer = require(‘multer’);
const axios = require(‘axios’);
const FormData = require(‘form-data’);
const app = express();
const upload = multer(); // Handle file uploads in-memory
const HUBSPOT_ACCESS_TOKEN = process.env.HUBSPOT_ACCESS_TOKEN;
const PORTAL_ID = ‘YOUR_PORTAL_ID’;
const FORM_ID = ‘YOUR_FORM_ID’;
app.post(‘/api/submit-form’, upload.single(‘user_file’), async (req, res) => {
try {
let fileUrl = ‘’;
// 1. If a file is uploaded, send it to HubSpot Files API first
if (req.file) {
const fileForm = new FormData();
fileForm.append('file', req.file.buffer, req.file.originalname);
fileForm.append('folderPath', '/form-attachments');
const fileOptions = {
access: 'PUBLIC_NOT_INDEXABLE' // Safe for emails, hidden from Google
};
fileForm.append('options', JSON.stringify(fileOptions));
const fileResponse = await axios.post('https://api.hubapi.com/files/v3/files', fileForm, {
headers: {
...fileForm.getHeaders(),
'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`
}
});
fileUrl = fileResponse.data.url; // Extract the public file URL
}
// 2. Submit the contact data and file URL to the Forms API
const formData = {
fields: [
{ name: 'email', value: req.body.email },
{ name: 'firstname', value: req.body.firstname },
{ name: 'attach_a_file', value: fileUrl } // Pass the file URL as text
]
};
await axios.post(`https://api.hubapi.com/submissions/v3/integration/submit/${PORTAL_ID}/${FORM_ID}`, formData, {
headers: { 'Content-Type': 'application/json' }
});
return res.status(200).json({ success: true, message: 'Form submitted successfully!' });
} catch (error) {
console.error(‘Integration Error:’, error.response ? error.response.data : error.message);
return res.status(500).json({ success: false, error: ‘Internal Server Error’ });
}
});
Official HubSpot Resources:
- Review file security, folder configurations, and response schemas in the
HubSpot Files API v3 Guide- Schemas API guide - HubSpot docs
- Understand submission parameters and GDPR consent tracking in the HubSpot
Submit Form Submission API Reference- Form submission v3