File Uploads
Learn how to securely upload files to sajn for use in document fields.
Overview
sajn provides a simple direct upload endpoint for files:
- Upload the file - Send the file directly to the upload API
- Use the storage key - Reference the uploaded file in document fields
Important: File uploads use a different base URL: https://upload.sajn.seThis is separate from the main API at https://app.sajn.se.
Benefits
- Simple: Single-step upload process
- Secure: Files are organized by your organization ID
- Fast: Direct upload to cloud storage
- Reliable: Leverages robust cloud infrastructure
Uploading Files
Send a PUT request to the Upload File endpoint:
Request
curl -X PUT https://upload.sajn.se/api/v1/putFile \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@contract.pdf"
Response
{
"key": "f/org_123/abc123def/contract.pdf",
"file": {
"id": "file_abc123",
"filename": "contract.pdf",
"mimeType": "application/pdf",
"size": 102400,
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
}
Response Fields
key - Storage key to reference in document fields
file.id - Unique file identifier
file.filename - Original filename
file.mimeType - MIME type of the file
file.size - File size in bytes
file.hash - SHA-256 hash for integrity verification
File Visibility
Control who can access your uploaded files:
Private Files (Default)
curl -X PUT https://upload.sajn.se/api/v1/putFile \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@document.pdf" \
-F "visibility=PRIVATE"
Private files require signed URLs for access (valid for 60 minutes).
Public Files
curl -X PUT https://upload.sajn.se/api/v1/putFile \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@image.png" \
-F "visibility=PUBLIC"
Public files are accessible via direct CloudFront URL.
Using Uploaded Files in Documents
After successful upload, use the storage key in document field metadata. You can either create a new PDF field or update an existing one:
PDF fields use value for the file storage key, while TEXT and HTML fields use content for their data. See the API Reference for full details on each field type.
Create New PDF Field
Use the Create Document Field endpoint to add the uploaded PDF:
curl -X POST https://app.sajn.se/api/v1/documents/doc_123/fields \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "PDF",
"position": 0,
"fieldMeta": {
"type": "PDF",
"value": "f/org_123/abc123def/contract.pdf"
}
}'
Update Existing PDF Field
Use the Update Document Field endpoint to replace an existing PDF:
curl -X PATCH https://app.sajn.se/api/v1/documents/doc_123/fields/field_456 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fieldMeta": {
"type": "PDF",
"value": "f/org_123/abc123def/contract.pdf"
}
}'
Complete Upload Workflow
Here’s a complete example of uploading a file and adding it to a document:
cURL Example
# Step 1: Upload the file
UPLOAD_RESPONSE=$(curl -s -X PUT https://upload.sajn.se/api/v1/putFile \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@contract.pdf")
# Extract the storage key from the response
STORAGE_KEY=$(echo "$UPLOAD_RESPONSE" | jq -r '.key')
echo "Uploaded file with key: $STORAGE_KEY"
# Step 2: Create a document
DOC_RESPONSE=$(curl -s -X POST https://app.sajn.se/api/v1/documents \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Employment Contract"
}')
DOC_ID=$(echo "$DOC_RESPONSE" | jq -r '.id')
echo "Created document: $DOC_ID"
# Step 3: Add the uploaded file as a PDF field
curl -X POST "https://app.sajn.se/api/v1/documents/${DOC_ID}/fields" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"PDF\",
\"position\": 0,
\"fieldMeta\": {
\"type\": \"PDF\",
\"value\": \"${STORAGE_KEY}\"
}
}"
JavaScript Example
const UPLOAD_BASE_URL = 'https://upload.sajn.se';
const API_BASE_URL = 'https://app.sajn.se';
// Step 1: Upload file
const uploadFile = async (file, apiKey, visibility = 'PRIVATE') => {
const formData = new FormData();
formData.append('file', file);
formData.append('visibility', visibility);
const response = await fetch(`${UPLOAD_BASE_URL}/api/v1/putFile`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`
},
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
return await response.json();
};
// Step 2: Add to document
const addFileToDocument = async (documentId, storageKey, apiKey) => {
const response = await fetch(`${API_BASE_URL}/api/v1/documents/${documentId}/fields`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'PDF',
position: 0,
fieldMeta: {
type: 'PDF',
value: storageKey
}
})
});
return await response.json();
};
// Complete workflow
const uploadAndAddToDocument = async (documentId, file, apiKey) => {
try {
// Upload file
const { key, file: fileInfo } = await uploadFile(file, apiKey);
console.log('File uploaded:', fileInfo.filename, `(${fileInfo.size} bytes)`);
// Add to document
const field = await addFileToDocument(documentId, key, apiKey);
console.log('File added to document:', field);
return field;
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
};
File Requirements
- Documents: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX
- Images: JPG, PNG, GIF, WEBP, SVG
- Video: MP4, MOV, AVI, WEBM
Limits
- Maximum size: 100 MB per file
- Content-Type: Automatically detected from file
Security
- Files are automatically organized by organization ID
- Each upload gets a unique folder ID for isolation
- Only authenticated API users can upload files
- File hash provided for integrity verification
Path Structure
Uploaded files follow this path structure:
f/{organizationId}/{uniqueFolderId}/{filename}
Example:
f/org_abc123/def456ghi/employment-contract.pdf
f/ - Files prefix
organizationId - Your organization identifier
uniqueFolderId - Random identifier for isolation
filename - Slugified version of your filename
Error Handling
Common Errors
Invalid File Type
{
"message": "File type not allowed"
}
Solution: Ensure file type is one of the supported formats.
File Too Large
{
"message": "File size exceeds limit"
}
Solution: Ensure file is under 100MB limit.
Missing Authentication
{
"message": "API token was not provided"
}
Solution: Include valid Authorization: Bearer header.
Best Practices
- Check file size before uploading
- Validate file type on client-side first
- Handle upload errors gracefully with retries
- Store storage keys for later reference
- Use unique filenames to avoid confusion
Integration Examples
React/Next.js
import { useState } from 'react';
const UPLOAD_URL = 'https://upload.sajn.se/api/v1/putFile';
const API_URL = 'https://app.sajn.se/api/v1';
const FileUploader = ({ documentId, apiKey, onUploadComplete }) => {
const [uploading, setUploading] = useState(false);
const [error, setError] = useState(null);
const handleFileUpload = async (event) => {
const file = event.target.files[0];
if (!file) return;
setUploading(true);
setError(null);
try {
// Upload file
const formData = new FormData();
formData.append('file', file);
const uploadResponse = await fetch(UPLOAD_URL, {
method: 'PUT',
headers: { 'Authorization': `Bearer ${apiKey}` },
body: formData
});
if (!uploadResponse.ok) {
throw new Error('Upload failed');
}
const { key } = await uploadResponse.json();
// Add to document
const fieldResponse = await fetch(`${API_URL}/documents/${documentId}/fields`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'PDF',
position: 0,
fieldMeta: { type: 'PDF', value: key }
})
});
const field = await fieldResponse.json();
onUploadComplete(field);
} catch (err) {
setError(err.message);
} finally {
setUploading(false);
}
};
return (
<div>
<input
type="file"
onChange={handleFileUpload}
disabled={uploading}
/>
{uploading && <p>Uploading...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
};
Node.js
const fs = require('fs');
const path = require('path');
const FormData = require('form-data');
const UPLOAD_URL = 'https://upload.sajn.se/api/v1/putFile';
const API_URL = 'https://app.sajn.se/api/v1';
const uploadFileToDocument = async (documentId, filePath, apiKey) => {
const fileName = path.basename(filePath);
const fileStream = fs.createReadStream(filePath);
// Create form data
const formData = new FormData();
formData.append('file', fileStream, fileName);
// Upload file
const uploadResponse = await fetch(UPLOAD_URL, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiKey}`,
...formData.getHeaders()
},
body: formData
});
if (!uploadResponse.ok) {
throw new Error(`Upload failed: ${uploadResponse.statusText}`);
}
const { key, file } = await uploadResponse.json();
console.log(`Uploaded: ${file.filename} (${file.size} bytes)`);
// Add to document
const fieldResponse = await fetch(`${API_URL}/documents/${documentId}/fields`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'PDF',
position: 0,
fieldMeta: {
type: 'PDF',
value: key
}
})
});
return await fieldResponse.json();
};
// Usage
uploadFileToDocument('doc_123', './contract.pdf', 'your-api-key')
.then(field => console.log('Added to document:', field))
.catch(err => console.error('Error:', err));
Guides
API Reference