Skip to main content

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:
  1. Upload the file - Send the file directly to the upload API
  2. 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

Supported Formats

  • 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

  1. Check file size before uploading
  2. Validate file type on client-side first
  3. Handle upload errors gracefully with retries
  4. Store storage keys for later reference
  5. 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